From d34d2c92df6a31d4ac7cdb4ff77cca7597ae953d Mon Sep 17 00:00:00 2001 From: surim0n Date: Thu, 24 Sep 2026 01:12:59 -0400 Subject: [PATCH 1/2] Resolve Codex through mise which instead of running the lazy launcher The Agents widget runs this collector on every refresh. The probe used shutil.which, which finds ~/.local/bin/codex first: a mise wrapper whose first step is `mise use -g`. On a machine without Codex, the read-only probe started a full install, and the orphaned mise kept going after the handshake timeout killed the launcher. Scan PATH for a real binary, skipping anything that looks like a lazy launcher (a regular file invoking mise, or a mise shim), then fall back to `mise which codex`, which only prints a binary that is already installed. A symlink at the launcher path is the user's own binary and is still probed directly. Fixes #12939 --- bin/omarchy-agent-usage-codex | 42 ++++++++++- .../shell.d/agent-usage-codex-scanner-test.sh | 70 +++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/bin/omarchy-agent-usage-codex b/bin/omarchy-agent-usage-codex index 972c164dee9..dbc42331e5b 100755 --- a/bin/omarchy-agent-usage-codex +++ b/bin/omarchy-agent-usage-codex @@ -91,6 +91,46 @@ def find_command(name): return shutil.which(name, path=ENV.get("PATH")) +def is_lazy_launcher(path): + # omarchy-mise-install writes a regular file whose first step is `mise use + # -g`, and mise's shims exec `mise x`; running either installs the tool it + # wraps. A symlink at the launcher path is the user's own binary. + if not path or os.path.islink(path): + return False + shims = os.path.join(str(Path.home()), ".local", "share", "mise", "shims") + if os.path.dirname(os.path.abspath(path)) == shims: + return True + try: + with open(path, "rb") as handle: + head = handle.read(4096) + return b"mise use" in head or b"mise x " in head + except OSError: + return False + + +def find_codex_binary(): + for directory in ENV.get("PATH", "").split(os.pathsep): + candidate = os.path.join(directory, "codex") + if os.path.isfile(candidate) and os.access(candidate, os.X_OK) and not is_lazy_launcher(candidate): + return candidate + # `mise which` only prints a binary that is already installed, so it is the + # safe way to resolve a codex managed by mise without running the launcher. + mise = find_command("mise") + if mise and not is_lazy_launcher(mise): + try: + out = subprocess.run( + [mise, "which", "codex"], capture_output=True, text=True, env=ENV, timeout=10 + ) + if out.returncode == 0: + for line in reversed(out.stdout.strip().splitlines()): + resolved = line.strip() + if resolved and os.path.isfile(resolved) and not is_lazy_launcher(resolved): + return resolved + except Exception: + pass + return None + + now = datetime.now() today = now.strftime("%Y-%m-%d") recent_dates = [(now - timedelta(days=offset)).strftime("%Y-%m-%d") for offset in range(6, -1, -1)] @@ -520,7 +560,7 @@ def limit_window(window): def fetch_codex_rpc(): result = {"limits": [], "tierLabel": "", "usageStatusText": "", "authHelpText": AUTH_HELP} - codex = find_command("codex") + codex = find_codex_binary() if not codex: result["usageStatusText"] = "Codex unavailable" result["authHelpText"] = "codex not found in PATH" diff --git a/test/shell.d/agent-usage-codex-scanner-test.sh b/test/shell.d/agent-usage-codex-scanner-test.sh index 3ce4883cdbf..824df20ce57 100644 --- a/test/shell.d/agent-usage-codex-scanner-test.sh +++ b/test/shell.d/agent-usage-codex-scanner-test.sh @@ -603,3 +603,73 @@ result=$(HOME="$INTERRUPTED_HOME" CODEX_HOME="$INTERRUPTED_HOME/.codex" XDG_CACH [[ $(jq -r '.todayTotalTokens' <<<"$result") == "9" ]] || fail "Codex collector does not reuse a snapshot from an interrupted scan" "$result" pass "Codex collector does not cache an interrupted opencode scan" + +# The lazy launcher at ~/.local/bin/codex runs `mise use -g` when executed, so +# a read-only usage probe on a machine without Codex must never spawn it. The +# sanitized PATH keeps a real codex or mise on the host out of the probe. +SAFE_PATH="$(dirname "$(command -v jq)"):/usr/bin:/bin" +LAUNCH_HOME=$(mktemp -d) +trap 'rm -rf "$TEST_HOME" "$PI_HOME" "$OPENCODE_HOME" "$CACHE_HOME" "$FRESH_HOME" "$MALFORMED_HOME" "$UNWRITABLE_HOME" "$INTERRUPTED_HOME" "$LAUNCH_HOME"' EXIT +mkdir -p "$LAUNCH_HOME/bin" "$LAUNCH_HOME/.local/bin" +cat >"$LAUNCH_HOME/.local/bin/codex" <<'LAUNCHER' +#!/bin/bash +export MISE_MINIMUM_RELEASE_AGE=0 +mise use -g --quiet "npm:@openai/codex" || exit 1 +exec mise x "npm:@openai/codex" -- codex "$@" +LAUNCHER +chmod +x "$LAUNCH_HOME/.local/bin/codex" +cat >"$LAUNCH_HOME/bin/mise" <<'STUB' +#!/bin/bash +printf '%s\n' "$*" >>"$MISE_CALLS_FILE" +exit 1 +STUB +chmod +x "$LAUNCH_HOME/bin/mise" + +result=$(HOME="$LAUNCH_HOME" CODEX_HOME="$LAUNCH_HOME/.codex" MISE_CALLS_FILE="$LAUNCH_HOME/mise-calls" XDG_DATA_HOME="$LAUNCH_HOME/.local/share" \ + PATH="$LAUNCH_HOME/bin:$SAFE_PATH" "$ROOT/bin/omarchy-agent-usage-codex" --limits-only) + +[[ $(jq -r '.usageStatusText' <<<"$result") == "Codex unavailable" ]] || + fail "Codex collector reports Codex unavailable when only the launcher exists" "$result" +# The launcher would log `use -g ...` through the mise stub if it ever ran; +# `which codex` is the only permitted call. +[[ ! -s $LAUNCH_HOME/mise-calls || $(cat "$LAUNCH_HOME/mise-calls") == "which codex" ]] || + fail "Codex collector must not execute the lazy codex launcher" "$(cat "$LAUNCH_HOME/mise-calls" 2>/dev/null)" +pass "Codex collector resolves through mise which instead of running the launcher" + +# When mise reports an installed binary, that binary is probed, not the +# launcher that shadows it on PATH. +cp "$TEST_HOME/bin/codex" "$LAUNCH_HOME/real-codex" +cat >"$LAUNCH_HOME/bin/mise" <>"$LAUNCH_HOME/mise-calls" +echo "$LAUNCH_HOME/real-codex" +STUB + +rm -f "$LAUNCH_HOME/mise-calls" +result=$(HOME="$LAUNCH_HOME" CODEX_HOME="$LAUNCH_HOME/.codex" CODEX_ARGS_FILE="$LAUNCH_HOME/codex-args" XDG_DATA_HOME="$LAUNCH_HOME/.local/share" \ + PATH="$LAUNCH_HOME/bin:$SAFE_PATH" "$ROOT/bin/omarchy-agent-usage-codex" --limits-only) + +[[ -f $LAUNCH_HOME/codex-args && $(cat "$LAUNCH_HOME/mise-calls") == "which codex" ]] || + fail "Codex collector probes the binary mise which reports" "$result" +pass "Codex collector probes the mise-resolved binary" + +# A symlink at the launcher path is the user's own binary, so it is probed +# directly without asking mise at all. +LINK_HOME=$(mktemp -d) +trap 'rm -rf "$TEST_HOME" "$PI_HOME" "$OPENCODE_HOME" "$CACHE_HOME" "$FRESH_HOME" "$MALFORMED_HOME" "$UNWRITABLE_HOME" "$INTERRUPTED_HOME" "$LAUNCH_HOME" "$LINK_HOME"' EXIT +mkdir -p "$LINK_HOME/bin" "$LINK_HOME/.local/bin" +ln -s "$TEST_HOME/bin/codex" "$LINK_HOME/.local/bin/codex" +cat >"$LINK_HOME/bin/mise" <>"$LINK_HOME/mise-calls" +echo "$LINK_HOME/real-codex" +STUB +chmod +x "$LINK_HOME/bin/mise" + +result=$(HOME="$LINK_HOME" CODEX_HOME="$LINK_HOME/.codex" CODEX_ARGS_FILE="$LINK_HOME/codex-args" XDG_DATA_HOME="$LINK_HOME/.local/share" \ + PATH="$LINK_HOME/bin:$SAFE_PATH" "$ROOT/bin/omarchy-agent-usage-codex" --limits-only) + +[[ -f $LINK_HOME/codex-args && ! -s $LINK_HOME/mise-calls ]] || + fail "Codex collector probes a symlinked codex without invoking mise" "$result" +pass "Codex collector probes a user-owned symlink at the launcher path" + From af251f2bd43c4daa05f0c4249100049cc6ae4194 Mon Sep 17 00:00:00 2001 From: surim0n Date: Thu, 24 Sep 2026 21:40:15 -0400 Subject: [PATCH 2/2] Treat mise shims as lazy, keep the which probe offline, and isolate the test PATH --- bin/omarchy-agent-usage-codex | 28 ++++++++++++--- .../shell.d/agent-usage-codex-scanner-test.sh | 36 +++++++++++++++++-- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/bin/omarchy-agent-usage-codex b/bin/omarchy-agent-usage-codex index dbc42331e5b..b2413413922 100755 --- a/bin/omarchy-agent-usage-codex +++ b/bin/omarchy-agent-usage-codex @@ -91,15 +91,32 @@ def find_command(name): return shutil.which(name, path=ENV.get("PATH")) +def mise_shims_dir(): + data_home = ENV.get("MISE_DATA_DIR") or os.path.join( + ENV.get("XDG_DATA_HOME") or os.path.join(str(Path.home()), ".local", "share"), "mise") + return os.path.join(data_home, "shims") + + def is_lazy_launcher(path): # omarchy-mise-install writes a regular file whose first step is `mise use # -g`, and mise's shims exec `mise x`; running either installs the tool it - # wraps. A symlink at the launcher path is the user's own binary. - if not path or os.path.islink(path): + # wraps. A shim is a symlink to the mise binary itself, so the symlink + # check comes after the shim checks: a symlink elsewhere is the user's own + # binary. + if not path: return False - shims = os.path.join(str(Path.home()), ".local", "share", "mise", "shims") + shims = mise_shims_dir() if os.path.dirname(os.path.abspath(path)) == shims: return True + if os.path.islink(path): + mise = find_command("mise") + if mise: + try: + if os.path.realpath(path) == os.path.realpath(mise): + return True + except OSError: + pass + return False try: with open(path, "rb") as handle: head = handle.read(4096) @@ -118,8 +135,11 @@ def find_codex_binary(): mise = find_command("mise") if mise and not is_lazy_launcher(mise): try: + # `mise which` can resolve a configured `latest` over the network; the + # usage probe stays offline. + env = dict(ENV, MISE_OFFLINE="1") out = subprocess.run( - [mise, "which", "codex"], capture_output=True, text=True, env=ENV, timeout=10 + [mise, "which", "codex"], capture_output=True, text=True, env=env, timeout=10 ) if out.returncode == 0: for line in reversed(out.stdout.strip().splitlines()): diff --git a/test/shell.d/agent-usage-codex-scanner-test.sh b/test/shell.d/agent-usage-codex-scanner-test.sh index 824df20ce57..3bf9801f122 100644 --- a/test/shell.d/agent-usage-codex-scanner-test.sh +++ b/test/shell.d/agent-usage-codex-scanner-test.sh @@ -605,10 +605,17 @@ result=$(HOME="$INTERRUPTED_HOME" CODEX_HOME="$INTERRUPTED_HOME/.codex" XDG_CACH pass "Codex collector does not cache an interrupted opencode scan" # The lazy launcher at ~/.local/bin/codex runs `mise use -g` when executed, so -# a read-only usage probe on a machine without Codex must never spawn it. The -# sanitized PATH keeps a real codex or mise on the host out of the probe. -SAFE_PATH="$(dirname "$(command -v jq)"):/usr/bin:/bin" +# a read-only usage probe on a machine without Codex must never spawn it. A +# private tools dir keeps a real codex or mise on the host out of the probe: +# PATH holds only the interpreter and file tools the collector may exec. LAUNCH_HOME=$(mktemp -d) +SAFE_PATH="$LAUNCH_HOME/tools" +mkdir -p "$SAFE_PATH" +for tool in python3 rg; do + if command -v "$tool" >/dev/null; then + ln -s "$(command -v "$tool")" "$SAFE_PATH/$tool" + fi +done trap 'rm -rf "$TEST_HOME" "$PI_HOME" "$OPENCODE_HOME" "$CACHE_HOME" "$FRESH_HOME" "$MALFORMED_HOME" "$UNWRITABLE_HOME" "$INTERRUPTED_HOME" "$LAUNCH_HOME"' EXIT mkdir -p "$LAUNCH_HOME/bin" "$LAUNCH_HOME/.local/bin" cat >"$LAUNCH_HOME/.local/bin/codex" <<'LAUNCHER' @@ -673,3 +680,26 @@ result=$(HOME="$LINK_HOME" CODEX_HOME="$LINK_HOME/.codex" CODEX_ARGS_FILE="$LINK fail "Codex collector probes a symlinked codex without invoking mise" "$result" pass "Codex collector probes a user-owned symlink at the launcher path" +# A mise shim is a symlink to the mise binary itself, so it resolves to mise, +# not to an installed codex — running it would exec `mise x` and install the +# tool. It must be treated as lazy despite being a symlink. +SHIM_HOME=$(mktemp -d) +trap 'rm -rf "$TEST_HOME" "$PI_HOME" "$OPENCODE_HOME" "$CACHE_HOME" "$FRESH_HOME" "$MALFORMED_HOME" "$UNWRITABLE_HOME" "$INTERRUPTED_HOME" "$LAUNCH_HOME" "$LINK_HOME" "$SHIM_HOME"' EXIT +mkdir -p "$SHIM_HOME/bin" "$SHIM_HOME/.local/share/mise/shims" +cat >"$SHIM_HOME/bin/mise" <>"$SHIM_HOME/mise-calls" +exit 1 +STUB +chmod +x "$SHIM_HOME/bin/mise" +ln -s "$SHIM_HOME/bin/mise" "$SHIM_HOME/.local/share/mise/shims/codex" + +result=$(HOME="$SHIM_HOME" CODEX_HOME="$SHIM_HOME/.codex" MISE_CALLS_FILE="$SHIM_HOME/mise-calls" XDG_DATA_HOME="$SHIM_HOME/.local/share" \ + PATH="$SHIM_HOME/bin:$SAFE_PATH" "$ROOT/bin/omarchy-agent-usage-codex" --limits-only) + +[[ $(jq -r '.usageStatusText' <<<"$result") == "Codex unavailable" ]] || + fail "Codex collector reports Codex unavailable when only a mise shim exists" "$result" +[[ ! -s $SHIM_HOME/mise-calls || $(cat "$SHIM_HOME/mise-calls") == "which codex" ]] || + fail "Codex collector must not execute a mise shim" "$(cat "$SHIM_HOME/mise-calls" 2>/dev/null)" +pass "Codex collector treats a shim symlink to mise as lazy" +