From 6ec4ca3f67702939c099d82d373efa9b32883540 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Mon, 8 Jun 2026 09:49:28 -0300 Subject: [PATCH] ci: speed up e2e shards with build and browser cache Upload the Next.js build from the build job and reuse it across E2E shards to avoid rebuilding in each shard. Increase Playwright sharding from 6 to 9, cache Chromium browsers, and lower the E2E timeout to match the faster expected runtime. Add a Codex CLI configuration skill for OmniRoute setup and ignore local credential-bearing setup prompts. --- .github/workflows/ci.yml | 35 +- .gitignore | 5 +- skills/config-codex-cli/SKILL.md | 367 ++++++++++++++++++ .../agent-skills/AgentSkillsPageClient.tsx | 8 +- .../agent-skills/components/SkillCard.tsx | 14 +- src/i18n/messages/en.json | 1 + src/i18n/messages/pt-BR.json | 1 + src/i18n/messages/ru.json | 1 + src/i18n/messages/zh-CN.json | 1 + src/lib/agentSkills/catalog.ts | 11 +- src/lib/agentSkills/types.ts | 5 +- src/shared/constants/agentSkills.ts | 13 + 12 files changed, 442 insertions(+), 20 deletions(-) create mode 100644 skills/config-codex-cli/SKILL.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 668c125dc7..80ce591759 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -144,6 +144,12 @@ jobs: - run: npm ci - run: npm run check:node-runtime - run: npm run build + - name: Upload Next.js build for E2E shards + uses: actions/upload-artifact@v4 + with: + name: e2e-next-build + path: .build/next/ + retention-days: 1 package-artifact: name: Package Artifact @@ -498,19 +504,18 @@ jobs: } test-e2e: - name: E2E Tests (${{ matrix.shard }}/6) + name: E2E Tests (${{ matrix.shard }}/9) runs-on: ubuntu-latest - # The heaviest shard (responsive viewport matrix + studio/smoke) re-runs - # `npm run build` (~5m) then ~24 serial tests; at 35m it was still cancelled - # mid-run, so it genuinely needs more wall-clock. 50m gives ample headroom - # while the per-test cap (playwright.config.ts) bounds any real hang to a fast - # visible failure and the `line` reporter streams which test is slow. - timeout-minutes: 50 + # Build artifact from the `build` job is downloaded instead of rebuilding + # (~5min saved per shard). 9 shards (up from 6) reduces tests per shard by + # ~33%. Playwright browser is cached across runs (~1.5min saved per shard). + # Heavy shard target: ≤20min (was ~40min). + timeout-minutes: 30 needs: build strategy: fail-fast: false matrix: - shard: [1, 2, 3, 4, 5, 6] + shard: [1, 2, 3, 4, 5, 6, 7, 8, 9] env: JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation API_KEY_SECRET: ci-test-api-key-secret-long @@ -524,9 +529,19 @@ jobs: cache: npm - run: npm ci - run: npm run check:node-runtime + - name: Cache Playwright browsers + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-chromium-${{ runner.os }}-${{ hashFiles('package-lock.json') }} + restore-keys: playwright-chromium-${{ runner.os }}- - run: npx playwright install --with-deps chromium - - run: npm run build - - run: npx playwright test tests/e2e/*.spec.ts --shard=${{ matrix.shard }}/6 + - name: Download Next.js build artifact + uses: actions/download-artifact@v4 + with: + name: e2e-next-build + path: .build/next/ + - run: npx playwright test tests/e2e/*.spec.ts --shard=${{ matrix.shard }}/9 test-integration: name: Integration Tests (${{ matrix.shard }}/2) diff --git a/.gitignore b/.gitignore index 746f05b94f..656b3a6f82 100644 --- a/.gitignore +++ b/.gitignore @@ -198,4 +198,7 @@ pr_reviews*.json #hidden local data directories (never commit) .local-data/ -.data-dev/ \ No newline at end of file +.data-dev/ + +# internal setup prompts with personal credentials — never commit +CODEX-SETUP-PROMPT.md \ No newline at end of file diff --git a/skills/config-codex-cli/SKILL.md b/skills/config-codex-cli/SKILL.md new file mode 100644 index 0000000000..db75f91d81 --- /dev/null +++ b/skills/config-codex-cli/SKILL.md @@ -0,0 +1,367 @@ +--- +name: config-codex-cli +description: Step-by-step agent workflow to configure the OpenAI Codex CLI on any machine (Linux, macOS, Windows) to use OmniRoute as backend. Detects OS and shell, writes config.toml and 7 named profiles, sets environment variables, and verifies the setup. +--- + +# /setup-codex-cli — Codex CLI Configuration Workflow + +Configure the Codex CLI on this machine to use an OmniRoute instance as backend. + +After this skill completes, `codex` will use OmniRoute by default with `cx/gpt-5.5` (xhigh reasoning), 7 named profiles for quick switching, and proper token limits configured for each model. + +--- + +## Step 0 — Collect required inputs from the user + +Before doing anything, ask the user for the two required values. Do not proceed until both are provided: + +1. **OmniRoute host** — the IP or hostname of the OmniRoute server (e.g. `192.168.0.1`, `100.x.x.x` for Tailscale, or `localhost`) +2. **OmniRoute API key** — the API key for OmniRoute (starts with `sk-`) + +Store these as local variables for the rest of the skill: +- `OMNI_HOST` = the host the user provided (no trailing slash, no port — port 20128 is appended by this skill) +- `OMNI_KEY` = the API key + +--- + +## Step 1 — Detect environment + +Run the following to gather machine facts. Store results — they are used in later steps. + +```bash +# OS detection +uname -s # Linux / Darwin (macOS) / MINGW*/CYGWIN*/MSYS* = Windows/Git Bash + +# Home directory +echo $HOME # Linux / macOS / Git Bash +echo $USERPROFILE # Windows native (PowerShell / cmd) + +# Current shell +echo $SHELL # Linux / macOS: /bin/bash, /bin/zsh, /bin/fish, etc. + +# Shell profile file +# Resolve which file to append env vars to: +# bash → ~/.bashrc (Linux) or ~/.bash_profile (macOS) +# zsh → ~/.zshrc +# fish → ~/.config/fish/config.fish +# PowerShell (Windows) → $PROFILE (run: echo $PROFILE inside PowerShell) + +# PATH and common tool directories (to populate shell_environment_policy later) +echo $PATH +which node 2>/dev/null && node --version +echo ${NVM_DIR:-not-set} +echo ${BUN_INSTALL:-not-set} +echo ${SDKMAN_DIR:-not-set} +echo ${JAVA_HOME:-not-set} +``` + +Based on the OS result, set the **Codex config directory**: +- Linux / macOS / Git Bash: `~/.codex/` +- Windows native (PowerShell): `$env:USERPROFILE\.codex\` + +--- + +## Step 2 — Verify Codex CLI is installed + +```bash +codex --version +``` + +If this fails, stop and tell the user to install the Codex CLI first: + +```bash +npm install -g @openai/codex +``` + +Then re-run the skill from Step 1. + +--- + +## Step 3 — Create the Codex config directory + +```bash +mkdir -p ~/.codex # Linux / macOS +# Windows PowerShell: New-Item -ItemType Directory -Force "$env:USERPROFILE\.codex" +``` + +--- + +## Step 4 — Write `~/.codex/config.toml` + +Read the existing file first if it exists (to avoid overwriting MCP server entries, skills, projects, or notify configurations the user may already have). + +If the file **does not exist**: create it with the full content below. + +If the file **already exists**: apply only the fields listed in the "Model/Inference", "Behaviour", "Auth/Credentials", "Features", "TUI", "Notice", and "Model providers" sections — do **not** remove existing `[mcp_servers.*]`, `[projects.*]`, `[[skills.config]]`, or `notify` entries. + +Replace `` with the value collected in Step 0. + +**Content to write (or merge):** + +```toml +# ── Model / Inference ───────────────────────────────────────────────────────── +model = "cx/gpt-5.5" +model_provider = "omniroute" +model_reasoning_effort = "xhigh" +model_reasoning_summary = "detailed" +model_verbosity = "high" +model_context_window = 400000 +model_auto_compact_token_limit = 350000 +model_max_output_tokens = 65536 +tool_output_token_limit = 32768 + +# ── Behaviour ───────────────────────────────────────────────────────────────── +approval_policy = "never" +sandbox_mode = "danger-full-access" +personality = "pragmatic" +web_search = "live" +check_for_update_on_startup = true + +# ── Auth / Credentials ──────────────────────────────────────────────────────── +cli_auth_credentials_store = "file" +mcp_oauth_credentials_store = "file" + +# ── Features ────────────────────────────────────────────────────────────────── +[features] +shell_snapshot = true +unified_exec = true +multi_agent = true +memories = true +js_repl = true +apps = false +terminal_resize_reflow = true + +# ── TUI ─────────────────────────────────────────────────────────────────────── +[tui] +theme = "dracula" +status_line = ["model-with-reasoning", "current-dir", "context-remaining", "context-used", "five-hour-limit"] + +[tui.model_availability_nux] +"gpt-5.5" = 4 + +# ── Shell environment passed into sandboxed commands ────────────────────────── +# Populate [shell_environment_policy.set] with the PATH and tool dirs discovered +# in Step 1. Only include paths that actually exist on this machine. +# Minimum required: SHELL. +[shell_environment_policy] +inherit = "all" +experimental_use_profile = true + +[shell_environment_policy.set] +SHELL = "" +# Add any of the following that exist on this machine: +# PATH = "" +# NVM_DIR = "" +# BUN_INSTALL = "" +# SDKMAN_DIR = "" +# JAVA_HOME = "" + +# ── Notice / UI flags ───────────────────────────────────────────────────────── +[notice] +hide_full_access_warning = true +hide_rate_limit_model_nudge = true +fast_default_opt_out = true + +# ── Model providers ─────────────────────────────────────────────────────────── +# env_key = NAME of the environment variable (not the value). +# The actual key is stored in the shell profile (Step 5), never here. +[model_providers.omniroute] +name = "OmniRoute" +base_url = "http://:20128/v1" +env_key = "OMNIROUTE_API_KEY" +requires_openai_auth = false +wire_api = "responses" +``` + +> **TOML rule:** `[[skills.config]]` array-of-tables must be the **last** section in the file. If the file already has `[[skills.config]]` entries, keep them at the end after inserting the new provider block. + +--- + +## Step 5 — Write profile files + +Create each file below in the Codex config directory. If a file already exists, overwrite it. + +### `profile-chat.config.toml` — no reasoning (server default = medium) + +```toml +model = "cx/gpt-5.5" +model_provider = "omniroute" +``` + +### `profile-low.config.toml` + +```toml +model = "cx/gpt-5.5" +model_reasoning_effort = "low" +model_provider = "omniroute" +``` + +### `profile-medium.config.toml` + +```toml +model = "cx/gpt-5.5" +model_reasoning_effort = "medium" +model_provider = "omniroute" +``` + +### `profile-high.config.toml` + +```toml +model = "cx/gpt-5.5" +model_reasoning_effort = "high" +model_provider = "omniroute" +``` + +### `profile-xhigh.config.toml` + +```toml +model = "cx/gpt-5.5" +model_reasoning_effort = "xhigh" +model_provider = "omniroute" +``` + +### `profile-deepseek.config.toml` — DeepSeek V4 Pro, 1M context + +```toml +model = "ds/deepseek-v4-pro" +model_provider = "omniroute" + +model_context_window = 1000000 +model_auto_compact_token_limit = 900000 +model_max_output_tokens = 65536 +tool_output_token_limit = 65536 +``` + +### `profile-mistral.config.toml` — Mistral Large Latest, 256k context + +```toml +model = "mistral/mistral-large-latest" +model_provider = "omniroute" + +model_context_window = 262144 +model_auto_compact_token_limit = 220000 +model_max_output_tokens = 32768 +tool_output_token_limit = 16384 +``` + +--- + +## Step 6 — Set environment variables in the shell profile + +Determine the correct shell profile file from Step 1, then append the following block **only if the variables are not already present**. + +Before writing, check: +```bash +grep -l "OMNIROUTE_API_KEY" ~/.bashrc ~/.zshrc ~/.bash_profile 2>/dev/null +``` + +If the variable already exists in any profile file, update the value in-place instead of appending a duplicate. + +**Block to append (replace `` with the key collected in Step 0):** + +```bash +# OmniRoute API key — used by Codex CLI (env_key = "OMNIROUTE_API_KEY" in config) +export OMNIROUTE_API_KEY="" + +# Codex CLI / Claude Code output cap (64k — covers any file or diff a coding assistant generates) +export CLAUDE_CODE_MAX_OUTPUT_TOKENS=65536 +``` + +**Shell profile file by OS/shell:** + +| OS / Shell | Profile file | +|------------|-------------| +| Linux — bash | `~/.bashrc` | +| Linux — zsh | `~/.zshrc` | +| macOS — zsh (default) | `~/.zshrc` | +| macOS — bash | `~/.bash_profile` | +| Linux/macOS — fish | `~/.config/fish/config.fish` (use `set -Ux` syntax instead of `export`) | +| Windows — PowerShell | `$PROFILE` (run `echo $PROFILE` in PowerShell to get the path) | +| Windows — Git Bash | `~/.bashrc` | + +**fish syntax:** + +```fish +set -Ux OMNIROUTE_API_KEY "" +set -Ux CLAUDE_CODE_MAX_OUTPUT_TOKENS 65536 +``` + +**PowerShell syntax:** + +```powershell +[System.Environment]::SetEnvironmentVariable("OMNIROUTE_API_KEY", "", "User") +[System.Environment]::SetEnvironmentVariable("CLAUDE_CODE_MAX_OUTPUT_TOKENS", "65536", "User") +``` + +--- + +## Step 7 — Apply and verify + +```bash +# Apply the shell profile (Linux/macOS) +source ~/.bashrc # or ~/.zshrc depending on Step 1 + +# Verify variables are set +echo $OMNIROUTE_API_KEY # must print the key +echo $CLAUDE_CODE_MAX_OUTPUT_TOKENS # must print 65536 + +# Verify Codex picks up the provider +codex config get model_provider # must print: omniroute + +# Smoke test — must return a response without auth errors +codex -p chat "reply with only the word OK" +``` + +If the smoke test returns an authentication error: +- Re-check that `source ~/.bashrc` was run (or open a new terminal) +- Run `curl http://:20128/v1/models` to confirm OmniRoute is reachable +- Confirm the key is correct with `echo $OMNIROUTE_API_KEY` + +--- + +## Reference — profiles quick table + +| Profile | Command | Best for | +|---------|---------|----------| +| `chat` | `codex -p chat "..."` | Explain, light questions | +| `low` | `codex -p low "..."` | Rename, format, trivial edits | +| `medium` | `codex -p medium "..."` | Debug, moderate refactor | +| `high` | `codex -p high "..."` | New features, complex tests | +| `xhigh` | *(default)* | Architecture, deep analysis | +| `deepseek` | `codex -p deepseek "..."` | Long codebase analysis (1M context) | +| `mistral` | `codex -p mistral "..."` | Cost-conscious tasks | + +Per-invocation overrides (bypass profiles entirely): + +```bash +codex -m cx/gpt-5.5 -c model_reasoning_effort=low "rename var x to count" +codex -m ds/deepseek-v4-pro "analyze the entire repo" +``` + +--- + +## Reference — why `wire_api = "responses"` works for all models + +Codex CLI deprecated `wire_api = "chat"` in February 2026. OmniRoute bridges the gap transparently: + +``` +Codex CLI → POST /v1/responses → OmniRoute → POST /chat/completions → DeepSeek / Mistral / any provider +``` + +All profiles use the same `wire_api = "responses"`. OmniRoute handles translation for every upstream provider. + +--- + +## Reference — token fields + +| Field | Controls | +|-------|----------| +| `model_context_window` | Total token budget | +| `model_auto_compact_token_limit` | Compaction trigger (max 90% of context window) | +| `model_max_output_tokens` | Max tokens per API response (sent on every request) | +| `tool_output_token_limit` | Max tokens stored per tool call in session history | +| `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | Same concept for the Claude Code CLI | + +--- + +*Full reference: `docs/guides/CODEX-CLI-CONFIGURATION.md` in the OmniRoute repository.* diff --git a/src/app/(dashboard)/dashboard/agent-skills/AgentSkillsPageClient.tsx b/src/app/(dashboard)/dashboard/agent-skills/AgentSkillsPageClient.tsx index 6521e0e3fe..3736347a7e 100644 --- a/src/app/(dashboard)/dashboard/agent-skills/AgentSkillsPageClient.tsx +++ b/src/app/(dashboard)/dashboard/agent-skills/AgentSkillsPageClient.tsx @@ -9,7 +9,7 @@ import { SkillCard } from "./components/SkillCard"; import { SkillPreviewPane } from "./components/SkillPreviewPane"; import type { AgentSkill, SkillCoverage } from "@/lib/agentSkills/types"; -type FilterCategory = "all" | "api" | "cli"; +type FilterCategory = "all" | "api" | "cli" | "config"; // ── Skeleton helpers ───────────────────────────────────────────────────────── @@ -238,7 +238,7 @@ export function AgentSkillsPageClient(): JSX.Element { />
- {(["all", "api", "cli"] as FilterCategory[]).map((cat) => ( + {(["all", "api", "cli", "config"] as FilterCategory[]).map((cat) => ( ))}
diff --git a/src/app/(dashboard)/dashboard/agent-skills/components/SkillCard.tsx b/src/app/(dashboard)/dashboard/agent-skills/components/SkillCard.tsx index aab1ac205f..61cd6b1331 100644 --- a/src/app/(dashboard)/dashboard/agent-skills/components/SkillCard.tsx +++ b/src/app/(dashboard)/dashboard/agent-skills/components/SkillCard.tsx @@ -26,7 +26,9 @@ export function SkillCard({ skill, selected, onClick }: SkillCardProps): JSX.Ele const previewItems: string[] = skill.category === "api" ? (skill.endpoints ?? []).slice(0, 2) - : (skill.cliCommands ?? []).slice(0, 2); + : skill.category === "cli" + ? (skill.cliCommands ?? []).slice(0, 2) + : []; return (
- {skill.category === "api" ? t("categoryApi") : t("categoryCli")} + {skill.category === "api" + ? t("categoryApi") + : skill.category === "cli" + ? t("categoryCli") + : t("categoryConfig")} {skill.isEntry && ( diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 3c04b237ba..bbdc7fd292 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -8596,6 +8596,7 @@ }, "categoryApi": "API", "categoryCli": "CLI", + "categoryConfig": "Config", "filterAll": "All", "coverageLabel": "Coverage", "mcpUrl": "MCP URL", diff --git a/src/i18n/messages/pt-BR.json b/src/i18n/messages/pt-BR.json index b7783e797a..30adc6ad2c 100644 --- a/src/i18n/messages/pt-BR.json +++ b/src/i18n/messages/pt-BR.json @@ -8522,6 +8522,7 @@ }, "categoryApi": "API", "categoryCli": "CLI", + "categoryConfig": "Config", "filterAll": "Todas", "coverageLabel": "Cobertura", "mcpUrl": "URL MCP", diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index a800474c1b..37f752d4e4 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -8477,6 +8477,7 @@ }, "categoryApi": "API", "categoryCli": "CLI", + "categoryConfig": "Config", "filterAll": "Все", "coverageLabel": "Покрытие", "mcpUrl": "MCP URL", diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index 4868ac7cb8..844e0492e1 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -8477,6 +8477,7 @@ }, "categoryApi": "API", "categoryCli": "命令行界面", + "categoryConfig": "Config", "filterAll": "所有", "coverageLabel": "覆盖率", "mcpUrl": "MCP URL", diff --git a/src/lib/agentSkills/catalog.ts b/src/lib/agentSkills/catalog.ts index 05e8c7119d..bde35158b7 100644 --- a/src/lib/agentSkills/catalog.ts +++ b/src/lib/agentSkills/catalog.ts @@ -38,6 +38,11 @@ export const API_SKILL_IDS: readonly string[] = [ "omni-inference", ] as const; +/** Config skill IDs. */ +export const CONFIG_SKILL_IDS: readonly string[] = [ + "config-codex-cli", +] as const; + /** 20 canonical CLI skill IDs, in spec order. */ export const CLI_SKILL_IDS: readonly string[] = [ "cli-serve", @@ -138,11 +143,14 @@ export function computeCoverage(): SkillCoverage { const apiHave = catalog.filter((s) => s.category === "api" && presentIds.has(s.id)).length; const cliHave = catalog.filter((s) => s.category === "cli" && presentIds.has(s.id)).length; + const configTotal = CONFIG_SKILL_IDS.length; + const configHave = catalog.filter((s) => s.category === "config" && presentIds.has(s.id)).length; return { api: { have: apiHave, total: 22 }, cli: { have: cliHave, total: 20 }, - totalSkills: apiHave + cliHave, + config: { have: configHave, total: configTotal }, + totalSkills: apiHave + cliHave + configHave, generatedAt: new Date().toISOString(), }; } @@ -190,7 +198,6 @@ export async function fetchSkillMarkdown(id: string): Promise { } const response = await fetch(skill.rawUrl, { - // @ts-expect-error — Next.js extended fetch options next: { revalidate: 3600 }, }); diff --git a/src/lib/agentSkills/types.ts b/src/lib/agentSkills/types.ts index d187ff1566..75f43fcdc3 100644 --- a/src/lib/agentSkills/types.ts +++ b/src/lib/agentSkills/types.ts @@ -1,4 +1,4 @@ -export type SkillCategory = "api" | "cli"; +export type SkillCategory = "api" | "cli" | "config"; export type SkillArea = // API areas (22) @@ -24,6 +24,8 @@ export type SkillArea = | "agents-a2a" | "version-manager" | "inference" + // Config skills + | "config-codex-cli" // CLI families (20) | "cli-serve" | "cli-health" @@ -64,6 +66,7 @@ export interface AgentSkill { export interface SkillCoverage { api: { have: number; total: 22 }; cli: { have: number; total: 20 }; + config: { have: number; total: number }; totalSkills: number; // sum generatedAt: string; // ISO datetime } diff --git a/src/shared/constants/agentSkills.ts b/src/shared/constants/agentSkills.ts index 0d92d10a80..5a8407320e 100644 --- a/src/shared/constants/agentSkills.ts +++ b/src/shared/constants/agentSkills.ts @@ -429,5 +429,18 @@ export const CURATED_SKILLS: CuratedSkillEntry[] = [ area: "cli-setup", icon: "build", }, + + // ── Config Skills ──────────────────────────────────────────────────────────── + + { + id: "config-codex-cli", + name: "Config: Codex CLI", + description: + "Step-by-step agent workflow to configure the OpenAI Codex CLI on any machine (Linux, macOS, Windows) to use OmniRoute as an OpenAI-compatible backend. Detects OS and shell, writes config.toml and 7 named profiles, sets environment variables, and verifies the setup.", + category: "config", + area: "config-codex-cli", + icon: "terminal", + isNew: true, + }, ];