Something went wrong. Try again.
presents omp as codex for use with chatgpt remote
Something went wrong. Try again.
34 kB · 900 lines
JavaScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901import crypto from "node:crypto";import fs from "node:fs/promises";import fsSync from "node:fs";import os from "node:os";import net from "node:net";import path from "node:path";import { CODEX_SOURCE_VERSION } from "./generated-compat.mjs";export const BRIDGE_VERSION = "0.145.0";export const PROTOCOL_COMPAT_VERSION = process.env.OMP_CODEX_COMPAT_VERSION || CODEX_SOURCE_VERSION || "0.0.0-omp";
export function id(prefix) { return `${prefix}_${crypto.randomUUID().replaceAll("-", "")}`;}
export function unixSeconds() { return Math.floor(Date.now() / 1000);}
export function nowIso() { return new Date().toISOString();}
export function deferred() { let resolve; let reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject };}
export function abortError(message = "Operation aborted") { const error = new Error(message); error.name = "AbortError"; error.code = "ABORT_ERR"; return error;}
export function throwIfAborted(signal) { if (signal?.aborted) throw signal.reason instanceof Error ? signal.reason : abortError();}
/** * Whether a Unix socket path currently has a listener accepting connections. * * A socket file on disk proves nothing: it outlives a crashed owner, and a * live owner's file looks identical. Only a successful connect distinguishes * the two, and getting that wrong means either orphaning a running daemon or * refusing to start over a dead one. */export function socketAccepting(socketPath, timeoutMs = 250) { return new Promise((resolve) => { let settled = false; const finish = (value) => { if (settled) return; settled = true; clearTimeout(timer); socket.destroy(); resolve(value); }; const socket = net.connect(socketPath); const timer = setTimeout(() => finish(false), timeoutMs); timer.unref?.(); socket.once("connect", () => finish(true)); socket.once("error", () => finish(false)); });}
export function sleep(ms, signal) { if (!ms) { throwIfAborted(signal); return Promise.resolve(); } return new Promise((resolve, reject) => { const timer = setTimeout(done, ms); function done() { cleanup(); resolve(); } function onAbort() { cleanup(); reject(signal.reason instanceof Error ? signal.reason : abortError()); } function cleanup() { clearTimeout(timer); signal?.removeEventListener("abort", onAbort); } signal?.addEventListener("abort", onAbort, { once: true }); if (signal?.aborted) onAbort(); });}
export function serializeError(error) { if (!error) return { message: "Unknown error" }; return { name: error.name || "Error", message: String(error.message || error), code: error.code, stack: process.env.OMP_CODEX_DEBUG ? error.stack : undefined, };}
export async function readJson(file, fallback) { try { const raw = await fs.readFile(file, "utf8"); if (!raw.trim()) return fallback; return JSON.parse(raw); } catch (error) { if (error?.code === "ENOENT" || error instanceof SyntaxError) return fallback; throw error; }}
export async function atomicWriteJson(file, value) { await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 }); const temp = `${file}.${process.pid}.${crypto.randomBytes(4).toString("hex")}.tmp`; try { await fs.writeFile(temp, `${JSON.stringify(value)}\n`, { mode: 0o600 }); await fs.rename(temp, file); } finally { try { await fs.unlink(temp); } catch {} }}
export function isInside(root, candidate) { const resolvedRoot = path.resolve(root); const resolvedCandidate = path.resolve(candidate); const rel = path.relative(resolvedRoot, resolvedCandidate); return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));}
export async function canonicalizeForPolicy(candidate) { const absolute = path.resolve(candidate); try { return await fs.realpath(absolute); } catch (error) { if (error?.code !== "ENOENT") throw error; let cursor = absolute; const suffix = []; while (true) { try { const existing = await fs.realpath(cursor); return path.join(existing, ...suffix.reverse()); } catch (inner) { if (inner?.code !== "ENOENT") throw inner; const parent = path.dirname(cursor); if (parent === cursor) return absolute; suffix.push(path.basename(cursor)); cursor = parent; } } }}
export function encodeCursor(value) { return Buffer.from(JSON.stringify(value), "utf8").toString("base64url");}
export function decodeCursor(cursor, fallback = null) { if (!cursor) return fallback; try { return JSON.parse(Buffer.from(cursor, "base64url").toString("utf8")); } catch { try { return parseInt(Buffer.from(cursor, "base64").toString("utf8"), 10) || fallback; } catch { return fallback; } }}
export function paginateArray(items = [], options = {}, mapOffset = false) { const { limit = null, cursor = null } = options; const decoded = decodeCursor(cursor, mapOffset ? { offset: 0 } : 0); const rawOffset = typeof decoded === "object" && decoded !== null ? decoded.offset : decoded; const offset = Number.isFinite(Number(rawOffset)) && Number(rawOffset) >= 0 ? Math.floor(Number(rawOffset)) : 0; const maxLimit = limit != null ? Math.max(1, Number(limit)) : null; const target = maxLimit ? items.slice(offset, offset + maxLimit) : items.slice(offset); const nextOffset = offset + target.length; const nextCursor = maxLimit && nextOffset < items.length ? (mapOffset ? encodeCursor({ offset: nextOffset }) : encodeCursor(nextOffset)) : null; return { items: target, nextCursor, total: items.length, offset };}
export function normalizeApprovalDecision(value) { const raw = typeof value === "string" ? value : value?.decision ?? value?.result ?? value?.choice; switch (String(raw || "").toLowerCase()) { case "accept": case "approved": case "approve": case "yes": return "accept"; case "acceptforsession": case "accept_for_session": case "approveforsession": case "approve_for_session": case "always": return "acceptForSession"; case "cancel": case "cancelled": case "canceled": return "cancel"; default: return "decline"; }}
export function defaultSocketPath() { return process.env.OMP_CODEX_SOCKET || path.join(process.env.OMP_CODEX_RUNTIME_DIR || path.join(os.homedir(), ".omp", "codex-bridge", "run"), "app-server.sock");}
export function parseListenUri(value) { if (!value || value === "stdio" || value === "stdio://") return { kind: "stdio" }; if (value === "unix" || value === "unix:" || value === "unix://" || value === "unix:///") { return { kind: "unix", path: defaultSocketPath() }; } if (value.startsWith("unix://")) { const raw = decodeURIComponent(value.slice("unix://".length)).trim(); return { kind: "unix", path: raw || defaultSocketPath() }; } if (value.startsWith("ws://")) { const url = new URL(value); return { kind: "tcp", host: url.hostname || "127.0.0.1", port: Number(url.port || 4500), path: url.pathname }; } if (value.startsWith("tcp://")) { const url = new URL(value.replace(/^tcp:/, "http:")); return { kind: "tcp", host: url.hostname || "127.0.0.1", port: Number(url.port || 4500), path: "/" }; } return { kind: "unix", path: value };}
export function parseArgv(argv) { const positional = []; const options = new Map(); const booleanFlags = new Set(["stdio", "daemon", "version", "help", "h", "v", "experimental-api"]);
for (let i = 0; i < argv.length; i += 1) { const arg = argv[i]; if (arg.startsWith("--")) { const eq = arg.indexOf("="); if (eq > 2) { options.set(arg.slice(2, eq), arg.slice(eq + 1)); continue; } const name = arg.slice(2); if (booleanFlags.has(name)) { options.set(name, true); continue; } const next = argv[i + 1]; if (next !== undefined && !next.startsWith("-")) { options.set(name, next); i += 1; } else { options.set(name, true); } } else if (arg.startsWith("-") && arg.length > 1) { const name = arg.slice(1); if (name === "n") { const next = argv[i + 1]; if (next !== undefined && !next.startsWith("-")) { options.set("n", next); options.set("lines", next); i += 1; } } else if (booleanFlags.has(name)) { options.set(name, true); } else { const next = argv[i + 1]; if (next !== undefined && !next.startsWith("-")) { options.set(name, next); i += 1; } else { options.set(name, true); } } } else { positional.push(arg); } } return { positional, options };}
const READ_RANGE = String.raw`\d+(?:(?:-\d*)|\+\d+)?`;const READ_SELECTOR = new RegExp(`(?::raw(?::${READ_RANGE}(?:,${READ_RANGE})*)?|:${READ_RANGE}(?:,${READ_RANGE})*(?::raw)?)$`);
function parseLocalReadTarget(value) { if (typeof value !== "string" || !value || /^[a-z][a-z0-9+.-]*:\/\//i.test(value)) return null; const withoutRange = value.replace(READ_SELECTOR, ""); const queryIndex = withoutRange.indexOf("?"); const withoutQuery = queryIndex >= 0 ? withoutRange.slice(0, queryIndex) : withoutRange; const databaseSelector = /^(.*\.(?:sqlite3?|db3?)):(.+)$/i.exec(withoutQuery); const pathValue = databaseSelector ? databaseSelector[1] : withoutQuery; if (pathValue.includes(":")) return null; const selector = databaseSelector?.[2]?.split(":")[0] || null; return { path: pathValue, descriptor: selector || (queryIndex >= 0 ? "query" : null), };}
export function filesystemPathFromReadTarget(value) { return parseLocalReadTarget(value)?.path || null;}
function readTargetDisplayName(target, baseCwd) { const parsed = parseLocalReadTarget(target); if (!parsed) return String(target || ""); const resolvedPath = path.isAbsolute(parsed.path) ? path.resolve(parsed.path) : path.resolve(baseCwd, parsed.path); const name = displayPathName(baseCwd, resolvedPath); return parsed.descriptor ? `${name} · ${parsed.descriptor}` : name;}
function formatReadTargets(targets, baseCwd = process.cwd()) { return String(targets || "") .split(";") .map((target) => target.trim()) .filter(Boolean) .map((target) => { const parsed = parseLocalReadTarget(target); if (!parsed) return target; const shownPath = path.isAbsolute(parsed.path) ? parsed.path : parsed.path; return parsed.descriptor ? `${shownPath} · ${parsed.descriptor}` : shownPath; }) .join(";");}
function displayPathName(baseCwd, resolvedPath) { const relative = path.relative(baseCwd, resolvedPath); return relative && !relative.startsWith("..") && !path.isAbsolute(relative) ? relative : path.basename(resolvedPath);}
function globSearchRoot(target, baseCwd) { const value = String(target || "").trim(); if (!value || value === ".") return baseCwd; const wildcardIndex = value.search(/[*?[\]{}]/); let root = wildcardIndex < 0 ? value : value.slice(0, wildcardIndex); if (wildcardIndex >= 0) { root = root.endsWith("/") || root.endsWith("\\") ? root.slice(0, -1) : path.dirname(root); } if (!root || root === ".") return baseCwd; return path.isAbsolute(root) ? path.resolve(root) : path.resolve(baseCwd, root);}
export function commandActionsForOmpTool(toolName, args = {}, command = "", cwd = process.cwd()) { const normalized = String(toolName || "").toLowerCase(); const baseCwd = path.resolve(cwd || process.cwd()); const rawTargets = String(args.path || args.filePath || args.file || "") .split(";") .map((target) => target.trim()) .filter(Boolean);
if (normalized === "read") { const actions = rawTargets.flatMap((target) => { const parsed = parseLocalReadTarget(target); if (!parsed) return []; const resolvedPath = path.isAbsolute(parsed.path) ? path.resolve(parsed.path) : path.resolve(baseCwd, parsed.path); try { if (fsSync.statSync(resolvedPath).isDirectory()) return [{ type: "listFiles", command, path: resolvedPath }]; } catch {} return [{ type: "read", command, name: readTargetDisplayName(target, baseCwd), path: resolvedPath, }]; }); if (actions.length) return actions; } else if (normalized === "glob") { const targets = rawTargets.length ? rawTargets : ["*"]; return targets.map((target) => ({ type: "listFiles", command, path: globSearchRoot(target, baseCwd) })); } else if (normalized === "grep") { const targets = rawTargets.length ? rawTargets : ["."]; return targets.map((target) => ({ type: "search", command, query: String(args.pattern || args.query || ""), path: globSearchRoot(target, baseCwd), })); } return [{ type: "unknown", command }];}
function diffTextLines(beforeLines, afterLines) { const beforeLength = beforeLines.length; const afterLength = afterLines.length; const maxDistance = beforeLength + afterLength; const frontier = new Map([[1, 0]]); const trace = []; for (let distance = 0; distance <= maxDistance; distance += 1) { trace.push(new Map(frontier)); for (let diagonal = -distance; diagonal <= distance; diagonal += 2) { const moveDown = diagonal === -distance || (diagonal !== distance && (frontier.get(diagonal - 1) ?? -1) < (frontier.get(diagonal + 1) ?? -1)); let beforeIndex = moveDown ? (frontier.get(diagonal + 1) ?? 0) : (frontier.get(diagonal - 1) ?? 0) + 1; let afterIndex = beforeIndex - diagonal; while ( beforeIndex < beforeLength && afterIndex < afterLength && beforeLines[beforeIndex] === afterLines[afterIndex] ) { beforeIndex += 1; afterIndex += 1; } frontier.set(diagonal, beforeIndex); if (beforeIndex >= beforeLength && afterIndex >= afterLength) { const edits = []; let x = beforeLength; let y = afterLength; for (let backtrackDistance = distance; backtrackDistance >= 0; backtrackDistance -= 1) { const previous = trace[backtrackDistance]; const currentDiagonal = x - y; const movedDown = currentDiagonal === -backtrackDistance || ( currentDiagonal !== backtrackDistance && (previous.get(currentDiagonal - 1) ?? -1) < (previous.get(currentDiagonal + 1) ?? -1) ); const previousDiagonal = movedDown ? currentDiagonal + 1 : currentDiagonal - 1; const previousX = previous.get(previousDiagonal) ?? 0; const previousY = previousX - previousDiagonal; while (x > previousX && y > previousY) { edits.push({ type: " ", line: beforeLines[x - 1] }); x -= 1; y -= 1; } if (backtrackDistance === 0) break; if (movedDown) { edits.push({ type: "+", line: afterLines[y - 1] }); y -= 1; } else { edits.push({ type: "-", line: beforeLines[x - 1] }); x -= 1; } } return edits.reverse(); } } } return [];}
export function unifiedDiffFromText(file, before, after, contextLines = 3) { const oldLines = before == null || before === "" ? [] : String(before).replace(/\n$/, "").split("\n"); const newLines = after == null || after === "" ? [] : String(after).replace(/\n$/, "").split("\n"); const oldPath = before == null ? "/dev/null" : `a/${file}`; const newPath = after == null ? "/dev/null" : `b/${file}`; const header = [ `diff --git a/${file} b/${file}`, ...(before == null ? ["new file mode 100644"] : []), ...(after == null ? ["deleted file mode 100644"] : []), `--- ${oldPath}`, `+++ ${newPath}`, ]; const edits = diffTextLines(oldLines, newLines); const changed = edits.flatMap((edit, index) => edit.type === " " ? [] : [index]); if (!changed.length) return `${header.join("\n")}\n`; const ranges = []; for (const index of changed) { const start = Math.max(0, index - contextLines); const end = Math.min(edits.length, index + contextLines + 1); const previous = ranges.at(-1); if (previous && start <= previous.end) previous.end = Math.max(previous.end, end); else ranges.push({ start, end }); } const body = []; for (const range of ranges) { let oldStart = 1; let newStart = 1; for (let index = 0; index < range.start; index += 1) { if (edits[index].type !== "+") oldStart += 1; if (edits[index].type !== "-") newStart += 1; } const slice = edits.slice(range.start, range.end); const oldCount = slice.filter((edit) => edit.type !== "+").length; const newCount = slice.filter((edit) => edit.type !== "-").length; body.push(`@@ -${oldCount ? oldStart : 0},${oldCount} +${newCount ? newStart : 0},${newCount} @@`); body.push(...slice.map((edit) => `${edit.type}${edit.line}`)); } return `${header.concat(body).join("\n")}\n`;}
export function unifiedDiffFromOmpEdit(file, diff, kind = "update") { const raw = String(diff || ""); if (!raw) return ""; if (/^diff --git /m.test(raw) && /^@@ /m.test(raw)) return raw.endsWith("\n") ? raw : `${raw}\n`;
const groups = []; let current = []; const flush = () => { if (current.some((line) => line.type !== " ")) groups.push(current); current = []; }; for (const rawLine of raw.split("\n")) { const match = /^([ +\-])(\d+)\|(.*)$/.exec(rawLine); if (!match) { flush(); continue; } current.push({ type: match[1], line: Number(match[2]), text: match[3] }); } flush(); if (!groups.length) return "";
const normalizedKind = /^(?:add|create)$/i.test(String(kind)) ? "add" : /^(?:delete|remove)$/i.test(String(kind)) ? "delete" : "update"; const body = []; let delta = 0; for (const group of groups) { const oldLines = group.filter((line) => line.type !== "+"); const newLines = group.filter((line) => line.type !== "-"); const firstOld = oldLines[0]?.line; const firstNew = newLines.find((line) => line.type === "+")?.line; const oldStart = normalizedKind === "add" ? 0 : (firstOld ?? Math.max(0, Number(firstNew || 1) - 1)); const newStart = normalizedKind === "delete" ? 0 : (group[0]?.type === "+" && firstNew != null ? firstNew : Math.max(0, oldStart + delta)); body.push(`@@ -${oldStart},${oldLines.length} +${newStart},${newLines.length} @@`); body.push(...group.map((line) => `${line.type}${line.text}`)); delta += newLines.length - oldLines.length; }
const oldPath = normalizedKind === "add" ? "/dev/null" : `a/${file}`; const newPath = normalizedKind === "delete" ? "/dev/null" : `b/${file}`; return [ `diff --git a/${file} b/${file}`, ...(normalizedKind === "add" ? ["new file mode 100644"] : []), ...(normalizedKind === "delete" ? ["deleted file mode 100644"] : []), `--- ${oldPath}`, `+++ ${newPath}`, ...body, "", ].join("\n");}
export function fileChangesFromOmpEditDetails(details = {}, fallbackChanges = []) { const fallback = Array.isArray(fallbackChanges) ? fallbackChanges : []; let rows = Array.isArray(details?.perFileResults) ? details.perFileResults : []; if (!rows.length && details?.diff) { const fallbackPath = fallback.length === 1 ? (fallback[0]?.path || fallback[0]?.file) : null; rows = [{ ...details, path: details.path || fallbackPath }]; } return rows.flatMap((row) => { const file = String(row?.path || row?.file || ""); if (!file || !row?.diff) return []; const op = /^(?:add|create)$/i.test(String(row.op || row.kind)) ? "add" : /^(?:delete|remove)$/i.test(String(row.op || row.kind)) ? "delete" : "update"; const unified = unifiedDiffFromOmpEdit(file, row.diff, op); if (!unified) return []; return [{ path: file, kind: op === "update" ? { type: op, movePath: null } : { type: op }, diff: unified }]; });}
export function compactWholeFileUnifiedDiff(file, diff) { const lines = String(diff || "").split("\n"); const hunk = lines.findIndex((line) => line.startsWith("@@ ")); if (hunk < 0) return diff; const body = lines.slice(hunk + 1).filter((line) => line.length > 0); if (body.some((line) => line.startsWith(" "))) return diff; if (body.some((line) => !line.startsWith("-") && !line.startsWith("+") && line !== "\\ No newline at end of file")) { return diff; } const before = body.filter((line) => line.startsWith("-")).map((line) => line.slice(1)).join("\n"); const after = body.filter((line) => line.startsWith("+")).map((line) => line.slice(1)).join("\n"); return unifiedDiffFromText(file, before ? `${before}\n` : "", after ? `${after}\n` : "");}
/** * Selected answers for an `ask` tool call, keyed by question id, from the * structured tool-result details OMP emits (single question: `selectedOptions` * / `customInput`; multi: `results[]`). Falls back to the human-readable * result text so older transcripts still resolve. */export function askAnswersFromResult(questions = [], details = null, text = "") { const list = Array.isArray(questions) ? questions : []; const answers = {}; const assign = (questionId, values) => { const cleaned = (Array.isArray(values) ? values : [values]) .filter((value) => value != null && String(value).length > 0) .map((value) => String(value).replace(/\s+\(Recommended\)$/i, "")); if (cleaned.length) answers[questionId] = cleaned; }; const rows = Array.isArray(details?.results) ? details.results : (details && (details.selectedOptions || details.customInput) ? [details] : []); rows.forEach((row, index) => { const questionId = String(row?.id || list[index]?.id || ""); if (!questionId) return; assign(questionId, row.customInput ? [row.customInput] : row.selectedOptions); }); if (Object.keys(answers).length) return answers;
const raw = String(text || ""); const single = raw.match(/^User (?:selected|provided custom input): (.+)$/m); if (single && list[0]?.id) { assign(String(list[0].id), single[1].split(",").map((value) => value.trim())); return answers; } for (const line of raw.split("\n")) { const match = line.match(/^([^:]+): "(.*)"$/); if (!match) continue; const questionId = match[1].trim(); if (!list.some((question) => String(question?.id) === questionId)) continue; assign(questionId, match[2].split(",").map((value) => value.trim())); } return answers;}
const NON_FILE_URI = /^[a-z][a-z0-9+.-]*:\/\//i;
// route dirs like [handle] put brackets inside the path, so match up to the// last #TAG] on the line, never the first ]export function hashlinePatchPaths(input) { return Array.from( String(input ?? "").matchAll(/^\[(.+)#[0-9A-Fa-f]{4}\]\s*$/gm), (match) => match[1], );}
export function isNonFilePath(value) { return NON_FILE_URI.test(String(value ?? ""));}
export function devicePayloadLabel(content) { const trimmed = typeof content === "string" ? content.trim() : ""; if (!trimmed.startsWith("{")) return ""; let payload = null; try { payload = JSON.parse(trimmed); } catch { return ""; } const parts = [payload?.action, payload?.operation, payload?.name] .filter((part) => typeof part === "string" && part.length > 0 && part.length <= 40); return Array.from(new Set(parts)).join(" ");}
export function formatToolCommand(toolName, args = {}, intent = null) { const name = String(toolName || "tool").toLowerCase(); const i = intent || args?.i;
if (name === "bash" || name === "sh" || name === "shell") { return args?.command || args?.cmd || (i ? `bash: ${i}` : "bash"); } if (name === "read") { const p = args?.path || args?.filePath || args?.file || ""; return p ? `read ${formatReadTargets(p)}` : (i ? `read: ${i}` : "read"); } if (name === "edit") { let p = args?.path || args?.filePath || args?.file || ""; if (!p) { const paths = hashlinePatchPaths(args?.input ?? args?.patch); if (paths.length) p = paths.length > 1 ? `${paths[0]} (+${paths.length - 1} more)` : paths[0]; } return p ? `edit ${p}` : (i ? `edit: ${i}` : "edit"); } if (name === "write") { const p = args?.path || args?.filePath || args?.file || ""; if (!p) return i ? `write: ${i}` : "write"; const label = isNonFilePath(p) ? devicePayloadLabel(args?.content) : ""; if (label) return `write ${p} ${label}`; return i ? `write ${p}: ${i}` : `write ${p}`; } if (name === "grep") { const pat = args?.pattern || ""; const p = args?.path ? ` in ${args.path}` : ""; return pat ? `grep /${pat}/${p}` : (i ? `grep: ${i}` : "grep"); } if (name === "glob") { const p = args?.path || "*"; return `glob ${p}`; } if (name === "eval") { const lang = args?.language || "js"; if (args?.title) return `eval (${lang}): ${args.title}`; if (i) return `eval (${lang}): ${i}`; if (args?.code) { const codeSnippet = String(args.code).trim().replace(/\s+/g, " ").slice(0, 60); return `eval (${lang}): ${codeSnippet}${args.code.length > 60 ? "…" : ""}`; } return `eval (${lang})`; } if (name === "task") { if (Array.isArray(args?.tasks)) { const names = args.tasks.map((t) => t.name || t.agent || "worker").join(", "); return `task (${names})`; } return i ? `task: ${i}` : "task"; } if (name === "ask") { const questions = Array.isArray(args?.questions) ? args.questions : []; const first = String(questions[0]?.question || questions[0]?.header || "").replace(/\s+/g, " ").trim(); if (first) { const text = first.length > 80 ? `${first.slice(0, 79)}…` : first; return questions.length > 1 ? `ask ${questions.length} questions: ${text}` : `ask ${text}`; } return i ? `ask: ${i}` : "ask"; }
if (i) return `${name}: ${i}`; if (args?.command || args?.cmd) return String(args.command || args.cmd); if (args?.path || args?.filePath) return `${name} ${args.path || args.filePath}`; return name;}export function extractReasoningSummary(text) { if (!text || typeof text !== "string") return null; const match = text.trim().match(/^(?:\*\*([^*]+)\*\*|#{1,3}\s+([^\n]+))/); return match ? (match[1] || match[2]).trim() : null;}
export function extractReasoningSummaryAndContent(thinkingText) { if (!thinkingText || typeof thinkingText !== "string") return { summary: [], content: [] }; const trimmed = thinkingText.trim(); if (!trimmed) return { summary: [], content: [] }; return { // omp thinking is already a user-facing summary, not raw chain-of-thought summary: [trimmed], content: [], };}
export function extractToolOutputText(output) { if (output === null || output === undefined) return ""; if (typeof output === "string") return output; if (Array.isArray(output)) { return output.map((part) => (typeof part === "string" ? part : extractToolOutputText(part))).join("\n"); } if (Array.isArray(output?.content)) { return output.content.map((part) => (typeof part === "string" ? part : part?.text || extractToolOutputText(part))).join("\n"); } if (typeof output?.text === "string") return output.text; if (typeof output?.output === "string") return output.output; if (typeof output?.stdout === "string" || typeof output?.stderr === "string") { return `${output?.stdout || ""}${output?.stderr || ""}`; } if (typeof output?.rawOutput === "string") return output.rawOutput; if (typeof output?.result === "string") return output.result; if (typeof output?.message === "string") return output.message; if (typeof output?.details?.output === "string") return output.details.output; return "";}export function extractInputText(input) { if (typeof input === "string") return input; if (!Array.isArray(input)) return input?.text || input?.prompt || ""; const chunks = []; for (const item of input) { if (typeof item === "string") chunks.push(item); else if (typeof item?.text === "string") chunks.push(item.text); else if (typeof item?.content === "string") chunks.push(item.content); else if (item?.type === "mention" && item?.name) chunks.push(`@${item.name}`); } return chunks.join("\n").trim();}
export async function extractInputPromptAndImages(input, cwd = process.cwd()) { if (typeof input === "string") return { prompt: input, images: [] }; if (!Array.isArray(input)) { const prompt = input?.text || input?.prompt || ""; return { prompt, images: [] }; }
const textChunks = []; const images = [];
for (const item of input) { if (typeof item === "string") { textChunks.push(item); } else if (typeof item?.text === "string") { textChunks.push(item.text); } else if (typeof item?.content === "string") { textChunks.push(item.content); } else if (item?.type === "mention" && item?.name) { textChunks.push(`@${item.name}`); } else if (item?.type === "localImage" || item?.type === "image") { const imgPath = item.path || item.filePath || (item.url?.startsWith("file://") ? item.url.replace("file://", "") : null); if (imgPath) { try { const resolvedPath = path.isAbsolute(imgPath) ? imgPath : path.resolve(cwd, imgPath); const buffer = await fs.readFile(resolvedPath); let mimeType = "image/jpeg"; const lower = resolvedPath.toLowerCase(); if (lower.endsWith(".png")) mimeType = "image/png"; else if (lower.endsWith(".webp")) mimeType = "image/webp"; else if (lower.endsWith(".gif")) mimeType = "image/gif"; else if (lower.endsWith(".svg")) mimeType = "image/svg+xml";
images.push({ type: "image", data: buffer.toString("base64"), mimeType, }); } catch (err) { log(`failed to read attached image at ${imgPath}:`, err.message); } } else if (item?.url?.startsWith("data:") || item?.data) { const dataUrl = item.url || item.data; if (dataUrl.startsWith("data:")) { const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/); if (match) { images.push({ type: "image", mimeType: match[1], data: match[2], }); } } else { images.push({ type: "image", mimeType: item.mimeType || "image/jpeg", data: item.data, }); } } } }
let prompt = textChunks.join("\n").trim(); if (!prompt && images.length > 0) { prompt = "Please inspect the attached image."; }
return { prompt, images };}export function safePreview(text, max = 160) { const oneLine = String(text || "").replace(/\s+/g, " ").trim(); return oneLine.length <= max ? oneLine : `${oneLine.slice(0, max - 1)}…`;}
export function appendBridgeLog(line) { if (process.env.OMP_CODEX_NO_LOG === "1") return; try { const file = process.env.OMP_CODEX_LOG_FILE || path.join(os.homedir(), ".omp", "codex-bridge", "bridge.log"); fsSync.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 }); fsSync.appendFileSync(file, `${line}\n`, { mode: 0o600 }); } catch {}}
function protocolTracePaths() { const root = path.join(os.homedir(), ".omp", "codex-bridge"); return { enabled: process.env.OMP_CODEX_PROTOCOL_TRACE_FLAG || path.join(root, "protocol-trace.enabled"), log: process.env.OMP_CODEX_PROTOCOL_TRACE_FILE || path.join(root, "protocol-trace.jsonl"), };}
export function protocolTraceEnabled() { if (process.env.OMP_CODEX_PROTOCOL_TRACE === "0") return false; if (process.env.OMP_CODEX_PROTOCOL_TRACE === "1") return true; return fsSync.existsSync(protocolTracePaths().enabled);}
export function protocolShape(value, depth = 0) { if (value === null) return "null"; if (value === undefined) return "undefined"; if (Buffer.isBuffer(value)) return { type: "buffer", bytes: value.length }; if (Array.isArray(value)) { return { type: "array", length: value.length, item: value.length && depth < 8 ? protocolShape(value[0], depth + 1) : null, }; } if (typeof value !== "object") return typeof value; if (depth >= 8) return { type: "object", keys: Object.keys(value).sort() }; return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, protocolShape(child, depth + 1)]));}
export function protocolTrace(event, details = {}) { if (!protocolTraceEnabled()) return; try { const { log: file } = protocolTracePaths(); fsSync.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 }); const record = JSON.stringify({ timestamp: nowIso(), pid: process.pid, event, ...details }); fsSync.appendFileSync(file, `${record}\n`, { mode: 0o600 }); } catch {}}
export function log(...args) { const formatted = args.map((a) => (typeof a === "object" && a !== null ? JSON.stringify(a) : String(a))).join(" "); appendBridgeLog(`[${nowIso()}] [pid:${process.pid}] ${formatted}`); if (process.env.OMP_CODEX_LOG_STDERR === "1" || process.env.OMP_CODEX_DEBUG === "1") { console.error(`[omp-codex ${nowIso()}]`, ...args); }}
export function debug(...args) { if (!process.env.OMP_CODEX_DEBUG) return; const formatted = args.map((a) => (typeof a === "object" && a !== null ? JSON.stringify(a) : String(a))).join(" "); appendBridgeLog(`[${nowIso()}:debug] [pid:${process.pid}] ${formatted}`); console.error(`[omp-codex:debug ${nowIso()}]`, ...args);}