adb-ready 1.0.2 → 1.0.4
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/CHANGELOG.md +30 -1
- package/README.md +10 -0
- package/dist/cli.js +56 -13
- package/docs/apps-and-evidence.md +6 -0
- package/docs/targets-and-wireless.md +6 -0
- package/package.json +2 -1
- package/schema/agent-tools-v1.json +1 -1
- package/skills/adb-ready/SKILL.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,33 @@ deprecation policy in [Stability and versioning](./docs/stability.md).
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [1.0.4] - 2026-10-04
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Add GitHub Sponsors repository metadata and npm funding metadata so users can
|
|
17
|
+
discover the supported funding path from GitHub and `npm fund`.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Surface a dedicated Sponsor action directly below the technical badges on
|
|
22
|
+
the GitHub and npm landing pages while retaining the detailed section below.
|
|
23
|
+
|
|
24
|
+
## [1.0.3] - 2026-10-01
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Allow large APK and split-APK installs up to five minutes by default instead
|
|
29
|
+
of inheriting the five-second probe timeout, while preserving an explicit
|
|
30
|
+
`--timeout` override.
|
|
31
|
+
- Recover the same remembered wireless target for `--last` after a local ADB
|
|
32
|
+
server restart or Wireless debugging port rotation, verifying hardware
|
|
33
|
+
identity before accepting a newly advertised endpoint.
|
|
34
|
+
- Explain when foreground launch verification is blocked by the Android lock
|
|
35
|
+
screen instead of returning only a generic launch-postcondition failure.
|
|
36
|
+
- Isolate runtime-smoke remembered-target state so repository verification can
|
|
37
|
+
never overwrite a developer's real `--last` target with a fixture device.
|
|
38
|
+
|
|
12
39
|
## [1.0.2] - 2026-09-30
|
|
13
40
|
|
|
14
41
|
### Changed
|
|
@@ -551,7 +578,9 @@ deprecation policy in [Stability and versioning](./docs/stability.md).
|
|
|
551
578
|
explainable configuration precedence.
|
|
552
579
|
- Human, plain, JSON, and NDJSON output across Node, Bun, and Deno entrypoints.
|
|
553
580
|
|
|
554
|
-
[Unreleased]: https://github.com/Adam014/adb-ready/compare/v1.0.
|
|
581
|
+
[Unreleased]: https://github.com/Adam014/adb-ready/compare/v1.0.4...HEAD
|
|
582
|
+
[1.0.4]: https://github.com/Adam014/adb-ready/compare/v1.0.3...v1.0.4
|
|
583
|
+
[1.0.3]: https://github.com/Adam014/adb-ready/compare/v1.0.2...v1.0.3
|
|
555
584
|
[1.0.2]: https://github.com/Adam014/adb-ready/compare/v1.0.1...v1.0.2
|
|
556
585
|
[1.0.1]: https://github.com/Adam014/adb-ready/compare/v1.0.0...v1.0.1
|
|
557
586
|
[1.0.0]: https://github.com/Adam014/adb-ready/compare/v0.9.0...v1.0.0
|
package/README.md
CHANGED
|
@@ -21,6 +21,8 @@ verified evidence—for developers, agents, and CI.
|
|
|
21
21
|
[](./docs/dev-sessions.md)
|
|
22
22
|
[](./COMPATIBILITY.md)
|
|
23
23
|
|
|
24
|
+
[](https://github.com/sponsors/Adam014)
|
|
25
|
+
|
|
24
26
|
[Quick start](#quick-start) · [What it does](#what-adb-ready-does) ·
|
|
25
27
|
[AI agents](#with-a-coding-agent) · [CI](#in-ci-and-automation) ·
|
|
26
28
|
[Documentation](#documentation)
|
|
@@ -307,6 +309,14 @@ Android transport backend.
|
|
|
307
309
|
Run `adb-ready --help` for the complete command list or
|
|
308
310
|
`adb-ready help COMMAND` for focused options.
|
|
309
311
|
|
|
312
|
+
## Sponsors
|
|
313
|
+
|
|
314
|
+
ADB Ready is independently developed and maintained. Sponsorship helps fund
|
|
315
|
+
compatibility testing, real-device validation, CI, documentation, and reliable
|
|
316
|
+
releases.
|
|
317
|
+
|
|
318
|
+
[Sponsor ADB Ready and my open-source work →](https://github.com/sponsors/Adam014)
|
|
319
|
+
|
|
310
320
|
## Project
|
|
311
321
|
|
|
312
322
|
[Changelog](./CHANGELOG.md) · [Contributing](./CONTRIBUTING.md) ·
|
package/dist/cli.js
CHANGED
|
@@ -3304,7 +3304,7 @@ import process15 from "node:process";
|
|
|
3304
3304
|
// package.json
|
|
3305
3305
|
var package_default = {
|
|
3306
3306
|
name: "adb-ready",
|
|
3307
|
-
version: "1.0.
|
|
3307
|
+
version: "1.0.4",
|
|
3308
3308
|
description: "Agent-ready Android CLI for reliable ADB sessions, app automation, and verified evidence.",
|
|
3309
3309
|
private: false,
|
|
3310
3310
|
type: "module",
|
|
@@ -3363,6 +3363,7 @@ var package_default = {
|
|
|
3363
3363
|
url: "https://github.com/Adam014/adb-ready/issues"
|
|
3364
3364
|
},
|
|
3365
3365
|
homepage: "https://github.com/Adam014/adb-ready#readme",
|
|
3366
|
+
funding: "https://github.com/sponsors/Adam014",
|
|
3366
3367
|
author: {
|
|
3367
3368
|
name: "Adam Stádník",
|
|
3368
3369
|
email: "adam.stadnik@seznam.cz",
|
|
@@ -45756,6 +45757,7 @@ async function runDev(options, config4 = {}, dependencies = {}, signal) {
|
|
|
45756
45757
|
}
|
|
45757
45758
|
|
|
45758
45759
|
// src/app/app-commands.ts
|
|
45760
|
+
var DEFAULT_APP_INSTALL_TIMEOUT_MS = 5 * 60000;
|
|
45759
45761
|
function plannedArgs(ready, config4, args) {
|
|
45760
45762
|
return [
|
|
45761
45763
|
...config4.adbHost === undefined ? [] : ["-H", config4.adbHost],
|
|
@@ -46030,7 +46032,9 @@ async function launchApp(ready, applicationId, explicitActivity, commandId, prob
|
|
|
46030
46032
|
}
|
|
46031
46033
|
const activeValue = active?.value;
|
|
46032
46034
|
if (active === undefined || !succeeded(active.process) || activeValue?.applicationId !== applicationId) {
|
|
46033
|
-
|
|
46035
|
+
const lockState = await ready.client.targetCommand(ready.target, "app-launch-lock-state", "Reading Android lock-screen state after launch", ["shell", "dumpsys", "window", "policy"], parseAndroidLockState, signal, { maxBufferBytes: 4 * 1024 * 1024 });
|
|
46036
|
+
const targetIsLocked = succeeded(lockState.process) && lockState.value === "locked";
|
|
46037
|
+
problems.push(problem("APP_LAUNCH_UNVERIFIED", "app.lifecycle", `${applicationId} did not become the foreground app.`, targetIsLocked ? "ADB launched the app, but Android is locked. Unlock the selected target, then retry." : "ADB accepted the launch request, but the foreground postcondition was not met.", commandId, [
|
|
46034
46038
|
{
|
|
46035
46039
|
source: "adb.activity",
|
|
46036
46040
|
field: "foreground",
|
|
@@ -46038,7 +46042,14 @@ async function launchApp(ready, applicationId, explicitActivity, commandId, prob
|
|
|
46038
46042
|
applicationId: activeValue.applicationId,
|
|
46039
46043
|
activity: activeValue.activity
|
|
46040
46044
|
}
|
|
46041
|
-
}
|
|
46045
|
+
},
|
|
46046
|
+
...targetIsLocked ? [
|
|
46047
|
+
{
|
|
46048
|
+
source: "adb.window-policy",
|
|
46049
|
+
field: "lockState",
|
|
46050
|
+
value: "locked"
|
|
46051
|
+
}
|
|
46052
|
+
] : []
|
|
46042
46053
|
]));
|
|
46043
46054
|
return;
|
|
46044
46055
|
}
|
|
@@ -46148,7 +46159,9 @@ async function runApp(request, config4 = {}, dependencies = {}, signal) {
|
|
|
46148
46159
|
...request.replace === true ? ["-r"] : [],
|
|
46149
46160
|
...request.grantRuntimePermissions === true ? ["-g"] : [],
|
|
46150
46161
|
...artifacts
|
|
46151
|
-
], (output) => output.trim(), signal
|
|
46162
|
+
], (output) => output.trim(), signal, {
|
|
46163
|
+
...config4.timeoutWasDefault === true ? { timeoutMs: DEFAULT_APP_INSTALL_TIMEOUT_MS } : config4.timeoutMs === undefined ? {} : { timeoutMs: config4.timeoutMs }
|
|
46164
|
+
});
|
|
46152
46165
|
if (!succeeded(installed.process) || !/\bSuccess\b/u.test(installed.value)) {
|
|
46153
46166
|
problems.push(operationProblem("app-install", installed, current.commandId));
|
|
46154
46167
|
return finish2(current, null, problems);
|
|
@@ -50569,6 +50582,7 @@ function commandConfig(loaded, bound, selector, transportId) {
|
|
|
50569
50582
|
...values.adbHost === undefined ? {} : { adbHost: values.adbHost },
|
|
50570
50583
|
...values.adbPort === undefined ? {} : { adbPort: values.adbPort },
|
|
50571
50584
|
timeoutMs: values.timeoutMs,
|
|
50585
|
+
timeoutWasDefault: loaded.provenance.timeoutMs?.source === "default",
|
|
50572
50586
|
uiTimeoutMs: loaded.provenance.timeoutMs?.source === "default" ? 15000 : values.timeoutMs,
|
|
50573
50587
|
...values.targetAliases === undefined ? {} : { targetAliases: values.targetAliases },
|
|
50574
50588
|
...bound?.transportId !== undefined ? { targetTransportId: bound.transportId } : bound !== undefined ? { targetSelector: bound.serial } : transportId !== undefined ? { targetTransportId: transportId } : selector === undefined ? {} : { targetSelector: selector }
|
|
@@ -60772,6 +60786,7 @@ async function runCliInternal(argv, io, dependencies = {}, signal) {
|
|
|
60772
60786
|
...values2.adbHost === undefined ? {} : { adbHost: values2.adbHost },
|
|
60773
60787
|
...values2.adbPort === undefined ? {} : { adbPort: values2.adbPort },
|
|
60774
60788
|
timeoutMs: values2.timeoutMs,
|
|
60789
|
+
timeoutWasDefault: loaded.config.provenance.timeoutMs?.source === "default",
|
|
60775
60790
|
uiTimeoutMs: loaded.config.provenance.timeoutMs?.source === "default" ? 15000 : values2.timeoutMs,
|
|
60776
60791
|
...options.device === undefined ? {} : { targetSelector: options.device },
|
|
60777
60792
|
...options.transportId === undefined ? {} : { targetTransportId: options.transportId },
|
|
@@ -60922,16 +60937,42 @@ async function runCliInternal(argv, io, dependencies = {}, signal) {
|
|
|
60922
60937
|
}
|
|
60923
60938
|
const offlineDevPlan = (options.command === "dev" || options.command === "run") && options.dryRun;
|
|
60924
60939
|
let preparedSelection;
|
|
60925
|
-
if (options.command === "app" && (options.appAction !== "resolve" || options.select) || options.command === "apps" || options.command === "capture" || (options.command === "dev" || options.command === "run") && !offlineDevPlan || options.command === "inspect" || options.command === "logs" || options.command === "open" || options.command === "ui" || options.command === "ports") {
|
|
60940
|
+
if (options.command === "app" && (options.appAction !== "resolve" || options.select) || options.command === "apps" || options.command === "capture" || options.command === "devices" && options.remembered || (options.command === "dev" || options.command === "run") && !offlineDevPlan || options.command === "inspect" || options.command === "logs" || options.command === "open" || options.command === "ui" || options.command === "ports") {
|
|
60926
60941
|
let inventory = await runDevices(config4, commandDependencies, signal);
|
|
60927
60942
|
let selectable = inventory.result.data?.targets.filter((target) => target.transports.some(({ stable, state }) => stable && state === "device")) ?? [];
|
|
60928
|
-
if ((options.command === "dev" || options.command === "run") && selectable.length === 0 && !options.dryRun) {
|
|
60929
|
-
|
|
60943
|
+
if ((options.command === "dev" || options.command === "run" || options.remembered) && selectable.length === 0 && !options.dryRun) {
|
|
60944
|
+
let acquisition = planTargetAcquisition({
|
|
60930
60945
|
...config4.targetSelector === undefined ? {} : { selector: config4.targetSelector },
|
|
60931
60946
|
...values2.targetAliases === undefined ? {} : { aliases: values2.targetAliases },
|
|
60932
60947
|
...lastTarget === undefined ? {} : { remembered: lastTarget },
|
|
60933
60948
|
services: inventory.result.data?.discovery.mdns.services ?? []
|
|
60934
60949
|
});
|
|
60950
|
+
if (acquisition.kind === "ambiguous" && options.remembered && lastTarget?.hardwareSerial !== undefined) {
|
|
60951
|
+
const services = inventory.result.data?.discovery.mdns.services ?? [];
|
|
60952
|
+
const endpoints = [
|
|
60953
|
+
...new Set(services.flatMap((service) => (service.serviceType === "legacy" || service.serviceType === "connect" && service.knownDevice !== false) && service.endpoint.version === 4 ? [service.endpoint.serial] : []))
|
|
60954
|
+
];
|
|
60955
|
+
for (const candidate of endpoints) {
|
|
60956
|
+
const verified = await runConnect(candidate, {
|
|
60957
|
+
...config4,
|
|
60958
|
+
endpointWasDiscovered: true,
|
|
60959
|
+
discoveredEndpointCandidates: [candidate]
|
|
60960
|
+
}, commandDependencies, signal);
|
|
60961
|
+
const data = verified.result.data;
|
|
60962
|
+
if (verified.result.ok && data !== null && "serial" in data && data.hardwareSerial === lastTarget.hardwareSerial) {
|
|
60963
|
+
acquisition = {
|
|
60964
|
+
kind: "connect",
|
|
60965
|
+
endpoint: data.serial,
|
|
60966
|
+
candidates: [data.serial],
|
|
60967
|
+
discovered: true,
|
|
60968
|
+
reason: "remembered-identity"
|
|
60969
|
+
};
|
|
60970
|
+
break;
|
|
60971
|
+
}
|
|
60972
|
+
if (signal?.aborted === true)
|
|
60973
|
+
break;
|
|
60974
|
+
}
|
|
60975
|
+
}
|
|
60935
60976
|
if (acquisition.kind === "connect") {
|
|
60936
60977
|
const connected = await runConnect(acquisition.endpoint, acquisition.discovered ? {
|
|
60937
60978
|
...config4,
|
|
@@ -60974,11 +61015,13 @@ async function runCliInternal(argv, io, dependencies = {}, signal) {
|
|
|
60974
61015
|
} else {
|
|
60975
61016
|
preparedSelection = selected2.result.data.selected;
|
|
60976
61017
|
const transport = preparedSelection.transport;
|
|
60977
|
-
if (options.
|
|
60978
|
-
|
|
60979
|
-
|
|
60980
|
-
|
|
60981
|
-
|
|
61018
|
+
if (options.command !== "devices") {
|
|
61019
|
+
if (options.avdName !== undefined || transport.transportId === undefined) {
|
|
61020
|
+
config4.targetSelector = transport.serial;
|
|
61021
|
+
delete config4.targetTransportId;
|
|
61022
|
+
} else {
|
|
61023
|
+
config4.targetTransportId = transport.transportId;
|
|
61024
|
+
}
|
|
60982
61025
|
}
|
|
60983
61026
|
}
|
|
60984
61027
|
}
|
|
@@ -61628,4 +61671,4 @@ try {
|
|
|
61628
61671
|
process16.removeListener("SIGTERM", abort);
|
|
61629
61672
|
}
|
|
61630
61673
|
|
|
61631
|
-
//# debugId=
|
|
61674
|
+
//# debugId=E6D58EBBDFCFA90A64756E2164756E21
|
|
@@ -66,8 +66,14 @@ created them, then pass those `.apk` files explicitly.
|
|
|
66
66
|
|
|
67
67
|
- Installation accepts one ordinary APK or an explicit complete split APK set.
|
|
68
68
|
`.apks`, `.aab`, and implicit downloads are not accepted yet.
|
|
69
|
+
- APK installation allows up to five minutes by default so large artifacts can
|
|
70
|
+
cross a real wireless ADB connection. An explicit global `--timeout` still
|
|
71
|
+
overrides that installation default.
|
|
69
72
|
- Install, launch, stop, restart, and explicit deep-link handlers are checked
|
|
70
73
|
after ADB accepts the request.
|
|
74
|
+
- When Android accepts a launch but the selected target remains behind its lock
|
|
75
|
+
screen, ADB Ready reports the locked state and asks you to unlock and retry;
|
|
76
|
+
it never attempts to bypass the device lock.
|
|
71
77
|
- `restart` is a verified stop followed by a verified launch.
|
|
72
78
|
- Add `--activity .MainActivity` only when Android cannot resolve a launchable
|
|
73
79
|
activity.
|
|
@@ -31,6 +31,12 @@ adb-ready devices --last
|
|
|
31
31
|
`--device` accepts an exact ADB serial or a configured alias. `--transport-id`
|
|
32
32
|
disambiguates duplicate transports. `--last` resolves the last verified target
|
|
33
33
|
identity; it does not silently choose an unrelated visible device.
|
|
34
|
+
If a local ADB server restart drops the stable wireless transport, `--last`
|
|
35
|
+
first reconnects the exact remembered endpoint when discovery still advertises
|
|
36
|
+
it, then verifies the target again. If Wireless debugging rotates the endpoint,
|
|
37
|
+
ADB Ready probes only already-known connect services and accepts a replacement
|
|
38
|
+
only after its hardware identity matches the remembered target. It never
|
|
39
|
+
substitutes an unrelated device.
|
|
34
40
|
|
|
35
41
|
An exact serial, serial-backed alias, or transport ID also bounds target-side
|
|
36
42
|
identity inspection to that transport. ADB Ready does not query unrelated
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adb-ready",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Agent-ready Android CLI for reliable ADB sessions, app automation, and verified evidence.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"url": "https://github.com/Adam014/adb-ready/issues"
|
|
60
60
|
},
|
|
61
61
|
"homepage": "https://github.com/Adam014/adb-ready#readme",
|
|
62
|
+
"funding": "https://github.com/sponsors/Adam014",
|
|
62
63
|
"author": {
|
|
63
64
|
"name": "Adam Stádník",
|
|
64
65
|
"email": "adam.stadnik@seznam.cz",
|
|
@@ -3,7 +3,7 @@ name: adb-ready
|
|
|
3
3
|
description: Prepare, automate, debug, and verify one Android target through the ADB Ready MCP server. Use for Android development sessions, app lifecycle work, UI automation, failure evidence, screenshots, and agent-driven device checks.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- Generated by adb-ready 1.0.
|
|
6
|
+
<!-- Generated by adb-ready 1.0.4; do not edit. -->
|
|
7
7
|
|
|
8
8
|
# ADB Ready
|
|
9
9
|
|
|
@@ -51,4 +51,4 @@ Use the typed `adb-ready` MCP tools. Do not replace them with generic shell or r
|
|
|
51
51
|
- `session`: 12 tools
|
|
52
52
|
- `ui`: 29 tools
|
|
53
53
|
|
|
54
|
-
Contract: ADB Ready 1.0.
|
|
54
|
+
Contract: ADB Ready 1.0.4, agent schema v1.
|