# dsh-logcat An Android real-device debugging workbench for the DSH Web GUI (an Android Studio-style Logcat view). [中文](README.md) | **English** ## Features - **Auto-connect**: probes adb on this machine (`ANDROID_HOME` / `ANDROID_SDK_ROOT` / default `%LOCALAPPDATA%\Android\Sdk` / PATH / `~/.dsh/adb`), polls `adb devices -l` every 2s; devices in debug mode get a logcat stream **attached automatically** (`-v threadtime`) — no need to open the panel first. - **One-click adb install**: when adb is missing, the panel's status bar shows a "One-click install adb" button — it downloads platform-tools from the USTC mirror (Google official fallback), unpacks them to `~/.dsh/adb` and connects automatically (~10MB). - **Live logs**: WebSocket push, 2000-line ring buffer per device; automatic reconnect with exponential backoff. - **Logcat panel** (sidebar "Logcat" entry, right-side drawer, **draggable width that is remembered**): - Device dropdown (model / serial / state, remembers the last selection) - Severity filter (V/D/I/W/E/F single-select, Android Studio colors) - Keyword filter (**space-separated keywords = any-match**) and a **test package input** (Enter to set; synced with the agent's `logcat_set_package`; shown live in the status bar) - **Screenshot button**: one-click device screen capture and PNG download (`exec-out screencap`) - **Crash highlighting**: FATAL EXCEPTION / ANR lines highlighted in red - **History button**: loads on-disk persisted logs (survives GUI restarts; merged with the live buffer and deduped by timestamp) - **Crash auto-snapshots**: on a FATAL EXCEPTION / ANR in the stream, a screenshot + log context are saved to `~/.dsh/logcat/crashes`; the panel shows a banner and you can inspect the screenshot/log in the "Crash" dialog - **Events view**: one-click live `logcat -b events` stream (app start/crash/lifecycle events) - **WiFi wireless debugging wizard**: IP/port/pairing-code in, `adb pair` + `adb connect` out — ditch the cable (Android 11+) - **Install APK button**: local file picker installs an APK to the current device (streamed upload, 1GB cap) - **Quick device keys**: Home / Back / Recents / Wake / Power / Volume ± with one click - **Performance sparklines**: CPU / memory / battery sampled every 2s with trend charts in the status bar - **Live screen casting + desktop control**: the "Screen" tab streams the device display as **H.264 video (WebCodecs hardware decode, measured 55-60fps)** with selectable resolution tiers (360x800 / 540x1200 / 720x1600 / 1080x2400) and two modes (smooth video / sharp PNG). It **follows device rotation** (a landscape device automatically streams 1200x540 — a full frame with no letterbox — and touch coordinates are remapped to the current orientation), and gestures are injected with **3-5ms** latency by a long-lived `app_process` injector (the stock `input` command boots a Java VM per call, ~480ms; the injector auto-deploys on first use and silently falls back to `input`). **Control the phone from your computer**: click = tap, drag = swipe, **wheel = scroll**, **right-click = BACK**, **long-press**, **double-click**, **type on your physical keyboard** (letters/digits/symbols go straight through; Enter/Backspace/Esc/arrows/Tab map to keys), a text box for IME (Chinese) input, quick keys (Home/Back/Wake/Volume), one-click frame download and a ⛶ fullscreen toggle. - **Reverse-engineering workbench** (Log / Screen / RE tabs): process list → **auto-loaded module map** (library / base / perms, click a base to dump it) → memory hex/string search → dump hits → **manual address + length dump**, with a **hex + ASCII** view. - Pause/resume (pause buffers new lines, resume replays them), clear, copy, export .txt - Windowed rendering + auto-scroll (pauses while you scroll up manually) - Unauthorized-device hint ("allow USB debugging on the phone") - **Agent tools** (30 in total, all exposed to the agent and announced in its system prompt): - Device: `logcat_devices` (list devices), `device_info` (model/version/SDK/resolution/memory/battery), `device_stats` (CPU/memory/battery samples), `app_info` (installed version name / versionCode / APK path) - Screen / multimodal: `screen_capture` (screenshot saved locally + embedded as an image block so a multimodal model can see it and fix bugs with `input_*`) - Execution: `adb_exec` (shell), `adb_install` (install a local APK), `adb_pull` (pull files), `app_launch` (start an app / specific Activity), `app_stop` (force-stop; destructive — confirm first) - Input: `input_tap` / `input_swipe` / `input_text` / `input_keyevent` (real-device UI automation), `ui_dump` (UI hierarchy XML), `activity_current` (current foreground Activity) - Logs: `logcat_recent` (filter by package/severity/keyword), `logcat_history` (disk history backfill), `logcat_crash` (crash/ANR blocks with context), `logcat_events` (events buffer), `crash_sessions` (crash snapshot history), `logcat_set_package` (lock the package under test) - Reverse engineering / memory: `proc_list` (process list), `proc_maps` (memory maps + module bases), `proc_status` (process state / memory summary), `proc_smaps` (top Pss regions), `mem_dump` (read memory at an address as hex), `mem_search` (search memory for hex/string patterns), `frida_server` (deploy/start/stop frida-server), `frida_script` (generate hook/trace/scan/bypass/dump script templates) - **Real-device debugging workflow**: while building an Android app, the agent's announcement dynamically lists connected devices (serial + model) and the current test package. After confirming with the user: `adb_install` to deploy → `adb_exec`/`app_launch` to start → `logcat_set_package` to lock → `logcat_recent` / `logcat_crash` for crashes → `ui_dump` + `input_*` for UI automation → `adb_pull` for artifacts. A closed loop on real hardware. - **Reverse-engineering workflow**: `proc_list` to find the process → `proc_maps` for module bases → `mem_dump` at a target address / `mem_search` for signatures → `proc_smaps` for memory detail → `frida_script` + `frida_server` for dynamic instrumentation. Reading another app's memory/maps needs root or a debuggable app (run-as); tools return clear errors otherwise. - **Panel form (DSH 0.2)**: the **right-hand drawer stays the default** — it overlays the conversation, so glancing at logs costs no layout switch and no screen space. For a persistent panel, set `window.__DSH_LOGCAT_NATIVE_PANEL__ = true` in the browser console and reload; the official `main` (central panel) + `sidebar.panellist` (sidebar row) seats are then used instead. `dsh.client.inject` is aligned with the 0.2 manifest; icons are a bundled SVG set (no icon dependency) and colours prefer the host's `--dsw-*` design tokens. - **Extra routes**: `POST /api/dsh-logcat/exec` (shell), `POST /api/dsh-logcat/package` (set package), `GET /api/dsh-logcat/screenshot`, `POST /api/dsh-logcat/install-adb`, `GET /api/dsh-logcat/history`, `GET /api/dsh-logcat/crashes` + `GET /api/dsh-logcat/crash-file`, `POST /api/dsh-logcat/install-apk`, `POST /api/dsh-logcat/keyevent`, `POST /api/dsh-logcat/wifi-connect` / `wifi-disconnect`, `GET /api/dsh-logcat/processes`, `POST /api/dsh-logcat/mem-search`, `GET /api/dsh-logcat/mem-dump`. - **On-disk data (bounded)**: logs are written per device per day to `~/.dsh/logcat/logs//logcat-MM-DD.log` (raw threadtime lines); a day past its cap rolls into `.001/.002…` parts, and the oldest files are pruned by retention window and per-device total cap. Defaults: 64 MB/part, 14-day retention, 512 MB/device — override with `DSH_LOGCAT_DAY_MB`, `DSH_LOGCAT_RETAIN_DAYS`, `DSH_LOGCAT_MAX_MB`, or disable persistence entirely with `DSH_LOGCAT_PERSIST=off` (the live stream, `logcat_recent` and `logcat_crash` keep working). Crash snapshots live in `~/.dsh/logcat/crashes//-/` (`screenshot.png` + `crash.log` + `meta.json`); crashes of the injector helper itself (`dsh.Injector` under `app_process`) are not archived. - **Gesture injector**: `tap/swipe/keyevent` go through the long-lived injector (a few ms per gesture). The dex is deployed with an explicit `chmod 600` — Android 14+ `app_process` refuses world-writable dex files — and after 3 failed launches the injector disables itself and falls back to the stock shell commands until the device reconnects. ## Installation ```bash # Recommended (npm install): dsh plugin --profile web add @windypro-rourou/dsh-logcat # Update to the latest (no auto-update; older installs show a version hint in the panel/agent prompt): dsh plugin --profile web update # latest within the current major # or force: dsh plugin --profile web add @windypro-rourou/dsh-logcat@latest # restart the GUI (dsh web) afterwards # Optional preview channel (frequent small iterations, may be unstable): dsh plugin --profile web add @windypro-rourou/dsh-logcat@preview ``` ### Optional: GitHub Packages registry (npm.pkg.github.com) The same package is mirrored to GitHub Packages for environments that only want GitHub credentials. Two caveats: - **Installing always requires auth**: the GitHub Packages npm registry wants a token (`read:packages`) even for public packages. - **Only the `latest` dist-tag exists there**; the `preview` channel is npmjs.org-only. ```ini # ~/.npmrc @windypro-rourou:registry=https://npm.pkg.github.com //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} ``` ```bash dsh plugin --profile web add @windypro-rourou/dsh-logcat # resolves through the registry above ``` Maintainer publish (both scripts **auto-load credentials**: they use the environment when present, otherwise they dot-source the portable loader `$env:DSH_HOME\env\load-secrets.ps1`; credentials only live in the process environment and are never written to disk): ```powershell ./publish-npm.ps1 # → npmjs.org (primary, needs NPM_TOKEN) ./publish-github-packages.ps1 # → GitHub Packages (needs GH_TOKEN with write:packages) ``` ## Release policy (main / latest first) - **`main` branch + npm `latest` tag**: stable releases — the main update channel. Features ship to main/latest as soon as a batch is ready. - **`preview` branch + npm `preview` tag**: optional early-access channel (frequent small iterations). If there are no experimental features since the last stable release, the preview tag simply stays at an older preview version — ignore it. - The version self-check notifies per channel only (stable users see `latest`, preview users see `preview`; no cross-talk). For development you can also link the source directly into the web profile and append the patch row in `~/.dsh/profiles/web/cordis.patch.yml`: ```bash pnpm --dir "%USERPROFILE%\.dsh\profiles\web" add link: # then add to ~/.dsh/profiles/web/cordis.patch.yml: # - insert: # - id: logcat # name: '@windypro-rourou/dsh-logcat' # The patch file is hot-watched by a running GUI; restart it if it does not take effect. ``` > Note: every method inserts the same `logcat` row into the profile tree — never use two of them at once, or the next boot fails on a duplicate plugin id. Dependency resolution: `ws` / `react` / `react-dom` / `@deepseek-ai/*` resolve through junctions in this directory's `node_modules` pointing at the host's actually-loaded packages (single instance). Re-point the junctions when the host upgrades its dependencies. ## Limitations - The device must have USB debugging enabled and be authorized on this machine (`unauthorized` state is surfaced). - logcat output may contain sensitive information; `/api/dsh-logcat/*` routes are loopback-only. - `adb shell` commands consume real device resources — confirm before running destructive ones. ## Files - `lib/index.js` — host half: adb engine, polling, logcat child processes, routes, WebSocket, agent tools. - `lib/client.js` — browser half: sidebar entry + Logcat panel (React, no build step). - `cordis.patch.yml` — profile bundle patch (applied automatically).