mirror of
https://github.com/stablyai/orca.git
synced 2026-10-28 00:02:54 +00:00
fix(browser): reuse capability verdict and exclude floating terminals
Cache the runtime browser capability check across multiple remote port opens instead of checking for each port independently. Prevent floating-terminal workspaces from routing to remote browsers.
This commit is contained in:
@@ -319,7 +319,7 @@ describe('PortsPanel runtime routing', () => {
|
||||
).toEqual(['runtime-repo::/srv/app', 'repo::/workspace/app'])
|
||||
})
|
||||
|
||||
it('opens remote workspace ports in the server-side browser and binds the local page handle', async () => {
|
||||
it('reuses the capability verdict across remote port opens', async () => {
|
||||
runtimeEnvironmentCall.mockImplementation(({ method }: { method: string }) =>
|
||||
Promise.resolve({
|
||||
id: method,
|
||||
@@ -332,6 +332,14 @@ describe('PortsPanel runtime routing', () => {
|
||||
const createBrowserTab = vi.fn(() => ({ activePageId: 'local-page-1' }))
|
||||
const setRemoteBrowserPageHandle = vi.fn()
|
||||
|
||||
await expect(
|
||||
openWorkspacePortInBrowser({
|
||||
port: workspacePort,
|
||||
runtimeTarget: { kind: 'environment', environmentId: 'env-1' },
|
||||
createBrowserTab: createBrowserTab as never,
|
||||
setRemoteBrowserPageHandle: setRemoteBrowserPageHandle as never
|
||||
})
|
||||
).resolves.toEqual({ ok: true })
|
||||
await expect(
|
||||
openWorkspacePortInBrowser({
|
||||
port: workspacePort,
|
||||
@@ -341,9 +349,11 @@ describe('PortsPanel runtime routing', () => {
|
||||
})
|
||||
).resolves.toEqual({ ok: true })
|
||||
|
||||
expect(activateAndRevealWorktreeMock).toHaveBeenCalledTimes(2)
|
||||
expect(activateAndRevealWorktreeMock).toHaveBeenCalledWith('repo::/workspace/app')
|
||||
expect(runtimeEnvironmentCall.mock.calls.map((call) => call[0].method)).toEqual([
|
||||
'status.get',
|
||||
'browser.tabCreate',
|
||||
'browser.tabCreate'
|
||||
])
|
||||
expect(runtimeEnvironmentCall.mock.calls[1][0].params).toEqual({
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
toRuntimeExecutionHostId,
|
||||
toSshExecutionHostId
|
||||
} from '../../../shared/execution-host'
|
||||
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants'
|
||||
import { BROWSER_SCREENCAST_RUNTIME_CAPABILITY } from '../../../shared/protocol-version'
|
||||
import {
|
||||
canOpenWorkspaceBrowserTabOnRuntime,
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
const mocks = vi.hoisted(() => ({
|
||||
createRemote: vi.fn(),
|
||||
getState: vi.fn(),
|
||||
pairedWeb: false,
|
||||
state: {} as Record<string, unknown>
|
||||
}))
|
||||
|
||||
@@ -24,6 +26,10 @@ vi.mock('@/runtime/web-runtime-session', () => ({
|
||||
createWebRuntimeSessionBrowserTab: (...args: unknown[]) => mocks.createRemote(...args)
|
||||
}))
|
||||
|
||||
vi.mock('./desktop-window-chrome', () => ({
|
||||
isPairedWebClientWindow: () => mocks.pairedWeb
|
||||
}))
|
||||
|
||||
const WORKSPACE_ID = 'repo-1::/repo/worktree'
|
||||
|
||||
function ownerState(hostId?: string, runtimeOwnerEnvironmentId?: string): Record<string, unknown> {
|
||||
@@ -55,6 +61,7 @@ function browserCapableRuntime(environmentId: string): Record<string, unknown> {
|
||||
beforeEach(() => {
|
||||
mocks.createRemote.mockReset().mockResolvedValue(true)
|
||||
mocks.getState.mockReset().mockImplementation(() => mocks.state)
|
||||
mocks.pairedWeb = false
|
||||
mocks.state = {}
|
||||
})
|
||||
|
||||
@@ -186,6 +193,70 @@ describe('openWorkspaceBrowserTab', () => {
|
||||
expectedRuntimeEnvironmentId: 'hub-a'
|
||||
})
|
||||
).rejects.toThrow('Unable to open URL.')
|
||||
expect(mocks.createRemote).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ environmentId: 'hub-a' })
|
||||
)
|
||||
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not route an asserted floating-terminal source to either runtime or local browser', async () => {
|
||||
const createBrowserTab = vi.fn()
|
||||
mocks.state = {
|
||||
activeWorktreeId: FLOATING_TERMINAL_WORKTREE_ID,
|
||||
activeWorkspaceExecutionHostId: toRuntimeExecutionHostId('hub-a'),
|
||||
...browserCapableRuntime('hub-a'),
|
||||
createBrowserTab,
|
||||
defaultBrowserSessionProfileId: 'client-profile',
|
||||
defaultBrowserSessionProfileIdByHostId: {}
|
||||
}
|
||||
|
||||
expect(
|
||||
canOpenWorkspaceBrowserTabOnRuntime(
|
||||
mocks.state as never,
|
||||
FLOATING_TERMINAL_WORKTREE_ID,
|
||||
'hub-a'
|
||||
)
|
||||
).toBe(false)
|
||||
|
||||
await expect(
|
||||
openWorkspaceBrowserTab({
|
||||
workspaceId: FLOATING_TERMINAL_WORKTREE_ID,
|
||||
url: 'http://localhost:3000/',
|
||||
intent: { kind: 'url' },
|
||||
expectedRuntimeEnvironmentId: 'hub-a'
|
||||
})
|
||||
).rejects.toThrow('Unable to open URL.')
|
||||
expect(mocks.createRemote).not.toHaveBeenCalled()
|
||||
expect(createBrowserTab).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps runtime browser actions unavailable in a paired-web floating workspace', async () => {
|
||||
mocks.pairedWeb = true
|
||||
mocks.state = {
|
||||
activeWorktreeId: FLOATING_TERMINAL_WORKTREE_ID,
|
||||
activeWorkspaceExecutionHostId: toRuntimeExecutionHostId('hub-a'),
|
||||
...browserCapableRuntime('hub-a'),
|
||||
createBrowserTab: vi.fn(),
|
||||
defaultBrowserSessionProfileId: 'client-profile',
|
||||
defaultBrowserSessionProfileIdByHostId: {}
|
||||
}
|
||||
|
||||
expect(
|
||||
canOpenWorkspaceBrowserTabOnRuntime(
|
||||
mocks.state as never,
|
||||
FLOATING_TERMINAL_WORKTREE_ID,
|
||||
'hub-a'
|
||||
)
|
||||
).toBe(false)
|
||||
await expect(
|
||||
openWorkspaceBrowserTab({
|
||||
workspaceId: FLOATING_TERMINAL_WORKTREE_ID,
|
||||
url: 'http://localhost:3000/',
|
||||
intent: { kind: 'url' },
|
||||
expectedRuntimeEnvironmentId: 'hub-a'
|
||||
})
|
||||
).rejects.toThrow('Unable to open URL.')
|
||||
expect(mocks.createRemote).not.toHaveBeenCalled()
|
||||
expect(mocks.state.createBrowserTab).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
type ExecutionHostId
|
||||
} from '../../../shared/execution-host'
|
||||
import { SEARCH_ENGINE_LABELS, type SearchEngine } from '../../../shared/browser-url'
|
||||
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants'
|
||||
import { BROWSER_SCREENCAST_RUNTIME_CAPABILITY } from '../../../shared/protocol-version'
|
||||
import {
|
||||
getClientCreationActionPolicy,
|
||||
@@ -27,10 +28,12 @@ export type OpenWorkspaceBrowserTabRequest = {
|
||||
|
||||
function isExpectedRuntimeBrowserRoute(
|
||||
state: AppState,
|
||||
availability: ClientCreationActionAvailability,
|
||||
route: ReturnType<typeof resolveWorktreeOperationRoute>,
|
||||
workspaceId: string,
|
||||
expectedRuntimeEnvironmentId: string
|
||||
): boolean {
|
||||
if (!route) {
|
||||
if (availability.state !== 'enabled' || workspaceId === FLOATING_TERMINAL_WORKTREE_ID || !route) {
|
||||
return false
|
||||
}
|
||||
const expectedEnvironmentId = expectedRuntimeEnvironmentId.trim()
|
||||
@@ -55,8 +58,15 @@ export function canOpenWorkspaceBrowserTabOnRuntime(
|
||||
workspaceId: string,
|
||||
expectedRuntimeEnvironmentId: string
|
||||
): boolean {
|
||||
const availability = getClientCreationActionPolicy(state, workspaceId)['managed-browser']
|
||||
const route = resolveWorktreeOperationRoute(state, workspaceId)
|
||||
return isExpectedRuntimeBrowserRoute(state, route, expectedRuntimeEnvironmentId)
|
||||
return isExpectedRuntimeBrowserRoute(
|
||||
state,
|
||||
availability,
|
||||
route,
|
||||
workspaceId,
|
||||
expectedRuntimeEnvironmentId
|
||||
)
|
||||
}
|
||||
|
||||
// Why: concurrent URL tabs are indistinguishable under a shared "Open URL"
|
||||
@@ -172,7 +182,13 @@ export async function openWorkspaceBrowserTab(
|
||||
const expectedEnvironmentId = request.expectedRuntimeEnvironmentId?.trim() || null
|
||||
if (
|
||||
expectedEnvironmentId &&
|
||||
!isExpectedRuntimeBrowserRoute(state, route, expectedEnvironmentId)
|
||||
!isExpectedRuntimeBrowserRoute(
|
||||
state,
|
||||
availability,
|
||||
route,
|
||||
request.workspaceId,
|
||||
expectedEnvironmentId
|
||||
)
|
||||
) {
|
||||
throw openFailure(presentation.error, 'asserted runtime cannot provide this managed browser')
|
||||
}
|
||||
|
||||
@@ -325,8 +325,7 @@ export async function assertRuntimeEnvironmentCapability(
|
||||
message: string,
|
||||
timeoutMs?: number
|
||||
): Promise<void> {
|
||||
const status = await getRuntimeEnvironmentStatus(environmentId, timeoutMs)
|
||||
if (!status.capabilities?.includes(capability)) {
|
||||
if (!(await runtimeEnvironmentSupportsCapability(environmentId, capability, timeoutMs))) {
|
||||
throw new Error(message)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user