Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion bin/omarchy-agent-usage-codex
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,66 @@ 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 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 = 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)
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:
# `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
)
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)]
Expand Down Expand Up @@ -520,7 +580,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"
Expand Down
100 changes: 100 additions & 0 deletions test/shell.d/agent-usage-codex-scanner-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -603,3 +603,103 @@ 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. 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'
#!/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" <<STUB
#!/bin/bash
printf '%s\n' "\$*" >>"$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" <<STUB
#!/bin/bash
printf '%s\n' "\$*" >>"$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"

# 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" <<STUB
#!/bin/bash
printf '%s\n' "\$*" >>"$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"