diff --git a/src/main/runtime/orca-runtime-create-agent-session.ts b/src/main/runtime/orca-runtime-create-agent-session.ts index 4c42117fb9e..103e76ec146 100644 --- a/src/main/runtime/orca-runtime-create-agent-session.ts +++ b/src/main/runtime/orca-runtime-create-agent-session.ts @@ -36,7 +36,7 @@ export abstract class OrcaRuntimeWithCreateAgentSession extends OrcaRuntimeWithG agentTeamsEnv?: Record ): Record - abstract getAuthoritativeWindow(): BrowserWindow + abstract getAuthoritativeWindow(missingWindowMessage?: string): BrowserWindow abstract waitForTerminalHandle(tabId: string, timeoutMs?: number): Promise diff --git a/src/main/runtime/orca-runtime-create-terminal-desktop.test.ts b/src/main/runtime/orca-runtime-create-terminal-desktop.test.ts new file mode 100644 index 00000000000..d78120de8c9 --- /dev/null +++ b/src/main/runtime/orca-runtime-create-terminal-desktop.test.ts @@ -0,0 +1,10 @@ +import { expect, it } from 'vitest' +import { wireHeadlessServeRuntime } from './orca-runtime-test-scenario-builders.spec' + +it('tells a workspace-less create on a windowless host to pass --worktree', async () => { + const runtime = wireHeadlessServeRuntime() + + await expect(runtime.createTerminal(undefined, { title: 'probe' })).rejects.toThrow( + /No workspace was given or found.*Pass --worktree.*No terminal was created\./ + ) +}) diff --git a/src/main/runtime/orca-runtime-create-terminal-desktop.ts b/src/main/runtime/orca-runtime-create-terminal-desktop.ts index 774b5f8acd1..6a656ebf79d 100644 --- a/src/main/runtime/orca-runtime-create-terminal-desktop.ts +++ b/src/main/runtime/orca-runtime-create-terminal-desktop.ts @@ -3,6 +3,10 @@ import * as dependencies from './orca-runtime-create-terminal-dependencies' import type { OrcaRuntimeWithCreateTerminal } from './terminal/launch/terminal-create' import type { RuntimeTerminalPresentation } from '../../shared/runtime-types' +// Without a window, the missing piece the caller can supply is the workspace, so name that. +const NO_WORKSPACE_OR_WINDOW_MESSAGE = + 'No workspace was given or found for this terminal, and this Orca host has no open app window to create one in. Pass --worktree to choose a workspace. No terminal was created.' + // A create with no workspace lands in whatever the window is showing, so only the window can make it. export async function createDesktopTerminal( runtime: OrcaRuntimeWithCreateTerminal, @@ -17,7 +21,7 @@ export async function createDesktopTerminal( } opts = applyRemoteViewerLaunchEnv(opts, opts.terminalImageViewerSupported) runtime.assertGraphReady() - const win = runtime.getAuthoritativeWindow() + const win = runtime.getAuthoritativeWindow(NO_WORKSPACE_OR_WINDOW_MESSAGE) const requestId = dependencies.randomUUID() const reply = await new Promise<{ tabId: string; title: string }>((resolve, reject) => { const timer = setTimeout(() => { diff --git a/src/main/runtime/orca-runtime-resolve-waiter.ts b/src/main/runtime/orca-runtime-resolve-waiter.ts index c90d5b081e4..ef2693bb8c4 100644 --- a/src/main/runtime/orca-runtime-resolve-waiter.ts +++ b/src/main/runtime/orca-runtime-resolve-waiter.ts @@ -25,10 +25,10 @@ export abstract class OrcaRuntimeWithResolveWaiter extends OrcaRuntimeWithResolv return `${tabId}::${leafId}` } - getAuthoritativeWindow(): BrowserWindow { + getAuthoritativeWindow(missingWindowMessage = 'No renderer window available'): BrowserWindow { const win = this.getAvailableAuthoritativeWindow() if (!win || win.isDestroyed()) { - throw new Error('No renderer window available') + throw new Error(missingWindowMessage) } return win } diff --git a/src/main/runtime/terminal/driver/terminal-driver-resources.ts b/src/main/runtime/terminal/driver/terminal-driver-resources.ts index 3aa1eb0cf72..25edbeee6ed 100644 --- a/src/main/runtime/terminal/driver/terminal-driver-resources.ts +++ b/src/main/runtime/terminal/driver/terminal-driver-resources.ts @@ -51,7 +51,7 @@ export abstract class OrcaRuntimeWithTerminalDrivers extends OrcaRuntimeWithFitO repoId?: string hostId?: ExecutionHostId }): BrowserNetworkExecutionHost | Promise - abstract getAuthoritativeWindow(): BrowserWindow + abstract getAuthoritativeWindow(missingWindowMessage?: string): BrowserWindow protected abstract getAvailableAuthoritativeWindow(): BrowserWindow | null protected abstract markHeadlessBrowserSessionTabActive( worktreeId: string | undefined,