mirror of
https://github.com/stablyai/orca.git
synced 2026-11-04 02:57:36 +00:00
* [comment audit] Align legacy failure-read counts with cached display * [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci] * [comment audit] Condense shared generation, path and preview comments [skip ci] * [comment audit] Condense host, file and Git contract rationale [skip ci] * [comment audit] Tighten Git lifecycle and provider contract comments [skip ci] * [comment audit] Clarify shared host authority and launch isolation [skip ci] * [comment audit] simplify local access mobile and native chat contracts [skip ci] * [comment audit] simplify native chat presentation and state contracts [skip ci] * [comment audit] simplify native runtime and provider contracts [skip ci] * [comment audit] simplify migration and orchestration contracts [skip ci] * [comment audit] simplify plugin trust and process capture rationale [skip ci] * [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci] * [comment audit] clarify remote recovery and launch wire rationale [skip ci] * [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci] * [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci] * [comment audit] clarify sidebar, skill and SSH invariants [skip ci] * [comment audit] condense structured chat delivery and timing rationale [skip ci] * [comment audit] condense terminal replay and telemetry invariants [skip ci] * [comment audit] clarify launch permission and terminal delivery rationale [skip ci] * [comment audit] clarify workspace ownership and layout contracts [skip ci] * [comment audit] wait for fitted and painted SSH terminal links * [comment audit] clarify layout persistence and session ownership [skip ci] * [comment audit] clarify host routing and workspace lifecycle contracts [skip ci] * [comment audit] clarify startup persistence and app lifetime comments [skip ci] * [comment audit] clarify recovery and activity boundaries [skip ci] * [comment audit] condense activity and artifact invariants [skip ci] * [comment audit] condense automation authority contracts [skip ci] * [comment audit] simplify automation scheduling and history contracts [skip ci] * [comment audit] clarify browser chrome and annotation lifecycles [skip ci] * [comment audit] clarify browser routing and retained guest ownership [skip ci] * [comment audit] clarify browser recovery and document ownership comments [skip ci] * [comment audit] restore hosted runners required by native IME fixtures * [comment audit] shorten preview, palette and dashboard invariants [skip ci] * [comment audit] clarify terminal preview, dashboard and retention comments [skip ci] * [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci] * [comment audit] shorten rich-text and combined-diff ownership comments [skip ci] * [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci] * [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci] * [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci] * [comment audit] clarify rich Markdown source and selection safeguards [skip ci] * [comment audit] clarify editor loading and emulator stream lifetimes [skip ci] * [comment audit] trim emulator and feature tour narration [skip ci] * [comment audit] clarify floating workspace and GitHub cache ownership [skip ci] * [comment audit] trim provider controls and Linear filter explanations [skip ci] * [comment audit] clarify pairing and chat resume lifetimes [skip ci] * [comment audit] clarify chat delivery and transcript lifetimes [skip ci] * [comment audit] clarify session options and host-owned status [skip ci] * [comment audit] clarify draft durability and live-status precedence [skip ci] * [comment audit] Trim chat send and model ownership explanations [skip ci] * [comment audit] Shorten transcript ownership and send recovery comments [skip ci] * [comment audit] Simplify native chat composer and transcript hook comments [skip ci] * [comment audit] Shorten native chat scroll and structured session explanations [skip ci] * [comment audit] Trim workspace picker and onboarding commentary [skip ci] * [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci] * [comment audit] Trim sidebar and session history explanations [skip ci] * [comment audit] Condense session resume and review checks comments [skip ci] * [comment audit] Shorten checks state and file explorer refresh comments [skip ci] * [comment audit] Trim plugin bridge and source-control action rationale [skip ci] * [comment audit] Trim source-control listing and review rationale [skip ci] * [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci] * [comment audit] Condense Settings account and appearance rationale [skip ci] * [comment audit] Trim pairing and repository Settings explanations [skip ci] * [comment audit] Condense Settings search SSH and terminal safeguards [skip ci] * [comment audit] Shorten Settings voice and provider ownership comments [skip ci] * [comment audit] Condense Settings draft persistence and host-state rationale [skip ci] * [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci] * [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci] * [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci] * [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci] * [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci] * [comment audit] Prune focus-read baseline after comment cleanup * [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci] * [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci] * [comment audit] Clarify viewport geometry and skill update safeguards [skip ci] * [comment audit] Tighten skill inventory, usage and host status comments [skip ci] * [comment audit] Clarify usage ownership and resource kill safeguards [skip ci] * [comment audit] Tighten tab selection, rename and search contracts [skip ci] * [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci] * [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci] * [comment audit] Tighten provider loading and terminal recovery comments [skip ci] * [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci] * [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci] * [comment audit] Tighten PTY startup and replay explanations [skip ci] * [comment audit] Clarify PTY identity and delivery ownership [skip ci] * [comment audit] Shorten remote reconnect and input replay rationale [skip ci] * [comment audit] Condense terminal parking clipboard and file link comments [skip ci] * [comment audit] Clarify IME ownership and trim trace history [skip ci] * [comment audit] Shorten pane recovery and parked watcher explanations [skip ci] * [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci] * [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci] * [comment audit] Trim renderer lifecycle and menu rationale [skip ci] * [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci] * [comment audit] Clarify renderer hook evidence and source ownership [skip ci] * [comment audit] Tighten IPC ownership and reconnect explanations [skip ci] * [comment audit] Trim renderer focus, paste and attention comments [skip ci] * [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci] * [comment audit] Trim agent launch, resume and ownership commentary [skip ci] * [comment audit] Shorten account, browser and editor comments [skip ci] * [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci] * [comment audit] Condense launch recovery and editor tokenization comments [skip ci] * [comment audit] Condense workspace selection and terminal pane rationale [skip ci] * [comment audit] Condense terminal fitting and rendering recovery comments [skip ci] * [comment audit] Condense terminal input and host routing explanations [skip ci] * [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci] * [comment audit] Condense structured launch and terminal link explanations [skip ci] * [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci] * [comment audit] Condense session persistence and workspace activation rationale [skip ci] * [comment audit] Condense workspace routing and browser ownership rationale [skip ci] * [comment audit] Condense mirror recovery and layout authority rationale [skip ci] * [comment audit] Condense runtime transport and file safety rationale [skip ci] * [comment audit] Condense runtime graph and structured session rationale [skip ci] * [comment audit] Condense browser creation and remote tab recovery rationale [skip ci] * [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci] * [comment audit] Condense frame, startup and provider cache rationale [skip ci] * [comment audit] Condense host twin and agent lifecycle rationale [skip ci] * [comment audit] Condense browser authority and editor cleanup rationale [skip ci] * [comment audit] Condense editor buffer identity and review cache rationale [skip ci] * [comment audit] Condense provider lifetime and repository ownership rationale [skip ci] * [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci] * [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci] * [comment audit] Condense cleanup ownership and listing admission rationale [skip ci] * [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci] * [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci] * [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci] * [comment audit] Condense cloud rollout and assignment safety rationale [skip ci] * [comment audit] Condense relay session and database recovery rationale [skip ci] * [comment audit] Condense relay placement and protocol compatibility rationale [skip ci] * [comment audit] Condense build and packaging constraints [skip ci] * [comment audit] Condense CI gates and validation isolation rationale [skip ci] * [comment audit] Condense catalog identity and browser validation rationale [skip ci] * [comment audit] Condense mobile browser and frame-budget rationale [skip ci] * [comment audit] Shorten mobile route and browser test explanations [skip ci] * [comment audit] Condense editor and session seam test rationale [skip ci] * [comment audit] Trim session and terminal closure history [skip ci] * [comment audit] Shorten terminal browser and census explanations [skip ci] * [comment audit] Preserve root-only E2E diagnostic baseline * [comment audit] Condense mobile seam and HTML preview test rationale [skip ci] * [comment audit] Condense preview instrumentation and render harness rationale [skip ci] * [comment audit] Condense bundle contracts and native-build constraints [skip ci] * [comment audit] Shorten native packaging and CI routing rationale [skip ci] * [comment audit] Condense release tooling and benchmark constraints [skip ci] * [comment audit] Condense packaging and platform gate rationale [skip ci] * [comment audit] Shorten mobile route and history rationale [skip ci] * [comment audit] Condense mobile browser and component rationale [skip ci] * [comment audit] Shorten drawer and workspace-form rationale [skip ci] * [comment audit] Trim mobile review and editor-document explanations [skip ci] * [comment audit] Condense editor lifecycle and Markdown explanations [skip ci] * [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci] * [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci] * [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci] * [comment audit] Condense bridge stream, native request and audio rationale [skip ci] * [comment audit] Reduce bridge schema and page-client commentary [skip ci] * [comment audit] Tighten mobile page bridge contracts [skip ci] * [comment audit] Clarify mobile cache and session constraints [skip ci] * [comment audit] Condense mobile route and lifecycle rationale [skip ci] * [comment audit] Tighten mobile shell hook timing rationale [skip ci] * [comment audit] Clarify route handoff and notification compatibility [skip ci] * [comment audit] Clarify push registration and dictation cleanup constraints [skip ci] * [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci] * [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci] * [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci] * [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci] * [comment audit] Tighten stream replay and queued session ownership rationale [skip ci] * [comment audit] Condense mobile composer and reply-schema constraints [skip ci] * [comment audit] Clarify mobile transcript recovery and session ownership [skip ci] * [comment audit] Clarify mobile queue and source-control compatibility [skip ci] * [comment audit] Condense storage and task launch constraints [skip ci] * [comment audit] Clarify task schemas and terminal document ownership [skip ci] * [comment audit] Tighten terminal input and viewport rationale [skip ci] * [comment audit] Shorten terminal and RPC recorder contracts [skip ci] * [comment audit] Condense recorder engine and connection contracts [skip ci] * [comment audit] Shorten host credential and relay recovery comments [skip ci] * [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci] * [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci] * [comment audit] Condense catalog and relay entry contracts [skip ci] * [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci] * [comment audit] Condense WSL relay and E2E regression rationale [skip ci] * [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci] * [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci] * [comment audit] Shorten layout oracle and regression explanations [skip ci] * [comment audit] Condense paired recovery and ownership regression contracts [skip ci] * [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci] * [comment audit] Condense terminal rendering and IME regression rationale [skip ci] * [comment audit] Shorten terminal recovery and benchmark contracts [skip ci] * [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci] * [comment audit] Condense workflow routing and release rationale [skip ci] * [comment audit] Condense mobile security and installer contracts [skip ci] * [comment audit] Shorten native rationale and stylesheet workarounds [skip ci] * [comment audit] Shorten Windows visibility probe rationale [skip ci] * [comment audit] Condense embedded release and signing rationale [skip ci] * [comment audit] Retain current main test behavior in final reconciliation [skip ci] * [comment audit] Complete final comment review across current source inventory * [comment audit] Handle consolidated audit reports before final CI * [comment audit] Preserve credential storage census detail [skip ci] * [comment audit] Scale broad changed E2E selections with verified timing shards * [comment audit] Align E2E assertions with browser close and settled session metadata * [comment audit] Match existing E2Es to current resource and windowing contracts * [comment audit] Observe the shared server outage notice in reconnect coverage * [comment audit] Make existing renderer probes measure settled tab and PTY work * [comment audit] Align palette E2E selector with task search * [comment audit] Refresh remaining palette search selectors * [comment audit] Keep selector correction within selected audit specs * [comment audit] Await emoji fixture settings inside renderer evaluation * [comment audit] Cancel fixture fit retries between terminal tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
274 lines
13 KiB
TypeScript
274 lines
13 KiB
TypeScript
// Exercise managed SSH Quick Open at monorepo scale, ports/forwards, outside-workspace image links and remote agent status.
|
|
import { expect, test } from './helpers/orca-app'
|
|
import { reconnect } from './helpers/orcad-convert-flow'
|
|
import { callEnvironment, createPairedHostTerminal } from './helpers/paired-host-terminal'
|
|
import { reserveLocalPort } from './helpers/reserve-local-port'
|
|
import { openTerminalWorkspaceRootLink } from './helpers/terminal-workspace-root-link'
|
|
import {
|
|
echoMarker,
|
|
expectTerminalAnswers,
|
|
activateManagedWorkspace,
|
|
launchManagedWorkspace,
|
|
openTerminalTab,
|
|
relaunchManagedApp,
|
|
skipUnlessManagedDockerHost,
|
|
typeInTerminal,
|
|
withManagedHost
|
|
} from './helpers/orcad-managed-workspace'
|
|
import { quotePosixShell } from '../../src/shared/shell-argument-quoting'
|
|
import { createServer } from 'node:net'
|
|
import { waitForSessionReady } from './helpers/store'
|
|
|
|
skipUnlessManagedDockerHost()
|
|
test.skip(process.platform === 'win32', 'The Docker SSH host uses POSIX tooling')
|
|
|
|
test('Quick Open finds a file in a monorepo-sized managed workspace', async (// oxlint-disable-next-line no-empty-pattern -- Owns its app launch through a restart session.
|
|
{}, testInfo) => {
|
|
test.setTimeout(10 * 60_000)
|
|
await withManagedHost(testInfo, async (fixture) => {
|
|
const repo = quotePosixShell(fixture.host.remoteRepoPath)
|
|
// ~22.6k tracked paths: past the size whose single reply once overflowed the transport (#12547).
|
|
fixture.host.exec!(
|
|
[
|
|
`cd ${repo}`,
|
|
'for d in $(seq 1 226); do mkdir -p packages/pkg-$d/src && (cd packages/pkg-$d/src && ' +
|
|
'touch $(seq -f "module-with-a-long-descriptive-name-%05g.ts" 1 100)); done',
|
|
'touch packages/pkg-200/src/quick-open-needle-target.ts',
|
|
'git add -A',
|
|
'git -c user.email=e2e@test.local -c user.name=e2e commit -q -m "monorepo-sized tree"'
|
|
].join(' && ')
|
|
)
|
|
const { page } = await launchManagedWorkspace(fixture)
|
|
await page.evaluate(() => window.__store?.getState().openModal('quick-open'))
|
|
const dialog = page.getByRole('dialog', { name: 'Go to file' })
|
|
await expect(dialog).toBeVisible()
|
|
const input = dialog.getByPlaceholder('Go to file...')
|
|
await input.fill('quick-open-needle-target')
|
|
await expect(
|
|
dialog.getByRole('option').filter({ hasText: 'quick-open-needle-target.ts' })
|
|
).toHaveCount(1, { timeout: 90_000 })
|
|
await input.fill('module-with-a-long-descriptive-name-00042')
|
|
await expect(dialog.getByRole('option').first()).toBeVisible({ timeout: 60_000 })
|
|
await expect(dialog.getByText('Loading files...')).toHaveCount(0)
|
|
await expect(dialog).not.toContainText('Remote Orca runtime closed the connection')
|
|
await page.screenshot({ path: testInfo.outputPath('managed-quick-open.png') })
|
|
})
|
|
})
|
|
|
|
for (const across of ['', 'reconnect', 'restart without an SSH connect'] as const) {
|
|
test(`a managed host reports its listener, and an SSH forward reaches it${across ? ` across a ${across}` : ''}`, async (// oxlint-disable-next-line no-empty-pattern -- Owns its app launch through a restart session.
|
|
{}, testInfo) => {
|
|
test.setTimeout(10 * 60_000)
|
|
const remotePort = 7860
|
|
const marker = `ORCA_FORWARD_${Date.now()}`
|
|
const reservation = await reserveLocalPort()
|
|
await withManagedHost(testInfo, async (fixture) => {
|
|
const launched = await launchManagedWorkspace(fixture)
|
|
const { seeded, environmentId } = launched
|
|
let page = launched.page
|
|
const script = `require('node:http').createServer((_q, r) => r.end(${JSON.stringify(marker)})).listen(${remotePort}, '127.0.0.1')`
|
|
// Started from the workspace so the scan attributes it to this workspace.
|
|
fixture.host.exec!(
|
|
`cd ${quotePosixShell(fixture.host.remoteRepoPath)} && nohup node -e ${quotePosixShell(script)} >/tmp/orca-http.log 2>&1 < /dev/null &`
|
|
)
|
|
// The scan the Ports panel shows for a workspace on this host.
|
|
await expect
|
|
.poll(
|
|
async () =>
|
|
JSON.stringify(await callEnvironment(page, environmentId, 'workspacePorts.scan', {})),
|
|
{ timeout: 60_000, message: 'the managed host never reported the listener' }
|
|
)
|
|
.toContain(`"port":${remotePort}`)
|
|
|
|
const localPort = reservation.port
|
|
await reservation.release()
|
|
const forward = await page.evaluate(
|
|
(args) => window.api.ssh.addPortForward({ ...args, remoteHost: '127.0.0.1' }),
|
|
{ targetId: seeded.targetId, localPort, remotePort, label: 'managed e2e' }
|
|
)
|
|
const fetchMarker = async (): Promise<string> => {
|
|
try {
|
|
return await (await fetch(`http://127.0.0.1:${localPort}/`)).text()
|
|
} catch (error) {
|
|
return String(error)
|
|
}
|
|
}
|
|
await expect.poll(fetchMarker, { timeout: 30_000 }).toBe(marker)
|
|
let forwardId = forward.id
|
|
if (across) {
|
|
if (across === 'reconnect') {
|
|
await reconnect(page, seeded.targetId)
|
|
} else {
|
|
// #32484: every SSH connect fails, so only the server's tunnel brings the host back.
|
|
page = await relaunchManagedApp(fixture, undefined, {
|
|
extraEnv: {
|
|
ORCA_E2E_FORBID_LOCAL_SSH_CONNECT_PROBE: testInfo.outputPath('ssh-connects.log')
|
|
}
|
|
})
|
|
await callEnvironment(page, environmentId, 'workspacePorts.scan', {})
|
|
}
|
|
await expect.poll(fetchMarker, { timeout: 60_000 }).toBe(marker)
|
|
// The restored forward is a new entry for the same saved port.
|
|
const restored = await page.evaluate(
|
|
(targetId) => window.api.ssh.listPortForwards({ targetId }),
|
|
seeded.targetId
|
|
)
|
|
forwardId = restored.find((entry) => entry.localPort === localPort)?.id ?? forwardId
|
|
}
|
|
await page.evaluate((id) => window.api.ssh.removePortForward({ id }), forwardId)
|
|
await expect.poll(fetchMarker, { timeout: 30_000 }).not.toBe(marker)
|
|
}).finally(() => reservation.release())
|
|
})
|
|
}
|
|
|
|
test('a saved forward whose local port is taken after a restart shows why, and can be removed', async (// oxlint-disable-next-line no-empty-pattern -- Owns its app launch through a restart session.
|
|
{}, testInfo) => {
|
|
// Deploys a managed server, then quits and relaunches the app once.
|
|
test.setTimeout(10 * 60_000)
|
|
const reservation = await reserveLocalPort()
|
|
const localPort = reservation.port
|
|
await reservation.release()
|
|
await withManagedHost(testInfo, async (fixture) => {
|
|
const launched = await launchManagedWorkspace(fixture)
|
|
const { seeded, environmentId } = launched
|
|
await launched.page.evaluate(
|
|
(args) => window.api.ssh.addPortForward({ ...args, remoteHost: '127.0.0.1' }),
|
|
{ targetId: seeded.targetId, localPort, remotePort: 7860, label: 'held' }
|
|
)
|
|
await fixture.session.close(launched.app)
|
|
fixture.app = null
|
|
// Another process takes the port while Orca is closed, so the restore can't bind it.
|
|
const holder = createServer()
|
|
await new Promise<void>((resolve) => holder.listen(localPort, '127.0.0.1', resolve))
|
|
try {
|
|
const { app, page } = await fixture.session.launch()
|
|
fixture.app = app
|
|
await waitForSessionReady(page)
|
|
await callEnvironment(page, environmentId, 'workspacePorts.scan', {})
|
|
await expect
|
|
.poll(
|
|
async () =>
|
|
JSON.stringify(
|
|
await page.evaluate(
|
|
(targetId) => window.api.ssh.listUnrestoredPortForwards({ targetId }),
|
|
seeded.targetId
|
|
)
|
|
),
|
|
{ timeout: 60_000, message: 'the held forward never reported why it did not start' }
|
|
)
|
|
.toContain('"error"')
|
|
await activateManagedWorkspace(page, seeded.worktreeId, environmentId)
|
|
await page.evaluate(() => {
|
|
const state = window.__store?.getState()
|
|
state?.setRightSidebarOpen(true)
|
|
state?.setRightSidebarTab('ports')
|
|
})
|
|
const row = page.locator('[data-ssh-unrestored-forward]')
|
|
await expect(row).toBeVisible({ timeout: 30_000 })
|
|
await expect(row).toContainText("Couldn't start")
|
|
await page.screenshot({ path: testInfo.outputPath('unrestored-forward-row.png') })
|
|
await row.getByRole('button').click()
|
|
await expect(row).toHaveCount(0)
|
|
await expect
|
|
.poll(() =>
|
|
page.evaluate(
|
|
(targetId) => window.api.ssh.listUnrestoredPortForwards({ targetId }),
|
|
seeded.targetId
|
|
)
|
|
)
|
|
.toEqual([])
|
|
} finally {
|
|
await new Promise<void>((resolve) => holder.close(() => resolve()))
|
|
}
|
|
})
|
|
})
|
|
|
|
test('a terminal link opens an image outside the managed workspace', async (// oxlint-disable-next-line no-empty-pattern -- Owns its app launch through a restart session.
|
|
{}, testInfo) => {
|
|
test.setTimeout(10 * 60_000)
|
|
const imagePath = '/tmp/orca-ssh-external-preview.png'
|
|
const imageBase64 =
|
|
'iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVR4AWN8z8DwnwEJMDGgAcICAO2mBAXmO4drAAAAAElFTkSuQmCC'
|
|
await withManagedHost(testInfo, async (fixture) => {
|
|
fixture.host.exec!(
|
|
`printf '%s' ${quotePosixShell(imageBase64)} | base64 -d > ${quotePosixShell(imagePath)}`
|
|
)
|
|
// Why /root: the default folder workspace is /tmp, which would make the image a workspace file.
|
|
const { page, seeded } = await launchManagedWorkspace(fixture, { folderPath: '/root' })
|
|
// A link is only clickable once the shell has printed it, so wait for one that answers.
|
|
await expectTerminalAnswers(page, await openTerminalTab(page, seeded.worktreeId))
|
|
await openTerminalWorkspaceRootLink(page, testInfo, imagePath, 'open')
|
|
const preview = page.locator(`img[alt="${imagePath.split('/').at(-1)}"]`)
|
|
await expect(preview).toBeVisible({ timeout: 30_000 })
|
|
expect(await preview.evaluate((element: HTMLImageElement) => element.naturalWidth)).toBe(2)
|
|
await expect(page.getByText('Unable to load file', { exact: true })).toHaveCount(0)
|
|
await page.screenshot({ path: testInfo.outputPath('managed-external-image.png') })
|
|
})
|
|
})
|
|
|
|
test('a managed terminal reports agent status from hooks and from Pi-style titles', async (// oxlint-disable-next-line no-empty-pattern -- Owns its app launch through a restart session.
|
|
{}, testInfo) => {
|
|
test.setTimeout(10 * 60_000)
|
|
await withManagedHost(testInfo, async (fixture) => {
|
|
const { page, seeded, environmentId } = await launchManagedWorkspace(fixture)
|
|
const tabId = await openTerminalTab(page, seeded.worktreeId)
|
|
await echoMarker(page, tabId, 'HOOK_READY')
|
|
// Backgrounded: a foreground post ends the shell command, which rightly clears the turn.
|
|
const prompt = `orca managed hook ${Date.now()}`
|
|
const payload = JSON.stringify({ hook_event_name: 'UserPromptSubmit', prompt })
|
|
await typeInTerminal(
|
|
page,
|
|
tabId,
|
|
[
|
|
'( sleep 0.2; curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/codex"',
|
|
'-H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}"',
|
|
'--data-urlencode "paneKey=${ORCA_PANE_KEY}" --data-urlencode "tabId=${ORCA_TAB_ID}"',
|
|
'--data-urlencode "worktreeId=${ORCA_WORKTREE_ID}" --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}"',
|
|
'--data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}"',
|
|
`--data-urlencode ${quotePosixShell(`payload=${payload}`)} >/tmp/orca-hook.log 2>&1 ) &`
|
|
].join(' ')
|
|
)
|
|
await expect
|
|
.poll(
|
|
() =>
|
|
page.evaluate(
|
|
(prompt) =>
|
|
Object.values(window.__store?.getState().agentStatusByPaneKey ?? {}).some(
|
|
(entry) =>
|
|
entry.prompt === prompt &&
|
|
entry.agentType === 'codex' &&
|
|
entry.state === 'working'
|
|
),
|
|
prompt
|
|
),
|
|
{ timeout: 30_000, message: 'the host hook never reached the agent status store' }
|
|
)
|
|
.toBe(true)
|
|
|
|
// The host classifies agent titles a remote terminal emits.
|
|
const terminal = await createPairedHostTerminal(page, environmentId, seeded.worktreeId, 'bash')
|
|
const status = async (): Promise<unknown> => {
|
|
const result = await callEnvironment(page, environmentId, 'terminal.agentStatus', {
|
|
terminal: terminal.terminal
|
|
})
|
|
return typeof result === 'object' && result !== null && 'agentStatus' in result
|
|
? result.agentStatus
|
|
: result
|
|
}
|
|
for (const [title, expected] of [
|
|
['⠋ OMP', 'working'],
|
|
['OMP ready', 'idle'],
|
|
['⠋ Pi', 'working']
|
|
] as const) {
|
|
await callEnvironment(page, environmentId, 'terminal.send', {
|
|
terminal: terminal.terminal,
|
|
text: `printf '\\033]0;${title}\\007'`,
|
|
enter: true
|
|
})
|
|
await expect
|
|
.poll(status, { timeout: 15_000, message: `title ${title}` })
|
|
.toMatchObject({ isRunningAgent: true, status: expected })
|
|
}
|
|
})
|
|
})
|