#!/usr/bin/env bash set -euo pipefail usage() { cat <<'EOF' Usage: portable.sh build [OUT_DIR] [NODE_TARBALL_URL] portable.sh pack [OUT_DIR] [ARCHIVE] portable.sh unpack [ARCHIVE] [OUT_DIR] Defaults: OUT_DIR: codex-portable ARCHIVE: codex-portable.tgz NODE_VERSION: 20.19.6 NODE_TARBALL_URL: auto (official if glibc >= 2.28, otherwise unofficial glibc-217) EOF } cmd="${1:-}" case "$cmd" in build) OUT_DIR="${2:-codex-portable}" NODE_TARBALL_URL="${3:-}" NODE_VERSION="${NODE_VERSION:-20.19.6}" TMP_DIR="${TMP_DIR:-/tmp/codex-portable-build}" rm -rf "$OUT_DIR" "$TMP_DIR" mkdir -p "$OUT_DIR" "$TMP_DIR" "$OUT_DIR/home" if [[ -z "$NODE_TARBALL_URL" ]]; then glibc_ver="" if command -v getconf >/dev/null 2>&1; then glibc_ver="$(getconf GNU_LIBC_VERSION 2>/dev/null | awk '{print $2}')" elif command -v ldd >/dev/null 2>&1; then glibc_ver="$(ldd --version 2>/dev/null | head -n1 | awk '{print $NF}')" fi if [[ -n "$glibc_ver" ]] && [[ "$(printf '%s\n' "2.28" "$glibc_ver" | sort -V | head -n1)" == "2.28" ]]; then NODE_TARBALL_URL="https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz" else NODE_TARBALL_URL="https://unofficial-builds.nodejs.org/download/release/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64-glibc-217.tar.xz" fi fi echo "Downloading Node from: $NODE_TARBALL_URL" if command -v curl >/dev/null 2>&1; then curl -fL "$NODE_TARBALL_URL" -o "$TMP_DIR/node.tar.xz" elif command -v wget >/dev/null 2>&1; then wget -O "$TMP_DIR/node.tar.xz" "$NODE_TARBALL_URL" else echo "Error: need curl or wget." >&2 exit 1 fi tar -xJf "$TMP_DIR/node.tar.xz" -C "$TMP_DIR" node_dir="$(find "$TMP_DIR" -maxdepth 1 -type d -name 'node-v*' | head -n1)" if [[ -z "$node_dir" ]]; then echo "Error: failed to unpack Node tarball." >&2 exit 1 fi mv "$node_dir" "$OUT_DIR/node" echo "Installing Codex..." export PATH="$OUT_DIR/node/bin:$PATH" "$OUT_DIR/node/bin/npm" install -g --prefix "$OUT_DIR/codex" @openai/codex cat > "$OUT_DIR/run.sh" <<'EOF' #!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" mkdir -p "$ROOT_DIR/home" export HOME="$ROOT_DIR/home" export CODEX_HOME="$ROOT_DIR/home" export PATH="$ROOT_DIR/node/bin:$ROOT_DIR/codex/bin:$PATH" exec "$ROOT_DIR/node/bin/node" \ "$ROOT_DIR/codex/lib/node_modules/@openai/codex/bin/codex.js" \ "$@" EOF chmod +x "$OUT_DIR/run.sh" echo "Portable build ready: $OUT_DIR" echo "Run: $OUT_DIR/run.sh" ;; pack) OUT_DIR="${2:-codex-portable}" ARCHIVE="${3:-codex-portable.tgz}" if [[ ! -d "$OUT_DIR" ]]; then echo "Error: $OUT_DIR not found." >&2 exit 1 fi tar -czf "$ARCHIVE" -C "$(dirname "$OUT_DIR")" "$(basename "$OUT_DIR")" echo "Packed: $ARCHIVE" ;; unpack) ARCHIVE="${2:-codex-portable.tgz}" OUT_DIR="${3:-codex-portable}" if [[ ! -f "$ARCHIVE" ]]; then echo "Error: $ARCHIVE not found." >&2 exit 1 fi rm -rf "$OUT_DIR" tar -xzf "$ARCHIVE" -C "$(dirname "$OUT_DIR")" echo "Unpacked to: $OUT_DIR" ;; *) usage exit 1 ;; esac