Skip to content

Commit d0addd9

Browse files
oranmizrahindycode
andauthored
feat: add mcodex launcher statusline (#500)
* feat: add mcodex launcher statusline * Improve mcodex tmux scrollback * Route wheel scroll to tmux history * fix(mcodex): close review findings on the launcher + statusline Security: - scripts/mcodex: validate MCODEX_MONITOR_INTERVAL (and MCODEX_TMUX_HISTORY_LIMIT) as numeric before they are interpolated into the `watch -n <n> ...` / tmux option strings that tmux hands to a shell. A value like "5; rm -rf ~" or "$(cmd)" was previously executed verbatim; it is now rejected and replaced with the safe default. Closes the command-injection findings on lines 53/68. Correctness: - scripts/codex.js maybeRefreshQuotaCacheInBackground: the status-refresh child's close/error handlers called the Atomics.wait-backed removeDirectoryWithRetry, blocking the PARENT event loop up to ~200ms on a transient Windows lock while Codex was running. Added removeDirectoryBestEffortAsync (fsPromises.rm with internal retry, no blocking sleep) and use it in those callbacks. - scripts/codex.js formatStatusPath: the `~` abbreviation used a hardcoded "/" separator, so on Windows (backslash paths from resolvePath) the cwd was never abbreviated. Use path.sep for the boundary check and normalize the remainder to forward slashes for a stable status line. mcodex polish: - quote_args no longer emits a trailing space (avoids an empty trailing token in the interpolated tmux command). - the tmux new-session command is built in a variable from printf %q-escaped args so arguments with embedded quotes/spaces are passed safely. - --monitor no longer forwards unused "$@"; configure_tmux_scrollback redirects stderr too for consistent silence. Docs / known-race notes: - documented the bounded TOCTOU in the stale-lock recovery (a slow-but-alive refresh owner can be evicted, briefly yielding two idempotent refresh children) and the detached-child caveat (close/error cleanup may not fire if the parent exits first; 10-minute stale-lock recovery reclaims the lock). Tests: - test/documentation.test.ts: add `mcodex` to the canonical bin surface the PR introduced (the assertion was left stale by the PR and was failing). - test/mcodex-launcher.test.ts (new): regression for the interval-injection guard — valid integer/fractional pass through; injection payloads and shell substitutions collapse to the safe default; asserts the shipped script still contains the numeric guard. Skips where bash is unavailable. typecheck + lint clean; full suite 4067 passed. * fix(mcodex): status line reads the per-project account pool P1 (Greptile): the forwarded status line resolved accounts via the global `resolveAccountsPath` (~/.codex/multi-auth/openai-codex-accounts.json), so inside a project with a per-project pool it either showed no status line (global pool empty) or showed the wrong account/quota — silently contradicting the account Codex actually routes through. Fix: add `resolveStatusAccountsDir`, which mirrors the runtime's own account scoping (lib/runtime/account-scope.ts) by reusing the built dist helpers (never re-deriving project keys from raw paths, per AGENTS.md): - perProjectAccounts enabled + Codex CLI sync OFF + cwd resolves to a project root → per-project pool via getProjectGlobalConfigDir( resolveProjectStorageIdentityRoot(projectRoot)); - otherwise (no project, sync on, config off, or dist unavailable) → global. Only the ACCOUNTS pool is per-project; quota-cache.json and runtime-observability.json stay global (lib: getCodexMultiAuthDir), so those reads are unchanged. `maybePrintForwardStatusLine` is now async to resolve the dir before formatting; any failure falls back to the global dir so the launcher never breaks. Note: the per-project pool is grounded in the dist config dir, which itself honors CODEX_MULTI_AUTH_DIR / CODEX_HOME — so no special-casing of those envs here, or the status line would diverge from where the runtime writes. test/mcodex-statusline-scope.test.ts: end-to-end regressions over the real wrapper + dist build — per-project pool is shown inside a project, global pool outside one, and global when Codex CLI sync is enabled (matching account-scope). typecheck + lint clean; full suite 4070 passed. * fix(mcodex): gate monitor modes on `watch` + assert bin publishing Follow-up review findings: - scripts/mcodex (Major): `watch` was invoked unconditionally in run_monitor and the two tmux live-account panes, with no equivalent of the existing `tmux` guard. Added require_watch; --monitor now fails fast with an actionable error and propagates a non-zero exit (exit $? instead of exit 0), and each live pane is gated on `&& require_watch` so a missing `watch` skips the pane instead of spawning a broken one (Codex still launches). - test/documentation.test.ts (Minor): the bin assertion locked the new `mcodex` entry but nothing verified package.json files[] publishes the shim, so npm could ship a bin pointing at a missing script while the test passed. Now every declared bin target is asserted present in files[]. Tests: test/mcodex-launcher.test.ts gains a missing-`watch` runtime guard case (drives require_watch with `watch` shadowed absent → non-zero + clear error) and a static check that all three watch sites are wired through require_watch. typecheck + lint clean; full suite 4072 passed. --------- Co-authored-by: Neil Daquioag <devzeian@gmail.com>
1 parent d930ac8 commit d0addd9

6 files changed

Lines changed: 933 additions & 1 deletion

File tree

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
"prepare": "husky"
115115
},
116116
"bin": {
117+
"mcodex": "scripts/mcodex",
117118
"codex-multi-auth-codex": "scripts/codex.js",
118119
"codex-multi-auth-app-launcher": "scripts/codex-app-launcher.js",
119120
"codex-multi-auth": "scripts/codex-multi-auth.js"
@@ -128,6 +129,7 @@
128129
"scripts/codex-bin-resolver.js",
129130
"scripts/codex-multi-auth.js",
130131
"scripts/codex-routing.js",
132+
"scripts/mcodex",
131133
"scripts/install-codex-auth-utils.js",
132134
"scripts/postinstall.js",
133135
"scripts/preuninstall.js",

0 commit comments

Comments
 (0)