release: v1.1.0 - fix files.list/stat, add SYNC-based list, reboot, port forwarding, and APK install
DroidSock v1.1.0 Changelog
Release Status: Minor release
Overview
v1.1.0 fixes the list/stat regression from v1.0.0 - previously written up as a planned v1.0.1, which was never actually published; this release supersedes that draft - and adds four new capabilities on top of the fix: a binary-safe SYNC-based list() with automatic shell fallback, real ADB reboot: service support, TCP port forwarding, and local APK installation.
🐛 Fixed
files.list(remotePath)andfiles.stat(remotePath)now actually work. Both threw in v1.0.0 due to a leftover reference to a module (../src.backup/sync.mjs) that never existed in this repository - not something removed during cleanup, but a dangling reference present since the very first commit.stat()shells out tostatand returns the raw output;list()goes further than a plain fix - see below.
✨ Added
Binary-safe directory listing (files.listSync / files.listShell)
files.list(remotePath) now prefers the ADB SYNC sub-protocol's LIST command - binary-safe, immune to the shell-metacharacter/whitespace edge cases ls -la text parsing can hit - and falls back to the previous shell-based approach (now files.listShell()) only when the SYNC service itself isn't usable. A real LIST failure (e.g. "No such file or directory") is never masked by the fallback; it propagates as-is. listSync() and listShell() also remain directly callable to force one path. listSync() is EXPERIMENTAL - implemented from the public protocol spec (AOSP SYNC.TXT), covered by mocked tests, not yet validated against a real device. See #1.
Real binary file transfer (files.push / files.pull)
Implements the ADB SYNC sub-protocol's SEND/RECV commands for actual binary file transfer, built directly from the public protocol spec and cross-checked against Google's own reference client (google/python-adb). Files are chunked at the protocol's 64KB DATA ceiling; options.onProgress reports bytes transferred; options.mode sets the pushed file's permissions. EXPERIMENTAL - covered by mocked unit tests (including a chunk-boundary-split reassembly case) but not yet exercised against a real device. See #1.
Real reboot service (device.reboot)
device.reboot(mode) opens the real ADB reboot: service directly - a distinct top-level service like shell:/sync:, not a shell command - supporting the standard mode variants ("", "bootloader", "recovery", "sideload", "sideload-auto-reboot") plus any vendor-specific target as a raw string. Convenience shortcuts device.rebootBootloader(), device.rebootRecovery(), and device.rebootSideload() are also available. The existing device.shell("reboot") path stays available unchanged as a fallback for a plain reboot - bootloader/recovery/sideload were never reachable through it, since they aren't real shell commands. EXPERIMENTAL - built from the protocol spec, not yet validated against a real device. See #1.
Port forwarding (device.forward)
device.forward(devicePort, options) - the adb forward tcp:<localPort> tcp:<devicePort> equivalent - listens on a local TCP port and, for each accepted connection, opens an ADB stream to the device's tcp:<devicePort> service, bridging bytes bidirectionally until either side closes. Returns { localPort, close() }; options.localPort (default 0, letting the OS pick a free port), options.host, and options.onError are all optional. Only the forward direction (host -> device) is implemented - reverse (device -> host) needs inbound-OPEN handling this doesn't have yet; tracked in #4. EXPERIMENTAL - built from the protocol spec, not yet validated against a real device. See #1.
Local APK install (device.install)
device.install(localPath, options) - the adb install <local.apk> equivalent - pushes a local APK to a device temp directory, runs pm install, then removes the temp file regardless of outcome. Pure composition of the existing push/shell-command primitives, no new protocol work of its own. options.flags passes flags to pm install (e.g. ["-r"] to reinstall), options.remoteDir overrides the push destination (default /data/local/tmp), options.onProgress reports push progress. Only the classic push-then-install flow is implemented - the modern streaming install path (exec:cmd package install, no on-device file at all) is separate follow-up work; tracked in #7. EXPERIMENTAL - depends on the experimental push(), so it's unvalidated against a real device too even without new protocol work of its own. See #1.
📋 Known Limitations
files.push/files.pull/files.listSync/device.reboot/device.forward/device.installare all markedEXPERIMENTALin code and have not been exercised against a real device. #1 tracks the real-device session needed to validate the protocol stack they depend on.- Port forwarding is outbound-only (#4 tracks
reverse); APK install is push-then-install only (#7 tracks the streaming path); SYNCLIST/SEND/RECVare the legacy 32-bit variants only (#8 tracks the 64-bit_V2variants needed for files/listings beyond ~2.14GB).