mirror of
https://github.com/morluto/rea.git
synced 2026-11-02 21:06:04 +00:00
* feat(adb): acquire installed APK sets from connected Android devices Add a read-only ADB device-acquisition family over the caller's adb binary: inspect_adb_client, list_adb_devices, inspect_adb_device, list_adb_packages, and pull_adb_package, exposed through the CLI and MCP with shared admission, Evidence provenance (binary, version, serial, transport), and fixed argv vectors that never reach a shell. pull_adb_package acquires the complete split set reported by pm path into a package-named directory that must not exist, digesting every file with SHA-256 and keeping per-file failure reasons and partial coverage. Device kind is reported as an inference with its basis; getprop whitelisting keeps build identity bounded. REA installs nothing and never manages devices or emulators; the first device query may start the caller's local adb server, recorded when observed. Verified against adb 34.0.5 on Linux with an Android 14 (API 34) x86_64 emulator: full 224-package inventory, a real two-APK split set pulled with byte-exact digests, and a 73.9 MB system-package pull. npm run verify:adb records the lane; parser goldens come from the same device. READMEs carry the new row in every supported language. * feat(adb): complete the adb surface with observation, transfer, and lifecycle tools Extend the ADB family from five to twenty-four operations, all through fixed adb argument vectors with no shell exposure: Observation: read_adb_logcat (bounded dump, count/buffer/pid), inspect_adb_package (dumpsys package projection), list_adb_processes (ps -A), list_adb_directory (ls -la with kinds and symlink targets), list_adb_features (hex GL versions), list_adb_services (AIDL slash-suffixed names), inspect_adb_display (wm size and density), inspect_adb_window (focus declarations), read_adb_setting (device-side nulls reported as such), capture_adb_screen (exec-out screencap with PNG dimensions), collect_adb_bugreport (digested device archive). Transfer and search: pull_adb_file and push_adb_file (device-side sha256sum verification, explicit overwrite authority), resolve_adb_packages (substring package search with exact-match flag — the find step of an easy get). Lifecycle for dynamic analysis, each declaring mutatesTarget: install_adb_package and uninstall_adb_package preserving the device's Success/Failure verdict, start_adb_app (resolve-activity then am start -W), start_adb_activity (typed extras as --es/--ez/--ei/--el/--ef), and stop_adb_app (am force-stop). Verified end-to-end against adb 34.0.5 on Linux with an Android 14 (API 34) x86_64 emulator: 287 services, 92 features, split-set pulls with byte-exact digests, verified push/pull roundtrip, sized screen capture, and the full install → resolve → start → observe-in-ps → force-stop → uninstall lifecycle. Fixed a v-mode regex incompatibility in the settings key pattern and added argument descriptions required by the CLI catalog audit. * fix(adb): quote remote shell operands and protect captured output * test(adb): cover bugreport destinations containing spaces --------- Co-authored-by: REA Contributor <contributor@rea.tools> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
97 lines
2.9 KiB
TypeScript
97 lines
2.9 KiB
TypeScript
import { expect, it } from "vitest";
|
|
|
|
import { AdbDeviceAnalysisService } from "./AdbDeviceAnalysisService.js";
|
|
import type { AdbDeviceAnalysisPort } from "./AdbDeviceAnalysisPort.js";
|
|
import { createAnalysisExecution } from "../AnalysisProvider.js";
|
|
import { AnalysisCancelledError } from "../../domain/analysisErrorCore.js";
|
|
import { parseEvidence } from "../../domain/evidence.js";
|
|
import { ok } from "../../domain/result.js";
|
|
|
|
const identity = { id: "adb", name: "Android Debug Bridge", version: null };
|
|
|
|
const execution = createAnalysisExecution(
|
|
{
|
|
client: {
|
|
binary_path: "/usr/bin/adb",
|
|
path_source: "path",
|
|
version: "34.0.5-debian",
|
|
installed_path: "/usr/bin/adb",
|
|
},
|
|
devices: [],
|
|
may_have_started_adb_server: false,
|
|
},
|
|
identity,
|
|
);
|
|
|
|
const refusedPort: AdbDeviceAnalysisPort = {
|
|
async inspectAvailability() {
|
|
throw new Error("must not probe");
|
|
},
|
|
async close() {},
|
|
async execute() {
|
|
throw new Error("must not execute");
|
|
},
|
|
};
|
|
|
|
it.each([
|
|
[{ serial: "" }],
|
|
[{ serial: "has space" }],
|
|
[{ serial: "emulator-5554", scope: "unknown" }],
|
|
[{ serial: "emulator-5554", extra: true }],
|
|
])("rejects invalid device selection before any effect: %j", async (input) => {
|
|
const service = new AdbDeviceAnalysisService(refusedPort);
|
|
expect(await service.execute("inspect_adb_device", input)).toMatchObject({
|
|
ok: false,
|
|
error: { _tag: "AnalysisInputError" },
|
|
});
|
|
});
|
|
|
|
it("rejects an unknown operation before any effect", async () => {
|
|
const service = new AdbDeviceAnalysisService(refusedPort);
|
|
expect(
|
|
await service.execute("inspect_adb_device" as never, {}),
|
|
).toMatchObject({ ok: false, error: { _tag: "AnalysisInputError" } });
|
|
});
|
|
|
|
it("composes observed Evidence with the caller parameters inline", async () => {
|
|
const port: AdbDeviceAnalysisPort = {
|
|
async inspectAvailability() {
|
|
return { status: "available", code: null, reason: null, diagnostics: {} };
|
|
},
|
|
async close() {},
|
|
async execute(request) {
|
|
expect(request).toEqual({
|
|
operation: "list_adb_devices",
|
|
input: {},
|
|
});
|
|
return ok(execution);
|
|
},
|
|
};
|
|
const result = await new AdbDeviceAnalysisService(port).execute(
|
|
"list_adb_devices",
|
|
{},
|
|
);
|
|
if (!result.ok) throw new Error(result.error.message);
|
|
const evidence = parseEvidence(result.value);
|
|
expect(evidence.provider).toEqual(identity);
|
|
expect(evidence.operation).toBe("list_adb_devices");
|
|
expect(evidence.parameters).toEqual({});
|
|
expect(evidence.confidence).toBe("observed");
|
|
});
|
|
|
|
it("preserves cancellation before execution", async () => {
|
|
const controller = new AbortController();
|
|
controller.abort();
|
|
const service = new AdbDeviceAnalysisService(refusedPort);
|
|
const result = await service.execute(
|
|
"list_adb_devices",
|
|
{},
|
|
{
|
|
signal: controller.signal,
|
|
},
|
|
);
|
|
expect(result.ok).toBe(false);
|
|
if (result.ok) return;
|
|
expect(result.error).toBeInstanceOf(AnalysisCancelledError);
|
|
});
|