Repository navigation
Conversation
…roxy Main-process Cloud and Relay traffic escaped Settings → Network → HTTP Proxy: the Cloud clients used bare fetch, the relay token/assign calls fell back to globalThis.fetch, and the relay control and cell data WebSockets dialled directly. On a proxy-only network the desktop then failed the sign-in exchange with UND_ERR_CONNECT_TIMEOUT and Relay pairing with relay_control_not_active. - Cloud and Relay HTTP now go through the main HTTP client port after applying the configured policy to the Chromium session, so the desktop sends them through net.fetch and the app's proxy — the pattern Jira already uses. The relay region catalog and probes inherit the same default. - Node-side relay sockets resolve the same policy (Chromium's resolver, with the proxy environment as the fallback on a host without a session) and tunnel through an HTTPS-CONNECT agent; the Chromium-less fetch fallback gets the equivalent undici dispatcher. Settings still win over the environment, loopback stays direct, and rules that cannot be tunnelled (HTTPS-proxy, SOCKS) stay direct as they already were. - Adds undici and https-proxy-agent as dependencies: Node's fetch accepts only an undici dispatcher, and ws accepts only an http.Agent.
There was a problem hiding this comment.
Important
The Chromium-less fallback this PR adds drops proxy credentials that came from the environment, so an authenticated HTTPS_PROXY/HTTP_PROXY yields an agent/dispatcher with no Proxy-Authorization and every proxied WebSocket or fetch on that host fails with 407. Detail inline.
Reviewed changes
- Proxy-aware fetch wrapper —
src/main/network/http-client.tsaddsfetchWithConfiguredProxy, which applies the configured policy to the Chromium session and then sends through the main HTTP client port, with an undici dispatcher on the Chromium-less Node fallback. - New proxy resolver —
src/main/network/outbound-proxy.tsresolves the proxy per target throughsession.resolveProxy(or the env policy when there is no session), attaches session credentials on host/port match, bypasses loopback, and memoizes a CONNECTHttpsProxyAgentfor sockets and an undiciProxyAgentfor fetch per proxy URL. - HTTP call-site migration — the Cloud clients, relay token/assignment HTTP, and the relay region catalog/probes now default to
fetchWithConfiguredProxy; an injectedfetchstill wins. - WebSocket migration —
relay-control-client.ts,relay-control-origin.ts, andrelay-transport.tspass the resolved agent into thewsconstructor, skipped when a socket factory is injected. - Credentials + audits + deps —
getElectronProxyCredentialsForSessionis exposed; the two fetch audit tables are updated;undiciandhttps-proxy-agentare added as production dependencies.
ℹ️ Proxy coverage stays partial
This removes the asymmetry for Cloud auth and Relay, but several other app-owned outbound paths still leave through globalThis.fetch and therefore still ignore Settings → Network → HTTP Proxy: main/artifacts/artifact-cloud-request.ts, main/azure-devops/azure-devops-api-request.ts, main/bitbucket/client.ts, main/bitbucket/user-request.ts, main/gitea/client.ts, main/rate-limits/codex-fetcher.ts, main/runtime/push/push-gateway-client.ts, main/source-control/hosted-review-api-request.ts, and main/speech/openai-transcription-client.ts are all still listed in AUDITED_GLOBAL_FETCH_LINES. The reporter's failure mode (fetch failed / relay_control_not_active behind a proxy-only network) applies equally to these, so this is only a partial fix for the underlying class. Not a blocker for this PR — flagging so the remaining set is a conscious follow-up rather than an oversight.
Technical details
# Remaining proxy-bypassing call sites
## Context
`fetchWithConfiguredProxy` is the only path that consults the app proxy for
app-owned HTTP. Every other file still in `AUDITED_GLOBAL_FETCH_LINES`
(`src/main/global-fetch-call-site-audit.test.ts:16-48`) sends over Node's
`globalThis.fetch`, which does not read the app's configured proxy.
## Open questions for the human
- Is a follow-up planned to migrate the rest, or is each of these intentionally
out of scope (e.g. already using `net.fetch` on a partition)?DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds outbound proxy resolution from Chromium proxy sessions and environment variables. Routes Cloud, Relay HTTP, and region-probing requests through proxy-aware fetches. Preserves Priority: ⬆️ High Merge Risk: 🟡 Moderate · up to Relay connections can leak duplicate sockets after a restart or become unable to reconnect after proxy or socket creation fails. These lifecycle defects should be fixed before merge. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 52918dcb-8535-4a80-a605-2d3b018f46b4
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (19)
package.jsonsrc/main/global-fetch-call-site-audit.test.tssrc/main/network/electron-proxy-credentials.tssrc/main/network/http-client.tssrc/main/network/outbound-proxy.test.tssrc/main/network/outbound-proxy.tssrc/main/orca-profiles/profile-cloud-client.test.tssrc/main/orca-profiles/profile-cloud-client.tssrc/main/orca-profiles/profile-cloud-org-members-client.tssrc/main/proxy-guarded-fetch-call-site-audit.test.tssrc/main/runtime/relay/relay-control-client-options.tssrc/main/runtime/relay/relay-control-client.test.tssrc/main/runtime/relay/relay-control-client.tssrc/main/runtime/relay/relay-control-origin.tssrc/main/runtime/relay/relay-http-client.test.tssrc/main/runtime/relay/relay-http-client.tssrc/main/runtime/relay/relay-region-preference.tssrc/main/runtime/rpc/relay-transport.test.tssrc/main/runtime/rpc/relay-transport.ts
💤 Files with no reviewable changes (1)
- src/main/global-fetch-call-site-audit.test.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.
- The Chromium-less branch read the resolved policy, which strips the proxy userinfo (Chromium answers an auth challenge instead) and has no session credential store to fall back on, so an authenticated HTTPS_PROXY produced an agent with no Proxy-Authorization and every proxied request failed with 407. It now reads the proxy env URL directly, credentials included. - A relay data socket whose proxy resolution outlived stop() was never terminated by it, because stop() snapshots the sockets it knows about. The socket is now dropped when stop() wins that race. - Adds a test that drives a real request through the Node fetch fallback's dispatcher against a local CONNECT proxy — the seam a runtime undici change could silently break — plus the regression tests for both fixes above.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
The delta (6a06695, harden the outbound proxy path after review) closes out the three findings from the prior review and adds real coverage for each.
- Env-proxy credentials now travel —
resolveOutboundProxyUrlreadsgetProxyUrlFromEnvironmentdirectly on the Chromium-less path, so an authenticatedHTTPS_PROXY/HTTP_PROXYreaches the agent/dispatcher instead of being stripped byseparateElectronProxyCredentials. - Real dispatcher interop test — new
http-client.test.tsdrivesnodeHttpClient.fetchthrough a local CONNECT proxy, proving the undiciProxyAgentper-request dispatcher tunnels an actual request rather than only asserting its type. - Relay socket stop race closed —
openSocketre-checksstoppedafter proxy resolution and returnsnull;openConnectionthrowsrelay_transport_stopped, with a test that stops during a heldresolveProxy. - Env credential assertion — the env-fallback test now uses a credentialed proxy URL and checks it reaches both
resolveOutboundProxyUrland the socket agent.
Verified locally: pnpm test on http-client.test.ts, outbound-proxy.test.ts, and relay-transport.test.ts — 19 passed.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
- Chromium's resolver returns an ordered failover list; its later entries are fallbacks for a failed attempt this layer cannot observe, so a route whose first directive is DIRECT (or one it cannot tunnel) now stays direct instead of being promoted to a fallback proxy. - The implicit loopback bypass covers 127.0.0.0/8, not only 127.0.0.1. - A host with no Chromium session reads the proxy environment directly, so it now honours NO_PROXY (host, leading-dot suffix, `*`, optional port) instead of sending a bypassed internal target through the proxy. - fetchWithConfiguredProxy keeps a Request input intact: the port accepts RequestInfo and forwards it, using only its URL to select the proxy. - A second openConnection for a connId whose first open is still resolving its proxy no longer creates a socket that overwrites the first one's mapping.
There was a problem hiding this comment.
Actionable comments posted: 3
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: ab3d0533-e47e-41a7-9d9c-19d47d0b0d49
📒 Files selected for processing (5)
src/main/network/http-client.test.tssrc/main/network/outbound-proxy.test.tssrc/main/network/outbound-proxy.tssrc/main/runtime/rpc/relay-transport.test.tssrc/main/runtime/rpc/relay-transport.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/runtime/rpc/relay-transport.test.ts
- src/main/network/outbound-proxy.test.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.
stop() then start() cleared the stopped flag, so an open whose proxy resolution was still in flight could register and authenticate a stale socket in the new lifecycle. The claim an open takes before that await now doubles as its lifecycle marker: stop() drops every claim, so the open aborts instead.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
This run covers the delta since the prior pullfrog review at 6a06695 — 7e7b417 (address the remaining proxy review findings) and 94d0ffa (cancel an open whose proxy resolution outlived a restart).
- Claim-based socket cancellation —
openSocketnow re-validates itsclaimedConnectionIdsclaim after awaiting the proxy instead of readingthis.stopped, so an open that outlivedstop()cannot adopt itself into a lifecycle restarted by a laterstart()(94d0ffa). - Stop→restart regression test — a new case holds
resolveProxyacrossstop()+start()and asserts the open rejects withrelay_transport_stoppedand the cell sees no connection. - Ordered resolver directives —
proxyUrlFromRuleshonours only the first Chromium directive, so a route that leads withDIRECT(or a directive it cannot tunnel) stays direct instead of being promoted to a fallback proxy. - Wider loopback bypass — the implicit bypass now covers all of
127.0.0.0/8, matching Chromium. NO_PROXYon the Chromium-less path —isEnvBypassedimplements host / leading-dot suffix /*/ optional-port matching, closing the last open question from the initial review.Requestinputs forwarded intact —fetchWithConfiguredProxykeeps theRequest(method, headers, body) and uses only its URL to select the proxy.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
🟠 Major · Block generation changes while a connection claim is pending.
src/main/runtime/rpc/relay-transport.ts:97-98
🎯 Functional Correctness | 🟠 Major | ⚡ Quick winBlock generation changes while a connection claim is pending.
openConnection()adds the connection ID toclaimedConnectionIdsbefore awaitingopenSocket(), whilesocketsByConnectionIdis still empty.setGeneration()can therefore updatethis.generation. When the socket opens,onOpensends the earlierconnTicketwith the new generation. The relay accepts the ticket only whensession.generation === generation, so it closes this connection as invalid host data authentication.Treat
claimedConnectionIdsas active connections in this transition guard.Proposed fix
if ( this.socketsByConnectionId.size > 0 || + this.claimedConnectionIds.size > 0 || !Number.isSafeInteger(generation) || generation <= 0
🟠 Major · Fence proxy resolution against a closed origin.
src/main/runtime/relay/relay-control-origin.ts:207-211
🩺 Stability & Availability | 🟠 Major | ⚡ Quick winFence proxy resolution against a closed origin.
openControl()can resume afterRelayControlOrigin.close()setsclosed, clearscontrols, and closes existing clients. The continuation then creates aRelayControlClient, adds it to the cleared set, and callsconnect(), which creates the control socket.activate()checks only set membership and liveness, so this stale client can become active after shutdown. Add an origin lifecycle check or epoch fence after proxy resolution and before activation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: a4ec2a73-ebd3-498b-8959-fd9851ed5982
📒 Files selected for processing (6)
src/main/network/http-client.test.tssrc/main/network/http-client.tssrc/main/network/outbound-proxy.test.tssrc/main/network/outbound-proxy.tssrc/main/runtime/rpc/relay-transport.test.tssrc/main/runtime/rpc/relay-transport.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- src/main/network/http-client.test.ts
- src/main/network/http-client.ts
- src/main/network/outbound-proxy.test.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 0 remain after this review.
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 768b4cc6-9958-40ba-ab10-ba2bde8c86d4
📒 Files selected for processing (2)
src/main/runtime/rpc/relay-transport.test.tssrc/main/runtime/rpc/relay-transport.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 0 remain after this review.
Both windows come from awaiting proxy resolution before the socket exists. - setGeneration() only guarded on registered sockets, so a generation change could land while a connection was still claimed and its open handshake would authenticate the ticket against the new generation. A claimed connection now blocks the transition, like a registered one. - openControl() resolved the socket agent before the client existed, so a close() in that window cleared `controls` first and the client built afterwards was never included in the teardown, still able to activate after shutdown. The origin now refuses to build a control once closed.
|
Both
Verification on |
There was a problem hiding this comment.
🟠 Major · Bind the claim to one open attempt.
src/main/runtime/rpc/relay-transport.ts:130
🩺 Stability & Availability | 🟠 Major | ⚡ Quick winBind the claim to one open attempt.
If
stop()clears the claim andstart()opens the sameconnIdbefore the oldopenSocket()continuation completes, the old continuation sees the new lifecycle'sclaimedConnectionIds.has(connId)result and creates a socket. The new open can also create a socket. The latersocketsByConnectionId.set()replaces the earlier mapping, so the replaced socket is no longer reachable through normal connection cleanup.Store a unique claim token for each open attempt. Check that the stored token still matches after proxy resolution. Delete the claim only when its token matches.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 980e0837-5404-4e8a-9fca-c533f1a2bc2d
📒 Files selected for processing (4)
src/main/runtime/relay/relay-control-origin.test.tssrc/main/runtime/relay/relay-control-origin.tssrc/main/runtime/rpc/relay-transport.test.tssrc/main/runtime/rpc/relay-transport.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 3 remain after this review.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
This run covers the delta since the prior pullfrog review at 94d0ffa — 2d157eb (fence the generation change and the origin teardown), which closes the two findings raised against 7e7b417.
- Generation transitions now wait for opening sockets —
CloudRelayTransport.setGenerationalso treatsclaimedConnectionIdsas blocking, so aconn-openwhose socket is still resolving its proxy cannot have its auth frame carry a generation assigned after the claim. - The origin fences proxy resolution against teardown —
RelayControlOrigin.openControlresolves the socket agent before constructing the client and throwsrelay_control_closedifclose()landed during thatawait, so no client can be created aftercontrolswas cleared. - Two regression tests — a held
resolveProxywith a live claim assertssetGenerationthrows; a held proxy acrossorigin.close()asserts the open rejects withrelay_control_closedand no client is constructed. Both fail against the pre-commit sources (verified locally).
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
A claim identified its connId but not the attempt that made it, so a stale continuation — one whose stop() already dropped its claim — saw a newer attempt's claim on the same connId and created a second socket, letting the later registration replace the earlier mapping and hide that socket from normal cleanup. Each attempt now takes a unique claim and only proceeds when its own claim is still the one recorded.
|
Fixed in Test Verification on |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
This run covers the delta since the prior pullfrog review at 2d157eb — cf2efef (bind each connection claim to one open attempt), which closes CodeRabbit's finding that a stale open could adopt a newer attempt's claim.
- Claim token per open attempt —
claimedConnectionIdsis now aMap<connId, symbol>;openSocketre-checks that the stored symbol is still its own after the proxy await, so an open that outlivedstop()(even across a laterstart()) throwsrelay_transport_stoppedinstead of registering a socket the new lifecycle never asked for. - Conditional claim release —
finalizedeletes the claim only while it still owns it, so a late finalize cannot clear a newer attempt's claim for the sameconnId. - Stop drops in-flight claims —
stop()clears the map, so a still-resolving open cannot survive into the next lifecycle. - Regression test — holds
resolveProxyacross astop()+start(), reopens the sameconnId, and asserts the stale open rejects while the cell sees exactly one connection (two before the fix).
Verified locally: pnpm test src/main/runtime/rpc/relay-transport.test.ts — 12 passed.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
A failed dial — the proxy lookup rejecting or the socket constructor throwing — left the claim behind, and every later open of that connId then returned without dialling until stop() cleared the claims. The proxy-aware dial moves to relay-cell-socket.ts, which is also where the claim is re-checked after its await, so a superseded open is dropped before it can produce a socket. The transport file was at its line budget, so this is the split rather than a suppression.
|
CodeRabbit's "Release the connection claim when socket creation fails" is fixed in
Verification on |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
This run covers the delta since the prior pullfrog review at cf2efef — e03378e (release a connection claim when the dial fails).
- Failed dials release their claim —
openConnectionnow wraps socket creation intry/catchand calls a new symbol-guardedreleaseClaim, so a rejectedoutboundProxySocketAgentor a throwing injected factory no longer leaves aconnIdclaimed and silently skipping every later open untilstop()clears the map. - Proxy dial extracted to
relay-cell-socket.ts—dialRelayCellSocket(url, isCurrent)owns the proxy-awareWebSocketconstruction and the post-await claim re-check; the transport stores its optional factory as a nullableinjectedSocketFactory, which also keepsrelay-transport.tswithin its max-lines budget without a suppression. - Regression test — a rejecting
resolveProxymakes the firstopenConnectionreject, then a reopen of the sameconnIdis asserted to tunnel to the cell; the leaked claim makes that reopen a no-op, so the case cannot pass against the pre-fix source.
Verified locally: pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/rpc/relay-transport.test.ts — 13 passed.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
…onflicts # Conflicts: # src/main/runtime/relay/relay-control-client.test.ts # src/main/runtime/relay/relay-control-client.ts
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
This run covers the delta since the prior pullfrog review at e03378e — d527325 (Merge remote-tracking branch 'upstream/main' into mmarabel/pr-20894-conflicts), which integrates main (646e9a5) and resolves the two conflicts it reported.
- Control-client conflict resolved additively —
relay-control-client.tskeeps this PR'ssocketAgentplumbing (thecreateSocket(url, token, agent?)seam and the...(agent ? { agent } : {})socket option) alongside upstream'sRelayControlLivenesswiring (livenessRandom,ponghandling), so neither the proxy agent nor the half-open-recovery probes were dropped. - Options type carries both fields —
relay-control-client-options.tsexposessocketAgentand upstream'slivenessRandom; the base'ssilenceLimitMsis gone, as expected. - Test-file conflict resolved additively —
relay-control-client.test.tsretains this PR'sdials the control socket through the app proxy agentcase and upstream's scripted-socket / half-open-recovery suites. - No other PR-touched file changed — the range-diff against the prior head isolates only the two conflicted relay-control files; the resolver, HTTP clients, transport, and audit tables are unchanged.
Verified the resolution locally: relay-control-client.test.ts (20) and relay-transport.test.ts (13) pass, and pnpm tc:node is clean. All prior pullfrog threads remain addressed and resolved.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

ELI5
Orca has a Settings → Network → HTTP Proxy setting. It already worked for the embedded browser, but the two things the app itself does with the internet — signing in to Orca Cloud and pairing a phone through Orca Relay — went out over Node's stack and ignored it. On a network where the only exit is a proxy, sign-in died with
fetch failedand pairing reportedrelay_control_not_active. Both now use the configured proxy.What Changed
src/main/network/http-client.ts: newfetchWithConfiguredProxy. It applies the configured policy to the Chromium session (ensureElectronProxyFromEnvironment, the stepsrc/main/jira/authenticated-request.tsalready performed) and then sends through the main HTTP client port, which is Electron'snet.fetchon the desktop — so Chromium's proxy applies. On a host without Chromium the port's Node fallback resolves the same policy and passes it as undici's per-request dispatcher.src/main/network/outbound-proxy.ts(new): resolves the proxy for one Node-side target through Chromium'ssession.resolveProxy— Settings, proxy env vars, OS proxy and the bypass list all apply — and falls back to the env policy when there is no session. Returns a CONNECT-tunnellinghttps-proxy-agentfor WebSockets and an undiciProxyAgentfor fetch, memoized per proxy URL; the credentials the same settings produced are attached when host and port match. Loopback is never proxied, matching Chromium.profile-cloud-client.ts,profile-cloud-org-members-client.ts), relay token exchange and assignment (relay-http-client.ts) and the relay region catalog/probes (relay-region-preference.ts) now usefetchWithConfiguredProxy. An injectedfetchstill wins, so callers and tests that pass their own transport are unaffected.relay-control-client.tspasses the resolved agent into thewsconstructor,relay-control-origin.tsresolves it per control connection, andrelay-transport.tsdoes the same for cell data sockets — both skipped when a socket factory is injected, which keeps injected factories synchronous exactly as before.electron-proxy-credentials.tsexposes the applied credentials for that agent; the two fetch call-site audit tables are updated.undiciandhttps-proxy-agentas production dependencies: Node'sfetchaccepts only an undici dispatcher, andwsaccepts only anhttp.Agent.Why
The reporter extracted
app.asarand reproduced it outside the app: the code exchange posts tologin.onorca.devwith Node's globalfetch, so it leaves without the configured proxy and times out (UND_ERR_CONNECT_TIMEOUT), and the relay control channel is a raw WebSocket with no proxy support at all. Sending app-owned HTTP through the main HTTP client port is the pattern already used for Jira, so this removes the asymmetry instead of adding a second proxy mechanism.Fixes #19976
Visual Proof
N/A — no UI or interaction change. The difference is whether sign-in and Relay pairing succeed behind a proxy.
Testing
wsconstructor.relay-transport.test.tsgains a case with a real local CONNECT proxy: the cell data socket is tunnelled torelay-cell.example:80, so the data plane is proven rather than only the option plumbing.src/main/network/outbound-proxy.test.ts(new, 8 cases) covers the resolver: proxy rule, dispatcher, DIRECT, loopback bypass, unsupported rule, unusable rule, credentials, env fallback.3 failed | 42 passed).pnpm tcclean; the 10 touched test files 107 passed; thesrc/main/**relay/network/profiles/jira/rpc/startup sweep 3516 passed;pnpm run check:code-quality:changedclean (ORCA_CODE_QUALITY_BASE=HEAD~1, because the gate otherwise compares against amainref that is hundreds of commits behind this worktree's base and reports pre-existing findings from the whole delta).Platform testing: Linux only. I did not run this against a real corporate proxy on macOS or Windows; the proof here is unit tests plus a local CONNECT proxy. The renderer/Chromium side is untouched, and the changed path is
net.fetchplussession.resolveProxy, both platform-neutral.Known limits, unchanged from before (not regressions):
HTTPS-proxy andSOCKS*rules still resolve direct — they need an agent this change does not add — and loopback stays direct by design.AI Disclosure
Written with an AI coding agent (deepseek-flash via opencode-go). All commands in Testing were run by the agent on Linux.
Review
AI-assisted code review summary, per CONTRIBUTING:
globalThis.fetch, only adding a dispatcher when a proxy resolves, so macOS, Windows and Linux take the same code path.resolveProxycall per relay socket plus memoized agents per proxy URL; HTTP calls add the same memoized proxy-ensure step Jira already performed.Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance
Review rounds after the first commit:
6a0669535c(proxy credentials on the Chromium-less path, thestop()window while a proxy resolves) and7e7b417203(ordered resolver directives, the whole127.0.0.0/8loopback bypass,NO_PROXYon the Chromium-less path,Requestinputs forwarded intact, duplicate open for oneconnId).Other main-process fetch call sites still leave through
globalThis.fetchand therefore still ignore the app proxy:main/artifacts/artifact-cloud-request.ts,main/azure-devops/azure-devops-api-request.ts,main/bitbucket/client.ts,main/bitbucket/user-request.ts,main/gitea/client.ts,main/rate-limits/codex-fetcher.ts,main/runtime/push/push-gateway-client.ts,main/source-control/hosted-review-api-request.tsandmain/speech/openai-transcription-client.ts. They stay inAUDITED_GLOBAL_FETCH_LINESon purpose: this PR is scoped to the Cloud auth and Relay paths #19976 reports, andfetchWithConfiguredProxyis the drop-in for them in a follow-up.Two production dependencies are added and justified above;
pnpm-lock.yamlchanges are limited to their importer entries. Injectedfetch/createSocketstill take precedence, so no caller or test loses control of its transport.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)