mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-14 11:12:17 +03:00
d259d9fcba5e9755ef58357ae747b59760edea70
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
57744aeb14 |
feat(cursor): proactively renews Cursor sessions and fixes manual refresh (#9173)
* refactor(cursor): extracts token extraction into shared lib
Moves tryIdeAuth/tryAgentAuth and supporting helpers out of the
auto-import route into src/lib/cursor/tokenExtractor.ts, and adds
an agent-cli-state.json fallback candidate path to tryAgentAuth
(alongside the existing auth.json candidate) so the extraction
logic can be reused by the upcoming renewal orchestrator.
* feat(cursor): adds cursor-agent-backed token renewal orchestrator
Builds the renewal orchestrator in src/lib/cursor/renewal.ts: a
bounded, unattended-safe --list-models nudge, a side-effect-free
status availability check, an in-flight spawn lock keyed by
command, and renewCursorConnection() which nudges cursor-agent
then independently re-scrapes the IDE and cursor-agent credential
sources to detect whichever refreshed. Extends cursorAgent.ts's
binary resolution and spawn helper with fixed-paths-only mode and
a SIGKILL follow-up for background use. Adds a generic keyed-mutex
utility (src/shared/utils/keyedMutex.ts) for serializing a
connection's renew-then-persist cycle, and forwards a busy-timeout
through driverFactory's node:sqlite fallback path.
* feat(cursor): proactively renews Cursor sessions in the sweep
Adds src/lib/tokenHealthCheckCursor.ts, sweep-side glue that calls
the renewal orchestrator and persists the result, wired into
tokenHealthCheck.ts's checkConnection() via a new Cursor-specific
branch placed ahead of the generic no-refresh-token fallthrough.
Carves out a non-terminal exception for a Cursor connection that
already landed at testStatus "expired" via the request-time 401
path, excluding permanently-dead account_deactivated connections.
Extends buildRefreshFailureUpdate() with an overrides param so
Cursor's failure path can use a distinct, non-terminal errorCode
instead of the generic refresh_failed/expired taxonomy.
* feat(cursor): adds local-only manual refresh route
Adds POST /api/providers/[id]/refresh-cursor, a dedicated
loopback-only route that calls the renewal orchestrator on demand
for a single Cursor connection, bounded by a 30s per-connection
cooldown. Classifies the new route in LOCAL_ONLY_API_PATTERNS and
closes the manage-scope-bypass gap for dynamic-segment spawn-capable
routes under /api/providers/ via a new SPAWN_CAPABLE_PATTERNS /
SPAWN_CAPABLE_PATTERN_ANCESTORS mechanism, which also retroactively
covers the pre-existing /login route. The existing shared
/api/providers/[id]/refresh route is untouched and stays
remote-reachable for every other provider.
* feat(cursor): surfaces a dismissible cursor-agent nudge
Adds GET /api/providers/cursor/agent-availability, a credential-free
LOCAL_ONLY route returning only { cursorAgentAvailable: boolean },
backed by a 5-minute cached wrapper around the renewal orchestrator's
existing availability check. Surfaces a dismissible dashboard banner
on the Cursor provider page suggesting cursor-agent installation
when it isn't detected, following the existing dismissible-banner
convention. Also fixes a pre-existing bracket character in a
routeGuard.ts comment that was silently truncating
check-openapi-security-tiers.mjs's view of LOCAL_ONLY_API_PREFIXES.
* fix(cursor): wires manual refresh button to the new route
Branches handleRefreshToken to call the dedicated Cursor refresh
route instead of the generic /refresh route, which silently 502s
for Cursor connections today since they carry no refresh token.
Every other provider's refresh behavior is unaffected. Adds the
cursorSessionUnchanged i18n key and syncs it (plus a pre-existing,
unrelated 28-key backlog) across all 42 locale files.
* fix(cursor): addresses Phase 4/4.5 review findings
Restores the legacy stdout/stderr auth-pattern fallback in
checkCursorAgentAvailability() that the plan's Task 2 Step 4
required but the implementation had dropped. Threads an optional
deps parameter through checkCursorConnectionIfNeeded() so its
error branch is reachable in tests, and switches both it and the
manual-refresh route to exhaustive switch statements over the
renewal result. Adds a short-lived host-keyed dedup cache around
tryIdeAuth() so multiple due Cursor connections sharing a host
don't each open the same state.vscdb file in one sweep tick.
Adds opportunistic eviction to the manual-refresh cooldown map,
an outer try/catch to the availability route for defense-in-depth
consistency with the plan's other routes, and corrects a stale
JSDoc claim about the /login route's auth check. Documents the
now-empirically-confirmed agent-cli-state.json schema mismatch
found while validating against a real cursor-agent install.
* docs(cursor): adds changelog fragments for the renewal plan
Adds one fragment per user-facing outcome per changelog.d/README.md's
convention for a PR that both fixes and adds. PR number placeholder
to be filled in once the PR is opened.
* fix(i18n): translates the new Cursor keys into Vietnamese
The i18n:sync-ui run in an earlier commit left __MISSING__
sentinels for the 4 new Cursor keys in every locale, but
Vietnamese has a dedicated completeness test requiring zero
internal missing markers. Provides real translations for
cursorSessionUnchanged, cursorAgentNudgeTitle,
cursorAgentNudgeBody, and cursorAgentNudgeDismiss.
* fix(cursor): addresses quality-gate Layer 1.5 findings
Restores a comment that misrepresented execFile's actual argv shape
after an earlier bracket-removal fix, this time avoiding literal
closing-bracket characters entirely so the openapi checker's naive
array parser can't be broken by either version. Bounds the sweep-
and manual-route-triggered tryIdeAuth() busy-timeout to 250ms
(down from the interactive auto-import path's 2000ms), since both
share the main event loop with all other in-flight requests and
should fail fast on a WAL-lock collision rather than block the
whole instance for up to ~4s. Has the manual refresh route bypass
the sweep's IDE-auth dedup cache so a click always sees a fresh
read, consistent with this plan's existing "manual actions never
see stale cached data" convention. Documents the previously-missing
agent-availability route in ROUTE_GUARD_TIERS.md's spawn-capable
table.
* fix(cursor): adds SIGKILL follow-up to the status-check spawn
Matches the nudge spawn's existing SIGTERM+SIGKILL pattern so an
unresponsive cursor-agent status check can't leak a lingering
process if it ignores SIGTERM.
* docs(cursor): fills in the PR number for changelog fragments
Renames the 3 changelog.d fragments to their PR-numbered filenames and replaces the (#PR) placeholder with #9173, now that the PR exists.
* fix(cursor): corrects changelog fragments to reference PR #9173
The prior commit only staged the git mv rename — a git add invocation with a stale (pre-rename) pathspec aborted before the actual (#PR) -> (#9173) content edit was staged, so the rename landed without the fix it was meant to carry. This captures the actual content change.
* docs(cursor): regenerates the agent-skills catalog for the new route
check:agent-skills-sync (CI's Merge integrity gate) requires SKILL.md files to stay in sync with the live route catalog. Adding /api/providers/cursor/agent-availability in an earlier commit needed a regen this branch never ran.
* chore(quality): rebaselines file-size caps grown by agentrouter merges
Two already-merged agentrouter commits (
|
||
|
|
813bea4184 |
feat(vnc-session): persistent noVNC browser login for web-cookie providers (#7892)
* 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> |
||
|
|
65e0aeda79 |
[Part 1/3]refactor(qwen): replace legacy Qwen Code and remove OAuth provider (#7866)
* refactor(cli): remove legacy Qwen Code integration * refactor(qwen): remove deprecated Qwen OAuth provider * feat(cli): rebuild Qwen Code integration for upstream V4 * fix(qwen): clear stale CLI auth on reset * test(qwen): align retired provider coverage * fix(db): renumber qwen-cleanup migration 129 -> 130 release/v3.8.49 tip took slot 129 via #7843 (usage_history_codex_strong_identity, itself renumbered from 128 during the #7838/#7840 base-red cleanup) after this branch forked; renumber remove_unregistered_qwen_data to 130. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
b9d18dd8c4 |
Continue fix bugs and upgrade skill_collector (#6294)
* fix(skills): gate skill-collector CLI detection behind management auth + loopback PR #6294 fork-main bundled genuinely new skill-collector CLI-detection routes (GET /api/skills/collect/detect, POST /api/skills/collect/install) on top of content already shipped via #6186. This reconstructs the PR against the current release tip, keeping only the new detect/install routes and their SKILL.md, and drops the 3 already-merged commits so two post-merge quality fixes on /api/github-skills (Zod validation + sanitizeErrorMessage) are not reverted. - GET /api/skills/collect/detect spawned a child process per CLI_TOOL_IDS entry via getCliRuntimeStatus(), unauthenticated and reachable over any tunnel. All 3 routes (github-skills GET/POST, skills/collect/detect, skills/collect/install) now require requireManagementAuth(), matching every sibling /api/skills/* route. - Classified /api/skills/collect/ in LOCAL_ONLY_API_PREFIXES and SPAWN_CAPABLE_PREFIXES (routeGuard.ts / spawnCapablePrefixes.ts) and added src/app/api/skills/collect to SPAWN_CAPABLE_ROUTE_ROOTS in check-route-guard-membership.ts so the automated gate actually scans it (Hard Rules #15 + #17). - omniroute_github_skills_install MCP tool now reports the honest action: "planned" instead of "installed", matching the REST route. - Dropped docker-compose.drive-d.yml, start.sh, and the unrelated @types/node/settings.ts changes (personal dev-machine / out-of-scope). - Added route-level tests for all 3 routes + the 3 MCP tools (auth-required and no-stack-trace-leak assertions) and a route-guard regression test. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(quality): register new routeGuard covering test in stryker.conf.json check:mutation-test-coverage --strict (Fast Quality Gates) flagged tests/unit/authz/route-guard-skills-collect.test.ts as a covering unit test for src/server/authz/routeGuard.ts that was missing from tap.testFiles, so its mutant kills would silently not count toward the mutation-test baseline. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * chore: resync CHANGELOG after merging release/v3.8.47 Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Moseyuh333 <Moseyuh333@users.noreply.github.com> |
||
|
|
0adae00c7b |
Release v3.8.42 (#5459)
Release v3.8.42 — full CHANGELOG in CHANGELOG.md. CI: 103 checks green incl. CodeQL (all languages), Semgrep, all 8 unit shards, coverage, Node 24 compat, and integration tests. Full unit suite validated locally: 19437 pass / 0 fail. The 3 red checks are advisory and do not gate main (no required status checks): SonarCloud/SonarQube new-code coverage gate, and PR Test Policy (test-masking detector flagging the legitimate dead-Phind provider removal in #5530 — reviewed, correct). Includes cycle-close reconciliation + repair of inherited base-red tests from #5480/#5527/#5427/#5521 that the PR->release fast-path did not exercise. |