adb-ready 1.0.2 → 1.0.3
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 +17 -1
- package/dist/cli.js +55 -13
- package/docs/apps-and-evidence.md +6 -0
- package/docs/targets-and-wireless.md +6 -0
- package/package.json +1 -1
- package/schema/agent-tools-v1.json +1 -1
- package/skills/adb-ready/SKILL.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,21 @@ deprecation policy in [Stability and versioning](./docs/stability.md).
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [1.0.3] - 2026-10-01
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Allow large APK and split-APK installs up to five minutes by default instead
|
|
17
|
+
of inheriting the five-second probe timeout, while preserving an explicit
|
|
18
|
+
`--timeout` override.
|
|
19
|
+
- Recover the same remembered wireless target for `--last` after a local ADB
|
|
20
|
+
server restart or Wireless debugging port rotation, verifying hardware
|
|
21
|
+
identity before accepting a newly advertised endpoint.
|
|
22
|
+
- Explain when foreground launch verification is blocked by the Android lock
|
|
23
|
+
screen instead of returning only a generic launch-postcondition failure.
|
|
24
|
+
- Isolate runtime-smoke remembered-target state so repository verification can
|
|
25
|
+
never overwrite a developer's real `--last` target with a fixture device.
|
|
26
|
+
|
|
12
27
|
## [1.0.2] - 2026-09-30
|
|
13
28
|
|
|
14
29
|
### Changed
|
|
@@ -551,7 +566,8 @@ deprecation policy in [Stability and versioning](./docs/stability.md).
|
|
|
551
566
|
explainable configuration precedence.
|
|
552
567
|
- Human, plain, JSON, and NDJSON output across Node, Bun, and Deno entrypoints.
|
|
553
568
|
|
|
554
|
-
[Unreleased]: https://github.com/Adam014/adb-ready/compare/v1.0.
|
|
569
|
+
[Unreleased]: https://github.com/Adam014/adb-ready/compare/v1.0.3...HEAD
|
|
570
|
+
[1.0.3]: https://github.com/Adam014/adb-ready/compare/v1.0.2...v1.0.3
|
|
555
571
|
[1.0.2]: https://github.com/Adam014/adb-ready/compare/v1.0.1...v1.0.2
|
|
556
572
|
[1.0.1]: https://github.com/Adam014/adb-ready/compare/v1.0.0...v1.0.1
|
|
557
573
|
[1.0.0]: https://github.com/Adam014/adb-ready/compare/v0.9.0...v1.0.0
|
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.3",
|
|
3308
3308
|
description: "Agent-ready Android CLI for reliable ADB sessions, app automation, and verified evidence.",
|
|
3309
3309
|
private: false,
|
|
3310
3310
|
type: "module",
|
|
@@ -45756,6 +45756,7 @@ async function runDev(options, config4 = {}, dependencies = {}, signal) {
|
|
|
45756
45756
|
}
|
|
45757
45757
|
|
|
45758
45758
|
// src/app/app-commands.ts
|
|
45759
|
+
var DEFAULT_APP_INSTALL_TIMEOUT_MS = 5 * 60000;
|
|
45759
45760
|
function plannedArgs(ready, config4, args) {
|
|
45760
45761
|
return [
|
|
45761
45762
|
...config4.adbHost === undefined ? [] : ["-H", config4.adbHost],
|
|
@@ -46030,7 +46031,9 @@ async function launchApp(ready, applicationId, explicitActivity, commandId, prob
|
|
|
46030
46031
|
}
|
|
46031
46032
|
const activeValue = active?.value;
|
|
46032
46033
|
if (active === undefined || !succeeded(active.process) || activeValue?.applicationId !== applicationId) {
|
|
46033
|
-
|
|
46034
|
+
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 });
|
|
46035
|
+
const targetIsLocked = succeeded(lockState.process) && lockState.value === "locked";
|
|
46036
|
+
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
46037
|
{
|
|
46035
46038
|
source: "adb.activity",
|
|
46036
46039
|
field: "foreground",
|
|
@@ -46038,7 +46041,14 @@ async function launchApp(ready, applicationId, explicitActivity, commandId, prob
|
|
|
46038
46041
|
applicationId: activeValue.applicationId,
|
|
46039
46042
|
activity: activeValue.activity
|
|
46040
46043
|
}
|
|
46041
|
-
}
|
|
46044
|
+
},
|
|
46045
|
+
...targetIsLocked ? [
|
|
46046
|
+
{
|
|
46047
|
+
source: "adb.window-policy",
|
|
46048
|
+
field: "lockState",
|
|
46049
|
+
value: "locked"
|
|
46050
|
+
}
|
|
46051
|
+
] : []
|
|
46042
46052
|
]));
|
|
46043
46053
|
return;
|
|
46044
46054
|
}
|
|
@@ -46148,7 +46158,9 @@ async function runApp(request, config4 = {}, dependencies = {}, signal) {
|
|
|
46148
46158
|
...request.replace === true ? ["-r"] : [],
|
|
46149
46159
|
...request.grantRuntimePermissions === true ? ["-g"] : [],
|
|
46150
46160
|
...artifacts
|
|
46151
|
-
], (output) => output.trim(), signal
|
|
46161
|
+
], (output) => output.trim(), signal, {
|
|
46162
|
+
...config4.timeoutWasDefault === true ? { timeoutMs: DEFAULT_APP_INSTALL_TIMEOUT_MS } : config4.timeoutMs === undefined ? {} : { timeoutMs: config4.timeoutMs }
|
|
46163
|
+
});
|
|
46152
46164
|
if (!succeeded(installed.process) || !/\bSuccess\b/u.test(installed.value)) {
|
|
46153
46165
|
problems.push(operationProblem("app-install", installed, current.commandId));
|
|
46154
46166
|
return finish2(current, null, problems);
|
|
@@ -50569,6 +50581,7 @@ function commandConfig(loaded, bound, selector, transportId) {
|
|
|
50569
50581
|
...values.adbHost === undefined ? {} : { adbHost: values.adbHost },
|
|
50570
50582
|
...values.adbPort === undefined ? {} : { adbPort: values.adbPort },
|
|
50571
50583
|
timeoutMs: values.timeoutMs,
|
|
50584
|
+
timeoutWasDefault: loaded.provenance.timeoutMs?.source === "default",
|
|
50572
50585
|
uiTimeoutMs: loaded.provenance.timeoutMs?.source === "default" ? 15000 : values.timeoutMs,
|
|
50573
50586
|
...values.targetAliases === undefined ? {} : { targetAliases: values.targetAliases },
|
|
50574
50587
|
...bound?.transportId !== undefined ? { targetTransportId: bound.transportId } : bound !== undefined ? { targetSelector: bound.serial } : transportId !== undefined ? { targetTransportId: transportId } : selector === undefined ? {} : { targetSelector: selector }
|
|
@@ -60772,6 +60785,7 @@ async function runCliInternal(argv, io, dependencies = {}, signal) {
|
|
|
60772
60785
|
...values2.adbHost === undefined ? {} : { adbHost: values2.adbHost },
|
|
60773
60786
|
...values2.adbPort === undefined ? {} : { adbPort: values2.adbPort },
|
|
60774
60787
|
timeoutMs: values2.timeoutMs,
|
|
60788
|
+
timeoutWasDefault: loaded.config.provenance.timeoutMs?.source === "default",
|
|
60775
60789
|
uiTimeoutMs: loaded.config.provenance.timeoutMs?.source === "default" ? 15000 : values2.timeoutMs,
|
|
60776
60790
|
...options.device === undefined ? {} : { targetSelector: options.device },
|
|
60777
60791
|
...options.transportId === undefined ? {} : { targetTransportId: options.transportId },
|
|
@@ -60922,16 +60936,42 @@ async function runCliInternal(argv, io, dependencies = {}, signal) {
|
|
|
60922
60936
|
}
|
|
60923
60937
|
const offlineDevPlan = (options.command === "dev" || options.command === "run") && options.dryRun;
|
|
60924
60938
|
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") {
|
|
60939
|
+
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
60940
|
let inventory = await runDevices(config4, commandDependencies, signal);
|
|
60927
60941
|
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
|
-
|
|
60942
|
+
if ((options.command === "dev" || options.command === "run" || options.remembered) && selectable.length === 0 && !options.dryRun) {
|
|
60943
|
+
let acquisition = planTargetAcquisition({
|
|
60930
60944
|
...config4.targetSelector === undefined ? {} : { selector: config4.targetSelector },
|
|
60931
60945
|
...values2.targetAliases === undefined ? {} : { aliases: values2.targetAliases },
|
|
60932
60946
|
...lastTarget === undefined ? {} : { remembered: lastTarget },
|
|
60933
60947
|
services: inventory.result.data?.discovery.mdns.services ?? []
|
|
60934
60948
|
});
|
|
60949
|
+
if (acquisition.kind === "ambiguous" && options.remembered && lastTarget?.hardwareSerial !== undefined) {
|
|
60950
|
+
const services = inventory.result.data?.discovery.mdns.services ?? [];
|
|
60951
|
+
const endpoints = [
|
|
60952
|
+
...new Set(services.flatMap((service) => (service.serviceType === "legacy" || service.serviceType === "connect" && service.knownDevice !== false) && service.endpoint.version === 4 ? [service.endpoint.serial] : []))
|
|
60953
|
+
];
|
|
60954
|
+
for (const candidate of endpoints) {
|
|
60955
|
+
const verified = await runConnect(candidate, {
|
|
60956
|
+
...config4,
|
|
60957
|
+
endpointWasDiscovered: true,
|
|
60958
|
+
discoveredEndpointCandidates: [candidate]
|
|
60959
|
+
}, commandDependencies, signal);
|
|
60960
|
+
const data = verified.result.data;
|
|
60961
|
+
if (verified.result.ok && data !== null && "serial" in data && data.hardwareSerial === lastTarget.hardwareSerial) {
|
|
60962
|
+
acquisition = {
|
|
60963
|
+
kind: "connect",
|
|
60964
|
+
endpoint: data.serial,
|
|
60965
|
+
candidates: [data.serial],
|
|
60966
|
+
discovered: true,
|
|
60967
|
+
reason: "remembered-identity"
|
|
60968
|
+
};
|
|
60969
|
+
break;
|
|
60970
|
+
}
|
|
60971
|
+
if (signal?.aborted === true)
|
|
60972
|
+
break;
|
|
60973
|
+
}
|
|
60974
|
+
}
|
|
60935
60975
|
if (acquisition.kind === "connect") {
|
|
60936
60976
|
const connected = await runConnect(acquisition.endpoint, acquisition.discovered ? {
|
|
60937
60977
|
...config4,
|
|
@@ -60974,11 +61014,13 @@ async function runCliInternal(argv, io, dependencies = {}, signal) {
|
|
|
60974
61014
|
} else {
|
|
60975
61015
|
preparedSelection = selected2.result.data.selected;
|
|
60976
61016
|
const transport = preparedSelection.transport;
|
|
60977
|
-
if (options.
|
|
60978
|
-
|
|
60979
|
-
|
|
60980
|
-
|
|
60981
|
-
|
|
61017
|
+
if (options.command !== "devices") {
|
|
61018
|
+
if (options.avdName !== undefined || transport.transportId === undefined) {
|
|
61019
|
+
config4.targetSelector = transport.serial;
|
|
61020
|
+
delete config4.targetTransportId;
|
|
61021
|
+
} else {
|
|
61022
|
+
config4.targetTransportId = transport.transportId;
|
|
61023
|
+
}
|
|
60982
61024
|
}
|
|
60983
61025
|
}
|
|
60984
61026
|
}
|
|
@@ -61628,4 +61670,4 @@ try {
|
|
|
61628
61670
|
process16.removeListener("SIGTERM", abort);
|
|
61629
61671
|
}
|
|
61630
61672
|
|
|
61631
|
-
//# debugId=
|
|
61673
|
+
//# debugId=2A90A8EB1D4EFD3D64756E2164756E21
|
|
@@ -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
|
@@ -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.3; 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.3, agent schema v1.
|