Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137#!/bin/bash# slab-tracked Codex launcher.## Registers this Codex session as a "prompt rock" so it shows up in the slab# menubar + prox exactly like a Claude session. Codex fires no per-event hooks# the way Claude Code does, so instead we:# 1. write an active-prompts marker (agent_type=codex) up front,# 2. run a background watcher that tails Codex's rollout JSONL and flips the# shared state files (active-prompts / awaiting-prompts / running-tools)# on task_started / task_complete — the same files the Claude hooks touch,# so the menubar's state→color reducer needs zero Codex-specific logic,# 3. clean everything up when Codex exits (or gets reaped if the tab dies,# since the marker carries agent_pid).## Invoked by the `co` / `cor` fish shortcuts. Run `codex` directly to skip# tracking.set -uSLAB_HOME=${SLAB_HOME:-$HOME/.local/share/slab}SLAB_BIN=${SLAB_BIN:-$HOME/.local/bin}ACTIVE_DIR="$SLAB_HOME/state/active-prompts"AWAITING_DIR="$SLAB_HOME/state/awaiting-prompts"RUNNING_DIR="$SLAB_HOME/state/running-tools"mkdir -p "$ACTIVE_DIR" "$AWAITING_DIR" "$RUNNING_DIR"# No jq or codex? Fall back to a plain, untracked codex so the shortcut never# breaks the user's ability to launch it.if ! command -v codex >/dev/null 2>&1; then echo "codex not found on PATH" >&2 exit 127fiif ! command -v jq >/dev/null 2>&1; then exec codex "$@"fisid=$(uuidgen)export SLAB_PROMPT_SESSION_ID="$sid"begin=$(date +%s)# The wrapper (this bash process) is the parent of codex and holds the tab's# controlling tty — use it for liveness + terminal-close, matching claude_pid.tty=${SLAB_TERMINAL_TTY:-$(ps -o tty= -p $$ 2>/dev/null | tr -d ' ')}cwd=$PWDts=$(date -u +%Y-%m-%dT%H:%M:%SZ)# Codex probes its terminal colors once. Set Slab's page BEFORE launching it,# then retain that appearance while its status hues change during the session.terminal_dark=nullif command -v node >/dev/null 2>&1 && [[ -f "$SLAB_BIN/slab-prepare-terminal.mjs" ]]; then terminal_dark=$(node "$SLAB_BIN/slab-prepare-terminal.mjs" "$tty" 2>/dev/null) || terminal_dark=nullficase "$terminal_dark" in true|false|null) ;; *) terminal_dark=null ;; esac# Fresh window reads "blank" (like Claude's SessionStart) until the first turn.jq -n \ --arg sid "$sid" --arg cwd "$cwd" --arg tty "$tty" \ --arg pid "$$" --arg ts "$ts" \ --argjson terminal_dark "$terminal_dark" \ --arg nudge "${SLAB_NUDGE_SCREEN:-}" --arg contact "${SLAB_LOOPBOY_CONTACT:-}" \ '{session_id:$sid, cwd:$cwd, subject:"codex session", summary:"codex", tty:$tty, agent_pid:($pid|tonumber), agent_type:"codex", updated:$ts, state:"blank", terminal_dark:$terminal_dark, nudge_screen:$nudge, loopboy_contact:$contact}' \ > "$ACTIVE_DIR/$sid" 2>/dev/null# Background watcher: derives per-turn state from the rollout JSONL.watcher=""if command -v node >/dev/null 2>&1 && [[ -f "$SLAB_BIN/codex-session-watch.mjs" ]]; then node "$SLAB_BIN/codex-session-watch.mjs" "$sid" "$begin" "$$" "$tty" "$cwd" \ >/dev/null 2>&1 & watcher=$!ficleanup() { [[ -n "$watcher" ]] && kill "$watcher" 2>/dev/null rm -f "$ACTIVE_DIR/$sid" "$AWAITING_DIR/$sid" "$RUNNING_DIR/$sid"}agent_pid=""stop_agent() { trap - INT TERM if [[ -n "$agent_pid" ]] && kill -0 "$agent_pid" 2>/dev/null; then kill -TERM "$agent_pid" 2>/dev/null || true for _ in {1..10}; do kill -0 "$agent_pid" 2>/dev/null || break sleep 0.05 done kill -KILL "$agent_pid" 2>/dev/null || true wait "$agent_pid" 2>/dev/null || true fi exit 143}trap cleanup EXITtrap stop_agent INT TERM# Loopboys are background client loops, never fleet/UI operators. Remove the# browser surfaces at process startup; the proxy separately scopes its private# inbox to the named contact and session identity.codex_guard_args=()if [[ -n "${SLAB_LOOPBOY_CONTACT:-}" ]]; then codex_guard_args+=( -c 'mcp_servers.prox={url="http://127.0.0.1:7773/mcp", env_http_headers={"x-slab-loopboy-contact"="SLAB_LOOPBOY_CONTACT", "x-slab-prompt-session-id"="SLAB_PROMPT_SESSION_ID"}}' -c 'mcp_servers.puppet.enabled=false' -c 'mcp_servers.frame.enabled=false' -c 'mcp_servers.fleet.enabled=false' --disable browser_use --disable browser_use_external --disable browser_use_full_cdp_access --disable in_app_browser )fi# Keep Codex on this wrapper's foreground process group and explicitly attach# its streams to the controlling TTY. Running it as a managed child lets a ZZZ# SIGTERM reach the actual agent before the wrapper exits, without closing the# terminal that will carry the resume receipt.# macOS still ships Bash 3.2, where expanding an empty array under `set -u`# raises "unbound variable". Keep the guarded and ordinary launch forms# explicit so the everyday `co` path never touches an empty array expansion.if (( ${#codex_guard_args[@]} )); then codex "${codex_guard_args[@]}" "$@" < "/dev/$tty" > "/dev/$tty" 2>&1 &else codex "$@" < "/dev/$tty" > "/dev/$tty" 2>&1 &fiagent_pid=$!# From this point on, lifecycle managers must signal the actual Codex process,# not this Bash wrapper. Bash defers TERM traps while it waits for a child;# tracking the wrapper here could therefore orphan Codex when ZZZ parks it.marker_tmp="$ACTIVE_DIR/.$sid.$$.tmp"if jq --arg pid "$agent_pid" --arg wrapper "$$" \ '.agent_pid=($pid|tonumber) | .wrapper_pid=($wrapper|tonumber)' \ "$ACTIVE_DIR/$sid" > "$marker_tmp" 2>/dev/null; then mv "$marker_tmp" "$ACTIVE_DIR/$sid"else rm -f "$marker_tmp"fiwait "$agent_pid"status=$?agent_pid=""exit "$status"