feat(cli): Codex CLI launcher + setup commands - #4270
Conversation
Two new CLI subcommands mirroring the `launch`/`configure` pattern, for driving the OpenAI Codex CLI against OmniRoute: - `omniroute launch-codex` — boots OmniRoute (if needed) and launches Codex CLI pointed at it (local or remote VPS), with no manual env/config editing. - `omniroute setup-codex` — generates ~/.codex profile files from OmniRoute's live model catalog. Registered in the command registry; en.json + pt-BR.json locale sections added (keeps the cli-i18n-catalog top-level parity gate green). CODEX-CLI-CONFIGURATION.md refreshed. Consolidated from work-in-progress that was uncommitted on the shared checkout; reconstructed on release/v3.8.30.
Disable VS Code's git auto-repository-detection / submodule scan / autofetch so the Source Control view stops indexing the ~44 nested repos (worktrees + _references/* + _mono_repo/*), which caused constant "validating" churn. Only the root repo is tracked. Editor-only settings; no runtime impact.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Code Review
This pull request introduces two new CLI commands, launch-codex and setup-codex, along with localized descriptions, documentation updates, and VS Code settings to optimize Git performance. The feedback highlights several areas for improvement: making URL parsing more robust against trailing slashes in both commands, aligning the model categorization logic in setup-codex.mjs with the updated documentation, and adding safer error handling and response parsing to prevent potential runtime crashes. Additionally, the reviewer notes that corresponding tests should be added for these new production commands to comply with the repository's style guide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| */ | ||
| async function healthCheck(baseUrl, timeoutMs = 3000) { | ||
| try { | ||
| const res = await fetch(`${baseUrl.replace(/\/v1$/, "")}/api/monitoring/health`, { |
There was a problem hiding this comment.
If baseUrl has a trailing slash (e.g., http://localhost:20128/v1/), the regex \/\/v1$/ will not match, leaving the trailing slash and resulting in a malformed URL like http://localhost:20128/v1//api/monitoring/health.
We can make this more robust by handling optional trailing slashes.
| const res = await fetch(`${baseUrl.replace(/\/v1$/, "")}/api/monitoring/health`, { | |
| const cleanBase = baseUrl.replace(/\/v1\/?$/, "").replace(/\/$/, ""); | |
| const res = await fetch(`${cleanBase}/api/monitoring/health`, { |
| */ | ||
| export async function runSetupCodexCommand(opts = {}) { | ||
| const port = Number(opts.port ?? process.env.PORT ?? 20128) || 20128; | ||
| const baseUrl = (opts.remote ?? `http://localhost:${port}`).replace(/\/v1$/, ""); |
There was a problem hiding this comment.
Similar to the health check, if opts.remote contains a trailing slash (e.g., http://100.67.86.91:20128/v1/), the regex \/\/v1$/ will fail to strip the /v1/ path correctly. Let's make this robust against trailing slashes.
| const baseUrl = (opts.remote ?? `http://localhost:${port}`).replace(/\/v1$/, ""); | |
| const baseUrl = (opts.remote ?? `http://localhost:${port}`).replace(/\/v1\/?$/, "").replace(/\/$/, ""); |
| const thinkingPatterns = [ | ||
| { re: /kmc\/kimi-k2\.7/, name: "kimi-k27", ctx: 131072, compact: 112000, toolLimit: 32768 }, | ||
| { re: /kmc\/kimi-k2\.6/, name: "kimi-k26", ctx: 131072, compact: 112000, toolLimit: 32768 }, | ||
| { re: /glm\/glm-5\.2-max/, name: "glm52max", ctx: 131072, compact: 112000, toolLimit: 32768 }, | ||
| { re: /glm\/glm-5\.2$/, name: "glm52", ctx: 131072, compact: 112000, toolLimit: 32768 }, | ||
| { re: /opencode-go\/mimo-v2\.5-pro/, name: "mimo-pro", ctx: 131072, compact: 112000, toolLimit: 32768 }, | ||
| { re: /opencode-go\/qwen3\.7-plus/, name: "qwen37plus", ctx: 32768, compact: 28000, toolLimit: 16384 }, | ||
| ]; | ||
|
|
||
| // ── Good models (high effort) ───────────────────────────────────────────── | ||
| const goodPatterns = [ | ||
| { re: /ollamacloud\/deepseek-v4-pro/, name: "deepseek-pro", ctx: 131072, compact: 112000, toolLimit: 32768 }, | ||
| { re: /opencode-go\/mimo-v2\.5$/, name: "mimo", ctx: 131072, compact: 112000, toolLimit: 32768 }, | ||
| ]; | ||
|
|
||
| // ── Simple models (no effort) ───────────────────────────────────────────── | ||
| const simplePatterns = [ | ||
| { re: /ollamacloud\/gemma4:31b/, name: "gemma4", ctx: 32768, compact: 28000, toolLimit: 16384 }, | ||
| { re: /ollamacloud\/nemotron-3-super/, name: "nemotron", ctx: 32768, compact: 28000, toolLimit: 16384 }, | ||
| { re: /ollamacloud\/gpt-oss:20b/, name: "gptoss", ctx: 32768, compact: 28000, toolLimit: 16384 }, | ||
| ]; | ||
|
|
||
| // ── Fast models (low effort) ────────────────────────────────────────────── | ||
| const fastPatterns = [ | ||
| { re: /ollamacloud\/deepseek-v4-flash/, name: "deepseek-flash", ctx: 65536, compact: 56000, toolLimit: 16384 }, | ||
| { re: /ollamacloud\/gemini-3-flash/, name: "gemini-flash", ctx: 1000000, compact: 850000, toolLimit: 32768 }, | ||
| { re: /glm\/glm-5-turbo/, name: "glm5turbo", ctx: 131072, compact: 112000, toolLimit: 16384 }, | ||
| { re: /glm\/glm-4\.7-flash/, name: "glm47flash", ctx: 131072, compact: 112000, toolLimit: 16384 }, | ||
| ]; |
There was a problem hiding this comment.
There are a few discrepancies between the model categorization patterns defined here and the documentation in CODEX-CLI-CONFIGURATION.md:
kimi-k2.6: Listed underthinkingPatternsin the code (which assignseffort: "xhigh"andsummary: true), but documented under### Good models (bons) — high effortin the markdown. It should be moved togoodPatterns.ds/deepseek-v4-pro: Documented under### Good models (bons) — high effortwith a 1M context window, but completely missing from the code patterns.mistral/mistral-large-latest: Documented under### Fast models (rápidos) — low effortwith a 256k context window, but completely missing from the code patterns.
Let's update the patterns to align with the documentation.
const thinkingPatterns = [
{ re: /kmc\/kimi-k2\.7/, name: "kimi-k27", ctx: 131072, compact: 112000, toolLimit: 32768 },
{ re: /glm\/glm-5\.2-max/, name: "glm52max", ctx: 131072, compact: 112000, toolLimit: 32768 },
{ re: /glm\/glm-5\.2$/, name: "glm52", ctx: 131072, compact: 112000, toolLimit: 32768 },
{ re: /opencode-go\/mimo-v2\.5-pro/, name: "mimo-pro", ctx: 131072, compact: 112000, toolLimit: 32768 },
{ re: /opencode-go\/qwen3\.7-plus/, name: "qwen37plus", ctx: 32768, compact: 28000, toolLimit: 16384 },
];
// ── Good models (high effort) ─────────────────────────────────────────────
const goodPatterns = [
{ re: /kmc\/kimi-k2\.6/, name: "kimi-k26", ctx: 131072, compact: 112000, toolLimit: 32768 },
{ re: /ollamacloud\/deepseek-v4-pro/, name: "deepseek-pro", ctx: 131072, compact: 112000, toolLimit: 32768 },
{ re: /ds\/deepseek-v4-pro/, name: "deepseek", ctx: 1000000, compact: 900000, toolLimit: 65536 },
{ re: /opencode-go\/mimo-v2\.5/, name: "mimo", ctx: 131072, compact: 112000, toolLimit: 32768 },
];
// ── Simple models (no effort) ─────────────────────────────────────────────
const simplePatterns = [
{ re: /ollamacloud\/gemma4:31b/, name: "gemma4", ctx: 32768, compact: 28000, toolLimit: 16384 },
{ re: /ollamacloud\/nemotron-3-super/, name: "nemotron", ctx: 32768, compact: 28000, toolLimit: 16384 },
{ re: /ollamacloud\/gpt-oss:20b/, name: "gptoss", ctx: 32768, compact: 28000, toolLimit: 16384 },
];
// ── Fast models (low effort) ──────────────────────────────────────────────
const fastPatterns = [
{ re: /ollamacloud\/deepseek-v4-flash/, name: "deepseek-flash", ctx: 65536, compact: 56000, toolLimit: 16384 },
{ re: /ollamacloud\/gemini-3-flash/, name: "gemini-flash", ctx: 1000000, compact: 850000, toolLimit: 32768 },
{ re: /glm\/glm-5-turbo/, name: "glm5turbo", ctx: 131072, compact: 112000, toolLimit: 16384 },
{ re: /glm\/glm-4\.7-flash/, name: "glm47flash", ctx: 131072, compact: 112000, toolLimit: 16384 },
{ re: /mistral\/mistral-large-latest/, name: "mistral", ctx: 262144, compact: 220000, toolLimit: 16384 },
];| }); | ||
| if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`); | ||
| const body = await res.json(); | ||
| models = body.data ?? body.models ?? []; |
There was a problem hiding this comment.
To make this more robust against different server response formats (e.g., if the server returns a direct JSON array instead of an object, or if body is nullish), we should perform a safer check.
| models = body.data ?? body.models ?? []; | |
| models = Array.isArray(body) ? body : (body?.data ?? body?.models ?? []); |
| const body = await res.json(); | ||
| models = body.data ?? body.models ?? []; | ||
| } catch (err) { | ||
| printError(`Failed to fetch models: ${err.message}`); |
There was a problem hiding this comment.
If err is not an object or is null/undefined, accessing err.message will throw a TypeError and crash the process. Let's safely access the message or fall back to the string representation of the error.
| printError(`Failed to fetch models: ${err.message}`); | |
| printError(`Failed to fetch models: ${err?.message || String(err)}`); |
| import { registerLaunchCodex } from "./launch-codex.mjs"; | ||
| import { registerSetupCodex } from "./setup-codex.mjs"; |
There was a problem hiding this comment.
According to the Repository Style Guide (Rule 9 under 2. Hard Rules):
Always include tests when changing production code (
src/,open-sse/,electron/,bin/).
Since this PR introduces new production CLI commands in bin/cli/commands/, please ensure that corresponding unit or integration tests are added within the tests/ directory to maintain coverage.
References
- Always include tests when changing production code (src/, open-sse/, electron/, bin/). (link)
* feat(cli): Codex CLI launcher + setup commands Two new CLI subcommands mirroring the `launch`/`configure` pattern, for driving the OpenAI Codex CLI against OmniRoute: - `omniroute launch-codex` — boots OmniRoute (if needed) and launches Codex CLI pointed at it (local or remote VPS), with no manual env/config editing. - `omniroute setup-codex` — generates ~/.codex profile files from OmniRoute's live model catalog. Registered in the command registry; en.json + pt-BR.json locale sections added (keeps the cli-i18n-catalog top-level parity gate green). CODEX-CLI-CONFIGURATION.md refreshed. Consolidated from work-in-progress that was uncommitted on the shared checkout; reconstructed on release/v3.8.30. * chore(vscode): reduce git repo-detection overhead for nested worktrees Disable VS Code's git auto-repository-detection / submodule scan / autofetch so the Source Control view stops indexing the ~44 nested repos (worktrees + _references/* + _mono_repo/*), which caused constant "validating" churn. Only the root repo is tracked. Editor-only settings; no runtime impact.
Consolidates work-in-progress that was sitting uncommitted on the shared checkout into a tracked PR (per operator request during the v3.8.29 release wrap-up). Reconstructed cleanly on
release/v3.8.30.What
omniroute launch-codex— boots OmniRoute (if needed) and launches the OpenAI Codex CLI pointed at it (local or remote VPS), no manual env/config editing.omniroute setup-codex— generates~/.codexprofile files from OmniRoute's live model catalog.en.json+pt-BR.jsonlocale sections added (keeps thecli-i18n-catalogtop-level parity gate green);CODEX-CLI-CONFIGURATION.mdrefreshed..vscode/settings.json— disables git auto-repo-detection for the nested worktrees (perf; editor-only).Validation
registerCommandsintactcli-i18n-catalogtest 7/7 (en keys complete + pt-BR parity)