Files
orca/src/cli/specs/emulator.ts
T
Jinjingand5Hyeons 4a9affd6e5 fix(emulator): iOS ax via plain-JSON serve-sim helper (supersedes #10007) (#10029)
* Revert "Enable accessibility tree (`ax`) command on iOS emulator sessions (#10007)"

This reverts commit 43ae014a64.

* fix(emulator): expose iOS accessibility tree

* fix(emulator): support device-only iOS AX

* fix(emulator): normalize iOS ax to 0..1 and heal missing axUrl

serve-sim's helper /ax reports element frames in absolute pixels, but
tap/gesture take normalized 0..1 coords. Normalize the raw AX node tree
into a compact nested shape whose frames are 0..1 over the device screen
(first root's frame), mirroring serve-sim's own normalizeAxTree, so agents
can feed ax output straight back into input commands.

Also heal sessions that were registered without an axUrl: #9924 only
derived /ax at parse time, so already-active sessions had no endpoint.
The bridge now derives it from the session's mjpeg stream URL, guarded to
the /stream.mjpeg suffix so a non-mjpeg URL never fabricates a bogus /ax.

* docs(emulator): mark ax working on iOS with correct raw-AX-tree shape

Both skill guides and the CLI summary described iOS ax as unsupported (or,
via the reverted #10007, as a normalized "screen + elements" shape that
never matched the endpoint). ax works on both backends: Android via
uiautomator, iOS via the serve-sim helper. Document the real iOS output —
a raw AX node tree (labels, roles, nested children) with frames normalized
to 0..1 — and regenerate the bundled skill guides.

* chore(skills): regenerate skill bundle manifests

CI verify failed because generated skill artifacts were stale after version/skill revision bumps.

* fix(emulator): read ax from explicit device without active session

Fall back to udid-keyed session lookup when a worktree has no active emulator,
allowing `--device` targeting to work the same way for ax as it does for tap/type.
Also clarify in docs that AX frames are normalized 0..1 with top-left origin,
and show how to tap an element at its frame center (x+width/2, y+height/2).

* fix(emulator): cap iOS AX tree at 500 nodes

Unbounded accessibility trees can flood agent output. Enforce a 500-node limit (matching serve-sim's snapshot cap) and mark truncated parents so consumers know the tree was cut.

---------

Co-authored-by: 5Hyeons <ohs2251@naver.com>
2026-07-22 21:30:53 -07:00

122 lines
4.9 KiB
TypeScript

import type { CommandSpec } from '../args'
import { GLOBAL_FLAGS } from '../args'
export const EMULATOR_COMMAND_SPECS: CommandSpec[] = [
{
path: ['emulator', 'list'],
summary: 'List available/running emulators (Orca-managed + raw serve-sim)',
usage: 'orca emulator list [--worktree <selector>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'worktree']
},
{
path: ['emulator', 'devices'],
summary: 'List all emulator devices/AVDs across iOS and Android',
usage: 'orca emulator devices [--worktree <selector>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'worktree']
},
{
path: ['emulator', 'attach'],
summary: 'Attach/start helper for a device and make it active for the worktree',
usage: 'orca emulator attach [device] [--worktree <selector>] [--focus] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'worktree', 'focus', 'device'],
positionalArgs: ['device']
},
{
path: ['emulator', 'tap'],
summary: 'Tap at normalized 0..1 coords (preferred for single taps)',
usage: 'orca emulator tap <x> <y> [--device <id>] [--worktree <selector>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'device', 'emulator', 'worktree', 'x', 'y'],
positionalArgs: ['x', 'y']
},
{
path: ['emulator', 'type'],
summary: 'Type text (US ASCII only)',
usage: 'orca emulator type <text> [--device <id>] [--worktree <selector>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'text', 'device', 'emulator', 'worktree'],
positionalArgs: ['text']
},
{
path: ['emulator', 'gesture'],
summary: 'Send a multi-point gesture sequence',
usage: "orca emulator gesture '<json>' [--device <id>] [--worktree <selector>] [--json]",
allowedFlags: [...GLOBAL_FLAGS, 'points', 'device', 'emulator', 'worktree'],
positionalArgs: ['points']
},
{
path: ['emulator', 'button'],
summary: 'Hardware button (home, side_button, etc.)',
usage: 'orca emulator button <name> [--device <id>] [--worktree <selector>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'device', 'emulator', 'worktree', 'name'],
positionalArgs: ['name']
},
{
path: ['emulator', 'rotate'],
summary: 'Rotate device',
usage: 'orca emulator rotate <orientation> [--device <id>] [--worktree <selector>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'device', 'emulator', 'worktree', 'orientation'],
positionalArgs: ['orientation']
},
{
path: ['emulator', 'exec'],
summary:
'Raw passthrough (e.g. orca emulator exec --command "tap 0.5 0.7" or "ca-debug blended on")',
usage: 'orca emulator exec --command <cmd> [--device <id>] [--worktree <selector>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'command', 'device', 'emulator', 'worktree']
},
{
path: ['emulator', 'kill'],
summary: 'Stop helper for device',
usage: 'orca emulator kill [--device <id>] [--worktree <selector>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'device', 'emulator', 'worktree']
},
{
path: ['emulator', 'shutdown'],
summary: 'Stop helper and shut down the simulator device',
usage:
'orca emulator shutdown [--device <id>] [--emulator <id>] [--worktree <selector>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'device', 'emulator', 'worktree']
},
{
path: ['emulator', 'install'],
summary: 'Install an APK onto the target Android device',
usage: 'orca emulator install <apkPath> [--reinstall] [--device <id>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'device', 'emulator', 'worktree', 'path', 'reinstall'],
positionalArgs: ['path']
},
{
path: ['emulator', 'launch'],
summary: 'Launch an Android app by package (and optional activity)',
usage: 'orca emulator launch <package> [--activity <name>] [--device <id>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'device', 'emulator', 'worktree', 'package', 'activity'],
positionalArgs: ['package']
},
{
path: ['emulator', 'permissions'],
summary: 'Grant/revoke an Android runtime permission, or reset all runtime grants',
usage:
'orca emulator permissions <grant|revoke> <package> <permission> [--device <id>] [--json]\n orca emulator permissions reset [--device <id>] [--json]',
allowedFlags: [
...GLOBAL_FLAGS,
'device',
'emulator',
'worktree',
'op',
'package',
'permission'
],
positionalArgs: ['op', 'package', 'permission']
},
{
path: ['emulator', 'ax'],
summary: 'Dump the accessibility tree (Android uiautomator; iOS serve-sim AX, frames 0..1)',
usage: 'orca emulator ax [--device <id>] [--worktree <selector>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'device', 'emulator', 'worktree']
},
{
path: ['emulator', 'logcat'],
summary: 'Capture a one-shot logcat dump from the Android device',
usage: 'orca emulator logcat [--lines <n>] [--device <id>] [--worktree <selector>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'device', 'emulator', 'worktree', 'lines']
}
]