deepadb 1.1.1 → 1.1.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/LICENSE +202 -21
- package/NOTICE +16 -0
- package/README.md +55 -29
- package/build/bridge/adb-bridge.d.ts +14 -0
- package/build/bridge/adb-bridge.d.ts.map +1 -1
- package/build/bridge/adb-bridge.js +34 -5
- package/build/bridge/adb-bridge.js.map +1 -1
- package/build/bridge/device-manager.d.ts.map +1 -1
- package/build/bridge/device-manager.js +18 -9
- package/build/bridge/device-manager.js.map +1 -1
- package/build/bridge/local-bridge.d.ts +10 -2
- package/build/bridge/local-bridge.d.ts.map +1 -1
- package/build/bridge/local-bridge.js +51 -16
- package/build/bridge/local-bridge.js.map +1 -1
- package/build/config/config.d.ts +7 -0
- package/build/config/config.d.ts.map +1 -1
- package/build/config/config.js +26 -10
- package/build/config/config.js.map +1 -1
- package/build/graphql-api.d.ts.map +1 -1
- package/build/graphql-api.js +73 -3
- package/build/graphql-api.js.map +1 -1
- package/build/http-transport.d.ts.map +1 -1
- package/build/http-transport.js +44 -7
- package/build/http-transport.js.map +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +41 -16
- package/build/index.js.map +1 -1
- package/build/middleware/auth.d.ts.map +1 -1
- package/build/middleware/auth.js +43 -6
- package/build/middleware/auth.js.map +1 -1
- package/build/middleware/chipset.d.ts.map +1 -1
- package/build/middleware/chipset.js +16 -1
- package/build/middleware/chipset.js.map +1 -1
- package/build/middleware/cleanup.d.ts +39 -7
- package/build/middleware/cleanup.d.ts.map +1 -1
- package/build/middleware/cleanup.js +148 -16
- package/build/middleware/cleanup.js.map +1 -1
- package/build/middleware/fetch-utils.d.ts +50 -7
- package/build/middleware/fetch-utils.d.ts.map +1 -1
- package/build/middleware/fetch-utils.js +127 -14
- package/build/middleware/fetch-utils.js.map +1 -1
- package/build/middleware/fs-utils.d.ts +126 -0
- package/build/middleware/fs-utils.d.ts.map +1 -0
- package/build/middleware/fs-utils.js +229 -0
- package/build/middleware/fs-utils.js.map +1 -0
- package/build/middleware/logger.d.ts +16 -0
- package/build/middleware/logger.d.ts.map +1 -1
- package/build/middleware/logger.js +18 -0
- package/build/middleware/logger.js.map +1 -1
- package/build/middleware/output-processor.d.ts.map +1 -1
- package/build/middleware/output-processor.js +43 -8
- package/build/middleware/output-processor.js.map +1 -1
- package/build/middleware/parse-utils.d.ts +19 -0
- package/build/middleware/parse-utils.d.ts.map +1 -0
- package/build/middleware/parse-utils.js +30 -0
- package/build/middleware/parse-utils.js.map +1 -0
- package/build/middleware/png-utils.d.ts.map +1 -1
- package/build/middleware/png-utils.js +33 -11
- package/build/middleware/png-utils.js.map +1 -1
- package/build/middleware/result-handle.d.ts +76 -0
- package/build/middleware/result-handle.d.ts.map +1 -0
- package/build/middleware/result-handle.js +378 -0
- package/build/middleware/result-handle.js.map +1 -0
- package/build/middleware/sanitize.d.ts +26 -0
- package/build/middleware/sanitize.d.ts.map +1 -1
- package/build/middleware/sanitize.js +43 -2
- package/build/middleware/sanitize.js.map +1 -1
- package/build/middleware/security.d.ts.map +1 -1
- package/build/middleware/security.js +30 -5
- package/build/middleware/security.js.map +1 -1
- package/build/middleware/ui-dump.d.ts.map +1 -1
- package/build/middleware/ui-dump.js +30 -10
- package/build/middleware/ui-dump.js.map +1 -1
- package/build/server.d.ts.map +1 -1
- package/build/server.js +34 -5
- package/build/server.js.map +1 -1
- package/build/tool-context.d.ts.map +1 -1
- package/build/tool-context.js +2 -0
- package/build/tool-context.js.map +1 -1
- package/build/tools/accessibility.d.ts.map +1 -1
- package/build/tools/accessibility.js +39 -40
- package/build/tools/accessibility.js.map +1 -1
- package/build/tools/at-commands.d.ts.map +1 -1
- package/build/tools/at-commands.js +31 -6
- package/build/tools/at-commands.js.map +1 -1
- package/build/tools/baseband.d.ts.map +1 -1
- package/build/tools/baseband.js +18 -7
- package/build/tools/baseband.js.map +1 -1
- package/build/tools/build.d.ts.map +1 -1
- package/build/tools/build.js +30 -8
- package/build/tools/build.js.map +1 -1
- package/build/tools/ci.d.ts.map +1 -1
- package/build/tools/ci.js +16 -5
- package/build/tools/ci.js.map +1 -1
- package/build/tools/control.d.ts.map +1 -1
- package/build/tools/control.js +19 -11
- package/build/tools/control.js.map +1 -1
- package/build/tools/device-farm.d.ts.map +1 -1
- package/build/tools/device-farm.js +27 -3
- package/build/tools/device-farm.js.map +1 -1
- package/build/tools/device-profiles.d.ts.map +1 -1
- package/build/tools/device-profiles.js +70 -8
- package/build/tools/device-profiles.js.map +1 -1
- package/build/tools/device.d.ts.map +1 -1
- package/build/tools/device.js +4 -2
- package/build/tools/device.js.map +1 -1
- package/build/tools/diagnostics.d.ts.map +1 -1
- package/build/tools/diagnostics.js +17 -9
- package/build/tools/diagnostics.js.map +1 -1
- package/build/tools/emulator.d.ts.map +1 -1
- package/build/tools/emulator.js +12 -13
- package/build/tools/emulator.js.map +1 -1
- package/build/tools/files.d.ts +1 -1
- package/build/tools/files.d.ts.map +1 -1
- package/build/tools/files.js +98 -56
- package/build/tools/files.js.map +1 -1
- package/build/tools/firmware-analysis.d.ts.map +1 -1
- package/build/tools/firmware-analysis.js +35 -12
- package/build/tools/firmware-analysis.js.map +1 -1
- package/build/tools/forwarding.d.ts.map +1 -1
- package/build/tools/forwarding.js +248 -6
- package/build/tools/forwarding.js.map +1 -1
- package/build/tools/health.d.ts.map +1 -1
- package/build/tools/health.js +14 -3
- package/build/tools/health.js.map +1 -1
- package/build/tools/input-gestures.d.ts.map +1 -1
- package/build/tools/input-gestures.js +65 -13
- package/build/tools/input-gestures.js.map +1 -1
- package/build/tools/logcat-watch.d.ts.map +1 -1
- package/build/tools/logcat-watch.js +21 -14
- package/build/tools/logcat-watch.js.map +1 -1
- package/build/tools/logs.d.ts.map +1 -1
- package/build/tools/logs.js +8 -4
- package/build/tools/logs.js.map +1 -1
- package/build/tools/mirroring.d.ts.map +1 -1
- package/build/tools/mirroring.js +21 -19
- package/build/tools/mirroring.js.map +1 -1
- package/build/tools/multi-device.d.ts.map +1 -1
- package/build/tools/multi-device.js +10 -0
- package/build/tools/multi-device.js.map +1 -1
- package/build/tools/network-capture.d.ts.map +1 -1
- package/build/tools/network-capture.js +32 -8
- package/build/tools/network-capture.js.map +1 -1
- package/build/tools/network-discovery.d.ts.map +1 -1
- package/build/tools/network-discovery.js +11 -2
- package/build/tools/network-discovery.js.map +1 -1
- package/build/tools/ota-monitor.d.ts.map +1 -1
- package/build/tools/ota-monitor.js +26 -12
- package/build/tools/ota-monitor.js.map +1 -1
- package/build/tools/packages.d.ts.map +1 -1
- package/build/tools/packages.js +18 -11
- package/build/tools/packages.js.map +1 -1
- package/build/tools/plugins.d.ts +18 -0
- package/build/tools/plugins.d.ts.map +1 -1
- package/build/tools/plugins.js +47 -2
- package/build/tools/plugins.js.map +1 -1
- package/build/tools/prompts.d.ts.map +1 -1
- package/build/tools/prompts.js +2 -0
- package/build/tools/prompts.js.map +1 -1
- package/build/tools/qemu.d.ts +6 -3
- package/build/tools/qemu.d.ts.map +1 -1
- package/build/tools/qemu.js +28 -16
- package/build/tools/qemu.js.map +1 -1
- package/build/tools/registry.d.ts.map +1 -1
- package/build/tools/registry.js +76 -26
- package/build/tools/registry.js.map +1 -1
- package/build/tools/regression.d.ts +37 -0
- package/build/tools/regression.d.ts.map +1 -1
- package/build/tools/regression.js +63 -22
- package/build/tools/regression.js.map +1 -1
- package/build/tools/resources.d.ts.map +1 -1
- package/build/tools/resources.js +62 -15
- package/build/tools/resources.js.map +1 -1
- package/build/tools/result-handles.d.ts +37 -0
- package/build/tools/result-handles.d.ts.map +1 -0
- package/build/tools/result-handles.js +286 -0
- package/build/tools/result-handles.js.map +1 -0
- package/build/tools/ril-intercept.d.ts.map +1 -1
- package/build/tools/ril-intercept.js +15 -18
- package/build/tools/ril-intercept.js.map +1 -1
- package/build/tools/screen-record.d.ts.map +1 -1
- package/build/tools/screen-record.js +34 -5
- package/build/tools/screen-record.js.map +1 -1
- package/build/tools/screenshot-diff.d.ts.map +1 -1
- package/build/tools/screenshot-diff.js +29 -21
- package/build/tools/screenshot-diff.js.map +1 -1
- package/build/tools/selinux-audit.d.ts.map +1 -1
- package/build/tools/selinux-audit.js +7 -5
- package/build/tools/selinux-audit.js.map +1 -1
- package/build/tools/sensors.d.ts.map +1 -1
- package/build/tools/sensors.js +26 -4
- package/build/tools/sensors.js.map +1 -1
- package/build/tools/shell.d.ts.map +1 -1
- package/build/tools/shell.js +9 -0
- package/build/tools/shell.js.map +1 -1
- package/build/tools/snapshot.d.ts.map +1 -1
- package/build/tools/snapshot.js +38 -19
- package/build/tools/snapshot.js.map +1 -1
- package/build/tools/split-apk.d.ts.map +1 -1
- package/build/tools/split-apk.js +48 -24
- package/build/tools/split-apk.js.map +1 -1
- package/build/tools/test-gen.d.ts.map +1 -1
- package/build/tools/test-gen.js +29 -10
- package/build/tools/test-gen.js.map +1 -1
- package/build/tools/testing.d.ts.map +1 -1
- package/build/tools/testing.js +27 -9
- package/build/tools/testing.js.map +1 -1
- package/build/tools/thermal-power.d.ts +8 -1
- package/build/tools/thermal-power.d.ts.map +1 -1
- package/build/tools/thermal-power.js +35 -10
- package/build/tools/thermal-power.js.map +1 -1
- package/build/tools/ui.d.ts.map +1 -1
- package/build/tools/ui.js +20 -13
- package/build/tools/ui.js.map +1 -1
- package/build/tools/wireless-firmware.d.ts.map +1 -1
- package/build/tools/wireless-firmware.js +21 -7
- package/build/tools/wireless-firmware.js.map +1 -1
- package/build/tools/wireless.d.ts.map +1 -1
- package/build/tools/wireless.js +39 -3
- package/build/tools/wireless.js.map +1 -1
- package/build/tools/workflow-market.d.ts.map +1 -1
- package/build/tools/workflow-market.js +67 -13
- package/build/tools/workflow-market.js.map +1 -1
- package/build/tools/workflow.d.ts.map +1 -1
- package/build/tools/workflow.js +77 -8
- package/build/tools/workflow.js.map +1 -1
- package/build/ws-transport.d.ts.map +1 -1
- package/build/ws-transport.js +21 -3
- package/build/ws-transport.js.map +1 -1
- package/package.json +20 -8
|
@@ -1,18 +1,97 @@
|
|
|
1
|
+
// Copyright 2026 Jason <fullread@github>
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
1
3
|
/**
|
|
2
4
|
* Port Forwarding Tools — TCP port mapping between host and device.
|
|
3
5
|
* Wraps adb forward and adb reverse for network debugging.
|
|
4
6
|
*/
|
|
5
7
|
import { z } from "zod";
|
|
8
|
+
import { randomBytes } from "crypto";
|
|
9
|
+
import { createServer } from "net";
|
|
6
10
|
import { OutputProcessor } from "../middleware/output-processor.js";
|
|
11
|
+
import { registerCleanup } from "../middleware/cleanup.js";
|
|
12
|
+
const trackedForwards = new Set();
|
|
13
|
+
let forwardCleanupRegistered = false;
|
|
14
|
+
function ensureForwardCleanupRegistered(ctx) {
|
|
15
|
+
if (forwardCleanupRegistered)
|
|
16
|
+
return;
|
|
17
|
+
registerCleanup("forwarding", async () => {
|
|
18
|
+
for (const fwd of trackedForwards) {
|
|
19
|
+
try {
|
|
20
|
+
const args = fwd.kind === "forward"
|
|
21
|
+
? ["forward", "--remove", fwd.spec]
|
|
22
|
+
: ["reverse", "--remove", fwd.spec];
|
|
23
|
+
await ctx.bridge.exec(args, { device: fwd.device, ignoreExitCode: true, timeout: 3000 });
|
|
24
|
+
}
|
|
25
|
+
catch { /* best-effort on shutdown */ }
|
|
26
|
+
}
|
|
27
|
+
trackedForwards.clear();
|
|
28
|
+
});
|
|
29
|
+
forwardCleanupRegistered = true;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* AF2 fix: format regex for adb forward / reverse specs.
|
|
33
|
+
*
|
|
34
|
+
* Accepted forms (all from adb documentation):
|
|
35
|
+
* tcp:<port> — TCP port (1-65535 enforced by separate parse)
|
|
36
|
+
* localabstract:<name> — Linux abstract socket namespace
|
|
37
|
+
* localreserved:<name> — Linux reserved socket namespace
|
|
38
|
+
* localfilesystem:<path> — Filesystem socket
|
|
39
|
+
* dev:<path> — Character device
|
|
40
|
+
* jdwp:<pid> — Java debug wire protocol (pid)
|
|
41
|
+
*
|
|
42
|
+
* Body characters intentionally restricted to [A-Za-z0-9_./-] so an operator
|
|
43
|
+
* cannot smuggle shell metacharacters via a forward spec. ADB itself will
|
|
44
|
+
* range-check the numeric values; this regex is a defense-in-depth at the
|
|
45
|
+
* tool boundary.
|
|
46
|
+
*/
|
|
47
|
+
const FORWARD_SPEC_RE = /^(tcp:\d{1,5}|localabstract:[A-Za-z0-9_./-]+|localreserved:[A-Za-z0-9_./-]+|localfilesystem:[A-Za-z0-9_./-]+|dev:[A-Za-z0-9_./-]+|jdwp:\d+)$/;
|
|
48
|
+
const tunnels = new Map();
|
|
49
|
+
/**
|
|
50
|
+
* Generate an opaque tunnel ID. Format: tun_XXXXXX (6 hex chars from
|
|
51
|
+
* randomBytes, ~16M space, collision-free within any realistic session).
|
|
52
|
+
* Opaque so operators don't try to parse it; metadata is surfaced by
|
|
53
|
+
* adb_tunnel_list.
|
|
54
|
+
*/
|
|
55
|
+
function generateTunnelId() {
|
|
56
|
+
return "tun_" + randomBytes(3).toString("hex");
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Find a free TCP port on the host. Uses net.createServer().listen(0)
|
|
60
|
+
* which asks the OS to bind any available port, then immediately closes
|
|
61
|
+
* the listener and returns the port the OS picked. The window between
|
|
62
|
+
* close and the caller's subsequent bind is a TOCTOU race in theory but
|
|
63
|
+
* in practice the kernel doesn't immediately re-hand-out a just-released
|
|
64
|
+
* port, so this is the standard cross-platform free-port-pick idiom.
|
|
65
|
+
* Returns 0 if the helper itself fails (caller should treat as error).
|
|
66
|
+
*/
|
|
67
|
+
function pickFreeHostPort() {
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
const srv = createServer();
|
|
70
|
+
srv.unref();
|
|
71
|
+
srv.on("error", reject);
|
|
72
|
+
srv.listen(0, "127.0.0.1", () => {
|
|
73
|
+
const addr = srv.address();
|
|
74
|
+
if (addr && typeof addr === "object") {
|
|
75
|
+
const port = addr.port;
|
|
76
|
+
srv.close(() => resolve(port));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
srv.close(() => reject(new Error("Failed to obtain port from server.address()")));
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
7
84
|
export function registerForwardingTools(ctx) {
|
|
8
85
|
ctx.server.tool("adb_forward", "Forward a local port to a port on the device (host → device). Use for connecting to services running on the device.", {
|
|
9
|
-
local: z.string().describe("Local (host) spec, e.g., 'tcp:8080'"),
|
|
10
|
-
remote: z.string().describe("Remote (device) spec, e.g., 'tcp:8080' or 'localabstract:app_socket'"),
|
|
86
|
+
local: z.string().regex(FORWARD_SPEC_RE, "Spec must be one of tcp:<port>, localabstract:<name>, localreserved:<name>, localfilesystem:<path>, dev:<path>, jdwp:<pid>").describe("Local (host) spec, e.g., 'tcp:8080'. AF2 fix: format-validated."),
|
|
87
|
+
remote: z.string().regex(FORWARD_SPEC_RE, "Spec must be one of tcp:<port>, localabstract:<name>, localreserved:<name>, localfilesystem:<path>, dev:<path>, jdwp:<pid>").describe("Remote (device) spec, e.g., 'tcp:8080' or 'localabstract:app_socket'. AF2 fix: format-validated."),
|
|
11
88
|
device: z.string().optional().describe("Device serial"),
|
|
12
89
|
}, async ({ local, remote, device }) => {
|
|
13
90
|
try {
|
|
14
91
|
const resolved = await ctx.deviceManager.resolveDevice(device);
|
|
92
|
+
ensureForwardCleanupRegistered(ctx);
|
|
15
93
|
const result = await ctx.bridge.exec(["forward", local, remote], { device: resolved.serial });
|
|
94
|
+
trackedForwards.add({ device: resolved.serial, spec: local, kind: "forward" });
|
|
16
95
|
return { content: [{ type: "text", text: result.stdout.trim() || `Forwarding ${local} → ${remote}` }] };
|
|
17
96
|
}
|
|
18
97
|
catch (error) {
|
|
@@ -20,13 +99,15 @@ export function registerForwardingTools(ctx) {
|
|
|
20
99
|
}
|
|
21
100
|
});
|
|
22
101
|
ctx.server.tool("adb_reverse", "Reverse-forward a device port to a port on the host (device → host). Use for letting device apps reach services on your machine.", {
|
|
23
|
-
remote: z.string().describe("Remote (device) spec, e.g., 'tcp:3000'"),
|
|
24
|
-
local: z.string().describe("Local (host) spec, e.g., 'tcp:3000'"),
|
|
102
|
+
remote: z.string().regex(FORWARD_SPEC_RE, "Spec must be one of tcp:<port>, localabstract:<name>, localreserved:<name>, localfilesystem:<path>, dev:<path>, jdwp:<pid>").describe("Remote (device) spec, e.g., 'tcp:3000'. AF2 fix: format-validated."),
|
|
103
|
+
local: z.string().regex(FORWARD_SPEC_RE, "Spec must be one of tcp:<port>, localabstract:<name>, localreserved:<name>, localfilesystem:<path>, dev:<path>, jdwp:<pid>").describe("Local (host) spec, e.g., 'tcp:3000'. AF2 fix: format-validated."),
|
|
25
104
|
device: z.string().optional().describe("Device serial"),
|
|
26
105
|
}, async ({ remote, local, device }) => {
|
|
27
106
|
try {
|
|
28
107
|
const resolved = await ctx.deviceManager.resolveDevice(device);
|
|
108
|
+
ensureForwardCleanupRegistered(ctx);
|
|
29
109
|
const result = await ctx.bridge.exec(["reverse", remote, local], { device: resolved.serial });
|
|
110
|
+
trackedForwards.add({ device: resolved.serial, spec: remote, kind: "reverse" });
|
|
30
111
|
return { content: [{ type: "text", text: result.stdout.trim() || `Reverse forwarding ${remote} → ${local}` }] };
|
|
31
112
|
}
|
|
32
113
|
catch (error) {
|
|
@@ -65,7 +146,7 @@ export function registerForwardingTools(ctx) {
|
|
|
65
146
|
}
|
|
66
147
|
});
|
|
67
148
|
ctx.server.tool("adb_forward_remove", "Remove a port forward (host → device), or all forwards. Use after testing to clean up.", {
|
|
68
|
-
local: z.string().optional().describe("Local spec to remove (e.g., 'tcp:8080'). Omit to remove all forwards."),
|
|
149
|
+
local: z.string().regex(FORWARD_SPEC_RE, "Spec must be one of tcp:<port>, localabstract:<name>, localreserved:<name>, localfilesystem:<path>, dev:<path>, jdwp:<pid>").optional().describe("Local spec to remove (e.g., 'tcp:8080'). Omit to remove all forwards. AF2 fix: format-validated."),
|
|
69
150
|
device: z.string().optional().describe("Device serial"),
|
|
70
151
|
}, async ({ local, device }) => {
|
|
71
152
|
try {
|
|
@@ -82,7 +163,7 @@ export function registerForwardingTools(ctx) {
|
|
|
82
163
|
}
|
|
83
164
|
});
|
|
84
165
|
ctx.server.tool("adb_reverse_remove", "Remove a reverse forward (device → host), or all reverse forwards. Use after testing to clean up.", {
|
|
85
|
-
remote: z.string().optional().describe("Remote spec to remove (e.g., 'tcp:3000'). Omit to remove all reverse forwards."),
|
|
166
|
+
remote: z.string().regex(FORWARD_SPEC_RE, "Spec must be one of tcp:<port>, localabstract:<name>, localreserved:<name>, localfilesystem:<path>, dev:<path>, jdwp:<pid>").optional().describe("Remote spec to remove (e.g., 'tcp:3000'). Omit to remove all reverse forwards. AF2 fix: format-validated."),
|
|
86
167
|
device: z.string().optional().describe("Device serial"),
|
|
87
168
|
}, async ({ remote, device }) => {
|
|
88
169
|
try {
|
|
@@ -98,5 +179,166 @@ export function registerForwardingTools(ctx) {
|
|
|
98
179
|
return { content: [{ type: "text", text: OutputProcessor.formatError(error) }], isError: true };
|
|
99
180
|
}
|
|
100
181
|
});
|
|
182
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
183
|
+
// Tunnel automation (adb_tunnel_open / list / close)
|
|
184
|
+
//
|
|
185
|
+
// High-level wrappers over adb_forward / adb_reverse. Each open
|
|
186
|
+
// returns an opaque `tun_XXXXXX` ID that close can reference, so
|
|
187
|
+
// operators don't have to remember the spec they passed to forward.
|
|
188
|
+
// Hooks into the existing AF4 cleanup path (trackedForwards Set)
|
|
189
|
+
// so server shutdown removes managed tunnels automatically.
|
|
190
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
191
|
+
ctx.server.tool("adb_tunnel_open", "Open a managed tunnel between host and device. Higher-level convenience wrapper over adb_forward / adb_reverse: auto-picks a free host port when one isn't specified (forward only), returns an opaque tunnel ID for later close, and registers cleanup so the tunnel is removed on server exit. Direction 'forward' = host port forwards TO device service; 'reverse' = device port forwards TO host service.", {
|
|
192
|
+
direction: z.enum(["forward", "reverse"])
|
|
193
|
+
.describe("'forward' (host → device) or 'reverse' (device → host)"),
|
|
194
|
+
deviceSpec: z.string().regex(FORWARD_SPEC_RE, "Spec must be one of tcp:<port>, localabstract:<name>, localreserved:<name>, localfilesystem:<path>, dev:<path>, jdwp:<pid>")
|
|
195
|
+
.describe("Device-side spec, e.g., 'tcp:3000' or 'localabstract:app_socket'"),
|
|
196
|
+
hostSpec: z.string().regex(FORWARD_SPEC_RE, "Spec must be one of tcp:<port>, localabstract:<name>, localreserved:<name>, localfilesystem:<path>, dev:<path>, jdwp:<pid>").optional()
|
|
197
|
+
.describe("Host-side spec, e.g., 'tcp:8080'. For 'forward' direction: omit to auto-pick a free host port. For 'reverse' direction: required (can't auto-pick device-side ports without an extra probe call)."),
|
|
198
|
+
device: z.string().optional().describe("Device serial"),
|
|
199
|
+
}, async ({ direction, deviceSpec, hostSpec, device }) => {
|
|
200
|
+
try {
|
|
201
|
+
const resolved = await ctx.deviceManager.resolveDevice(device);
|
|
202
|
+
ensureForwardCleanupRegistered(ctx);
|
|
203
|
+
// Resolve hostSpec: auto-pick free port for forward + omitted; require for reverse.
|
|
204
|
+
let effectiveHostSpec = hostSpec;
|
|
205
|
+
if (!effectiveHostSpec) {
|
|
206
|
+
if (direction === "reverse") {
|
|
207
|
+
return { content: [{ type: "text", text: "hostSpec is required for direction='reverse'. Auto-pick is supported only for 'forward' (host-side port). For reverse, specify the host endpoint explicitly (e.g., hostSpec: 'tcp:3000')." }], isError: true };
|
|
208
|
+
}
|
|
209
|
+
const port = await pickFreeHostPort();
|
|
210
|
+
effectiveHostSpec = "tcp:" + port;
|
|
211
|
+
}
|
|
212
|
+
// Build adb args. For forward: adb forward LOCAL REMOTE. For reverse: adb reverse REMOTE LOCAL.
|
|
213
|
+
// The order in the adb CLI is "device side first" for reverse, "host side first" for forward.
|
|
214
|
+
const args = direction === "forward"
|
|
215
|
+
? ["forward", effectiveHostSpec, deviceSpec]
|
|
216
|
+
: ["reverse", deviceSpec, effectiveHostSpec];
|
|
217
|
+
await ctx.bridge.exec(args, { device: resolved.serial });
|
|
218
|
+
// Register for AF4 cleanup-on-exit. trackedForwards.spec is the
|
|
219
|
+
// arg passed to --remove, which for forward is the host spec and
|
|
220
|
+
// for reverse is the device spec.
|
|
221
|
+
const removeSpec = direction === "forward" ? effectiveHostSpec : deviceSpec;
|
|
222
|
+
trackedForwards.add({ device: resolved.serial, spec: removeSpec, kind: direction });
|
|
223
|
+
// Generate ID + store metadata for ID-based close + list.
|
|
224
|
+
const id = generateTunnelId();
|
|
225
|
+
tunnels.set(id, {
|
|
226
|
+
id,
|
|
227
|
+
device: resolved.serial,
|
|
228
|
+
direction,
|
|
229
|
+
hostSpec: effectiveHostSpec,
|
|
230
|
+
deviceSpec,
|
|
231
|
+
createdAt: Date.now(),
|
|
232
|
+
});
|
|
233
|
+
const lines = [
|
|
234
|
+
`Tunnel opened: ${id}`,
|
|
235
|
+
` device: ${resolved.serial}`,
|
|
236
|
+
` direction: ${direction}`,
|
|
237
|
+
` host: ${effectiveHostSpec}${hostSpec ? "" : " (auto-picked)"}`,
|
|
238
|
+
` device: ${deviceSpec}`,
|
|
239
|
+
"",
|
|
240
|
+
`Use adb_tunnel_close with id=${id} to remove. Tunnel will also be auto-removed on server shutdown.`,
|
|
241
|
+
];
|
|
242
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
return { content: [{ type: "text", text: OutputProcessor.formatError(error) }], isError: true };
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
ctx.server.tool("adb_tunnel_list", "List all active managed tunnels (opened via adb_tunnel_open). Shows tunnel ID, device, direction, and both endpoints. Note: this only surfaces tunnels created via adb_tunnel_open — tunnels created via the low-level adb_forward / adb_reverse won't appear here. For those, use adb_forward_list.", {
|
|
249
|
+
device: z.string().optional().describe("Filter to a specific device serial (default: all devices)"),
|
|
250
|
+
}, async ({ device }) => {
|
|
251
|
+
try {
|
|
252
|
+
// device param is a filter — don't resolveDevice (it'd require a connected device).
|
|
253
|
+
const filtered = device
|
|
254
|
+
? [...tunnels.values()].filter((t) => t.device === device)
|
|
255
|
+
: [...tunnels.values()];
|
|
256
|
+
if (filtered.length === 0) {
|
|
257
|
+
return { content: [{ type: "text", text: device
|
|
258
|
+
? `No managed tunnels open for device ${device}. (Use adb_forward_list for tunnels not created via adb_tunnel_open.)`
|
|
259
|
+
: "No managed tunnels open. (Use adb_forward_list for tunnels not created via adb_tunnel_open.)" }] };
|
|
260
|
+
}
|
|
261
|
+
// Sort by createdAt for stable, predictable output.
|
|
262
|
+
filtered.sort((a, b) => a.createdAt - b.createdAt);
|
|
263
|
+
const lines = [`${filtered.length} managed tunnel(s):`, ""];
|
|
264
|
+
for (const t of filtered) {
|
|
265
|
+
const ageSec = Math.round((Date.now() - t.createdAt) / 1000);
|
|
266
|
+
lines.push(` ${t.id} [${t.direction}] device=${t.device}`);
|
|
267
|
+
lines.push(` host: ${t.hostSpec}`);
|
|
268
|
+
lines.push(` device: ${t.deviceSpec}`);
|
|
269
|
+
lines.push(` age: ${ageSec}s`);
|
|
270
|
+
lines.push("");
|
|
271
|
+
}
|
|
272
|
+
return { content: [{ type: "text", text: lines.join("\n").trimEnd() }] };
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
return { content: [{ type: "text", text: OutputProcessor.formatError(error) }], isError: true };
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
ctx.server.tool("adb_tunnel_close", "Close a managed tunnel by ID, or all managed tunnels at once. The corresponding adb forward/reverse entry is removed and the tunnel is dropped from the cleanup registry. Use adb_tunnel_list to see active tunnel IDs.", {
|
|
279
|
+
id: z.string().describe("Tunnel ID (tun_XXXXXX) returned by adb_tunnel_open, or the literal string 'all' to close every managed tunnel."),
|
|
280
|
+
}, async ({ id }) => {
|
|
281
|
+
try {
|
|
282
|
+
if (id === "all") {
|
|
283
|
+
if (tunnels.size === 0) {
|
|
284
|
+
return { content: [{ type: "text", text: "No managed tunnels to close." }] };
|
|
285
|
+
}
|
|
286
|
+
const all = [...tunnels.values()];
|
|
287
|
+
let closed = 0;
|
|
288
|
+
const failures = [];
|
|
289
|
+
for (const t of all) {
|
|
290
|
+
try {
|
|
291
|
+
const args = t.direction === "forward"
|
|
292
|
+
? ["forward", "--remove", t.hostSpec]
|
|
293
|
+
: ["reverse", "--remove", t.deviceSpec];
|
|
294
|
+
await ctx.bridge.exec(args, { device: t.device, ignoreExitCode: true, timeout: 5000 });
|
|
295
|
+
// Drop from both maps.
|
|
296
|
+
tunnels.delete(t.id);
|
|
297
|
+
for (const fwd of trackedForwards) {
|
|
298
|
+
if (fwd.device === t.device && fwd.kind === t.direction &&
|
|
299
|
+
fwd.spec === (t.direction === "forward" ? t.hostSpec : t.deviceSpec)) {
|
|
300
|
+
trackedForwards.delete(fwd);
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
closed++;
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
failures.push(`${t.id}: ${err instanceof Error ? err.message : err}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const lines = [`Closed ${closed} of ${all.length} managed tunnel(s).`];
|
|
311
|
+
if (failures.length > 0) {
|
|
312
|
+
lines.push("");
|
|
313
|
+
lines.push("Failures:");
|
|
314
|
+
for (const f of failures)
|
|
315
|
+
lines.push(" " + f);
|
|
316
|
+
}
|
|
317
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
318
|
+
}
|
|
319
|
+
// Single-ID close.
|
|
320
|
+
const t = tunnels.get(id);
|
|
321
|
+
if (!t) {
|
|
322
|
+
return { content: [{ type: "text", text: `No managed tunnel with id='${id}'. Use adb_tunnel_list to see active tunnels.` }], isError: true };
|
|
323
|
+
}
|
|
324
|
+
const args = t.direction === "forward"
|
|
325
|
+
? ["forward", "--remove", t.hostSpec]
|
|
326
|
+
: ["reverse", "--remove", t.deviceSpec];
|
|
327
|
+
await ctx.bridge.exec(args, { device: t.device, ignoreExitCode: true, timeout: 5000 });
|
|
328
|
+
tunnels.delete(t.id);
|
|
329
|
+
// Drop from trackedForwards too — match by device + kind + spec.
|
|
330
|
+
for (const fwd of trackedForwards) {
|
|
331
|
+
if (fwd.device === t.device && fwd.kind === t.direction &&
|
|
332
|
+
fwd.spec === (t.direction === "forward" ? t.hostSpec : t.deviceSpec)) {
|
|
333
|
+
trackedForwards.delete(fwd);
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return { content: [{ type: "text", text: `Tunnel ${id} closed (${t.direction}, host=${t.hostSpec}, device=${t.deviceSpec}).` }] };
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
return { content: [{ type: "text", text: OutputProcessor.formatError(error) }], isError: true };
|
|
341
|
+
}
|
|
342
|
+
});
|
|
101
343
|
}
|
|
102
344
|
//# sourceMappingURL=forwarding.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forwarding.js","sourceRoot":"","sources":["../../src/tools/forwarding.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"forwarding.js","sourceRoot":"","sources":["../../src/tools/forwarding.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/tools/health.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/tools/health.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAKH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGjD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAuH1D"}
|
package/build/tools/health.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
// Copyright 2026 Jason <fullread@github>
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
1
3
|
/**
|
|
2
4
|
* Health Check Tools — Validate the ADB chain and diagnose connection issues.
|
|
3
5
|
* Runs through: ADB binary → ADB server → device connection → authorization.
|
|
4
6
|
*/
|
|
5
7
|
import { z } from "zod";
|
|
6
8
|
import { existsSync } from "fs";
|
|
9
|
+
import { shellQuote } from "../middleware/sanitize.js";
|
|
7
10
|
import { isOnDevice } from "../config/config.js";
|
|
8
11
|
export function registerHealthTools(ctx) {
|
|
9
12
|
ctx.server.tool("adb_health_check", "Run a comprehensive health check of the ADB toolchain. Validates: ADB binary, server, device connection, authorization, and root access.", { device: z.string().optional().describe("Device serial to check (optional)") }, async ({ device }) => {
|
|
@@ -34,7 +37,6 @@ export function registerHealthTools(ctx) {
|
|
|
34
37
|
}
|
|
35
38
|
catch (error) {
|
|
36
39
|
checks.push(`✗ ADB server: unreachable — ${error instanceof Error ? error.message : error}`);
|
|
37
|
-
allPassed = false;
|
|
38
40
|
checks.push("\n--- Health check aborted: ADB server must be running ---");
|
|
39
41
|
return { content: [{ type: "text", text: checks.join("\n") }], isError: true };
|
|
40
42
|
}
|
|
@@ -87,7 +89,16 @@ export function registerHealthTools(ctx) {
|
|
|
87
89
|
checks.push("○ Root access: not available (non-critical)");
|
|
88
90
|
}
|
|
89
91
|
// Temp dir writable
|
|
90
|
-
|
|
92
|
+
// AG1 fix: include PID + timestamp + random suffix in the probe filename
|
|
93
|
+
// so two concurrent health checks (from different DeepADB processes
|
|
94
|
+
// sharing the same device) don't race on a fixed path. The race is
|
|
95
|
+
// benign — both succeed since touch+rm is idempotent — but if one
|
|
96
|
+
// process's rm runs between the other's touch and rm, the OK echo
|
|
97
|
+
// could fire incorrectly. Using a per-invocation unique name eliminates
|
|
98
|
+
// the cross-process interleaving entirely.
|
|
99
|
+
const probeId = `${process.pid}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
100
|
+
const probePath = `/sdcard/.DA_health_check_${probeId}`;
|
|
101
|
+
const tempCheck = await ctx.bridge.shell(`touch ${shellQuote(probePath)} && rm ${shellQuote(probePath)} && echo OK`, {
|
|
91
102
|
device: resolved.serial, ignoreExitCode: true,
|
|
92
103
|
});
|
|
93
104
|
if (tempCheck.stdout.includes("OK")) {
|
|
@@ -98,7 +109,7 @@ export function registerHealthTools(ctx) {
|
|
|
98
109
|
allPassed = false;
|
|
99
110
|
}
|
|
100
111
|
}
|
|
101
|
-
catch
|
|
112
|
+
catch {
|
|
102
113
|
// No target device — already reported above
|
|
103
114
|
if (!device) {
|
|
104
115
|
checks.push("\n○ No target device to deep-check (expected if no device connected)");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/tools/health.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/tools/health.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input-gestures.d.ts","sourceRoot":"","sources":["../../src/tools/input-gestures.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"input-gestures.d.ts","sourceRoot":"","sources":["../../src/tools/input-gestures.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOjD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CA0sChE"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Copyright 2026 Jason <fullread@github>
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
1
3
|
/**
|
|
2
4
|
* Input Gestures, UI Automation & Device Awareness Tools.
|
|
3
5
|
*
|
|
@@ -8,9 +10,9 @@
|
|
|
8
10
|
*/
|
|
9
11
|
import { z } from "zod";
|
|
10
12
|
import { OutputProcessor } from "../middleware/output-processor.js";
|
|
11
|
-
import {
|
|
13
|
+
import { shellQuote } from "../middleware/sanitize.js";
|
|
14
|
+
import { sanitizeFilenameComponentDotted } from "../middleware/fs-utils.js";
|
|
12
15
|
import { captureUiDump, parseUiNodes } from "../middleware/ui-dump.js";
|
|
13
|
-
import { isOnDevice } from "../config/config.js";
|
|
14
16
|
import { join } from "path";
|
|
15
17
|
export function registerInputGestureTools(ctx) {
|
|
16
18
|
ctx.server.tool("adb_input_drag", "Drag from one point to another on screen. Uses Android's draganddrop input command. Useful for drag-and-drop UI elements, sliders, map panning, and reorder operations.", {
|
|
@@ -101,7 +103,7 @@ export function registerInputGestureTools(ctx) {
|
|
|
101
103
|
// Android's input text treats spaces as argument separators — must use %s
|
|
102
104
|
// Also need to escape shell special characters
|
|
103
105
|
const escaped = text.replace(/ /g, "%s");
|
|
104
|
-
const shellCmd = `input text
|
|
106
|
+
const shellCmd = `input text ${shellQuote(escaped)}`;
|
|
105
107
|
await ctx.bridge.shell(shellCmd, { device: resolved.serial, ignoreExitCode: true });
|
|
106
108
|
return { content: [{ type: "text", text: `Typed ${text.length} character(s)` }] };
|
|
107
109
|
}
|
|
@@ -110,12 +112,12 @@ export function registerInputGestureTools(ctx) {
|
|
|
110
112
|
}
|
|
111
113
|
});
|
|
112
114
|
ctx.server.tool("adb_open_url", "Open a URL on the device in the default browser or handling app. Uses Android's VIEW intent action.", {
|
|
113
|
-
url: z.
|
|
115
|
+
url: z.url().describe("URL to open (must be a valid URL with scheme)"),
|
|
114
116
|
device: z.string().optional().describe("Device serial"),
|
|
115
117
|
}, async ({ url, device }) => {
|
|
116
118
|
try {
|
|
117
119
|
const resolved = await ctx.deviceManager.resolveDevice(device);
|
|
118
|
-
const result = await ctx.bridge.shell(`am start -a android.intent.action.VIEW -d
|
|
120
|
+
const result = await ctx.bridge.shell(`am start -a android.intent.action.VIEW -d ${shellQuote(url)}`, { device: resolved.serial, ignoreExitCode: true });
|
|
119
121
|
const output = result.stdout.trim();
|
|
120
122
|
if (output.includes("Error") || output.includes("Exception")) {
|
|
121
123
|
return { content: [{ type: "text", text: `Failed to open URL: ${output}` }], isError: true };
|
|
@@ -179,12 +181,55 @@ export function registerInputGestureTools(ctx) {
|
|
|
179
181
|
return { content: [{ type: "text", text: "text parameter is required when action='write'" }], isError: true };
|
|
180
182
|
}
|
|
181
183
|
// Use am broadcast to set clipboard via a helper — this is the most reliable cross-version approach
|
|
182
|
-
await ctx.bridge.shell(`am broadcast -a clipper.set -e text
|
|
184
|
+
await ctx.bridge.shell(`am broadcast -a clipper.set -e text ${shellQuote(text)}`, { device: serial, ignoreExitCode: true });
|
|
183
185
|
// Fallback: use service call if clipper app isn't installed
|
|
184
186
|
// service call clipboard 2 (SET_PRIMARY_CLIP) requires parcel construction
|
|
185
187
|
// For simplicity, also try the settings-based approach
|
|
186
|
-
await ctx.bridge.shell(`input keyevent --longpress KEYCODE_UNKNOWN 2>/dev/null; settings put system clipboard_text
|
|
187
|
-
|
|
188
|
+
await ctx.bridge.shell(`input keyevent --longpress KEYCODE_UNKNOWN 2>/dev/null; settings put system clipboard_text ${shellQuote(text)} 2>/dev/null`, { device: serial, ignoreExitCode: true });
|
|
189
|
+
// AH7 fix: read-back verification. Previously the tool reported
|
|
190
|
+
// "Clipboard set" even when BOTH fallback writes failed silently
|
|
191
|
+
// (no clipper app + Android 12+ blocking settings put). Now we
|
|
192
|
+
// immediately read the clipboard back and surface success/failure
|
|
193
|
+
// honestly. The read uses the same dumpsys / service call methods
|
|
194
|
+
// as the read action below — duplicated here to avoid moving the
|
|
195
|
+
// methods array above the function structure.
|
|
196
|
+
//
|
|
197
|
+
// Caveats this verification cannot catch: (1) On Android 12+ the
|
|
198
|
+
// clipboard requires the requesting app to be in the foreground,
|
|
199
|
+
// and adb shell isn't a foreground app, so read-back may fail even
|
|
200
|
+
// if write succeeded for foreground consumers. We surface that as
|
|
201
|
+
// an explicit "could not verify" rather than a hard failure.
|
|
202
|
+
let verified = null;
|
|
203
|
+
const verifyMethods = [
|
|
204
|
+
{ cmd: "dumpsys clipboard | grep -A 2 'mPrimaryClip'", parse: (s) => {
|
|
205
|
+
const m = s.match(/Text\s*\{(.+?)\}/);
|
|
206
|
+
return m ? m[1] : null;
|
|
207
|
+
} },
|
|
208
|
+
{ cmd: "service call clipboard 1 2>/dev/null", parse: (s) => {
|
|
209
|
+
const parts = s.match(/'([^']*)'/g);
|
|
210
|
+
return parts ? parts.map(p => p.replace(/'/g, "").replace(/\.\s*$/g, "")).join("") : null;
|
|
211
|
+
} },
|
|
212
|
+
];
|
|
213
|
+
for (const method of verifyMethods) {
|
|
214
|
+
try {
|
|
215
|
+
const result = await ctx.bridge.shell(method.cmd, { device: serial, timeout: 3000, ignoreExitCode: true });
|
|
216
|
+
const parsed = method.parse(result.stdout);
|
|
217
|
+
if (parsed && parsed.trim().length > 0) {
|
|
218
|
+
verified = parsed;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch { /* try next */ }
|
|
223
|
+
}
|
|
224
|
+
if (verified === text) {
|
|
225
|
+
return { content: [{ type: "text", text: `Clipboard set (${text.length} chars). Verified: read-back matches.` }] };
|
|
226
|
+
}
|
|
227
|
+
else if (verified !== null && verified !== text) {
|
|
228
|
+
return { content: [{ type: "text", text: `Clipboard write may have partially failed: wrote ${text.length} chars but read back ${verified.length} chars of different content. Some apps may have own clipboard implementations.` }], isError: true };
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
return { content: [{ type: "text", text: `Clipboard write attempted (${text.length} chars) but read-back returned empty — could not verify. On Android 12+, this is normal: the clipboard requires a foreground app to read. The write may still have succeeded; try paste in a foreground app to confirm. If pasting also yields empty, no clipper helper is installed.` }] };
|
|
232
|
+
}
|
|
188
233
|
}
|
|
189
234
|
// action === "read"
|
|
190
235
|
// Try multiple methods to read clipboard
|
|
@@ -452,7 +497,7 @@ export function registerInputGestureTools(ctx) {
|
|
|
452
497
|
}
|
|
453
498
|
});
|
|
454
499
|
// ── Batch 3: Efficiency Features ──────────────────────────────────────
|
|
455
|
-
ctx.server.tool("adb_screenshot_compressed", "Take a screenshot and
|
|
500
|
+
ctx.server.tool("adb_screenshot_compressed", "Take a screenshot and pull the full-resolution PNG, plus metadata for client-side compression. Returns local file path, dimensions, file size, and a ready-to-run imagemagick command for converting to a smaller JPEG. (Android doesn't ship imagemagick, so the actual compression is post-processing — this tool sets up everything you need.)", {
|
|
456
501
|
quality: z.number().int().min(10).max(100).optional().default(50)
|
|
457
502
|
.describe("JPEG quality (10-100, default 50). Lower = smaller file, more artifacts"),
|
|
458
503
|
scale: z.number().min(0.1).max(1.0).optional().default(0.5)
|
|
@@ -463,13 +508,13 @@ export function registerInputGestureTools(ctx) {
|
|
|
463
508
|
try {
|
|
464
509
|
const resolved = await ctx.deviceManager.resolveDevice(device);
|
|
465
510
|
const serial = resolved.serial;
|
|
466
|
-
const remoteDir =
|
|
511
|
+
const remoteDir = "/data/local/tmp";
|
|
467
512
|
const timestamp = Date.now();
|
|
468
513
|
const remotePng = `${remoteDir}/DA_cap_${timestamp}.png`;
|
|
469
514
|
const remoteJpg = `${remoteDir}/DA_cap_${timestamp}.jpg`;
|
|
470
515
|
// Sanitize filename
|
|
471
516
|
const outName = filename
|
|
472
|
-
? filename
|
|
517
|
+
? sanitizeFilenameComponentDotted(filename)
|
|
473
518
|
: `screenshot_compressed_${timestamp}.jpg`;
|
|
474
519
|
const localPath = join(ctx.config.tempDir, outName);
|
|
475
520
|
try {
|
|
@@ -511,7 +556,7 @@ export function registerInputGestureTools(ctx) {
|
|
|
511
556
|
ctx.server.tool("adb_batch_actions", "Execute multiple input actions in a single tool call. Reduces ADB round-trips for multi-step UI interactions. Each action runs sequentially with an optional delay between them. Supported action types: tap, swipe, fling, long_press, double_tap, keyevent, text, drag, pinch, back, home, sleep.", {
|
|
512
557
|
actions: z.array(z.object({
|
|
513
558
|
type: z.enum(["tap", "swipe", "fling", "long_press", "double_tap", "keyevent", "text", "drag", "pinch", "back", "home", "sleep"])
|
|
514
|
-
.describe("Action type"),
|
|
559
|
+
.describe("Action type — one of: tap, swipe, fling, long_press, double_tap, keyevent, text, drag, pinch, back, home, sleep. The args field documents the expected argument format per type."),
|
|
515
560
|
args: z.string().optional()
|
|
516
561
|
.describe("Action arguments: tap='x y', swipe='x1 y1 x2 y2 [ms]', fling='x1 y1 x2 y2 [ms]', long_press='x y [ms]', double_tap='x y', keyevent='KEYCODE_*', text='string', drag='x1 y1 x2 y2 [ms]', pinch='cx cy startRadius endRadius [durationMs]', sleep='ms', back/home=none"),
|
|
517
562
|
})).min(1).max(50).describe("Array of actions to execute (1-50)"),
|
|
@@ -581,7 +626,7 @@ export function registerInputGestureTools(ctx) {
|
|
|
581
626
|
cmd = `input keyevent ${args}`;
|
|
582
627
|
break;
|
|
583
628
|
case "text":
|
|
584
|
-
cmd = `input text
|
|
629
|
+
cmd = `input text ${shellQuote(args.replace(/ /g, "%s"))}`;
|
|
585
630
|
break;
|
|
586
631
|
case "drag":
|
|
587
632
|
if (!/^[\d\s]+$/.test(args))
|
|
@@ -605,6 +650,13 @@ export function registerInputGestureTools(ctx) {
|
|
|
605
650
|
const pf2sy = Math.round(pcy - psr * Math.sin(prad));
|
|
606
651
|
const pf2ex = Math.round(pcx - per * Math.cos(prad));
|
|
607
652
|
const pf2ey = Math.round(pcy - per * Math.sin(prad));
|
|
653
|
+
// AH11 note: the bare & here is INTENTIONAL, not a typo or shell-
|
|
654
|
+
// injection vector. Pinch/spread requires two input swipe commands
|
|
655
|
+
// running in parallel so both fingers move simultaneously; the &
|
|
656
|
+
// backgrounds the first while the second runs. All coordinate
|
|
657
|
+
// values were validated against /^[\d\s]+$/ above (pure digits +
|
|
658
|
+
// whitespace), so there's no operator-supplied content here that
|
|
659
|
+
// could exploit the & — only validated integers from parseInt.
|
|
608
660
|
cmd = `input swipe ${pf1sx} ${pf1sy} ${pf1ex} ${pf1ey} ${pdur} & input swipe ${pf2sx} ${pf2sy} ${pf2ex} ${pf2ey} ${pdur}`;
|
|
609
661
|
break;
|
|
610
662
|
}
|