portholejs 0.1.0 → 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.
- package/LICENSE +186 -0
- package/README.md +285 -0
- package/client-dist/assets/index-DluwPdUK.js +49 -0
- package/client-dist/index.html +1 -1
- package/dist/cli.js +351 -83
- package/dist/cli.js.map +1 -1
- package/dist/control-client.d.ts +1 -0
- package/dist/control-client.d.ts.map +1 -1
- package/dist/control-client.js +10 -2
- package/dist/control-client.js.map +1 -1
- package/dist/engine/scrcpy-engine.d.ts +2 -2
- package/dist/engine/scrcpy-engine.d.ts.map +1 -1
- package/dist/engine/scrcpy-engine.js.map +1 -1
- package/dist/engine/types.d.ts +3 -8
- package/dist/engine/types.d.ts.map +1 -1
- package/dist/focus-navigation.d.ts +27 -0
- package/dist/focus-navigation.d.ts.map +1 -0
- package/dist/focus-navigation.js +118 -0
- package/dist/focus-navigation.js.map +1 -0
- package/dist/gesture.d.ts +14 -0
- package/dist/gesture.d.ts.map +1 -0
- package/dist/gesture.js +110 -0
- package/dist/gesture.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/input-validation.d.ts.map +1 -1
- package/dist/input-validation.js +4 -56
- package/dist/input-validation.js.map +1 -1
- package/dist/input.d.ts +1 -21
- package/dist/input.d.ts.map +1 -1
- package/dist/keycodes.d.ts +2 -1
- package/dist/keycodes.d.ts.map +1 -1
- package/dist/keycodes.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +183 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/mp4-writer.d.ts +18 -0
- package/dist/mp4-writer.d.ts.map +1 -0
- package/dist/mp4-writer.js +195 -0
- package/dist/mp4-writer.js.map +1 -0
- package/dist/protocol.d.ts +74 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +154 -7
- package/dist/protocol.js.map +1 -1
- package/dist/recording.d.ts +22 -0
- package/dist/recording.d.ts.map +1 -0
- package/dist/recording.js +132 -0
- package/dist/recording.js.map +1 -0
- package/dist/screen-diff.d.ts +21 -0
- package/dist/screen-diff.d.ts.map +1 -0
- package/dist/screen-diff.js +70 -0
- package/dist/screen-diff.js.map +1 -0
- package/dist/server/http.d.ts +5 -4
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +69 -31
- package/dist/server/http.js.map +1 -1
- package/dist/server/ws.d.ts +4 -3
- package/dist/server/ws.d.ts.map +1 -1
- package/dist/server/ws.js +32 -18
- package/dist/server/ws.js.map +1 -1
- package/dist/session.d.ts +25 -10
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +117 -67
- package/dist/session.js.map +1 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +1 -3
- package/dist/state.js.map +1 -1
- package/package.json +18 -3
- package/skills/porthole/SKILL.md +130 -0
- package/client-dist/assets/index-C_qSS_Gy.js +0 -49
package/dist/state.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,aAAa,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,qBAAqB,EAAE,CAAC;CACnC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAsB,SAAS,CAAC,IAAI,SAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAY1E;AAED,wBAAsB,UAAU,CAC9B,KAAK,EAAE,aAAa,EACpB,IAAI,SAAc,GACjB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,qBAAqB,EAC7B,IAAI,SAAc,GACjB,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,aAAa,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,qBAAqB,EAAE,CAAC;CACnC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAsB,SAAS,CAAC,IAAI,SAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAY1E;AAED,wBAAsB,UAAU,CAC9B,KAAK,EAAE,aAAa,EACpB,IAAI,SAAc,GACjB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,qBAAqB,EAC7B,IAAI,SAAc,GACjB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACvD,IAAI,SAAc,GACjB,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAK/E"}
|
package/dist/state.js
CHANGED
|
@@ -26,9 +26,7 @@ export async function writeState(state, path = statePath()) {
|
|
|
26
26
|
}
|
|
27
27
|
export async function upsertSession(record, path = statePath()) {
|
|
28
28
|
const state = await readState(path);
|
|
29
|
-
const sessions = state.sessions.filter((session) => session.serial !== record.serial
|
|
30
|
-
session.port !== record.port &&
|
|
31
|
-
session.pid !== record.pid);
|
|
29
|
+
const sessions = state.sessions.filter((session) => session.serial !== record.serial);
|
|
32
30
|
sessions.push(record);
|
|
33
31
|
await writeState({ sessions }, path);
|
|
34
32
|
}
|
package/dist/state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAoB1C,MAAM,UAAU,SAAS;IACvB,OAAO,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;AACxF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAI,GAAG,SAAS,EAAE;IAChD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA2B,CAAC;QACzD,OAAO;YACL,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC;gBACtC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,eAAe,CAAC;gBACzC,CAAC,CAAC,EAAE;SACP,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAAoB,EACpB,IAAI,GAAG,SAAS,EAAE;IAElB,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;IAC7C,MAAM,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAChE,MAAM,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA6B,EAC7B,IAAI,GAAG,SAAS,EAAE;IAElB,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "portholejs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A window into your Android emulator — stream and control AVDs from the browser",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./protocol": {
|
|
14
|
+
"types": "./dist/protocol.d.ts",
|
|
15
|
+
"import": "./dist/protocol.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
8
19
|
"bin": {
|
|
9
20
|
"porthole": "dist/cli.js"
|
|
10
21
|
},
|
|
11
22
|
"scripts": {
|
|
12
|
-
"build": "
|
|
23
|
+
"build": "npm run build:ts && npm run copy-package-assets",
|
|
24
|
+
"build:ts": "tsc",
|
|
25
|
+
"copy-package-assets": "node scripts/copy-package-assets.mjs",
|
|
13
26
|
"typecheck": "tsc --noEmit",
|
|
14
27
|
"postinstall": "node scripts/download-scrcpy-server.mjs",
|
|
15
28
|
"prepublishOnly": "npm run build --prefix ../.."
|
|
@@ -38,7 +51,8 @@
|
|
|
38
51
|
"dist/",
|
|
39
52
|
"!dist/__tests__/",
|
|
40
53
|
"scripts/download-scrcpy-server.mjs",
|
|
41
|
-
"client-dist/"
|
|
54
|
+
"client-dist/",
|
|
55
|
+
"skills/"
|
|
42
56
|
],
|
|
43
57
|
"engines": {
|
|
44
58
|
"node": ">=20"
|
|
@@ -52,6 +66,7 @@
|
|
|
52
66
|
"@yume-chan/stream-extra": "^2.5.3",
|
|
53
67
|
"commander": "^14.0.3",
|
|
54
68
|
"jpeg-js": "^0.4.4",
|
|
69
|
+
"pixelmatch": "^7.2.0",
|
|
55
70
|
"pngjs": "^7.0.0",
|
|
56
71
|
"ws": "^8.21.0"
|
|
57
72
|
},
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: porthole
|
|
3
|
+
description: Boot, stream, and control Android phone/TV emulators headlessly with the porthole CLI or MCP tools — screenshots, touch/D-pad input, semantic UI dumps, logcat, and APK install. Use when asked to run, test, drive, or inspect an Android emulator or AVD, navigate an Android TV app, or verify Android UI state from an agent workflow.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Porthole Agent Skill
|
|
7
|
+
|
|
8
|
+
Use Porthole when you need to inspect or control an Android emulator from an
|
|
9
|
+
agent workflow. Porthole supports Android emulator AVDs only; do not use it for
|
|
10
|
+
iOS, physical Android devices, or audio.
|
|
11
|
+
|
|
12
|
+
## Workflow
|
|
13
|
+
|
|
14
|
+
1. List devices:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
porthole list -q
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
If anything fails at any step (SDK missing, port busy, offline adb,
|
|
21
|
+
missing scrcpy-server), diagnose first:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
porthole doctor -q
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
2. Start a session. Prefer detached JSON mode for automation:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
porthole start <AVD_NAME> --detach -q
|
|
31
|
+
porthole start <PHONE_AVD> <TV_AVD> --detach -q
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
In GitHub Actions, prefer the bundled repository action so the emulator,
|
|
35
|
+
Porthole server, and smoke script share one lifecycle:
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
- uses: giolaq/porthole@main
|
|
39
|
+
with:
|
|
40
|
+
profile: tv
|
|
41
|
+
script: |
|
|
42
|
+
"$PORTHOLE_CLI" focus-on "Library" --select -q -p "$PORTHOLE_PORT"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
3. Verify the screen:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
porthole screenshot -q
|
|
49
|
+
porthole assert-screen baseline.png --threshold 0.02 -q
|
|
50
|
+
porthole record run.mp4 --duration 10s -q
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
4. Drive input:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
porthole tap 0.5 0.9
|
|
57
|
+
porthole scroll down
|
|
58
|
+
porthole swipe 0.5 0.85 0.5 0.2
|
|
59
|
+
porthole longpress 0.5 0.5
|
|
60
|
+
porthole key 4
|
|
61
|
+
porthole text "hello"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
When one Porthole server is streaming multiple emulators, pass
|
|
65
|
+
`-d <serial>` to target the right device:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
porthole tap 0.5 0.9 -d emulator-5554
|
|
69
|
+
porthole remote dpad_down -d emulator-5556
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
5. For Android TV, use remote buttons instead of touch:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
porthole remote dpad_down
|
|
76
|
+
porthole focus-on "Library" --select
|
|
77
|
+
porthole remote select
|
|
78
|
+
porthole remote back
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
6. Read logs when behavior is unclear:
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
curl "http://127.0.0.1:3200/api/logcat?lines=200"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
7. Clean up:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
porthole kill -q
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Semantic UI
|
|
94
|
+
|
|
95
|
+
Prefer semantic checks before screenshot interpretation when possible:
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
porthole focused -q
|
|
99
|
+
porthole dump-ui --filter "Settings" -q
|
|
100
|
+
porthole wait-for "Continue" --timeout 10000 -q
|
|
101
|
+
porthole open-url "example://deep/link"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For TV AVDs, prefer `focus-on` / `focus_on` for named destinations. Use
|
|
105
|
+
`remote`, then `focused`, then assert the focused node's `text`, `resourceId`,
|
|
106
|
+
or `contentDesc` for manual focus work. For phones, `find_element` is exposed
|
|
107
|
+
through MCP and can tap the center of a matching node.
|
|
108
|
+
|
|
109
|
+
Note: `focused` is a D-pad/leanback concept. On phone profiles it usually
|
|
110
|
+
returns `null` — that is expected, not an error; use `dump-ui` or
|
|
111
|
+
`find_element` on phones instead.
|
|
112
|
+
|
|
113
|
+
## MCP Loop
|
|
114
|
+
|
|
115
|
+
When MCP is configured, use:
|
|
116
|
+
|
|
117
|
+
- `list_devices` to pick an AVD
|
|
118
|
+
- `boot_device` and `wait_for_boot` for cold starts
|
|
119
|
+
- `attach_device` before input and screenshots
|
|
120
|
+
- `screenshot` after each meaningful input step
|
|
121
|
+
- `assert_screen` when a baseline image is available
|
|
122
|
+
- `start_recording` and `stop_recording` when an MP4 artifact is useful
|
|
123
|
+
- `dump_ui`, `get_focused`, and `wait_for` for semantic assertions
|
|
124
|
+
- `get_crashes` after risky actions
|
|
125
|
+
- `focus_on` or `remote` for TV navigation
|
|
126
|
+
- `tap`, `swipe`, `scroll`, and `long_press` only for phone profiles
|
|
127
|
+
- `read_logcat` to diagnose crashes or focus issues
|
|
128
|
+
- `install_apk` to load a freshly built APK
|
|
129
|
+
|
|
130
|
+
Keep the loop tight: input, screenshot, compare, adjust.
|