dsh-mobilecode 0.4.0 → 0.6.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/README.md CHANGED
@@ -87,6 +87,11 @@ drawer:
87
87
  - `device_apps` / `device_launch_app` — list installed packages (third-party by
88
88
  default) so a package name is never guessed / launch one by package or a
89
89
  unique substring, with `relaunch` for a cold start.
90
+ - `device_intent` — open anything by Android intent: an `action` (e.g.
91
+ `android.settings.WIFI_SETTINGS`), a deep-link `uri` (`geo:`, `https://`,
92
+ `file://`), or an explicit `component` (`pkg/.Activity`). Reaches screens no
93
+ tap can address. Values are single-quoted for the device shell (apostrophes
94
+ escaped, control characters refused), so metacharacters stay inert.
90
95
  - `device_stream` — drive the live screen stream the panel shows: `start` an
91
96
  online device (returns a signed `streamUrl`), `status`, or `stop`. Agents that
92
97
  just need to see the screen should prefer `device_screen` / `device_ui_tree`.
@@ -133,6 +138,34 @@ the plain JSON summary (path + UI tree + OCR) with no new error.
133
138
  emulator binary, and what the plugin currently runs (preview servers, Metro,
134
139
  builds per directory).
135
140
 
141
+ **Device-ultimate port (v0.6.0)** — tools ported from
142
+ [dsh-adb-ultimate](https://github.com/newborne/dsh-adb-ultimate), rebuilt on the
143
+ classified adb boundary (argv-based, quoted, replay-safe):
144
+
145
+ - `device_scroll_to` — scroll until an element (`resource_id` or `text`/
146
+ content-desc, exact then substring) comes into view, then report it with its
147
+ center tap point. Completes the control loop for long lists: `device_wait_for`
148
+ waits without scrolling, this brings the element on-screen so a follow-up
149
+ `device_tap_element` can hit it. Defaults to 8 swipes, swipe direction
150
+ `up`/`left`.
151
+ - `device_connect` — attach a Wi-Fi device: `adb connect <host>:<port>`
152
+ (default port 5555); pass `pairing_code` + `pairing_port` (default 37000) for
153
+ the first-time Android 11+ "Pair device" flow. Reattaching a known device
154
+ needs no code.
155
+ - `device_pair_qr` — the full pairing journey in one call: generates a
156
+ `WIFI:T:ADB;S:...;P:...;;` QR string (render it with any QR generator and scan
157
+ with Settings → Connected devices → Pair by QR), polls `adb mdns services`
158
+ (adb ≥ 31) for `_adb-tls-pairing._tcp`, then auto-pairs and connects.
159
+ - `device_perf` — one-shot RAM / battery / CPU snapshot (meminfo +
160
+ `dumpsys battery` + cpuinfo → used %, level/temp/status/health, cores).
161
+ - `device_app_info` — per-app detail from `dumpsys package`: versionName/
162
+ versionCode, minSdk/targetSdk, requested permissions, exported activities.
163
+ - `device_install` / `device_uninstall` — sideload a local APK
164
+ (`-r -g`: replace + grant runtime permissions) / remove an app (optionally
165
+ `-k` keep data). Uninstall is destructive.
166
+ - `device_reboot` — reboot into `normal` / `recovery` / `bootloader`
167
+ (the device drops offline and comes back in a minute or two).
168
+
136
169
  **First-run experience & settings**
137
170
 
138
171
  - On first start after installation, a **welcome window** explains how to use the
@@ -148,6 +181,13 @@ the plain JSON summary (path + UI tree + OCR) with no new error.
148
181
  Fix button for auto-fixable checks (PaddleOCR install).
149
182
  - **PaddleOCR** — install status, progress log, and the install button.
150
183
  - **AI Prompt** — the copyable agent prompt.
184
+ - **Connection** — the resolved adb binary path and every device `adb` sees
185
+ (serial, state dot, model, USB / Wi-Fi badge), straight from
186
+ `GET /connection`. A **connect form** (device IP + port, optional pairing
187
+ code + pair port → `POST /connect`) and a **Pair by QR** button
188
+ (`POST /pair-qr`, one blocking call that generates the `WIFI:T:ADB` QR,
189
+ waits for the mDNS scan and auto-pairs + connects) plus an in-line refresh.
190
+ Explains the Wi-Fi reconnect policy.
151
191
  - The same settings appear as a **`MobileCode` page in the DSH Settings**
152
192
  (registered as a `settings.section` slot, like the other installed plugins),
153
193
  so they are reachable from Settings even when the Devices pane is closed.
@@ -159,7 +199,23 @@ the plain JSON summary (path + UI tree + OCR) with no new error.
159
199
  `directory` + `platform` body, mirroring mobilecode's `server.devicePreview`
160
200
  group — plus setup endpoints: `GET /welcome`, `POST /welcome/dismiss`,
161
201
  `GET /doctor`, `POST /doctor/fix {id}`, `GET /ocr`, `POST /ocr/install`,
162
- `GET/POST /settings`.
202
+ `GET/POST /settings`, `GET /connection` — plus the Wi-Fi actions
203
+ `POST /connect` (host/port/pairing code) and `POST /pair-qr` (mDNS QR flow).
204
+
205
+ **One classified adb boundary** — every serial-targeted adb command runs through
206
+ `adbRun()` in `lib/device-build.js`:
207
+
208
+ - A transport failure (`device not found` / `offline` / `unauthorized` /
209
+ `closed` / `no devices`) on a **Wi-Fi serial** (`ip:port`) gets exactly ONE
210
+ `adb connect` retry. Read-only commands (the `replaySafeAdb` allowlist:
211
+ `exec-out`/`screencap`/`uiautomator`/`dumpsys`/`getprop`/`logcat`/`cat`/…)
212
+ then replay automatically; **side-effectful ones never do** — a replayed tap
213
+ could double-tap — they raise "reconnected — call again" instead.
214
+ - USB serials and non-transport failures (a real `am start` error, a
215
+ `SecurityException`) surface as classified, actionable errors — the old
216
+ silent `capture()` → `""` swallowing is gone on agent-facing paths.
217
+ - Tolerant internal callers (boot polling, IME checks) opt back into
218
+ best-effort with an explicit `.catch(() => "")`.
163
219
 
164
220
  ## How it works
165
221
 
@@ -247,7 +247,7 @@ export class AndroidStreamHost {
247
247
 
248
248
  async getRotation(serial) {
249
249
  try {
250
- const value = Number((await DeviceBuild.capture(DeviceBuild.adb(), ['-s', serial, 'shell', 'settings', 'get', 'system', 'user_rotation'])).trim())
250
+ const value = Number((await DeviceBuild.adbRun(serial, ['shell', 'settings', 'get', 'system', 'user_rotation'])).trim())
251
251
  return ROTATION_CYCLE.includes(value) ? value : 0
252
252
  } catch {
253
253
  return 0
@@ -263,8 +263,7 @@ export class AndroidStreamHost {
263
263
  }
264
264
 
265
265
  async #shell(serial, shell) {
266
- const code = await DeviceBuild.exec(DeviceBuild.adb(), ['-s', serial, 'shell', ...shell], { timeoutMs: CONTROL_TIMEOUT_MS }).exit
267
- if (code !== 0) throw new Error(`adb shell ${shell.join(' ')} failed (exit ${code})`)
266
+ await DeviceBuild.adbRun(serial, ['shell', ...shell], { timeoutMs: CONTROL_TIMEOUT_MS })
268
267
  }
269
268
 
270
269
  async #keepAliveTick() {
@@ -371,7 +370,7 @@ export class AndroidStreamHost {
371
370
 
372
371
  /** Override-aware physical screen size for the non-streaming control fallback. */
373
372
  async function screenSize(serial) {
374
- const output = await DeviceBuild.capture(DeviceBuild.adb(), ['-s', serial, 'shell', 'wm', 'size'])
373
+ const output = await DeviceBuild.adbRun(serial, ['shell', 'wm', 'size'])
375
374
  const match = /Override size:\s*(\d+)x(\d+)/.exec(output) ?? /Physical size:\s*(\d+)x(\d+)/.exec(output)
376
375
  if (!match) throw new Error(`cannot read the screen size of ${serial}`)
377
376
  return { width: Number(match[1]), height: Number(match[2]) }