fix(codex): use global runtime launcher without symlinks#753
Conversation
Add a Codex marketplace launcher that prefers the global npm context-mode runtime and falls back to the materialized plugin cache so Windows installs avoid git-tracked symlink shims. Constraint: keep Codex discovery files real while only delegating MCP and hooks at runtime Tested: pnpm exec vitest run tests/plugins/codex-manifest.test.ts tests/plugins/codex-global-runtime.test.ts tests/codex/marketplace-layout.test.ts Tested: pnpm exec tsc --noEmit Tested: npm pack plus npm install -g tarball on Windows; APPDATA npm runtime selected and hook/MCP smoke passed Co-authored-by: OmX <omx@oh-my-codex.dev>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR introduces a Codex “global runtime” launcher shim so the marketplace plugin can delegate MCP + hooks execution to a globally installed context-mode runtime when available, while retaining a plugin-cache fallback.
Changes:
- Updated Codex MCP + hooks manifests to invoke
.codex-plugin/global-runtime.mjsinstead of direct entrypoints. - Added a new
.codex-plugin/global-runtime.mjsresolver/launcher that discovers a global runtime via env vars, standard npm-global locations, and (for MCP)npm root -g. - Expanded/added Vitest coverage for the new launcher behavior and updated documentation to describe the global-runtime mode.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/plugins/codex-manifest.test.ts | Updates manifest expectations to validate the new global-runtime shim wiring. |
| tests/plugins/codex-global-runtime.test.ts | Adds focused tests for runtime root discovery and entrypoint mapping. |
| docs/platform-support.md | Documents the “thin launcher” behavior and global runtime resolution order. |
| README.md | Adds user-facing explanation of global runtime mode and fallback behavior. |
| .codex-plugin/mcp.json | Switches MCP server args to run the global-runtime shim. |
| .codex-plugin/hooks.json | Switches hook commands to route through the global-runtime shim. |
| .codex-plugin/global-runtime.mjs | Implements runtime discovery + delegation for MCP and hooks. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const REPO_ROOT = resolve(__dirname, "..", ".."); | ||
| const runtimeModule = await import(pathToFileURL(resolve(REPO_ROOT, ".codex-plugin/global-runtime.mjs")).href); |
| if (platform === "win32") { | ||
| pushUnique(candidates, seen, join(env.APPDATA || "", "npm", "node_modules", PACKAGE_NAME), "APPDATA npm"); | ||
| } else { |
| function pushUnique(candidates, seen, rawPath, source) { | ||
| const path = trimPath(rawPath); | ||
| if (!path) return; | ||
| const root = resolve(path); | ||
| const key = root.toLowerCase(); | ||
| if (seen.has(key)) return; | ||
| seen.add(key); | ||
| candidates.push({ root, source }); | ||
| } |
| pushUnique(candidates, seen, join(homedir(), ".npm-global", "lib", "node_modules", PACKAGE_NAME), "home npm-global"); | ||
| pushUnique(candidates, seen, "/usr/local/lib/node_modules/context-mode", "usr-local npm"); |
|
@ken-jo CI has error |
|
@mksglu I'm fixing it |
Address Copilot review by guarding empty APPDATA candidates, preserving case-sensitive path dedupe, and making runtime tests compare canonical macOS realpaths. Constraint: keep hook startup free of npm root probes while preserving plugin-cache fallback Tested: pnpm exec vitest run tests/plugins/codex-manifest.test.ts tests/plugins/codex-global-runtime.test.ts tests/codex/marketplace-layout.test.ts Tested: pnpm exec tsc --noEmit Tested: node --check .codex-plugin/global-runtime.mjs Co-authored-by: OmX <omx@oh-my-codex.dev>
|
Follow-up commit
Local verification: |
|
@mksglu @ken-jo tested this on Windows. Ran the PR test set + extra Codex hook/routing coverage:
Manual Codex install with a fresh temp Looks good on my Windows side. |
What / Why / How
Fixes #752.
What:
This PR makes the Codex marketplace plugin a thin launcher for the runtime code. The Codex marketplace package still exposes real discovery files (
.codex-plugin/plugin.json,.codex-plugin/mcp.json,.codex-plugin/hooks.json), but MCP and hook commands now go through.codex-plugin/global-runtime.mjs.Why:
The previous Windows marketplace failure came from relying on a Git-tracked symlink shim (
plugins/context-mode -> ..). Native Windows checkouts can materialize that shim as a regular file containing.., which makes Codex install fail withmissing plugin.json. The currenturl: "./"marketplace layout fixes discovery by avoiding the symlink, but Codex still benefits from a single global runtime model: users should be able to run onenpm install -g context-modeand have Codex use the same runtime as the other supported hosts.How:
.codex-plugin/global-runtime.mjs..codex-plugin/mcp.jsonto launchnode ./.codex-plugin/global-runtime.mjs mcp..codex-plugin/hooks.jsonto launchnode "${PLUGIN_ROOT}/.codex-plugin/global-runtime.mjs" hook <event>.CONTEXT_MODE_GLOBAL_ROOTCONTEXT_MODE_RUNTIME_ROOT%APPDATA%\npm\node_modules\context-mode/ standard Unix global locationsnpm root -gduring MCP startupnpm root -gon every hook call; hooks stay on explicit env vars, standard locations, and cache fallback.CONTEXT_MODE_PLATFORM=codexbefore delegating.CONTEXT_MODE_EFFECTIVE_ROOTfor debugging.This keeps Codex marketplace installation symlink-free while allowing a global npm install to be the single source of runtime truth.
Affected platforms
Test plan
tests/plugins/codex-global-runtime.test.tsfor env override, npm-global discovery, Windows%APPDATA%\npm\node_modules\context-modediscovery, plugin-cache fallback, entry path generation, and Codex hook event name mapping.tests/plugins/codex-manifest.test.tsso Codex MCP and hooks are pinned to the new global runtime shim contract.npm pack --pack-destination C:\tmp\context-mode-pack-testnpm install -g C:\tmp\context-mode-pack-test\context-mode-1.0.161.tgzsource: "APPDATA npm"and rootC:\Users\ikcha\AppData\Roaming\npm\node_modules\context-modehook pretooluseglobal-runtime.mjs mcpremains alive without stderrcontext-mode doctorand confirmed storage, server test, FTS5/SQLite, and npm version checks passCommands run:
Checklist
npm testpassesnpm run typecheckpassesnextbranch (unless hotfix)Cross-platform notes
Our CI runs on Ubuntu, macOS, and Windows.
path.join()/path.resolve().${PLUGIN_ROOT}only where Codex hook command interpolation already exists; MCP args still avoid placeholders because Codex does not expand them.%APPDATA%\npm\node_modules\context-modeas a standard location and was validated with an actual tarball global install.npm root -gon every hook invocation.