Repository navigation
fix(ssh): agents on a managed SSH host can drive the desktop Orca browser again, behind the per-host switch - #29384
Conversation
aa7986d to
8f9939b
Compare
1727f96 to
912a0ce
Compare
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 1 minute. View limit details
📝 Walkthrough
Priority: ⬆️ High Severity of issue fixed: Medium Merge Risk: 🟡 Moderate · up to
|
912a0ce to
bc67127
Compare
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:
35bd06d0-60e5-400e-8dda-a465ea83548d
⛔ Files ignored due to path filters: 1
src/shared/rpc-contract/rpc-params-catalog.generated.tsis excluded by!**/*.generated.*
📒 Files selected for processing: 68
config/scripts/ci-e2e-job-selection.test.mjsconfig/scripts/locale-ko-key-overrides.jsonconfig/scripts/pr-e2e-source-routing.mjssrc/main/browser/agent-browser-bridge-capture-commands.tssrc/main/browser/agent-browser-bridge-queue.tssrc/main/browser/browser-manager-queries.tssrc/main/browser/browser-session-partition-policies.test.tssrc/main/browser/browser-session-partition-policies.tssrc/main/browser/desktop-browser-bridge/desktop-browser-bridge-connection.tssrc/main/browser/desktop-browser-bridge/desktop-browser-bridge-executor.test.tssrc/main/browser/desktop-browser-bridge/desktop-browser-bridge-executor.tssrc/main/browser/desktop-browser-bridge/desktop-browser-bridge-manager.test.tssrc/main/browser/desktop-browser-bridge/desktop-browser-bridge-manager.tssrc/main/browser/desktop-browser-bridge/desktop-browser-bridge-renderer-scope.tssrc/main/browser/desktop-browser-bridge/remote-cli-driven-workspaces.test.tssrc/main/browser/desktop-browser-bridge/remote-cli-driven-workspaces.tssrc/main/browser/local-ssh-browser-partitions.tssrc/main/ipc/desktop-browser-bridge-reconcile.tssrc/main/ipc/desktop-browser-bridge-wiring.tssrc/main/ipc/runtime-environment-manual-disconnect.tssrc/main/ipc/runtime-environments.tssrc/main/ipc/ssh-renderer-broadcast.tssrc/main/ipc/ssh-target-crud-handlers.tssrc/main/ipc/ssh.tssrc/main/orcad/orcad-lifecycle.tssrc/main/persistence/leasing-ssh-ptys/ssh-normalization-remote-cli-consent.test.tssrc/main/persistence/leasing-ssh-ptys/ssh-normalization.tssrc/main/runtime/desktop-browser-bridge/desktop-browser-bridge-allowlist.test.tssrc/main/runtime/desktop-browser-bridge/desktop-browser-bridge-dispatch.test.tssrc/main/runtime/desktop-browser-bridge/desktop-browser-bridge-registry.test.tssrc/main/runtime/desktop-browser-bridge/desktop-browser-bridge-registry.tssrc/main/runtime/desktop-browser-bridge/desktop-browser-bridge-route.tssrc/main/runtime/desktop-browser-bridge/remote-cli-browser-scope.tssrc/main/runtime/orca-runtime-get-status.tssrc/main/runtime/rpc/dispatcher.tssrc/main/runtime/rpc/methods/desktop-browser-bridge.tssrc/main/runtime/rpc/methods/index.tssrc/main/runtime/session-graph/session-graph-authority.tssrc/main/runtime/terminal/driver/terminal-driver-resources.tssrc/main/ssh/ssh-connection-store.test.tssrc/main/ssh/ssh-connection-store.tssrc/main/startup/main-process-quit.tssrc/preload/api/ui-bridge-state-and-menu-commands.tssrc/preload/api/ui-command-event-api.tssrc/renderer/src/components/settings/SshHostAdvancedFields.tsxsrc/renderer/src/components/settings/ssh-target-draft.tssrc/renderer/src/hooks/ipc-events-agent-status-window-test-fixtures.tssrc/renderer/src/hooks/ipc-events-close-routing-test-harness.tssrc/renderer/src/hooks/ipc-events-terminal-create-window-test-fixtures.tssrc/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.owner.test.tssrc/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.profile-switch.test.tssrc/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.tssrc/renderer/src/hooks/ipc-events/browser-request-workspace-owner.tssrc/renderer/src/hooks/useIpcEvents-browser-tab-create.test.tssrc/renderer/src/hooks/useIpcEvents-cli-worktree-activation.test.tssrc/renderer/src/hooks/useIpcEvents-close-routing-browser-pages.test.tssrc/renderer/src/hooks/useIpcEvents-ssh-disconnect-cleanup.test.tssrc/renderer/src/hooks/useIpcEvents-updater-status.test.tssrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/fr.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh.jsonsrc/renderer/src/web/preload-api/web-ui-api.tssrc/shared/desktop-browser-bridge-protocol.tssrc/shared/ssh-types.tstests/e2e/ssh-orcad-desktop-browser-bridge.spec.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.
| if (active?.revoked?.aborted) { | ||
| throw new BrowserError( | ||
| DESKTOP_BROWSER_BRIDGE_UNAVAILABLE_CODE, | ||
| 'The Orca desktop stopped sharing its browser before this command ran.' | ||
| ) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
A missed desktop deadline is reported as "the desktop stopped sharing its browser".
In src/main/browser/desktop-browser-bridge/desktop-browser-bridge-connection.ts (lines 110-112), answer() merges this.revoked.signal and AbortSignal.timeout(request.timeoutMs) into one signal. That merged signal reaches this scope as revoked. Several checks then treat any abort as a detach:
captureRemoteCliBrowserRevocation(this range).onRevokedinboundRemoteCliBrowserWork(lines 99-105).- The pre-dispatch check in
desktop-browser-bridge-executor.ts(lines 96-101).
Each one returns DESKTOP_BROWSER_BRIDGE_UNAVAILABLE_CODE with a "stopped sharing" message.
Trigger: a command waits in the per-page queue behind a slow command until its deadline passes. The bridge is still attached, so orcad accepts the result and the CLI reports the bridge as unavailable. The agent or user then looks at the setting or the connection, but the real cause was a timeout.
Fix: check signal.reason. AbortSignal.timeout aborts with a DOMException whose name is 'TimeoutError'. Map that reason to browser_timeout.
Proposed fix
--- "a/src/main/runtime/desktop-browser-bridge/remote-cli-browser-scope.ts"
+++ "b/src/main/runtime/desktop-browser-bridge/remote-cli-browser-scope.ts"
@@ -38,13 +38,16 @@
*/
export function captureRemoteCliBrowserRevocation(): (webContentsId?: number) => void {
const active = scope.getStore()
return (webContentsId) => {
if (active?.revoked?.aborted) {
+ if ((active.revoked.reason as { name?: string } | undefined)?.name === 'TimeoutError') {
+ throw new BrowserError('browser_timeout', 'The browser command passed its deadline before it ran.')
+ }
throw new BrowserError(
DESKTOP_BROWSER_BRIDGE_UNAVAILABLE_CODE,
'The Orca desktop stopped sharing its browser before this command ran.'
)
}
// Why at execution: routing can be turned off while the command waits, remounting the page unrouted.
if (webContentsId !== undefined && active?.admitGuest && !active.admitGuest(webContentsId)) {
throw new BrowserError(Apply the same check in onRevoked (lines 99-105) and in executeDesktopBrowserBridgeRequest (lines 96-101).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (active?.revoked?.aborted) { | |
| throw new BrowserError( | |
| DESKTOP_BROWSER_BRIDGE_UNAVAILABLE_CODE, | |
| 'The Orca desktop stopped sharing its browser before this command ran.' | |
| ) | |
| } | |
| if (active?.revoked?.aborted) { | |
| if ((active.revoked.reason as { name?: string } | undefined)?.name === 'TimeoutError') { | |
| throw new BrowserError('browser_timeout', 'The browser command passed its deadline before it ran.') | |
| } | |
| throw new BrowserError( | |
| DESKTOP_BROWSER_BRIDGE_UNAVAILABLE_CODE, | |
| 'The Orca desktop stopped sharing its browser before this command ran.' | |
| ) | |
| } |
| ...updates, | ||
| allowRemoteCliControlConsent: | ||
| updates.allowRemoteCliControl === true ? 'desktop-browser-bridge' : undefined, | ||
| allowRemoteCliControlReset: undefined |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 5 'buildSshTargetSavePayload\s*\(|payload\.updates|updateTarget\s*\(' \
src/renderer/src/components/settings src/main/ipcRepository: stablyai/orca
Length of output: 12629
🏁 Script executed:
set -euo pipefail
printf '%s\n' '--- payload helper ---'
nl -ba src/renderer/src/components/settings/ssh-target-save-payload.ts | sed -n '1,180p'
printf '%s\n' '--- store consent helper and callers ---'
nl -ba src/main/ssh/ssh-connection-store.ts | sed -n '240,335p'
printf '%s\n' '--- relevant tests ---'
rg -n -C 8 -F -- 'allowRemoteCliControlReset' src/main src/renderer/src/components/settingsRepository: stablyai/orca
Length of output: 21023
🏁 Script executed:
set -euo pipefail
nl -ba src/main/ssh/ssh-connection-store.ts | sed -n '1,145p'Repository: stablyai/orca
Length of output: 7214
Preserve the reset notice when the switch value is unchanged.
buildSshTargetSavePayload always includes allowRemoteCliControl: undefined in updates when the switch is off. The normal settings-save path sends those updates to SshConnectionStore.updateTarget, and withRemoteCliControlConsent clears allowRemoteCliControlReset based on property presence. An unrelated host edit can therefore remove the migration notice.
Compare the effective switch value with the existing target before clearing the notice. Preserve consent stamping when the update includes the switch.
Suggested fix
-function withRemoteCliControlConsent<T extends Partial<SshTarget>>(updates: T): T {
+function withRemoteCliControlConsent<T extends Partial<SshTarget>>(
+ updates: T,
+ clearResetNotice = true
+): T {
if (!('allowRemoteCliControl' in updates)) {
return updates
}
return {
...updates,
allowRemoteCliControlConsent:
updates.allowRemoteCliControl === true ? 'desktop-browser-bridge' : undefined,
- allowRemoteCliControlReset: undefined
+ ...(clearResetNotice ? { allowRemoteCliControlReset: undefined } : {})
}
}- const consented = withRemoteCliControlConsent(updates)
+ const remoteCliControlChanged =
+ 'allowRemoteCliControl' in updates &&
+ (existing?.allowRemoteCliControl === true) !==
+ (updates.allowRemoteCliControl === true)
+ const consented = withRemoteCliControlConsent(updates, remoteCliControlChanged)| // Why null, not inferred: this desktop's runtime only ever asks for a page this desktop | ||
| // renders, including one in a managed SSH host's workspace (as the tab bar's + does). | ||
| browserRuntimeEnvironmentId: null, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
🔎 Supported by static analysis
🏁 Script executed:
#!/bin/bash
rg -nP -C6 "browser:requestTabCreate" src/main
rg -nP -C4 "browserRuntimeEnvironmentId" src/renderer/src/store/slices/browser | head -80Repository: stablyai/orca
Length of output: 16423
🏁 Script executed:
set -o pipefail
printf '%s\n' '--- diff ---'
git diff --no-ext-diff --unified=30 0acb4933c098489a7a670f71f9619243a0244a24 bc67127cad8a44863a6e4789c046d90be286b076 -- src/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.ts src/main/runtime/browser/browser-tab-renderer-create.ts src/main/runtime/browser/browser-commands.ts src/main/runtime/orca-runtime-browser.ts
printf '%s\n' '--- renderer bridge ---'
nl -ba src/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.ts | sed -n '1,180p'
printf '%s\n' '--- main create and callers ---'
rg -n -F --glob '*.ts' -e 'createBrowserTab' -e 'requestTabCreate' -e 'resolveRemoteCliBrowserScopeSelector' src/main src/renderer/src | head -240
printf '%s\n' '--- owner test ---'
nl -ba src/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.owner.test.ts | sed -n '1,230p'Repository: stablyai/orca
Length of output: 41754
🏁 Script executed:
set -o pipefail
printf '%s\n' '--- main browser creation ---'
nl -ba src/main/runtime/browser/browser-tab-creation.ts | sed -n '1,240p'
nl -ba src/main/runtime/browser/browser-tab-renderer-create.ts | sed -n '1,120p'
printf '%s\n' '--- renderer tab/page creation ---'
nl -ba src/renderer/src/store/slices/browser/browser-tab-actions.ts | sed -n '1,120p'
nl -ba src/renderer/src/store/slices/browser/browser-page-create-actions.ts | sed -n '1,100p'
printf '%s\n' '--- runtime id and ownership helpers ---'
rg -n -F --glob '*.ts' -e 'browserRuntimeEnvironmentId' -e 'isBrowserWorkspaceOwnedByPairedServer' -e 'resolveRemoteCliBrowserWorkspace' src/renderer/src src/main | head -240Repository: stablyai/orca
Length of output: 42681
🏁 Script executed:
set -o pipefail
printf '%s\n' '--- exact runtime-owner semantics ---'
nl -ba src/renderer/src/store/slices/browser/browser-host-state.ts | sed -n '1,145p'
nl -ba src/renderer/src/store/slices/browser-page-records.ts | sed -n '45,95p'
nl -ba src/renderer/src/hooks/ipc-events/browser-request-workspace-owner.ts | sed -n '1,130p'
printf '%s\n' '--- inference consumers ---'
rg -n -F --glob '*.ts' -e 'getRuntimeEnvironmentIdForWorktree' -e 'browserRuntimeEnvironmentId ??' -e 'browserRuntimeEnvironmentId === undefined' -e 'browserRuntimeEnvironmentId === null' src/renderer/src | head -180
printf '%s\n' '--- resolveBrowserWorkspace binding ---'
rg -n -F --glob '*.ts' -e 'resolveBrowserWorkspace:' -e 'resolveBrowserWorkspace(' -e 'resolveBrowserWorkspace =' src/main | head -180Repository: stablyai/orca
Length of output: 41734
🏁 Script executed:
set -o pipefail
printf '%s\n' '--- bound main resolver ---'
nl -ba src/main/runtime/session-graph/session-graph-authority.ts | sed -n '145,245p'
printf '%s\n' '--- page owner routing ---'
rg -n -F --glob '*.ts' -e 'page.browserRuntimeEnvironmentId' -e 'browserRuntimeEnvironmentId)' -e 'browserRuntimeEnvironmentId,' -e 'browserRuntimeEnvironmentId ?' src/renderer/src/components src/renderer/src/hooks src/renderer/src/lib src/renderer/src/store | head -180Repository: stablyai/orca
Length of output: 7409
Preserve runtime-owner inference for the desktop CLI.
browser:requestTabCreate now forces every request to use local rendering. For an explicit paired-server worktree, the desktop CLI still resolves and sends the request, but the renderer stores browserRuntimeEnvironmentId: null instead of inferring the worktree owner. This creates a desktop-rendered page instead of the paired server's page.
Pass a bridge-only marker through IPC and set browserRuntimeEnvironmentId: null only for bridged creates. Keep the field omitted for ordinary desktop CLI requests, or reject or reroute explicit paired-server worktrees before sending browser:requestTabCreate.
…t's orca CLI again
…sktop pages, and route the e2e
…sconnect, closes desktop tabs
…ever leave the browser queue
…; report undeliverable results
…s by a deadline and revocation
… at run time, and cancels host-driven downloads
…arts a command past orcad's deadline
…tarting before orcad's deadline
…drove the desktop browser
bc67127 to
ec97dea
Compare
|
since #27214 profile commands from an ssh host don't work anymore, earlier the relay used to forward them
can we allow a host to list/create/use/delete only its own isolated profiles ? import, cookies etc can stay blocked our agents on remote seats need a separate login for testing and right now there is no way to do it |
ELI5
Before 1.4.225, an agent working in a terminal on an SSH host could use the Orca browser on your desktop. That browser is already signed in to your accounts, so the agent did not need its own login. 1.4.225 moved SSH hosts onto a small Orca server that runs on the host. After that move, the host had no way to reach your desktop's browser, so every
orca tab …/orca goto …command on the host failed with "Browser automation is unavailable on this host."This PR brings it back, but only for hosts where you turned on "Allow this host's orca CLI to control Orca". When that switch is on, your desktop keeps a private channel open to that host's Orca server. Browser commands from the host's
orcaCLI travel down that channel, and your desktop runs them in browser tabs that belong to that host's workspaces. When the switch is off, the host gets a clear error that names the switch.What Changed
Before (1.4.225+): on a managed SSH host,
orca tab create,orca tab list,orca snapshotand the rest returnedbrowser_unavailable. The per-host switch was stored and shown, but nothing read it. Its help text promised access to "other hosts, orchestration, files, and integrations", and none of that worked.After:
orcaCLI can open, list, switch, close, navigate, read (snapshot, screenshot, get/is/find/wait, eval, console) and type into or click desktop browser tabs in that host's workspaces. A new tab is a normal desktop tab in that workspace. If SSH browser routing is on (the default), its traffic goes out through the host, the same as a tab opened from the tab bar.desktop_browser_bridge_unavailablewith a message that names the switch.desktop_browser_bridge_not_forwarded.truesaved while the switch did nothing (1.4.225 onward) is not consent to this new behavior. On first load it is turned off, and the host's SSH settings show a notice under the switch explaining why. Turning the switch on again stampsallowRemoteCliControlConsent: 'desktop-browser-bridge'in main, so the value is kept from then on.Mechanism:
orcad (host side). orcad now serves two RPC methods,
desktopBrowserBridge.attach(a long-lived stream) anddesktopBrowserBridge.result. It advertises the new capabilitybrowser.desktopBridge.v1in its status. When the host's own CLI calls an allowlistedbrowser.*method and a desktop is attached, the dispatcher sends the call down the stream instead of to orcad's own browser layer. Before it does, orcad turns the caller's workspace selector (path:,branch:, cwd) into a concrete workspace id. orcad does this only for calls from its local CLI, never for paired clients. The desktop must answer over the same socket that attached, or the answer is ignored. Detaching fails any waiting command at once.Desktop. A new
DesktopBrowserBridgeManagerattaches to exactly the managed SSH hosts that have the switch on, are connected now, and whose server advertises the capability. It re-checks on every SSH state change and on every host-settings save, and it detaches at once when a host stops qualifying. Each forwarded command goes throughexecuteDesktopBrowserBridgeRequest, which:--pagetarget, checks the page's workspace;RpcDispatcherwhose registry holds only the allowlisted methods.While the command runs, an
AsyncLocalStoragescope pins every workspace selector the browser layer resolves to that one workspace. Any other selector is refused.Renderer. A tab that this desktop's runtime asks for is now always created as a desktop page (
browserRuntimeEnvironmentId: null), which is what the tab bar's + already does in a managed SSH workspace. Before, the renderer took the page's owner from the workspace, so a page created in a host's workspace was treated as one the host's server streams, and it never loaded.Unchanged: local browser automation, paired-server client-hosted pages, headless hosts that use
ORCA_BROWSER_EXECUTABLEwhile no desktop is attached, and the desktop's own CLI.Threat model
A remote host that you may not fully trust (for example a sandbox VM that runs agents) gets to drive a browser that is signed in to your accounts. This is what each layer prevents:
--pagein any other workspace is reported as not found. While the command runs, every workspace selector is pinned to that workspace, and anything else is refused. A command that names no workspace is refused rather than widened to all tabs.exec(raw agent-browser commands), browser profiles and identity, credentials and headers, interception, certificate bypass, screencast and client-host methods. Orchestration, files and the rest of the old CLI surface are not forwarded. orcad serves those on the host itself now. The desktop re-checks the allowlist, and its dispatcher registry contains only the allowlisted methods.desktop_browser_bridge_unrouted. Routed tabs use a SOCKS5 proxy with no loopback bypass (<-loopback>), so all page traffic leaves through the host: subresources,fetchfrom eval, WebSockets and DNS (resolved by the proxy). The existing WebRTC, QUIC/H3 and DNS-prefetch guards and their tests cover the remaining paths. While the route is being prepared or has failed, the gate mounts no page at all. Turning routing off for a host detaches its bridge at once and cancels any command still waiting. A queued command checks, just before it runs, that its page's session is this host's prepared route partition.persist:orca-browser-v<N>-<digest(orca-profile, browser-profile, local SSH provider identity, ssh:<targetId>)>(deriveBrowserRoutePartition). It is separate from local browsing and from every other host. It is shared only with the user's own tabs in that same host's workspaces, which is the signed-in context this feature exists to reach.evalis bounded at 30s. It also stops at once when the bridge detaches. Either way the page's script is terminated withRuntime.terminateExecution, and the page's command queue is freed.downloadis not forwarded. A page in a workspace a host has driven also has every native download cancelled, so a navigation to a response that forces a download (Content-Disposition: attachment) cannot drop a file into the desktop's Downloads folder.gotoandtab createaccept onlyhttp://,https://orabout:blank. A bare path would otherwise becomefile://, andloadURLskips the guest navigation guard. A command also refuses to run on a page that currently shows a local document, such as afile://the user opened in that tab. Chromium's own error page still allows navigating away.clipboard-readpermission for the rest of the run, in both the request handler and the check handler. Once a host has driven any page, a guest whose owning page is unknown is also refused. Paste chords (Ctrl/Cmd+V, Shift+Insert) are refused inkeypress. Typed and filled text still work.--cdpanywhere in its argv. Any host value that reaches that argv and starts with-is refused, including every chunk of typed text. Eval runs over CDP and fill writes through stdin, so those two are exempt.tab createdrops the profile, page id, tab group, activation and placement fields. New tabs open in the background on the default profile.[desktop-browser-bridge] auditline per forwarded command: host, environment, method, workspace, page, URL origin (never the path or query, which can hold tokens) and outcome. Params are not logged, because typed text can be a password.Residual risk: with the switch on, the host can do in that workspace's desktop tabs what a person at the keyboard could do on those pages, including running JavaScript on the page (
eval). That is the point of the feature, and it is why the switch is per host and off by default.Why
ssh-relay-session.tsandssh-remote-orca-cli.tsatv1.4.223). It forwarded the whole CLI throughorca.cli. This PR forwards a curated browser-only subset on purpose.Linked Issue
Fixes #29145
Visual Proof
N/A for a UI change: the only UI change is the help text under an existing switch. The Docker e2e saves a screenshot (
desktop-browser-bridge-on.png) of the desktop after the host opened and navigated a tab.Testing
Unit tests:
src/main/runtime/desktop-browser-bridge/desktop-browser-bridge-dispatch.test.tsruns a realRpcDispatcherfor orcad. It covers forwarding and the answer, rejected spoofed answers from another socket, non-allowlisted methods, the error that names the switch when no desktop is attached (an old desktop or the switch off), a refused attach without the capability, refused unauthenticated or phone sockets, a desktop runtime refusing to serve, failing waiting commands on detach, keeping a local headless provider, and never forwarding a paired client's call.desktop-browser-bridge-allowlist.test.tschecks that every allowlisted method is a real one-shotworkspace-tier browser method, and that the risky methods are excluded.src/main/browser/desktop-browser-bridge/desktop-browser-bridge-executor.test.tscovers scope enforcement: a foreign workspace, a foreign page, no workspace, a mismatched selector, the scope pinning other selectors during dispatch,tab createdropping fields, 16 refused methods, and audit entries that never contain the URL query.desktop-browser-bridge-manager.test.tscovers attach and answer over the same socket, an old orcad never being attached, detaching when the switch turns off, a request before readiness, a non-allowlisted request at the protocol layer, a transient status failure retrying soon, and a removed host never retrying.browser-request-ipc-bridge.owner.test.tscovers the renderer's owner check: the owning environment, another environment, a local workspace, an unknown workspace, a page resolved to its workspace, a page in another workspace, and a store that throws. It also checks that a requested page in a host's workspace is created as a desktop page.Docker SSH e2e,
tests/e2e/ssh-orcad-desktop-browser-bridge.spec.ts, run locally withORCA_BACKGROUND_LAUNCH=1and hidden windows against a real managed orcad on a Docker host:orca tab liston the host is refused with the switch's name.orca tab create --urlopens a desktop tab in the host's workspace. A host-only hostname loads through the host.orca goto --pagenavigates the tab, andorca tab listshows it.pnpm tc,pnpm run check:code-quality:changedand the localization checks pass.I manually tested these changes locally
Automated tests added/updated, or explained why not below
Review
Agent skill upstream boundary
Notes
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)