portholejs 0.1.1 → 0.2.1

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 (75) hide show
  1. package/README.md +156 -46
  2. package/client-dist/assets/index-cy7uJgaT.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 +4 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +2 -1
  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 +185 -2
  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/dist/version.d.ts +2 -0
  70. package/dist/version.d.ts.map +1 -0
  71. package/dist/version.js +6 -0
  72. package/dist/version.js.map +1 -0
  73. package/package.json +18 -3
  74. package/skills/porthole/SKILL.md +130 -0
  75. 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,108 @@ 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.** Walk the D-pad focus to any
83
+ element by its text and press OK, then read where leanback focus landed as
84
+ JSON:
85
+
86
+ ```sh
87
+ porthole focus-on "Library" --select
88
+ porthole focused -q
89
+ ```
90
+
91
+ - **Record a bug repro as a real video** while you drive the phone from the
92
+ CLI. The swipe below opens the notification shade:
93
+
94
+ ```sh
95
+ porthole record repro.mp4 --duration 30s
96
+ porthole scroll down
97
+ porthole swipe 0.5 0.05 0.5 0.7
98
+ ```
99
+
100
+ - **Catch visual regressions in one line.** Save a baseline, then the assert
101
+ exits non-zero and writes a diff image the moment the screen drifts:
102
+
103
+ ```sh
104
+ porthole screenshot -o good.png
105
+ porthole assert-screen good.png --threshold 0.02
106
+ ```
107
+
108
+ - **Stream a phone and a TV side by side** from one command. Both devices
109
+ share the same browser tab with a device picker:
110
+
111
+ ```sh
112
+ porthole start Pixel_8 Tv_1080p
113
+ ```
114
+
115
+ - **Let an agent run the whole loop.** Add the MCP server to Claude Code
116
+ (below) and ask it to "open my app on the TV emulator, navigate to Settings,
117
+ and check nothing crashed". It will drive the D-pad, read the UI as JSON
118
+ with `dump_ui`, and pull `get_crashes` on its own.
119
+
120
+ - **If anything misbehaves**, diagnose your setup in seconds:
121
+
122
+ ```sh
123
+ porthole doctor
124
+ ```
125
+
79
126
  ## CLI Reference
80
127
 
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 |
128
+ | Command | Purpose |
129
+ | ------------------------------------ | --------------------------------------------------- |
130
+ | `porthole start [avd...]` | Boot or attach one or more emulators and serve them |
131
+ | `porthole list` | List AVDs, running emulators, and known sessions |
132
+ | `porthole kill [avd]` | Stop emulators Porthole booted and detached servers |
133
+ | `porthole tap <x> <y>` | Touch normalized phone coordinates from `0..1` |
134
+ | `porthole swipe <x1> <y1> <x2> <y2>` | Swipe between normalized phone coordinates |
135
+ | `porthole longpress <x> <y>` | Long-press normalized phone coordinates |
136
+ | `porthole scroll <direction>` | Scroll phone content up, down, left, or right |
137
+ | `porthole key <keycode>` | Send an Android keycode down/up pair |
138
+ | `porthole remote <button>` | Press a TV D-pad/media button |
139
+ | `porthole text "<string>"` | Type text into the active session |
140
+ | `porthole screenshot [-o file.png]` | Save a PNG screenshot |
141
+ | `porthole assert-screen <png>` | Compare the current screen with a PNG baseline |
142
+ | `porthole record <out.mp4>` | Record the current H.264 stream to MP4 |
143
+ | `porthole focused` | Print the currently focused UI node |
144
+ | `porthole focus-on <text>` | Move TV D-pad focus to matching text |
145
+ | `porthole dump-ui [--filter text]` | Dump the Android UI hierarchy |
146
+ | `porthole wait-for <text>` | Wait until UI text appears |
147
+ | `porthole open-url <url>` | Open a URL or Android deep link |
148
+ | `porthole stop-app <package>` | Force-stop an app |
149
+ | `porthole clear-app <package>` | Clear app data |
150
+ | `porthole rotate <orientation>` | Rotate a phone emulator |
151
+ | `porthole emu <args...>` | Pass through to `adb emu` |
152
+ | `porthole doctor` | Diagnose Node/SDK/adb/AVD/session problems |
153
+ | `porthole mcp` | Run the MCP server over stdio |
101
154
 
102
155
  Common options:
103
156
 
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 |
157
+ | Option | Meaning |
158
+ | ----------------------- | ------------------------------------------------------- |
159
+ | `-p, --port <port>` | Preview/control port, default `3200` |
160
+ | `-d, --device <serial>` | Target a running emulator serial; comma-list on `start` |
161
+ | `--host <host>` | Bind address, default `127.0.0.1` |
162
+ | `--no-preview` | Do not open the browser automatically |
163
+ | `--detach` | Start the preview server in the background |
164
+ | `--mjpeg` | Force MJPEG screenshot polling |
165
+ | `-q, --quiet` | Emit one JSON object/array on stdout |
166
+ | `--max-size <px>` | Maximum scrcpy stream dimension |
167
+ | `--max-fps <fps>` | Maximum scrcpy FPS |
168
+ | `--bitrate <bps>` | Scrcpy video bitrate |
169
+ | `--wipe-data` | Wipe emulator data before boot |
170
+ | `--no-snapshot` | Disable loading/saving emulator snapshots |
171
+ | `--cold-boot` | Alias for `--no-snapshot` |
172
+ | `--gpu <mode>` | Pass emulator GPU mode |
173
+ | `--keep-alive` | Leave a Porthole-booted emulator running on exit |
121
174
 
122
175
  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": ... }`.
176
+ `{ "devices": [...], "sessions": [...] }`; single-device `start --detach -q`
177
+ returns the session record with `url`, `pid`, `serial`, `avdName`, `port`, and
178
+ `profile`; multi-device detached starts return `{ "sessions": [...] }`; input
179
+ commands return `{ "ok": true, "session": ... }`; `screenshot -q` returns
180
+ `{ "ok": true, "path": "...", "session": ... }`. Pass `-d <serial>` to
181
+ port-scoped commands when a server is streaming more than one emulator.
127
182
 
128
183
  ## MCP Setup
129
184
 
@@ -147,15 +202,70 @@ claude mcp add porthole -- npx portholejs mcp
147
202
  ```
148
203
 
149
204
  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`.
205
+ `attach_device`, `tap`, `swipe`, `long_press`, `scroll`, `key`, `remote`,
206
+ `type_text`, `screenshot`, `assert_screen`, `start_recording`, `stop_recording`,
207
+ `dump_ui`, `get_focused`, `focus_on`, `find_element`, `wait_for`, `open_url`,
208
+ `stop_app`, `clear_app`, `get_crashes`, `read_logcat`, and `install_apk`.
209
+
210
+ ## Agent Skill
211
+
212
+ Porthole ships an agent skill (`skills/porthole/`) that teaches agents the full workflow: doctor triage,
213
+ boot/attach, semantic UI assertions, TV navigation, and cleanup.
214
+
215
+ Install it by copying the skill directory into your skills folder — for the
216
+ current project:
217
+
218
+ ```sh
219
+ mkdir -p .claude/skills
220
+ cp -r node_modules/portholejs/skills/porthole .claude/skills/
221
+ ```
222
+
223
+ or for all your projects:
224
+
225
+ ```sh
226
+ mkdir -p ~/.claude/skills
227
+ cp -r node_modules/portholejs/skills/porthole ~/.claude/skills/
228
+ ```
229
+
230
+ From a clone of this repository, copy `skills/porthole` instead. Claude Code
231
+ picks the skill up automatically; ask it to "test my app on the Android
232
+ emulator" and it will follow the Porthole workflow.
233
+
234
+ ## GitHub Action
235
+
236
+ Porthole ships an in-repo GitHub Action for CI smoke tests. It wraps
237
+ `reactivecircus/android-emulator-runner`, starts `npx --yes portholejs` against
238
+ the booted emulator, and runs your script while the emulator is alive.
239
+
240
+ ```yaml
241
+ jobs:
242
+ tv-smoke:
243
+ runs-on: ubuntu-latest
244
+ steps:
245
+ - uses: actions/checkout@v4
246
+ - id: porthole
247
+ uses: giolaq/porthole@main
248
+ with:
249
+ avd-name: porthole-tv
250
+ api-level: "35"
251
+ profile: tv
252
+ script: |
253
+ mkdir -p artifacts
254
+ "$PORTHOLE_CLI" focus-on "Library" --select -q -p "$PORTHOLE_PORT"
255
+ "$PORTHOLE_CLI" screenshot -o artifacts/library.png -q -p "$PORTHOLE_PORT"
256
+ ```
257
+
258
+ The action exposes `url`, `serial`, and `port` outputs, and also sets
259
+ `PORTHOLE_URL`, `PORTHOLE_SERIAL`, `PORTHOLE_PORT`, and `PORTHOLE_CLI` for the
260
+ smoke script. See `docs/github-action-example.yml` for a fuller `focus-on` /
261
+ `assert-screen` workflow that uploads screenshot artifacts on failure.
153
262
 
154
263
  ## Browser UI
155
264
 
156
- The preview shows the active device, stream status, screenshots, copy-to-clipboard,
265
+ The preview shows device tabs, stream status, screenshots, copy-to-clipboard,
157
266
  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.
267
+ and a TV D-pad remote. Switching tabs re-subscribes to that device's stream
268
+ without reloading the page. TV sessions reject touch input server-side.
159
269
 
160
270
  Video uses WebCodecs by default. Browsers without `VideoDecoder` automatically
161
271
  fall back to `/stream.mjpeg`, and `--mjpeg` or `?video=mjpeg` forces that mode.