Skip to content

feat(vnc-session): persistent noVNC browser login for web-cookie providers - #7892

Merged
diegosouzapw merged 13 commits into
diegosouzapw:release/v3.8.49from
Capslockb:feat/vnc-session-login
Jul 22, 2026
Merged

diegosouzapw merged 13 commits into
diegosouzapw:release/v3.8.49from
Capslockb:feat/vnc-session-login

Conversation

@Capslockb

@Capslockb Capslockb commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

What

Adds a connection-scoped interactive browser-login flow for cookie/token web providers such as ChatGPT Web, Gemini Web, Claude Web, DeepSeek Web, and other providers whose canonical web-session credential requirements can be reconstructed safely.

The operator starts a temporary Chromium container, opens its noVNC viewer, signs in to the real provider, and asks OmniRoute to harvest only the declared credential fields. The selected provider_connections row is updated through the existing database helpers and then validated through the existing provider-validation path.

Flow

  1. Open a provider connection.
  2. Start a browser-login session for that specific connection.
  3. OmniRoute starts the bundled Chromium/noVNC container.
  4. The operator signs in to the provider.
  5. Harvest declared cookies/localStorage/sessionStorage values.
  6. Merge them into the selected connection only.
  7. Run existing provider validation.
  8. Stop and remove the browser container and, by default, its profile directory.

API

All routes are management-authenticated.

Method Path Purpose
GET /api/vnc-session List active sessions and supported providers
GET /api/vnc-session/:connectionId List sessions for one connection
GET /api/vnc-session/:connectionId/:sessionId Read one session
POST /api/vnc-session/:connectionId/start Start a browser session
POST /api/vnc-session/:connectionId/:sessionId/harvest Persist harvested credentials and validate the connection
POST /api/vnc-session/:connectionId/:sessionId/touch Refresh viewer activity
DELETE /api/vnc-session/:connectionId/:sessionId Stop and remove the session

Implementation

  • src/lib/vncSession/manifest.ts — derives the supported-provider catalog from the canonical web-session credential requirements.
  • src/lib/vncSession/harvest.ts — bounded CDP client with abort/command timeouts and declared-key credential harvesting.
  • src/lib/vncSession/service.ts — connection-scoped lifecycle, Docker reconciliation, loopback-only random ports, profile permissions/cleanup, DB update, and provider validation.
  • src/app/api/vnc-session/** — management-authenticated API routes with sanitized error responses.
  • src/lib/gracefulShutdown.ts — removes active browser-login sessions during shutdown.
  • docker/vnc-browser/chromium — Chromium/noVNC image and the in-container CDP bridge.

Security and lifecycle properties

  • noVNC and CDP ports are published only on 127.0.0.1 using random host ports.
  • No credential values are returned from the API.
  • Public session responses omit container names and host profile paths.
  • Only canonical declared storage keys are inspected; arbitrary localStorage is not persisted.
  • Sessions are scoped to an exact provider-connection ID, avoiding accidental updates to another account.
  • Startup failures, concurrent stops, idle expiry, maximum lifetime, and application shutdown all clean up session state and containers.
  • Profiles are mode 0700 and ephemeral by default; persistence is opt-in.

Remote use currently requires running on the OmniRoute host or forwarding the loopback noVNC port over SSH. An authenticated same-origin websocket proxy is intentionally not introduced in this change.

Browser image

The default image is omniroute-vnc-chromium:local, built with:

docker build -t omniroute-vnc-chromium:local docker/vnc-browser/chromium

The container uses Chromium CDP through the bundled internal bridge. The image and runtime values remain configurable through the documented OMNIROUTE_VNC_* environment variables.

Tests

tests/unit/vnc-session.test.ts covers provider discovery, canonical credential requirements, Chromium defaults, allowlisted cookie harvesting, token extraction, and multi-cookie providers.

Recent review fixes

  • Replaced the invalid VNC_PROVIDER_MANIFEST import with listVncProviders() and requirement.kind.
  • Updated stale tests that still expected the removed static manifest and Firefox defaults.
  • Removed the host profile path from the public session-list response.

@Capslockb
Capslockb requested a review from diegosouzapw as a code owner July 20, 2026 16:20
…n 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>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a persistent VNC login browser feature to support interactive logins for cookie and token web providers, adding Docker configurations, a Next.js API route, a WebSocket-based CDP cookie harvester, a provider manifest, and a lifecycle management service. The code review identified several critical issues and improvement opportunities, including an unused timeout parameter in the harvester that could cause hangs, a potential session slot leak on container startup failure leading to a Denial of Service, memory and timer leaks in the WebSocket client, excessive storage of sensitive local storage data, redundant profile path configuration logic, race conditions in session teardown, and potential credential overwrites when multiple connections exist for a single provider.

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.

Comment thread src/lib/vncSession/harvest.ts
Comment thread src/lib/vncSession/service.ts Outdated
Comment thread src/lib/vncSession/harvest.ts Outdated
Comment thread src/lib/vncSession/harvest.ts Outdated
Comment thread src/lib/vncSession/harvest.ts Outdated
Comment thread src/lib/vncSession/service.ts Outdated
Comment thread src/lib/vncSession/service.ts Outdated
Comment thread src/lib/vncSession/service.ts Outdated
Comment thread src/lib/vncSession/service.ts Outdated
@Capslockb
Capslockb force-pushed the feat/vnc-session-login branch from 81e6405 to 4929bef Compare July 20, 2026 16:24

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 81e6405417

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/vncSession/service.ts Outdated
Comment thread src/lib/vncSession/service.ts Outdated
Comment thread src/app/api/vnc-session/[...params]/route.ts Outdated
@Capslockb
Capslockb marked this pull request as draft July 20, 2026 16:30

Copy link
Copy Markdown
Contributor Author

This PR is currently an architectural proof of concept and is not merge-ready. The next revision should address the following in order:

  1. Use one demonstrably supported browser implementation. The current harvester is Chromium/CDP-specific, so jlesage/firefox should not be the default unless a separate Firefox driver is implemented and tested.
  2. Remove the parallel credential manifest. Derive credential kind and storage keys from src/shared/providers/webSessionCredentials.ts, with only a small separate login-URL map if needed.
  3. Scope every browser session to a concrete providerConnectionId; never select rows[0] by provider.
  4. Bind noVNC and CDP to loopback or an isolated Docker network. CDP must never be externally reachable.
  5. Replace raw host-port vncUrl exposure with an authenticated same-origin proxy suitable for HTTPS, Docker, VPS and Electron deployments.
  6. Add provider/dashboard UI: choose connection, start login, show state, harvest, validate, stop, and report which connection was updated.
  7. Harvest only explicitly declared credential keys. Do not copy all localStorage and do not persist whole cookie jars unless the existing provider requirement explicitly calls for a full Cookie header.
  8. Never return credential fragments from the API; return only updated field names and connection/session metadata.
  9. Scope profiles by connection/session UUID, create them with restrictive permissions, document retention, and support deletion. Prefer ephemeral profiles by default.
  10. Replace in-memory-only port/session assumptions with Docker labels, startup reconciliation, stale-container cleanup and reliable loopback port allocation.
  11. Add HTTP/CDP command timeouts, close/error rejection for pending requests, target-origin selection, browser readiness polling and provider credential validation after harvest.
  12. Add Docker integration tests and provider-specific tests for ChatGPT, Gemini, Claude, DeepSeek, T3 and Copilot.

Suggested API shape:

POST   /api/provider-connections/:connectionId/browser-login/start
GET    /api/provider-connections/:connectionId/browser-login/:sessionId
POST   /api/provider-connections/:connectionId/browser-login/:sessionId/harvest
POST   /api/provider-connections/:connectionId/browser-login/:sessionId/touch
DELETE /api/provider-connections/:connectionId/browser-login/:sessionId

Suggested implementation sequence: core credential integration → connection-scoped service → hardened Chromium runtime → authenticated proxy → dashboard workflow → integration/security tests.

The PR should remain draft until these items are implemented and tested.

@Capslockb
Capslockb marked this pull request as ready for review July 20, 2026 21:04
@Capslockb
Capslockb marked this pull request as draft July 20, 2026 21:04

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 90f0ebe63d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/app/api/vnc-session/route.ts Outdated
@diegosouzapw

Copy link
Copy Markdown
Owner

Thanks for this - the connection-scoped browser-login flow is a genuinely useful piece of work, and I can see from your own earlier comment on this thread that you already iterated hard on the proof-of-concept critique (Chromium-only default, canonical webSessionCredentials.ts contract instead of a parallel manifest, connection-scoping instead of rows[0], loopback-only ports, declared-key-only harvest, profile permissions, docker reconciliation, CDP timeouts). That work shows in the diff.

Two notes before this is mergeable:

1. Diff size is a base-branch artifact, not scope creep. GitHub reports 2113 files / +159218/-54342 because the PR targets main while feat/vnc-session-login was actually cut from release/v3.8.49. I verified with git merge-base: against release/v3.8.49 the diff is 315 files (mostly unrelated release drift the branch is 13 commits behind on), and the isolated feature diff is 12 files / +1425/-2. Please retarget: gh pr edit 7892 --base release/v3.8.49 (then double-check with gh pr view --json baseRefName - the edit can fail silently).

2. Route-guard gap (blocking). /api/vnc-session/* spawns Docker containers via child_process.spawn in src/lib/vncSession/service.ts, but the routes aren't registered in LOCAL_ONLY_API_PREFIXES (src/server/authz/routeGuard.ts) or SPAWN_CAPABLE_PREFIXES (src/shared/constants/spawnCapablePrefixes.ts). requireManagementAuth() alone doesn't enforce loopback, so any manage-scope API key or dashboard session reachable through a tunnel can currently trigger container spawning on the host - the exact CVE class (GHSA-fhh6-4qxv-rpqj) those two lists exist to close. I confirmed this live: isLocalOnlyPath('/api/vnc-session/x/start', 'POST') returns false on this branch. Worth noting manifest.ts already exports VNC_ROUTE_PREFIX but it's never imported anywhere - looks like the wiring was planned and dropped. Adding both entries (with a comment citing the security doc, same pattern as the other /api/local/, /api/headroom/* entries) should be a small fix.

Along with that, service.ts's actual container lifecycle (start/harvest/stop/reconcile) and both route handlers currently have zero test coverage - only the pure manifest/credential-mapping helpers are tested (6/6 passing, verified). Please add tests for those plus a regression test asserting isLocalOnlyPath() covers the new prefix.

Once those two land this looks ready - happy to take another pass quickly after the push.

@diegosouzapw
diegosouzapw changed the base branch from main to release/v3.8.49 July 21, 2026 00:42
diegosouzapw and others added 2 commits July 21, 2026 04:35
/diegosouzapw#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>
@diegosouzapw
diegosouzapw force-pushed the feat/vnc-session-login branch from 5fdddb6 to 4b00c17 Compare July 22, 2026 13:45
…ession-login

# Conflicts:
#	src/server/authz/routeGuard.ts
@diegosouzapw
diegosouzapw force-pushed the feat/vnc-session-login branch from 4b00c17 to 25a8518 Compare July 22, 2026 13:45
@diegosouzapw
diegosouzapw marked this pull request as ready for review July 22, 2026 13:45
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@diegosouzapw
diegosouzapw merged commit 813bea4 into diegosouzapw:release/v3.8.49 Jul 22, 2026
6 of 7 checks passed
@diegosouzapw diegosouzapw mentioned this pull request Jul 23, 2026
fenix007 pushed a commit to fenix007/OmniRoute that referenced this pull request Jul 23, 2026
…s + eslint baseline

The release branch accumulated deterministic unit-test failures (fast-path red on
every open PR). These are the ones with a clear, surgical root cause:

1. diegosouzapw#6863 combo model-lockout — the diegosouzapw#7940/diegosouzapw#7980 "cap exactCooldownMs against
   maxCooldownMs" clamp was also clamping an AUTHORITATIVE parsed upstream quota
   reset (e.g. "Resets in 92h27m28s") down to maxCooldownMs, so an exhausted model
   was retried far too early. recordModelLockoutFailure now takes
   exactCooldownIsUpstreamReset — set by the combo callers when the exact cooldown
   is a real upstream reset — which exempts it from the cap. The diegosouzapw#7980 computed
   until-midnight cap is unchanged (flag absent → still capped).

2. diegosouzapw#5786 streaming claude←codex — stripInternalReasoningPlaceholder (diegosouzapw#8081/diegosouzapw#8162)
   unconditionally .trim()'d every value. On the per-delta streaming path this ate
   the meaningful edge spaces of each delta ("Hello, " + "world." + " Bye." glued to
   "Hello,world.Bye."). It now only collapses to "" when whitespace is all that
   remains after removing the placeholder, preserving real content verbatim.

3. SPAWN_CAPABLE_PREFIXES test — diegosouzapw#7892 added /api/vnc-session (11th spawn-capable
   prefix, spawns Docker) but the client-safe guard test still expected 10 and did
   not list it. Aligned to 11 + added the entry to the checklist.

4. ESLint baseline — diegosouzapw#8008/diegosouzapw#8062 merged new test files with no-explicit-any without
   refreshing the frozen suppressions, so "No new ESLint warnings" went red for the
   whole branch. Regenerated the two affected entries
   (combo-routing-engine.test.ts 269→271, oauth-refresh-connection-dedup-8059.test.ts +1).

Validated: the three failing tests now pass; the sibling guards they interact with
stay green (diegosouzapw#7980 exact-cooldown-cap 4/4, diegosouzapw#8162 placeholder suites 17+12+41,
account-fallback 77); typecheck:core clean; lint:json --max-warnings 0 exits 0.

NOTE: the release branch has ~20 further real base-red failures (compression-engine
catalog, handleChat fallback, provider candidate transparency, i18n, misc). Those are
tracked separately, one focused PR per root-cause cluster; this PR is the first slice.
fenix007 pushed a commit to fenix007/OmniRoute that referenced this pull request Jul 24, 2026
…s + eslint baseline

The release branch accumulated deterministic unit-test failures (fast-path red on
every open PR). These are the ones with a clear, surgical root cause:

1. diegosouzapw#6863 combo model-lockout — the diegosouzapw#7940/diegosouzapw#7980 "cap exactCooldownMs against
   maxCooldownMs" clamp was also clamping an AUTHORITATIVE parsed upstream quota
   reset (e.g. "Resets in 92h27m28s") down to maxCooldownMs, so an exhausted model
   was retried far too early. recordModelLockoutFailure now takes
   exactCooldownIsUpstreamReset — set by the combo callers when the exact cooldown
   is a real upstream reset — which exempts it from the cap. The diegosouzapw#7980 computed
   until-midnight cap is unchanged (flag absent → still capped).

2. diegosouzapw#5786 streaming claude←codex — stripInternalReasoningPlaceholder (diegosouzapw#8081/diegosouzapw#8162)
   unconditionally .trim()'d every value. On the per-delta streaming path this ate
   the meaningful edge spaces of each delta ("Hello, " + "world." + " Bye." glued to
   "Hello,world.Bye."). It now only collapses to "" when whitespace is all that
   remains after removing the placeholder, preserving real content verbatim.

3. SPAWN_CAPABLE_PREFIXES test — diegosouzapw#7892 added /api/vnc-session (11th spawn-capable
   prefix, spawns Docker) but the client-safe guard test still expected 10 and did
   not list it. Aligned to 11 + added the entry to the checklist.

4. ESLint baseline — diegosouzapw#8008/diegosouzapw#8062 merged new test files with no-explicit-any without
   refreshing the frozen suppressions, so "No new ESLint warnings" went red for the
   whole branch. Regenerated the two affected entries
   (combo-routing-engine.test.ts 269→271, oauth-refresh-connection-dedup-8059.test.ts +1).

Validated: the three failing tests now pass; the sibling guards they interact with
stay green (diegosouzapw#7980 exact-cooldown-cap 4/4, diegosouzapw#8162 placeholder suites 17+12+41,
account-fallback 77); typecheck:core clean; lint:json --max-warnings 0 exits 0.

NOTE: the release branch has ~20 further real base-red failures (compression-engine
catalog, handleChat fallback, provider candidate transparency, i18n, misc). Those are
tracked separately, one focused PR per root-cause cluster; this PR is the first slice.
diegosouzapw added a commit that referenced this pull request Jul 24, 2026
…ate (#8386)

* test: realign catalog snapshot tests to current deliberate catalog state

Six catalog/snapshot tests drifted behind deliberate catalog changes that
were already validated by newer sibling tests. No production code touched;
every change aligns a stale snapshot to behavior already validated by newer
sibling tests.

Root causes (all confirmed against the current code before editing):

- tests/unit/providers-constants-split.test.ts: APIKEY_PROVIDERS grew from
  187 to 195 entries via #8077 (clova-studio/internlm/ant-ling, regional),
  #8161 (sarvam/plamo → regional, writer → frontier-labs) and #8170
  (typhoon → regional, inception → frontier-labs). Family counts verified
  to sum to 195 (gateways 60, frontier-labs 24, inference-hosts 28,
  enterprise-cloud 17, regional 40, specialty-media 26) with no duplicates.
  Updated the two assertions and extended the changelog comment.

- tests/unit/qianfan-provider.test.ts: the expected Baidu Qianfan website
  URL was the pre-#8128 wenxinworkshop path. #8128/#6271 moved it to
  https://cloud.baidu.com/product-s/qianfan_home, already locked by the
  sibling regression test tests/unit/baidu-qianfan-website-urls-6271.test.ts.

- tests/unit/t31-t33-t34-t38-model-specs.test.ts and
  tests/unit/auto-combo-credentialed-model-pool.test.ts: the Antigravity
  catalog refactor (#8013) retired gemini-3-pro-preview/claude-sonnet-5 and
  renamed the Gemini 3.5 Flash tiers (low/medium/high ->
  extra-low/low/gemini-3-flash-agent), confirmed against
  ANTIGRAVITY_PUBLIC_MODELS and tests/unit/antigravity-retired-public-models.test.ts.
  Swapped the retired IDs for currently-registered ones
  (gemini-3.6-flash-high, claude-sonnet-4-6, gemini-3-flash-agent,
  gemini-3.5-flash-low/extra-low) and moved the wildcard-exclusion prefix
  test from the now-2-tier "gemini-3.5-*" group to "gemini-3.6-*", which has
  3 real tiers today (same >=3 semantics, just pointed at a prefix that
  still has 3 members).

- tests/unit/model-alias-seed.test.ts: getModelInfo("gemini-3.1-pro") now
  canonicalizes through ALIAS_TO_PROVIDER_ID["agy"] = "antigravity" (#8050),
  the same pattern already applied to opencode -> opencode-zen. Updated the
  expected provider id.

- tests/unit/video-dashscope.test.ts (deleted, 216 lines): #8266
  reorganized the Alibaba video catalog so the flat wan2.7-t2v id no longer
  exists under the plain "alibaba" provider (only the dated
  wan2.7-t2v-2026-06-12 does); the flat id now lives only under
  "qwen-cloud". All 6 tests in the file failed because they built  requests
  against alibaba/wan2.7-t2v, which the new allowlist now rejects with 400
  ("unsupported alibaba video model") - verified directly against
  VIDEO_PROVIDERS in open-sse/config/videoRegistry.ts. Coverage already
  exists and was confirmed passing pre-deletion in
  tests/unit/alibaba-video-media.test.ts (including an explicit
  "Alibaba rejects video models outside its own allowlist" case for this
  exact id) and tests/unit/qwen-cloud-video-media.test.ts (covers the same
  id under qwen-cloud). Note: the deleted file's DashScope upstream
  error-path assertions (401 missing credentials, 502 missing task_id, 502
  FAILED status, 504 poll timeout) don't have a byte-for-byte equivalent in
  the two replacement files, though the shared dashscopeHandler.ts code
  path they exercise remains covered by several sibling *-media.test.ts
  files for the happy path and local validation.

- tests/unit/authz/spawn-capable-prefixes-client-safe.test.ts: #7892 added
  /api/vnc-session to the SPAWN_CAPABLE_PREFIXES deny-list (Hard Rules
  #15/#17 hardening). Bumped the expected length 10 -> 11 and added the
  entry to the test's named list for documentation.

Refs #8013, #8050, #8266, #7892, #8128

* chore(quality): allowlist the video-dashscope.test.ts deletion with its replacements

check:test-masking (pr-test-policy CI gate) requires a _deletedWithReplacement
entry for any deleted test file, even when the deletion is a verified-legitimate
supersession. Documents the same #8266 rationale from the prior commit in the
machine-checked allowlist so the deletion is not flagged as unexplained masking.

Refs #8266
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…iders (diegosouzapw#7892)

* chore(ci): add .mergify.yml to main — Mergify only reads config from the default branch (diegosouzapw#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 diegosouzapw#15/diegosouzapw#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>
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…ate (diegosouzapw#8386)

* test: realign catalog snapshot tests to current deliberate catalog state

Six catalog/snapshot tests drifted behind deliberate catalog changes that
were already validated by newer sibling tests. No production code touched;
every change aligns a stale snapshot to behavior already validated by newer
sibling tests.

Root causes (all confirmed against the current code before editing):

- tests/unit/providers-constants-split.test.ts: APIKEY_PROVIDERS grew from
  187 to 195 entries via diegosouzapw#8077 (clova-studio/internlm/ant-ling, regional),
  diegosouzapw#8161 (sarvam/plamo → regional, writer → frontier-labs) and diegosouzapw#8170
  (typhoon → regional, inception → frontier-labs). Family counts verified
  to sum to 195 (gateways 60, frontier-labs 24, inference-hosts 28,
  enterprise-cloud 17, regional 40, specialty-media 26) with no duplicates.
  Updated the two assertions and extended the changelog comment.

- tests/unit/qianfan-provider.test.ts: the expected Baidu Qianfan website
  URL was the pre-diegosouzapw#8128 wenxinworkshop path. diegosouzapw#8128/diegosouzapw#6271 moved it to
  https://cloud.baidu.com/product-s/qianfan_home, already locked by the
  sibling regression test tests/unit/baidu-qianfan-website-urls-6271.test.ts.

- tests/unit/t31-t33-t34-t38-model-specs.test.ts and
  tests/unit/auto-combo-credentialed-model-pool.test.ts: the Antigravity
  catalog refactor (diegosouzapw#8013) retired gemini-3-pro-preview/claude-sonnet-5 and
  renamed the Gemini 3.5 Flash tiers (low/medium/high ->
  extra-low/low/gemini-3-flash-agent), confirmed against
  ANTIGRAVITY_PUBLIC_MODELS and tests/unit/antigravity-retired-public-models.test.ts.
  Swapped the retired IDs for currently-registered ones
  (gemini-3.6-flash-high, claude-sonnet-4-6, gemini-3-flash-agent,
  gemini-3.5-flash-low/extra-low) and moved the wildcard-exclusion prefix
  test from the now-2-tier "gemini-3.5-*" group to "gemini-3.6-*", which has
  3 real tiers today (same >=3 semantics, just pointed at a prefix that
  still has 3 members).

- tests/unit/model-alias-seed.test.ts: getModelInfo("gemini-3.1-pro") now
  canonicalizes through ALIAS_TO_PROVIDER_ID["agy"] = "antigravity" (diegosouzapw#8050),
  the same pattern already applied to opencode -> opencode-zen. Updated the
  expected provider id.

- tests/unit/video-dashscope.test.ts (deleted, 216 lines): diegosouzapw#8266
  reorganized the Alibaba video catalog so the flat wan2.7-t2v id no longer
  exists under the plain "alibaba" provider (only the dated
  wan2.7-t2v-2026-06-12 does); the flat id now lives only under
  "qwen-cloud". All 6 tests in the file failed because they built  requests
  against alibaba/wan2.7-t2v, which the new allowlist now rejects with 400
  ("unsupported alibaba video model") - verified directly against
  VIDEO_PROVIDERS in open-sse/config/videoRegistry.ts. Coverage already
  exists and was confirmed passing pre-deletion in
  tests/unit/alibaba-video-media.test.ts (including an explicit
  "Alibaba rejects video models outside its own allowlist" case for this
  exact id) and tests/unit/qwen-cloud-video-media.test.ts (covers the same
  id under qwen-cloud). Note: the deleted file's DashScope upstream
  error-path assertions (401 missing credentials, 502 missing task_id, 502
  FAILED status, 504 poll timeout) don't have a byte-for-byte equivalent in
  the two replacement files, though the shared dashscopeHandler.ts code
  path they exercise remains covered by several sibling *-media.test.ts
  files for the happy path and local validation.

- tests/unit/authz/spawn-capable-prefixes-client-safe.test.ts: diegosouzapw#7892 added
  /api/vnc-session to the SPAWN_CAPABLE_PREFIXES deny-list (Hard Rules
  diegosouzapw#15/diegosouzapw#17 hardening). Bumped the expected length 10 -> 11 and added the
  entry to the test's named list for documentation.

Refs diegosouzapw#8013, diegosouzapw#8050, diegosouzapw#8266, diegosouzapw#7892, diegosouzapw#8128

* chore(quality): allowlist the video-dashscope.test.ts deletion with its replacements

check:test-masking (pr-test-policy CI gate) requires a _deletedWithReplacement
entry for any deleted test file, even when the deletion is a verified-legitimate
supersession. Documents the same diegosouzapw#8266 rationale from the prior commit in the
machine-checked allowlist so the deletion is not flagged as unexplained masking.

Refs diegosouzapw#8266
@Capslockb
Capslockb deleted the feat/vnc-session-login branch August 11, 2026 12:23
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…iders (diegosouzapw#7892)

* chore(ci): add .mergify.yml to main — Mergify only reads config from the default branch (diegosouzapw#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 diegosouzapw#15/diegosouzapw#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>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…ate (diegosouzapw#8386)

* test: realign catalog snapshot tests to current deliberate catalog state

Six catalog/snapshot tests drifted behind deliberate catalog changes that
were already validated by newer sibling tests. No production code touched;
every change aligns a stale snapshot to behavior already validated by newer
sibling tests.

Root causes (all confirmed against the current code before editing):

- tests/unit/providers-constants-split.test.ts: APIKEY_PROVIDERS grew from
  187 to 195 entries via diegosouzapw#8077 (clova-studio/internlm/ant-ling, regional),
  diegosouzapw#8161 (sarvam/plamo → regional, writer → frontier-labs) and diegosouzapw#8170
  (typhoon → regional, inception → frontier-labs). Family counts verified
  to sum to 195 (gateways 60, frontier-labs 24, inference-hosts 28,
  enterprise-cloud 17, regional 40, specialty-media 26) with no duplicates.
  Updated the two assertions and extended the changelog comment.

- tests/unit/qianfan-provider.test.ts: the expected Baidu Qianfan website
  URL was the pre-diegosouzapw#8128 wenxinworkshop path. diegosouzapw#8128/diegosouzapw#6271 moved it to
  https://cloud.baidu.com/product-s/qianfan_home, already locked by the
  sibling regression test tests/unit/baidu-qianfan-website-urls-6271.test.ts.

- tests/unit/t31-t33-t34-t38-model-specs.test.ts and
  tests/unit/auto-combo-credentialed-model-pool.test.ts: the Antigravity
  catalog refactor (diegosouzapw#8013) retired gemini-3-pro-preview/claude-sonnet-5 and
  renamed the Gemini 3.5 Flash tiers (low/medium/high ->
  extra-low/low/gemini-3-flash-agent), confirmed against
  ANTIGRAVITY_PUBLIC_MODELS and tests/unit/antigravity-retired-public-models.test.ts.
  Swapped the retired IDs for currently-registered ones
  (gemini-3.6-flash-high, claude-sonnet-4-6, gemini-3-flash-agent,
  gemini-3.5-flash-low/extra-low) and moved the wildcard-exclusion prefix
  test from the now-2-tier "gemini-3.5-*" group to "gemini-3.6-*", which has
  3 real tiers today (same >=3 semantics, just pointed at a prefix that
  still has 3 members).

- tests/unit/model-alias-seed.test.ts: getModelInfo("gemini-3.1-pro") now
  canonicalizes through ALIAS_TO_PROVIDER_ID["agy"] = "antigravity" (diegosouzapw#8050),
  the same pattern already applied to opencode -> opencode-zen. Updated the
  expected provider id.

- tests/unit/video-dashscope.test.ts (deleted, 216 lines): diegosouzapw#8266
  reorganized the Alibaba video catalog so the flat wan2.7-t2v id no longer
  exists under the plain "alibaba" provider (only the dated
  wan2.7-t2v-2026-06-12 does); the flat id now lives only under
  "qwen-cloud". All 6 tests in the file failed because they built  requests
  against alibaba/wan2.7-t2v, which the new allowlist now rejects with 400
  ("unsupported alibaba video model") - verified directly against
  VIDEO_PROVIDERS in open-sse/config/videoRegistry.ts. Coverage already
  exists and was confirmed passing pre-deletion in
  tests/unit/alibaba-video-media.test.ts (including an explicit
  "Alibaba rejects video models outside its own allowlist" case for this
  exact id) and tests/unit/qwen-cloud-video-media.test.ts (covers the same
  id under qwen-cloud). Note: the deleted file's DashScope upstream
  error-path assertions (401 missing credentials, 502 missing task_id, 502
  FAILED status, 504 poll timeout) don't have a byte-for-byte equivalent in
  the two replacement files, though the shared dashscopeHandler.ts code
  path they exercise remains covered by several sibling *-media.test.ts
  files for the happy path and local validation.

- tests/unit/authz/spawn-capable-prefixes-client-safe.test.ts: diegosouzapw#7892 added
  /api/vnc-session to the SPAWN_CAPABLE_PREFIXES deny-list (Hard Rules
  diegosouzapw#15/diegosouzapw#17 hardening). Bumped the expected length 10 -> 11 and added the
  entry to the test's named list for documentation.

Refs diegosouzapw#8013, diegosouzapw#8050, diegosouzapw#8266, diegosouzapw#7892, diegosouzapw#8128

* chore(quality): allowlist the video-dashscope.test.ts deletion with its replacements

check:test-masking (pr-test-policy CI gate) requires a _deletedWithReplacement
entry for any deleted test file, even when the deletion is a verified-legitimate
supersession. Documents the same diegosouzapw#8266 rationale from the prior commit in the
machine-checked allowlist so the deletion is not flagged as unexplained masking.

Refs diegosouzapw#8266
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants