Something went wrong. Try again.
presents omp as codex for use with chatgpt remote
Something went wrong. Try again.
16 kB · 401 lines
JavaScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402import { execFile } from "node:child_process";import fs from "node:fs/promises";import os from "node:os";import path from "node:path";import { promisify } from "node:util";import { log } from "./util.mjs";
const execFileAsync = promisify(execFile);
function defaultWorkspacesDir() { return process.env.OMP_CODEX_WORKSPACES_DIR || path.join(os.homedir(), ".omp", "codex-bridge", "workspaces");}
function sanitizeWorkspaceName(threadId) { return `ws_${String(threadId).replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 48)}`;}
export async function detectVcs(cwd) { const targetCwd = path.resolve(cwd || process.cwd());
// 1. Try Jujutsu (jj) try { const { stdout } = await execFileAsync("jj", ["root", "-R", targetCwd], { timeout: 3000 }); const root = stdout.trim(); if (root) { let changeId = null; let branch = null; let origin_url = null; try { const { stdout: logOut } = await execFileAsync( "jj", ["log", "-R", root, "-r", "::@ & (bookmarks() | @)", "--no-graph", "-T", 'change_id ++ "\\t" ++ bookmarks ++ "\\t" ++ commit_id ++ "\\n"'], { timeout: 2000 } ); const lines = logOut.trim().split("\n").filter(Boolean); for (const line of lines.reverse()) { const [cId, bNames, commitId] = line.split("\t"); if (!changeId) changeId = commitId || cId || null; if (bNames && bNames.trim()) { const firstBm = bNames.trim().split(/\s+/)[0].replace(/\*$/, ""); if (firstBm) { branch = firstBm; break; } } } } catch {} try { const { stdout: remoteOut } = await execFileAsync("jj", ["git", "remote", "list", "-R", root], { timeout: 2000 }); const firstLine = remoteOut.trim().split("\n")[0] || ""; const parts = firstLine.trim().split(/\s+/); if (parts.length >= 2) origin_url = parts[1]; } catch {} return { type: "jj", root, changeId, branch, origin_url }; } } catch {}
// 2. Try Git try { const { stdout } = await execFileAsync("git", ["-C", targetCwd, "rev-parse", "--show-toplevel"], { timeout: 3000 }); const root = stdout.trim(); if (root) { let branch = null; let commit = null; let origin_url = null; try { const { stdout: branchOut } = await execFileAsync("git", ["-C", targetCwd, "rev-parse", "--abbrev-ref", "HEAD"], { timeout: 2000 }); branch = branchOut.trim() || null; } catch {} try { const { stdout: commitOut } = await execFileAsync("git", ["-C", targetCwd, "rev-parse", "HEAD"], { timeout: 2000 }); commit = commitOut.trim() || null; } catch {} try { const { stdout: remoteOut } = await execFileAsync("git", ["-C", targetCwd, "config", "--get", "remote.origin.url"], { timeout: 2000 }); origin_url = remoteOut.trim() || null; } catch {} return { type: "git", root, branch, commit, origin_url }; } } catch {}
return { type: "none", root: targetCwd };}
export async function getVcsInfo(cwd) { const vcs = await detectVcs(cwd); if (vcs.type === "jj") { return { sha: vcs.changeId || null, branch: vcs.branch || null, origin_url: vcs.origin_url || null, }; } if (vcs.type === "git") { return { sha: vcs.commit || null, branch: vcs.branch || null, origin_url: vcs.origin_url || null, }; } return null;}
export async function createIsolatedWorkspace({ threadId, sourceCwd, isolationMode = "auto", baseDir = null }) { const resolvedSourceCwd = path.resolve(sourceCwd || process.cwd()); const mode = String(isolationMode || "auto").toLowerCase();
if (mode === "none" || mode === "off" || mode === "false") { return { isolated: false, type: "none", cwd: resolvedSourceCwd, sourceCwd: resolvedSourceCwd, root: resolvedSourceCwd }; }
const vcs = await detectVcs(resolvedSourceCwd); if (vcs.type === "none") { return { isolated: false, type: "none", cwd: resolvedSourceCwd, sourceCwd: resolvedSourceCwd, root: resolvedSourceCwd }; }
const wsRootDir = baseDir || defaultWorkspacesDir(); await fs.mkdir(wsRootDir, { recursive: true }); const wsName = sanitizeWorkspaceName(threadId); const wsPath = path.join(wsRootDir, `${wsName}_${Date.now()}`);
// Jujutsu workspace if (vcs.type === "jj" && (mode === "auto" || mode === "jj")) { try { await execFileAsync("jj", ["workspace", "add", "--name", wsName, wsPath], { cwd: vcs.root, timeout: 10000 }); try { const excludeFile = path.join(vcs.root, ".git", "info", "exclude"); try { const currentExclude = await fs.readFile(excludeFile, "utf8").catch(() => ""); if (!currentExclude.includes(".jj")) { await fs.mkdir(path.dirname(excludeFile), { recursive: true }); await fs.appendFile(excludeFile, "\n.jj\n.jj/\n"); } } catch {}
const gitDir = path.join(vcs.root, ".git", "worktrees", wsName); await fs.mkdir(gitDir, { recursive: true }); await fs.writeFile(path.join(gitDir, "gitdir"), path.join(wsPath, ".git") + "\n"); await fs.writeFile(path.join(gitDir, "commondir"), "../../\n"); let headSha = null; try { const { stdout } = await execFileAsync("jj", ["log", "-R", wsPath, "-r", "@-", "--no-graph", "-T", "commit_id"], { timeout: 5000 }); headSha = stdout.trim(); } catch {} await fs.writeFile(path.join(gitDir, "HEAD"), (headSha || "HEAD") + "\n"); await fs.writeFile(path.join(wsPath, ".git"), `gitdir: ${gitDir}\n`); const infoExclude = path.join(gitDir, "info", "exclude"); await fs.mkdir(path.dirname(infoExclude), { recursive: true }); await fs.writeFile(infoExclude, "\n.jj\n.jj/\n"); await execFileAsync("git", ["-C", wsPath, "reset", "--mixed", "HEAD"], { timeout: 5000 }).catch(() => {}); } catch {} log(`Created jj workspace ${wsName} at ${wsPath} for thread ${threadId}`); return { isolated: true, type: "jj", cwd: wsPath, sourceCwd: resolvedSourceCwd, root: vcs.root, workspaceName: wsName, }; } catch (err) { log(`Failed to create jj workspace for thread ${threadId}: ${err?.message || err}; falling back to source cwd`); return { isolated: false, type: "none", cwd: resolvedSourceCwd, sourceCwd: resolvedSourceCwd, root: vcs.root }; } }
// Git worktree fallback if (vcs.type === "git" && (mode === "auto" || mode === "git")) { try { await execFileAsync("git", ["-C", vcs.root, "worktree", "add", "--detach", wsPath, "HEAD"], { timeout: 10000 }); log(`Created git worktree at ${wsPath} for thread ${threadId}`); return { isolated: true, type: "git", cwd: wsPath, sourceCwd: resolvedSourceCwd, root: vcs.root, workspaceName: wsName, }; } catch (err) { log(`Failed to create git worktree for thread ${threadId}: ${err?.message || err}; falling back to source cwd`); return { isolated: false, type: "none", cwd: resolvedSourceCwd, sourceCwd: resolvedSourceCwd, root: vcs.root }; } }
return { isolated: false, type: "none", cwd: resolvedSourceCwd, sourceCwd: resolvedSourceCwd, root: resolvedSourceCwd };}
export async function cleanupWorkspace(workspaceInfo) { if (!workspaceInfo || !workspaceInfo.isolated || !workspaceInfo.cwd) { return; }
const { type, cwd: wsPath, root, workspaceName } = workspaceInfo;
if (type === "jj") { try { if (workspaceName) { await execFileAsync("jj", ["workspace", "forget", workspaceName], { cwd: root || process.cwd(), timeout: 5000 }).catch(() => {}); } else { await execFileAsync("jj", ["workspace", "forget", wsPath], { cwd: root || process.cwd(), timeout: 5000 }).catch(() => {}); } } catch {} } else if (type === "git") { try { await execFileAsync("git", ["-C", root || process.cwd(), "worktree", "remove", "--force", wsPath], { timeout: 5000 }).catch(() => {}); } catch {} }
try { await fs.rm(wsPath, { recursive: true, force: true }); log(`Cleaned up ${type} workspace at ${wsPath}`); } catch {}}
const GIT_WORKTREE_FUNCTION_WRAPPER = `_real_git() { command git "$@"; }git() { local root="." local args=() while [ $# -gt 0 ]; do case "$1" in -C) root="$2" if [ -n "$2" ] && [ ! -d "$2" ]; then mkdir -p "$2" 2>/dev/null || true fi args+=("-C" "$2") shift 2 ;; worktree) shift local subcmd="\${1:-}" shift || true if [ "$subcmd" = "add" ]; then local dest="" local ref="" while [ $# -gt 0 ]; do case "$1" in --detach|--checkout|--no-checkout|--lock|--guess-remote) shift ;; -b|-B|--reason) shift; [ $# -gt 0 ] && shift ;; --orphan) shift ;; -*) shift ;; *) if [ -z "$dest" ]; then dest="$1"; elif [ -z "$ref" ]; then ref="$1"; fi shift ;; esac done local is_jj=0 if command -v jj >/dev/null 2>&1; then if [ -d "$root/.jj" ] || jj root -R "$root" >/dev/null 2>&1; then is_jj=1 fi fi if [ -n "$dest" ] && [ "$is_jj" = "1" ]; then local jj_root jj_root="$(jj root -R "$root" 2>/dev/null || echo "$root")" local id id="$(basename "$(dirname "$dest")" | tr -cd 'a-zA-Z0-9' | head -c 16)" local base base="$(basename "$dest" | tr -cd 'a-zA-Z0-9' | head -c 16)" local ws_name="ws_\${id:-\$(date +%s)}_\${base}" jj workspace forget "$ws_name" -R "$jj_root" >/dev/null 2>&1 || true if [ -d "$dest" ] && [ "$(ls -A "$dest" 2>/dev/null)" ]; then local tmp_ws tmp_ws="$(mktemp -d)" jj workspace add --name "$ws_name" "$tmp_ws/ws" -R "$jj_root" >/dev/null 2>&1 || true local repo_dir="$jj_root/.jj/repo" local index_file="$repo_dir/workspace_store/index" if [ -f "$index_file" ]; then local rel_tmp rel_tmp="$(node -e "const p = require('path'); console.log(p.relative('$repo_dir', '$tmp_ws/ws'))" 2>/dev/null || true)" local rel_dest rel_dest="$(node -e "const p = require('path'); console.log(p.relative('$repo_dir', '$dest'))" 2>/dev/null || true)" if [ -n "$rel_tmp" ] && [ -n "$rel_dest" ]; then node -e "const fs = require('fs'); const f = '$index_file'; fs.writeFileSync(f, fs.readFileSync(f, 'utf8').replaceAll('$rel_tmp', '$rel_dest'))" 2>/dev/null || true fi fi rm -rf "$dest/.jj" 2>/dev/null || true cp -R "$tmp_ws/ws/.jj" "$dest/" 2>/dev/null || true local rel_repo rel_repo="$(node -e "const p = require('path'); console.log(p.relative('$dest/.jj', '$repo_dir'))" 2>/dev/null || true)" if [ -n "$rel_repo" ]; then printf '%s' "$rel_repo" > "$dest/.jj/repo" fi rm -rf "$tmp_ws" 2>/dev/null || true else rm -rf "$dest" 2>/dev/null || true jj workspace add --name "$ws_name" "$dest" -R "$jj_root" fi local git_dir="$jj_root/.git/worktrees/$ws_name" mkdir -p "$git_dir" printf '%s\n' "$dest/.git" > "$git_dir/gitdir" printf '%s\n' "../../" > "$git_dir/commondir" local head_commit head_commit="$(jj log -R "$dest" -r '@-' --no-graph -T 'commit_id' 2>/dev/null || jj log -R "$jj_root" -r '@' --no-graph -T 'commit_id' 2>/dev/null || true)" if [ -n "$head_commit" ]; then printf '%s\n' "$head_commit" > "$git_dir/HEAD" else printf '%s\n' "ref: refs/heads/master" > "$git_dir/HEAD" fi printf 'gitdir: %s\n' "$git_dir" > "$dest/.git" mkdir -p "$jj_root/.git/info" "$git_dir/info" 2>/dev/null || true printf '\n.jj\n.jj/\n' >> "$jj_root/.git/info/exclude" 2>/dev/null || true printf '\n.jj\n.jj/\n' >> "$git_dir/info/exclude" 2>/dev/null || true _real_git -C "$dest" reset --mixed HEAD >/dev/null 2>&1 || true return 0 else _real_git "\${args[@]}" worktree add --detach "$dest" \${ref:+"$ref"} return $? fi elif [ "$subcmd" = "remove" ]; then local dest="" while [ $# -gt 0 ]; do case "$1" in -*) shift ;; *) dest="$1"; shift ;; esac done if [ -n "$dest" ] && command -v jj >/dev/null 2>&1; then local jj_root jj_root="$(jj root -R "$root" 2>/dev/null || echo "$root")" local id id="$(basename "$(dirname "$dest")" | tr -cd 'a-zA-Z0-9' | head -c 16)" local base base="$(basename "$dest" | tr -cd 'a-zA-Z0-9' | head -c 16)" local ws_name="ws_\${id:-\$(date +%s)}_\${base}" jj workspace forget "$ws_name" -R "$jj_root" >/dev/null 2>&1 || jj workspace forget "$dest" -R "$jj_root" >/dev/null 2>&1 || true fi _real_git "\${args[@]}" worktree remove --force "$dest" return $? else _real_git "\${args[@]}" worktree "$subcmd" "$@" return $? fi ;; *) args+=("$1") shift ;; esac done _real_git "\${args[@]}"}`;
export function adaptWorktreeSpawnCommand(command, env = {}) { if (!Array.isArray(command) || !command.length) return command;
// Case 1: Direct git invocation e.g. ["git", "worktree", "add", ...] or ["git", "-C", "/path", "worktree", "add", ...] if (path.basename(command[0]) === "git" && command.includes("worktree")) { return ["/bin/sh", "-c", `${GIT_WORKTREE_FUNCTION_WRAPPER}\ngit "$@"`, "git", ...command.slice(1)]; }
// Case 2: Shell script invocation e.g. ["/bin/sh", "-lc", "script"] if (command.length >= 3 && typeof command[2] === "string") { const shell = command[0]; const flag = command[1]; const script = command[2];
const hasWorktreeEnv = Object.keys(env).some((key) => key.startsWith("CODEX_DRAFT_") || key.startsWith("CODEX_WORKTREE_")); const hasWorktreeScript = script.includes("worktree") || (script.includes("git_root") && script.includes("worktree_git_root")); const hasDiffScript = script.includes("preferred_comparison_ref") || script.includes("GIT_OPTIONAL_LOCKS");
if (!hasWorktreeEnv && !hasWorktreeScript && !hasDiffScript) return command;
const JJ_DIFF_SYNC_WRAPPER = `if command -v jj >/dev/null 2>&1 && { [ -d .jj ] || [ -f .git ]; }; then if jj root >/dev/null 2>&1; then _cur_jj_parent="$(jj log -r '@-' --no-graph -T 'commit_id' 2>/dev/null || true)" if [ -n "$_cur_jj_parent" ] && [ -f .git ]; then _git_dir="$(cat .git 2>/dev/null | sed 's/^gitdir: //')" if [ -n "$_git_dir" ] && [ -d "$_git_dir" ]; then mkdir -p "$_git_dir/info" printf '\\n.jj\\n.jj/\\n' > "$_git_dir/info/exclude" _cur_git_head="$(cat "$_git_dir/HEAD" 2>/dev/null || true)" if [ "$_cur_git_head" != "$_cur_jj_parent" ]; then printf '%s\\n' "$_cur_jj_parent" > "$_git_dir/HEAD" command git reset --mixed HEAD >/dev/null 2>&1 || true fi fi fi preferred_comparison_ref="HEAD" fifi`;
let finalScript = script; if (hasDiffScript) { finalScript = `${JJ_DIFF_SYNC_WRAPPER}\n${finalScript}`; } if (hasWorktreeEnv || hasWorktreeScript) { finalScript = `${GIT_WORKTREE_FUNCTION_WRAPPER}\n${finalScript}`; }
return [shell, flag, finalScript, ...command.slice(3)]; }
return command;}