portholejs 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +130 -46
  2. package/client-dist/assets/index-DluwPdUK.js +49 -0
  3. package/client-dist/index.html +1 -1
  4. package/dist/cli.js +351 -83
  5. package/dist/cli.js.map +1 -1
  6. package/dist/control-client.d.ts +1 -0
  7. package/dist/control-client.d.ts.map +1 -1
  8. package/dist/control-client.js +10 -2
  9. package/dist/control-client.js.map +1 -1
  10. package/dist/engine/scrcpy-engine.d.ts +2 -2
  11. package/dist/engine/scrcpy-engine.d.ts.map +1 -1
  12. package/dist/engine/scrcpy-engine.js.map +1 -1
  13. package/dist/engine/types.d.ts +3 -8
  14. package/dist/engine/types.d.ts.map +1 -1
  15. package/dist/focus-navigation.d.ts +27 -0
  16. package/dist/focus-navigation.d.ts.map +1 -0
  17. package/dist/focus-navigation.js +118 -0
  18. package/dist/focus-navigation.js.map +1 -0
  19. package/dist/gesture.d.ts +14 -0
  20. package/dist/gesture.d.ts.map +1 -0
  21. package/dist/gesture.js +110 -0
  22. package/dist/gesture.js.map +1 -0
  23. package/dist/index.d.ts +3 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +1 -0
  26. package/dist/index.js.map +1 -1
  27. package/dist/input-validation.d.ts.map +1 -1
  28. package/dist/input-validation.js +4 -56
  29. package/dist/input-validation.js.map +1 -1
  30. package/dist/input.d.ts +1 -21
  31. package/dist/input.d.ts.map +1 -1
  32. package/dist/keycodes.d.ts +2 -1
  33. package/dist/keycodes.d.ts.map +1 -1
  34. package/dist/keycodes.js.map +1 -1
  35. package/dist/mcp/server.d.ts.map +1 -1
  36. package/dist/mcp/server.js +183 -1
  37. package/dist/mcp/server.js.map +1 -1
  38. package/dist/mp4-writer.d.ts +18 -0
  39. package/dist/mp4-writer.d.ts.map +1 -0
  40. package/dist/mp4-writer.js +195 -0
  41. package/dist/mp4-writer.js.map +1 -0
  42. package/dist/protocol.d.ts +74 -2
  43. package/dist/protocol.d.ts.map +1 -1
  44. package/dist/protocol.js +154 -7
  45. package/dist/protocol.js.map +1 -1
  46. package/dist/recording.d.ts +22 -0
  47. package/dist/recording.d.ts.map +1 -0
  48. package/dist/recording.js +132 -0
  49. package/dist/recording.js.map +1 -0
  50. package/dist/screen-diff.d.ts +21 -0
  51. package/dist/screen-diff.d.ts.map +1 -0
  52. package/dist/screen-diff.js +70 -0
  53. package/dist/screen-diff.js.map +1 -0
  54. package/dist/server/http.d.ts +5 -4
  55. package/dist/server/http.d.ts.map +1 -1
  56. package/dist/server/http.js +69 -31
  57. package/dist/server/http.js.map +1 -1
  58. package/dist/server/ws.d.ts +4 -3
  59. package/dist/server/ws.d.ts.map +1 -1
  60. package/dist/server/ws.js +32 -18
  61. package/dist/server/ws.js.map +1 -1
  62. package/dist/session.d.ts +25 -10
  63. package/dist/session.d.ts.map +1 -1
  64. package/dist/session.js +117 -67
  65. package/dist/session.js.map +1 -1
  66. package/dist/state.d.ts.map +1 -1
  67. package/dist/state.js +1 -3
  68. package/dist/state.js.map +1 -1
  69. package/package.json +18 -3
  70. package/skills/porthole/SKILL.md +130 -0
  71. package/client-dist/assets/index-C_qSS_Gy.js +0 -49
package/README.md CHANGED
@@ -65,6 +65,7 @@ in sync (see `src/engine/scrcpy-engine.ts`).
65
65
 
66
66
  ```sh
67
67
  npx portholejs start Pixel_8_Pro_API_34
68
+ npx portholejs start Pixel_8_Pro_API_34 Television_1080p_API_34
68
69
  ```
69
70
 
70
71
  For a headless agent workflow:
@@ -76,54 +77,82 @@ npx portholejs screenshot -q
76
77
  npx portholejs kill -q
77
78
  ```
78
79
 
80
+ ## Things to try
81
+
82
+ Test a TV app **without touching a remote**: `porthole focus-on "Library"
83
+ --select` walks the D-pad focus to any element by its text and presses OK —
84
+ then `porthole focused -q` tells you exactly where leanback focus landed, as
85
+ JSON. Record a bug repro as a real video with
86
+ `porthole record repro.mp4 --duration 30s` while you swipe, scroll, and
87
+ long-press the phone from the CLI (`porthole scroll down`,
88
+ `porthole swipe 0.5 0.05 0.5 0.7` opens the notification shade). Catch visual
89
+ regressions in one line: save a baseline with `porthole screenshot -o good.png`,
90
+ then `porthole assert-screen good.png --threshold 0.02` exits non-zero and
91
+ writes a diff image the moment the screen drifts. Stream a **phone and a TV
92
+ side by side** from one command — `porthole start Pixel_8 Tv_1080p` puts both
93
+ in the same browser tab with a device picker. And the whole loop works with no
94
+ human at all: add the MCP server to Claude Code (below) and ask it to _"open
95
+ my app on the TV emulator, navigate to Settings, and check nothing crashed"_ —
96
+ it will drive the D-pad, read the UI as JSON (`dump_ui`), and pull
97
+ `get_crashes` on its own. If anything misbehaves, `porthole doctor` diagnoses
98
+ your setup in seconds.
99
+
79
100
  ## CLI Reference
80
101
 
81
- | Command | Purpose |
82
- | ----------------------------------- | --------------------------------------------------- |
83
- | `porthole start [avd]` | Boot or attach and serve the browser preview |
84
- | `porthole list` | List AVDs, running emulators, and known sessions |
85
- | `porthole kill [avd]` | Stop emulators Porthole booted and detached servers |
86
- | `porthole tap <x> <y>` | Touch normalized phone coordinates from `0..1` |
87
- | `porthole key <keycode>` | Send an Android keycode down/up pair |
88
- | `porthole remote <button>` | Press a TV D-pad/media button |
89
- | `porthole text "<string>"` | Type text into the active session |
90
- | `porthole screenshot [-o file.png]` | Save a PNG screenshot |
91
- | `porthole focused` | Print the currently focused UI node |
92
- | `porthole dump-ui [--filter text]` | Dump the Android UI hierarchy |
93
- | `porthole wait-for <text>` | Wait until UI text appears |
94
- | `porthole open-url <url>` | Open a URL or Android deep link |
95
- | `porthole stop-app <package>` | Force-stop an app |
96
- | `porthole clear-app <package>` | Clear app data |
97
- | `porthole rotate <orientation>` | Rotate a phone emulator |
98
- | `porthole emu <args...>` | Pass through to `adb emu` |
99
- | `porthole doctor` | Diagnose Node/SDK/adb/AVD/session problems |
100
- | `porthole mcp` | Run the MCP server over stdio |
102
+ | Command | Purpose |
103
+ | ------------------------------------ | --------------------------------------------------- |
104
+ | `porthole start [avd...]` | Boot or attach one or more emulators and serve them |
105
+ | `porthole list` | List AVDs, running emulators, and known sessions |
106
+ | `porthole kill [avd]` | Stop emulators Porthole booted and detached servers |
107
+ | `porthole tap <x> <y>` | Touch normalized phone coordinates from `0..1` |
108
+ | `porthole swipe <x1> <y1> <x2> <y2>` | Swipe between normalized phone coordinates |
109
+ | `porthole longpress <x> <y>` | Long-press normalized phone coordinates |
110
+ | `porthole scroll <direction>` | Scroll phone content up, down, left, or right |
111
+ | `porthole key <keycode>` | Send an Android keycode down/up pair |
112
+ | `porthole remote <button>` | Press a TV D-pad/media button |
113
+ | `porthole text "<string>"` | Type text into the active session |
114
+ | `porthole screenshot [-o file.png]` | Save a PNG screenshot |
115
+ | `porthole assert-screen <png>` | Compare the current screen with a PNG baseline |
116
+ | `porthole record <out.mp4>` | Record the current H.264 stream to MP4 |
117
+ | `porthole focused` | Print the currently focused UI node |
118
+ | `porthole focus-on <text>` | Move TV D-pad focus to matching text |
119
+ | `porthole dump-ui [--filter text]` | Dump the Android UI hierarchy |
120
+ | `porthole wait-for <text>` | Wait until UI text appears |
121
+ | `porthole open-url <url>` | Open a URL or Android deep link |
122
+ | `porthole stop-app <package>` | Force-stop an app |
123
+ | `porthole clear-app <package>` | Clear app data |
124
+ | `porthole rotate <orientation>` | Rotate a phone emulator |
125
+ | `porthole emu <args...>` | Pass through to `adb emu` |
126
+ | `porthole doctor` | Diagnose Node/SDK/adb/AVD/session problems |
127
+ | `porthole mcp` | Run the MCP server over stdio |
101
128
 
102
129
  Common options:
103
130
 
104
- | Option | Meaning |
105
- | ----------------------- | ------------------------------------------------ |
106
- | `-p, --port <port>` | Preview/control port, default `3200` |
107
- | `-d, --device <serial>` | Attach to a running emulator serial |
108
- | `--host <host>` | Bind address, default `127.0.0.1` |
109
- | `--no-preview` | Do not open the browser automatically |
110
- | `--detach` | Start the preview server in the background |
111
- | `--mjpeg` | Force MJPEG screenshot polling |
112
- | `-q, --quiet` | Emit one JSON object/array on stdout |
113
- | `--max-size <px>` | Maximum scrcpy stream dimension |
114
- | `--max-fps <fps>` | Maximum scrcpy FPS |
115
- | `--bitrate <bps>` | Scrcpy video bitrate |
116
- | `--wipe-data` | Wipe emulator data before boot |
117
- | `--no-snapshot` | Disable loading/saving emulator snapshots |
118
- | `--cold-boot` | Alias for `--no-snapshot` |
119
- | `--gpu <mode>` | Pass emulator GPU mode |
120
- | `--keep-alive` | Leave a Porthole-booted emulator running on exit |
131
+ | Option | Meaning |
132
+ | ----------------------- | ------------------------------------------------------- |
133
+ | `-p, --port <port>` | Preview/control port, default `3200` |
134
+ | `-d, --device <serial>` | Target a running emulator serial; comma-list on `start` |
135
+ | `--host <host>` | Bind address, default `127.0.0.1` |
136
+ | `--no-preview` | Do not open the browser automatically |
137
+ | `--detach` | Start the preview server in the background |
138
+ | `--mjpeg` | Force MJPEG screenshot polling |
139
+ | `-q, --quiet` | Emit one JSON object/array on stdout |
140
+ | `--max-size <px>` | Maximum scrcpy stream dimension |
141
+ | `--max-fps <fps>` | Maximum scrcpy FPS |
142
+ | `--bitrate <bps>` | Scrcpy video bitrate |
143
+ | `--wipe-data` | Wipe emulator data before boot |
144
+ | `--no-snapshot` | Disable loading/saving emulator snapshots |
145
+ | `--cold-boot` | Alias for `--no-snapshot` |
146
+ | `--gpu <mode>` | Pass emulator GPU mode |
147
+ | `--keep-alive` | Leave a Porthole-booted emulator running on exit |
121
148
 
122
149
  Quiet schemas are intentionally simple: `list -q` returns
123
- `{ "devices": [...], "sessions": [...] }`; `start --detach -q` returns the
124
- session record with `url`, `pid`, `serial`, `avdName`, `port`, and `profile`;
125
- input commands return `{ "ok": true, "session": ... }`; `screenshot -q` returns
126
- `{ "ok": true, "path": "...", "session": ... }`.
150
+ `{ "devices": [...], "sessions": [...] }`; single-device `start --detach -q`
151
+ returns the session record with `url`, `pid`, `serial`, `avdName`, `port`, and
152
+ `profile`; multi-device detached starts return `{ "sessions": [...] }`; input
153
+ commands return `{ "ok": true, "session": ... }`; `screenshot -q` returns
154
+ `{ "ok": true, "path": "...", "session": ... }`. Pass `-d <serial>` to
155
+ port-scoped commands when a server is streaming more than one emulator.
127
156
 
128
157
  ## MCP Setup
129
158
 
@@ -147,15 +176,70 @@ claude mcp add porthole -- npx portholejs mcp
147
176
  ```
148
177
 
149
178
  Useful MCP tools: `list_devices`, `boot_device`, `wait_for_boot`,
150
- `attach_device`, `tap`, `key`, `remote`, `type_text`, `screenshot`,
151
- `dump_ui`, `get_focused`, `find_element`, `wait_for`, `open_url`, `stop_app`,
152
- `clear_app`, `get_crashes`, `read_logcat`, and `install_apk`.
179
+ `attach_device`, `tap`, `swipe`, `long_press`, `scroll`, `key`, `remote`,
180
+ `type_text`, `screenshot`, `assert_screen`, `start_recording`, `stop_recording`,
181
+ `dump_ui`, `get_focused`, `focus_on`, `find_element`, `wait_for`, `open_url`,
182
+ `stop_app`, `clear_app`, `get_crashes`, `read_logcat`, and `install_apk`.
183
+
184
+ ## Agent Skill
185
+
186
+ Porthole ships an agent skill (`skills/porthole/`) that teaches agents the full workflow: doctor triage,
187
+ boot/attach, semantic UI assertions, TV navigation, and cleanup.
188
+
189
+ Install it by copying the skill directory into your skills folder — for the
190
+ current project:
191
+
192
+ ```sh
193
+ mkdir -p .claude/skills
194
+ cp -r node_modules/portholejs/skills/porthole .claude/skills/
195
+ ```
196
+
197
+ or for all your projects:
198
+
199
+ ```sh
200
+ mkdir -p ~/.claude/skills
201
+ cp -r node_modules/portholejs/skills/porthole ~/.claude/skills/
202
+ ```
203
+
204
+ From a clone of this repository, copy `skills/porthole` instead. Claude Code
205
+ picks the skill up automatically; ask it to "test my app on the Android
206
+ emulator" and it will follow the Porthole workflow.
207
+
208
+ ## GitHub Action
209
+
210
+ Porthole ships an in-repo GitHub Action for CI smoke tests. It wraps
211
+ `reactivecircus/android-emulator-runner`, starts `npx --yes portholejs` against
212
+ the booted emulator, and runs your script while the emulator is alive.
213
+
214
+ ```yaml
215
+ jobs:
216
+ tv-smoke:
217
+ runs-on: ubuntu-latest
218
+ steps:
219
+ - uses: actions/checkout@v4
220
+ - id: porthole
221
+ uses: giolaq/porthole@main
222
+ with:
223
+ avd-name: porthole-tv
224
+ api-level: "35"
225
+ profile: tv
226
+ script: |
227
+ mkdir -p artifacts
228
+ "$PORTHOLE_CLI" focus-on "Library" --select -q -p "$PORTHOLE_PORT"
229
+ "$PORTHOLE_CLI" screenshot -o artifacts/library.png -q -p "$PORTHOLE_PORT"
230
+ ```
231
+
232
+ The action exposes `url`, `serial`, and `port` outputs, and also sets
233
+ `PORTHOLE_URL`, `PORTHOLE_SERIAL`, `PORTHOLE_PORT`, and `PORTHOLE_CLI` for the
234
+ smoke script. See `docs/github-action-example.yml` for a fuller `focus-on` /
235
+ `assert-screen` workflow that uploads screenshot artifacts on failure.
153
236
 
154
237
  ## Browser UI
155
238
 
156
- The preview shows the active device, stream status, screenshots, copy-to-clipboard,
239
+ The preview shows device tabs, stream status, screenshots, copy-to-clipboard,
157
240
  stream stats, logcat, drag-and-drop APK install/file push, phone hardware keys,
158
- and a TV D-pad remote. TV sessions reject touch input server-side.
241
+ and a TV D-pad remote. Switching tabs re-subscribes to that device's stream
242
+ without reloading the page. TV sessions reject touch input server-side.
159
243
 
160
244
  Video uses WebCodecs by default. Browsers without `VideoDecoder` automatically
161
245
  fall back to `/stream.mjpeg`, and `--mjpeg` or `?video=mjpeg` forces that mode.