Move back, home, appSwitcher, clipboard read/write, setSetting, and
screenshot into the Interactor abstraction, eliminating scattered
if-platform checks in dispatch.ts.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: resolve screenshot path against caller cwd (#220)
When the daemon process starts from a different directory than the
caller (e.g. executor install dir vs suite workspace), relative
screenshot paths were resolved against the daemon's cwd instead of
the caller's cwd.
Apply the same SessionStore.expandHome(path, meta.cwd) pattern already
used by replay and record before dispatching the command.
Closes#220
* fix: record normalized screenshot paths
---------
Co-authored-by: Michał Pierzchała <thymikee@gmail.com>
* fix: prevent auto-selection of physical iOS devices when simulators are available
When running with --platform ios and a physical iPhone connected but no
simulators booted, the tool would connect to the physical device instead
of booting a simulator. Add a safeguard in resolveTargetDevice that
rejects auto-selected physical devices and falls back to
findBootableIosSimulator, which queries simctl without the isAvailable
filter to find any bootable simulator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: handle empty device list and filter unavailable simulators in fallback
- Extract resolveIosDevice() so the iOS fallback logic is testable via
dependency injection without module mocking
- Catch DEVICE_NOT_FOUND from selectDevice when no explicit selector was
used, so the simulator fallback runs even when listIosDevices returns
an empty list (all simulators filtered by isAvailable)
- Restore isAvailable check in findBootableIosSimulator to avoid
returning simulators with missing runtimes that cannot boot
- Add 8 tests covering: physical-device rejection, empty-list recovery,
explicit-selector passthrough, and normal simulator-present path
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Physical iOS devices always report booted=true, so the "prefer booted"
tiebreaker would pick them over unbooted simulators. This caused sessions
to bind to a connected iPhone instead of a simulator when no explicit
device selector (--udid/--device/--serial) was provided.
Now, when multiple candidates exist and no explicit device was requested,
physical devices are filtered out if virtual alternatives are available.
The simulator is then booted on demand by the existing ensureBootedSimulator
flow.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The 1.5s per-command timeout in resolveAndroidEmulatorAvdName was too
aggressive for emulators over remote adb daemon transport, causing
open --relaunch to fail during device discovery.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
adb shell concatenates all arguments with spaces into a single string
for the remote shell. Without quoting, the && in the mkdir/cat script
was interpreted as a command separator, causing mkdir to run with no
arguments ("mkdir: Needs 1 argument").
Wrapping the script in single quotes ensures the remote device shell
passes the entire command as one argument to sh -c.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>