diff --git a/src/cli/runtime/launch.ts b/src/cli/runtime/launch.ts index 94a2aa7a06b7..45797d858cca 100644 --- a/src/cli/runtime/launch.ts +++ b/src/cli/runtime/launch.ts @@ -26,12 +26,8 @@ import { import { RuntimeClientError } from './types' import { SERVE_RUNTIME_ELECTRON, SERVE_RUNTIME_ENV } from '../../shared/orcad-local-serve-selection' import { findBundledWebClientRoot } from '../../shared/bundled-web-client-root' -import { - resolveLocalServeRuntime, - serveOptionArgs, - serveWithOrcad, - type ServeOrcaAppArgs -} from './serve-orcad-launch' +import { resolveLocalServeRuntime, serveWithOrcad } from './serve-orcad-launch' +import { serveOptionArgs, type ServeOrcaAppArgs } from '../../shared/orcad-serve-argv' import { waitForRecipeJson } from './serve-recipe-json' import { preflightElectronServe, diff --git a/src/cli/runtime/serve-orcad-launch.test.ts b/src/cli/runtime/serve-orcad-launch.test.ts index 534105fe9274..1bc1bf4b654e 100644 --- a/src/cli/runtime/serve-orcad-launch.test.ts +++ b/src/cli/runtime/serve-orcad-launch.test.ts @@ -1,7 +1,8 @@ import { describe, expect, it, vi } from 'vitest' import { formatServeRuntimeSelection } from '../../shared/orcad-local-serve-selection' import type { runProcess } from '@orca/process-host' -import { orcadServeArgs, resolveLocalServeRuntime, serveOptionArgs } from './serve-orcad-launch' +import { resolveLocalServeRuntime } from './serve-orcad-launch' +import { orcadServeArgs, serveOptionArgs } from '../../shared/orcad-serve-argv' type RunProcess = typeof runProcess diff --git a/src/cli/runtime/serve-orcad-launch.ts b/src/cli/runtime/serve-orcad-launch.ts index 130142c2516a..302c83d2e820 100644 --- a/src/cli/runtime/serve-orcad-launch.ts +++ b/src/cli/runtime/serve-orcad-launch.ts @@ -15,21 +15,7 @@ import { waitForRecipeJson } from './serve-recipe-json' import { superviseForegroundServe } from './serve-update-supervisor' import { isSupervisedSpawnHeld } from './serve-supervised-stop' import { redactingSpawn, shouldRedactSupervisedStdout } from './serve-pairing-redaction' - -export type ServeOrcaAppArgs = { - json?: boolean - port?: string | null - pairingAddress?: string | null - noPairing?: boolean - mobilePairing?: boolean - grantDesktopControl?: boolean - recipeJson?: boolean - projectRoot?: string | null - /** Starts Electron serve despite a remembered macOS startup abort in this environment. */ - force?: boolean - /** Restart the server under a service manager instead of exiting with it; never inferred. */ - supervise?: boolean -} +import { orcadServeArgs, type ServeOrcaAppArgs } from '../../shared/orcad-serve-argv' /** A first run may download and verify the pinned Node; bound it well past that. */ const SELECTION_TIMEOUT_MS = 10 * 60_000 @@ -119,26 +105,3 @@ export function serveWithOrcad( expectedHandoff: null }) } - -export function serveOptionArgs(args: ServeOrcaAppArgs, prefix: '--' | '--serve-'): string[] { - return [ - ...(args.json ? [`${prefix}json`] : []), - ...(args.port ? [`${prefix}port`, args.port] : []), - ...(args.pairingAddress ? [`${prefix}pairing-address`, args.pairingAddress] : []), - ...(args.noPairing ? [`${prefix}no-pairing`] : []), - ...(args.mobilePairing ? [`${prefix}mobile-pairing`] : []), - ...(args.grantDesktopControl ? [`${prefix}grant-desktop-control`] : []), - ...(args.recipeJson && args.projectRoot - ? [`${prefix}recipe-json`, `${prefix}project-root`, args.projectRoot] - : []) - ] -} - -export function orcadServeArgs(args: ServeOrcaAppArgs, webClientRoot?: string): string[] { - return [ - '--bind', - '0.0.0.0', - ...serveOptionArgs(args, '--'), - ...(webClientRoot ? ['--web-client-root', webClientRoot] : []) - ] -} diff --git a/src/main/orcad/orcad-serve-parity.integration.test.ts b/src/main/orcad/orcad-serve-parity.integration.test.ts index 942c71f2c19d..34285f83c35c 100644 --- a/src/main/orcad/orcad-serve-parity.integration.test.ts +++ b/src/main/orcad/orcad-serve-parity.integration.test.ts @@ -1,14 +1,19 @@ /** * `orca serve` on orcad, against the packaged slot: the native preflight the launcher asks - * first, desktop-serve flag parity on stdout (the readiness contract), and the shared-profile - * refusal while the desktop app holds the profile. + * first, desktop-serve flag parity on stdout (the readiness contract), the browser client the + * CLI hands orcad, and the shared-profile refusal while the desktop app holds the profile. */ -import { existsSync, mkdtempSync, realpathSync, writeFileSync } from 'node:fs' +import { existsSync, mkdirSync, mkdtempSync, realpathSync, writeFileSync } from 'node:fs' import { tmpdir, userInfo } from 'node:os' import { join, resolve } from 'node:path' import { afterEach, describe, expect, it } from 'vitest' import { runProcess, spawnProcess } from '@orca/process-host' import { ORCAD_NODE_RUNTIME_MARKER_FILENAME } from '../../shared/orcad-artifacts' +import { + findBundledWebClientRoot, + WEB_CLIENT_INDEX_FILENAME +} from '../../shared/bundled-web-client-root' +import { orcadServeArgs } from '../../shared/orcad-serve-argv' import { ORCAD_NATIVE_PREFLIGHT_FLAG, parseOrcadNativePreflightReport @@ -189,6 +194,32 @@ describe.skipIf(skip)('orca serve on orcad', () => { expect(result.stderr).toContain('The Orca desktop app') }, 90_000) + // #30120: the CLI never handed orcad the app's web client, so no URL was printed and GETs hung. + it('serves the web client the CLI hands it, and answers other paths with 404', async () => { + const userData = realpathSync( + mkdtempSync(join(process.platform === 'win32' ? tmpdir() : '/tmp', 'orca-serve-web-')) + ) + roots.push(userData) + const appRoot = profile() + mkdirSync(join(appRoot, 'out', 'web'), { recursive: true }) + writeFileSync(join(appRoot, 'out', 'web', WEB_CLIENT_INDEX_FILENAME), '