From e6d24550d076962611f52299494e6393484616f6 Mon Sep 17 00:00:00 2001 From: Phenix Date: Sat, 7 Feb 2026 08:57:20 +0800 Subject: [PATCH] feat: add Windows WSL auto-detection and bridge mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Auto-detect Windows (MINGW/MSYS/CYGWIN) in install.sh instead of erroring - Install dependencies inside WSL automatically when on Windows - Create Windows-side codex-agent shim with Win→WSL path conversion - Add codex-agent-wsl.sh bridge wrapper handling 3 path formats - Fix Linux script command syntax in tmux.ts (BSD vs util-linux) - Update README and SKILL.md with Windows/WSL documentation --- README.md | 20 +- .../scripts/codex-agent-wsl.sh | 46 ++++ plugins/codex-orchestrator/scripts/install.sh | 212 +++++++++++++++++- .../skills/codex-orchestrator/SKILL.md | 8 + src/tmux.ts | 15 +- 5 files changed, 289 insertions(+), 12 deletions(-) create mode 100644 plugins/codex-orchestrator/scripts/codex-agent-wsl.sh diff --git a/README.md b/README.md index 6c68327..6f7f628 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,24 @@ Or use the automated installer: bash <(curl -fsSL https://raw.githubusercontent.com/kingbootoshi/codex-orchestrator/main/plugins/codex-orchestrator/scripts/install.sh) ``` +### Windows (WSL) + +On Windows, the installer automatically detects the environment and routes through WSL: + +1. Ensure WSL is installed: `wsl --install` (requires admin PowerShell, restart may be needed) +2. Run the installer from Git Bash, PowerShell, or CMD: + ```bash + bash scripts/install.sh + ``` +3. The installer will: + - Detect Windows and check WSL availability + - Run the full install inside WSL (tmux, bun, codex CLI) + - Create a Windows-side `codex-agent` shim at `~/bin/codex-agent` +4. All `codex-agent` commands work directly from Git Bash/PowerShell — no `wsl` prefix needed +5. Windows paths are automatically converted to WSL paths for `-d` and `-f` flags + +**Requirements:** WSL 2 with a default Linux distribution configured. + ### Requirements | Dependency | Purpose | Install | @@ -72,7 +90,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/kingbootoshi/codex-orchestra | [Codex CLI](https://github.com/openai/codex) | OpenAI's coding agent - the thing being orchestrated | `npm install -g @openai/codex` | | OpenAI account | API access for Codex agents | `codex --login` | -**Platform support:** macOS and Linux. Windows users should use WSL. +**Platform support:** macOS, Linux, and Windows (via WSL — auto-detected). ## Why? diff --git a/plugins/codex-orchestrator/scripts/codex-agent-wsl.sh b/plugins/codex-orchestrator/scripts/codex-agent-wsl.sh new file mode 100644 index 0000000..8fa6cbf --- /dev/null +++ b/plugins/codex-orchestrator/scripts/codex-agent-wsl.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# codex-agent WSL bridge - Routes commands through WSL with path conversion +# Converts Windows paths (Git Bash, CMD, PowerShell) to WSL /mnt/ format + +convert_win_to_wsl() { + local win_path="$1" + if [[ "$win_path" =~ ^/([a-zA-Z])/ ]]; then + echo "/mnt/${BASH_REMATCH[1],,}/${win_path:3}" + elif [[ "$win_path" =~ ^([a-zA-Z]):\\ ]]; then + local drive="${win_path:0:1}" + drive="${drive,,}" + local rest="${win_path:3}" + rest="${rest//\\//}" + echo "/mnt/${drive}/${rest}" + elif [[ "$win_path" =~ ^([a-zA-Z]):/ ]]; then + local drive="${win_path:0:1}" + drive="${drive,,}" + echo "/mnt/${drive}/${win_path:3}" + else + echo "$win_path" + fi +} + +ARGS=() +SKIP_NEXT=false + +for i in "$@"; do + if $SKIP_NEXT; then + SKIP_NEXT=false + ARGS+=("$(convert_win_to_wsl "$i")") + continue + fi + case "$i" in + -d|--dir|-f|--file) + ARGS+=("$i") + SKIP_NEXT=true + ;; + *) + ARGS+=("$i") + ;; + esac +done + +WSL_CWD="$(convert_win_to_wsl "$(pwd)")" + +wsl -e bash -lc "cd '${WSL_CWD}' 2>/dev/null; export PATH=\"\$HOME/.bun/bin:\$HOME/.codex-orchestrator/bin:\$PATH\"; codex-agent ${ARGS[*]}" diff --git a/plugins/codex-orchestrator/scripts/install.sh b/plugins/codex-orchestrator/scripts/install.sh index 8074c90..0c6d9e3 100755 --- a/plugins/codex-orchestrator/scripts/install.sh +++ b/plugins/codex-orchestrator/scripts/install.sh @@ -14,6 +14,15 @@ NC='\033[0m' # No Color INSTALL_DIR="${CODEX_ORCHESTRATOR_HOME:-$HOME/.codex-orchestrator}" REPO_URL="https://github.com/kingbootoshi/codex-orchestrator.git" +# Resolve the directory where this script lives +SCRIPT_SOURCE="${BASH_SOURCE[0]}" +while [ -L "$SCRIPT_SOURCE" ]; do + SCRIPT_DIR="$(cd -P "$(dirname "$SCRIPT_SOURCE")" && pwd)" + SCRIPT_SOURCE="$(readlink "$SCRIPT_SOURCE")" + [[ $SCRIPT_SOURCE != /* ]] && SCRIPT_SOURCE="$SCRIPT_DIR/$SCRIPT_SOURCE" +done +SCRIPT_DIR="$(cd -P "$(dirname "$SCRIPT_SOURCE")" && pwd)" + info() { echo -e "${BLUE}[info]${NC} $1"; } success() { echo -e "${GREEN}[ok]${NC} $1"; } warn() { echo -e "${YELLOW}[warn]${NC} $1"; } @@ -27,13 +36,7 @@ detect_platform() { Linux*) PLATFORM="linux" ;; Darwin*) PLATFORM="macos" ;; CYGWIN*|MINGW*|MSYS*) - error "Windows is not directly supported." - echo "" - echo "Please use WSL (Windows Subsystem for Linux):" - echo " 1. Install WSL: wsl --install" - echo " 2. Open a WSL terminal" - echo " 3. Re-run this script inside WSL" - exit 1 + PLATFORM="windows" ;; *) error "Unsupported platform: $(uname -s)" @@ -44,6 +47,195 @@ detect_platform() { info "Platform: $PLATFORM ($(uname -m))" } +# ------------------------------------------------------------------- +# Convert a Git Bash / MSYS path to a WSL /mnt/ path +# ------------------------------------------------------------------- +convert_to_wsl_path() { + local win_path="$1" + if [[ "$win_path" =~ ^/([a-zA-Z])/ ]]; then + echo "/mnt/${BASH_REMATCH[1],,}/${win_path:3}" + elif [[ "$win_path" =~ ^([a-zA-Z]):\\ ]]; then + local drive="${win_path:0:1}" + drive="${drive,,}" + local rest="${win_path:3}" + rest="${rest//\\//}" + echo "/mnt/${drive}/${rest}" + elif [[ "$win_path" =~ ^([a-zA-Z]):/ ]]; then + local drive="${win_path:0:1}" + drive="${drive,,}" + echo "/mnt/${drive}/${win_path:3}" + else + echo "$win_path" + fi +} + +# ------------------------------------------------------------------- +# Windows: install via WSL and create bridge shim +# ------------------------------------------------------------------- +install_via_wsl() { + info "Windows detected — installing via WSL" + echo "" + + # Check wsl.exe is available + if ! command -v wsl.exe &>/dev/null && ! command -v wsl &>/dev/null; then + error "WSL is not installed." + echo "" + echo "Install WSL from an admin PowerShell:" + echo " wsl --install" + echo "" + echo "Then restart your computer and re-run this script." + exit 1 + fi + + # Check WSL has a default distro configured + local wsl_cmd="wsl.exe" + command -v wsl.exe &>/dev/null || wsl_cmd="wsl" + + if ! $wsl_cmd --status &>/dev/null; then + error "WSL is installed but no default distribution is configured." + echo "" + echo "Set up a Linux distribution:" + echo " wsl --install -d Ubuntu" + echo "" + echo "Then re-run this script." + exit 1 + fi + + success "WSL is available" + + # Convert this script's path to WSL format and run inside WSL + local wsl_script_path + wsl_script_path="$(convert_to_wsl_path "$SCRIPT_DIR/install.sh")" + + info "Running install inside WSL..." + echo "" + + if ! $wsl_cmd -e bash -c "bash '${wsl_script_path}'"; then + error "WSL installation failed." + exit 1 + fi + + success "WSL-side installation complete" + echo "" + + # Create Windows-side bridge shim + create_windows_shim + + # Verify the shim works + echo "" + info "Verifying Windows bridge..." + echo "" + + local shim_path="$HOME/bin/codex-agent" + if [ -f "$shim_path" ] && bash "$shim_path" health; then + echo "" + success "Windows WSL bridge is working!" + echo "" + echo "Quick start:" + echo " codex-agent start \"Review this codebase for issues\" --map" + echo " codex-agent jobs --json" + echo " codex-agent capture " + echo "" + echo "All commands work directly from Git Bash or PowerShell." + echo "Windows paths are automatically converted to WSL paths." + else + error "Bridge verification failed." + echo "" + echo "Try running manually:" + echo " bash $shim_path health" + exit 1 + fi + + if ! command -v codex-agent &>/dev/null; then + warn "codex-agent is not on your PATH." + echo "" + echo "Add this to your shell profile:" + echo " export PATH=\"\$HOME/bin:\$PATH\"" + fi +} + +# ------------------------------------------------------------------- +# Create the Windows-side codex-agent shim and WSL bridge +# ------------------------------------------------------------------- +create_windows_shim() { + local bin_dir="$HOME/bin" + local shim_path="$bin_dir/codex-agent" + local bridge_path="$bin_dir/codex-agent-wsl.sh" + + mkdir -p "$bin_dir" + + info "Creating WSL bridge at $bridge_path" + + # Copy the bridge wrapper from the scripts directory + local source_bridge="$SCRIPT_DIR/codex-agent-wsl.sh" + if [ -f "$source_bridge" ]; then + cp "$source_bridge" "$bridge_path" + else + # Generate inline if source not found (e.g. curl install) + cat > "$bridge_path" << 'BRIDGE_EOF' +#!/bin/bash +# codex-agent WSL bridge - Routes commands through WSL with path conversion + +convert_win_to_wsl() { + local win_path="$1" + if [[ "$win_path" =~ ^/([a-zA-Z])/ ]]; then + echo "/mnt/${BASH_REMATCH[1],,}/${win_path:3}" + elif [[ "$win_path" =~ ^([a-zA-Z]):\\ ]]; then + local drive="${win_path:0:1}" + drive="${drive,,}" + local rest="${win_path:3}" + rest="${rest//\\//}" + echo "/mnt/${drive}/${rest}" + elif [[ "$win_path" =~ ^([a-zA-Z]):/ ]]; then + local drive="${win_path:0:1}" + drive="${drive,,}" + echo "/mnt/${drive}/${win_path:3}" + else + echo "$win_path" + fi +} + +ARGS=() +SKIP_NEXT=false + +for i in "$@"; do + if $SKIP_NEXT; then + SKIP_NEXT=false + ARGS+=("$(convert_win_to_wsl "$i")") + continue + fi + case "$i" in + -d|--dir|-f|--file) + ARGS+=("$i") + SKIP_NEXT=true + ;; + *) + ARGS+=("$i") + ;; + esac +done + +WSL_CWD="$(convert_win_to_wsl "$(pwd)")" + +wsl -e bash -lc "cd '${WSL_CWD}' 2>/dev/null; export PATH=\"\$HOME/.bun/bin:\$HOME/.codex-orchestrator/bin:\$PATH\"; codex-agent ${ARGS[*]}" +BRIDGE_EOF + fi + + chmod +x "$bridge_path" + + info "Creating shim at $shim_path" + + cat > "$shim_path" << SHIM_EOF +#!/bin/bash +# codex-agent — Windows WSL bridge shim +exec bash "$bridge_path" "\$@" +SHIM_EOF + + chmod +x "$shim_path" + + success "Windows shim created at $shim_path" +} + # ------------------------------------------------------------------- # Detect package manager (Linux only) # ------------------------------------------------------------------- @@ -282,6 +474,12 @@ main() { detect_platform echo "" + # Windows: delegate to WSL and create bridge shim + if [ "$PLATFORM" = "windows" ]; then + install_via_wsl + return + fi + check_tmux check_bun check_codex diff --git a/plugins/codex-orchestrator/skills/codex-orchestrator/SKILL.md b/plugins/codex-orchestrator/skills/codex-orchestrator/SKILL.md index 74d972a..8c8fb6d 100644 --- a/plugins/codex-orchestrator/skills/codex-orchestrator/SKILL.md +++ b/plugins/codex-orchestrator/skills/codex-orchestrator/SKILL.md @@ -130,6 +130,14 @@ codex --login **All dependencies use official sources only.** tmux from system package managers, Bun from bun.sh, Codex CLI from npm. No third-party scripts or unknown URLs. +### Windows/WSL + +On Windows, codex-agent runs through WSL automatically. The install script detects Windows +and creates a bridge shim. All commands work directly from Git Bash or PowerShell — no `wsl` +prefix needed. Paths are auto-converted. + +If WSL is not installed, run `wsl --install` from an admin PowerShell first. + ## The Factory Pipeline ``` diff --git a/src/tmux.ts b/src/tmux.ts index a768ce7..3d6be94 100644 --- a/src/tmux.ts +++ b/src/tmux.ts @@ -73,10 +73,17 @@ export function createSession(options: { // Create tmux session with codex running // Use script to capture all output, and keep shell alive after codex exits - // This allows us to capture the output even after completion - // Create detached session that runs codex and stays open after it exits - // Using script to log all terminal output - const shellCmd = `script -q "${logFile}" codex ${codexArgs}; echo "\\n\\n[codex-agent: Session complete. Press Enter to close.]"; read`; + // macOS BSD script: script -q logfile command + // Linux util-linux script: script -q -c "command" logfile + const os = require("os"); + const platform = os.platform(); + + let shellCmd: string; + if (platform === "darwin") { + shellCmd = `script -q "${logFile}" codex ${codexArgs}; echo "\\n\\n[codex-agent: Session complete. Press Enter to close.]"; read`; + } else { + shellCmd = `script -q -c "codex ${codexArgs}" "${logFile}"; echo "\\n\\n[codex-agent: Session complete. Press Enter to close.]"; read`; + } execSync( `tmux new-session -d -s "${sessionName}" -c "${options.cwd}" '${shellCmd}'`,