17 de 18 checks verdes, **zero falhas** — incluindo os quatro shards de unit, Vitest, CodeQL, semgrep, Docs Gates, Merge integrity e o **Fast Quality Gates**, que era exatamente o que o rebaseline do `RoutingTab.tsx` (1606→1607, a linha do seletor que acompanha a nova versão de identidade) veio consertar.
O único check restante, `No new ESLint warnings`, ficou enfileirado sem iniciar (duração 0) atrás da saturação de runners de hoje. Cobri os dois comandos que ele executa, localmente e sobre este HEAD:
- `npm run check:codeql-ratchet` → **0 alertas abertos** contra baseline 6 (sem regressão).
- `npx eslint --max-warnings 0` nos 9 arquivos de código que esta branch altera → **exit 0**, nenhum warning.
Conteúdo: os dois commits do #12402 que não estavam subsumidos, com autoria do @ggiak preservada pelo cherry-pick `-x`, mais o rebaseline e o fragmento de changelog que faltava. `typecheck:core` limpo e **138/138** nos testes focados. As três versões (`claudeCodeClient.ts`, `Dockerfile`, `compose.yml`) conferem em 2.1.258, e `npm view @anthropic-ai/claude-code@2.1.258` resolve — o Dockerfile instala esse pin exato.
The playwright:v1.62.0-noble base ships Chromium as a Chrome for Testing build, which extracts to chrome-linux64/chrome. The CMD's find -path '*/chrome-linux/chrome' matched nothing, $chrome_path came out empty, and the container crash-looped on `exec: --headless=new: not found`. Widening the glob to '*/chrome-linux*/chrome' resolves both the legacy and the Chrome for Testing layout.
Validated in a combined worktree with all 25 PRs of this batch boarded together: typecheck:core clean, 443/443 node-runner tests plus 14/14 vitest across every test file the batch touches, and check-changelog-integrity, check:cycles (418 files), check:provider-consistency (272 REGISTRY entries, 355 canonical providers), check:docs-counts, check:docs-sync (42 locales) and check-file-size all green.
Thanks @pacocartones.
Refresh the existing MIT-licensed miuuyy/codex-chatgpt-web vendor snapshot and its OmniRoute integration as one reviewable change.
Co-authored-by: backryun <backryun@daonlab.local>
* Bypass proxy compaction for native Codex context
* Add native ChatGPT Web provider pipeline
* Add managed browser and tunnel deployment
* Add ChatGPT Web setup and doctor UI
* Document and test ChatGPT Web integration
* fix(security): register chatgpt-web-codex-doctor in LOCAL_ONLY_API_PATTERNS
The diagnostic route under /api/providers/{id}/chatgpt-web-codex-doctor
was not registered in the spawn-capable route guard. Adding it for
parity with the existing /login pattern.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* fix(providers): route chatgpt-web-codex admin routes through a service boundary
The provider CRUD/doctor routes imported chatgpt-web-codex helpers
(finalizeValidatedChatGptWebCodexSecrets, encode/decodeChatGptWebCodexSecrets,
getChatGptWebCodexDoctorStatus) directly from open-sse/executors/**, which
no-restricted-imports (EXECUTOR_IMPORT_RESTRICTION) forbids for src/app/**
files — executor implementations must stay behind an open-sse handler or
service boundary.
Add open-sse/services/chatgptWebCodexAdmin.ts as a thin re-export boundary
(mirroring the existing tokenRefresh.ts re-export pattern) and import from
there instead. No behavior change.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* chore(ci): add .mergify.yml to main — Mergify only reads config from the default branch (#7168)
* feat(vnc-session): persistent noVNC browser login for web cookie/token providers
## Why (the headless-install problem)
OmniRoute's web cookie/token providers (ChatGPT Web, Gemini Web, Claude
Web, DeepSeek Web, …) need a live browser session, but the gateway normally
runs **headless** — as a systemd service, inside Docker, or on a VPS with no
display. There is no desktop for the operator to log into the provider in.
Today the operator has to obtain the session cookie/token *out of band* (open a
real browser elsewhere, export cookies, paste them into the connection row). That
is fiddly, breaks on every provider UI change, and is a non-starter on a
headless box where you can't open a browser at all.
This PR adds an **on-demand interactive login**: OmniRoute boots a
containerized browser that exposes a noVNC web UI at the host. The operator
opens that URL in *their own* browser, logs in normally, and OmniRoute then
harvests the resulting cookies / localStorage back into the provider's
`provider_connections` row over the DevTools Protocol. No display required on
the host — the headless server renders the login into a container and the human
just drives it through a web page.
## How we ran into this
- The shipped `dist/` bundle has **no App Router source**, so the only visible
seam was `dist/server-ws.mjs`'s `http.createServer` monkeypatch. That seam
is **dead**: Next's standalone `startServer` creates its own http server in a
way that bypasses the override, so a route registered there never fires
(debug logs confirmed: zero requests reached it). The real seam is the Next
**App Router** (`src/app/api/...`), which lives in the dev tree, not `dist/`.
- **Chromium ≥130 forces the remote-debugging port onto `127.0.0.1`** and
ignores `--remote-debugging-address=0.0.0.0`. A plain published port can't
reach it, so cookie harvest needs an in-container TCP bridge to republish the
loopback CDP onto `0.0.0.0`. We shipped that bridge, but the cleaner
default is **Firefox** (`jlesage/firefox`): its debugger binds `0.0.0.0` out
of the box, so harvest works with no bridge at all.
- The CDP harvester **hung forever** on the first tries: the message handler was
defined but never attached to the socket, so every `send()` promise stayed
pending. We replaced Playwright's `connectOverCDP` (which stalls through the
bridge) with a **raw `ws` client** and wired the handler — now resolves.
## What
New management API (scoped like the other admin endpoints via
`requireManagementAuth`):
| Method | Path | Purpose |
| --- | --- | --- |
| GET | `/api/vnc-session` | list active sessions + supported providers |
| GET | `/api/vnc-session/:provider` | session state |
| POST | `/api/vnc-session/:provider/start` | boot browser container → returns `vncUrl` |
| POST | `/api/vnc-session/:provider/harvest` | persist cookies into the provider row |
| POST | `/api/vnc-session/:provider/touch` | defer idle auto-stop |
| DELETE | `/api/vnc-session/:provider` | stop + remove the container |
## Implementation
- `src/lib/vncSession/manifest.ts` — provider → login URL + cookie/token map + config
- `src/lib/vncSession/harvest.ts` — raw-CDP cookie/localStorage harvester (`ws`)
- `src/lib/vncSession/service.ts` — docker lifecycle, port allocation, idle sweep, DB write
- `src/app/api/vnc-session/**` — App Router routes
- `src/lib/gracefulShutdown.ts` — tears down running login containers on exit
## Browser image choice
Default is **`jlesage/firefox`** (0.0.0.0-friendly CDP, no bridge). The
Chromium image + in-container bridge lives under `docker/vnc-browser/chromium`,
selectable via `OMNIROUTE_VNC_IMAGE`. See `docker/vnc-browser/README.md`.
## Config (env)
`OMNIROUTE_VNC_IMAGE`, `OMNIROUTE_VNC_CONTAINER_VNC_PORT`,
`OMNIROUTE_VNC_CONTAINER_CDP_PORT`, `OMNIROUTE_VNC_PROFILE_DIR`,
`OMNIROUTE_VNC_IDLE_MS`, `OMNIROUTE_VNC_MAX_MS`, `OMNIROUTE_VNC_MAX_SESSIONS`,
`OMNIROUTE_DOCKER_BIN` — all documented in the docker README.
## Tests
`tests/unit/vnc-session.test.ts` — manifest lookup + credential mapping
(cookie / token / whole-jar). All passing via the Node test runner.
## Notes
- Docker is the only external dependency; if the `docker` CLI is missing, `start`
throws a clear error and shutdown is a no-op.
- No secrets are returned by any endpoint — only session metadata + ports.
Co-authored-by: Sora <138304505+Capslockb@users.noreply.github.com>
Co-authored-by: Bernardo <138304505+Capslockb@users.noreply.github.com>
* refactor(vnc-session): derive provider credentials from shared contract
* fix(vnc-session): harden CDP harvesting and credential filtering
* refactor(vnc-session): scope lifecycle to provider connections
* fix(vnc-session): sanitize and scope management routes
* fix(vnc-session): use canonical provider list in API
* test(vnc-session): align coverage with canonical manifest
* docs(vnc-session): align browser setup with current implementation
* fix(security): loopback-gate /api/vnc-session (Hard Rule #15/#17)
The new /api/vnc-session/* routes spawn Docker containers via
child_process.spawn (src/lib/vncSession/service.ts) but were never
registered in LOCAL_ONLY_API_PREFIXES or SPAWN_CAPABLE_PREFIXES, so
they were reachable from non-loopback callers (any manage-scope API
key or dashboard session over a tunnel) - the same CVE class
(GHSA-fhh6-4qxv-rpqj) those constants exist to close.
Register VNC_ROUTE_PREFIX (already exported but unused in
manifest.ts) in both prefix lists, and add a regression test
asserting isLocalOnlyPath()/isLocalOnlyBypassableByManageScope()
correctly classify the new prefix.
Co-authored-by: CAPSLOCKB <138304505+Capslockb@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouzapw@users.noreply.github.com>