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.
Files changed (230) hide show
  1. package/LICENSE +202 -21
  2. package/NOTICE +16 -0
  3. package/README.md +55 -29
  4. package/build/bridge/adb-bridge.d.ts +14 -0
  5. package/build/bridge/adb-bridge.d.ts.map +1 -1
  6. package/build/bridge/adb-bridge.js +34 -5
  7. package/build/bridge/adb-bridge.js.map +1 -1
  8. package/build/bridge/device-manager.d.ts.map +1 -1
  9. package/build/bridge/device-manager.js +18 -9
  10. package/build/bridge/device-manager.js.map +1 -1
  11. package/build/bridge/local-bridge.d.ts +10 -2
  12. package/build/bridge/local-bridge.d.ts.map +1 -1
  13. package/build/bridge/local-bridge.js +51 -16
  14. package/build/bridge/local-bridge.js.map +1 -1
  15. package/build/config/config.d.ts +7 -0
  16. package/build/config/config.d.ts.map +1 -1
  17. package/build/config/config.js +26 -10
  18. package/build/config/config.js.map +1 -1
  19. package/build/graphql-api.d.ts.map +1 -1
  20. package/build/graphql-api.js +73 -3
  21. package/build/graphql-api.js.map +1 -1
  22. package/build/http-transport.d.ts.map +1 -1
  23. package/build/http-transport.js +44 -7
  24. package/build/http-transport.js.map +1 -1
  25. package/build/index.d.ts.map +1 -1
  26. package/build/index.js +41 -16
  27. package/build/index.js.map +1 -1
  28. package/build/middleware/auth.d.ts.map +1 -1
  29. package/build/middleware/auth.js +43 -6
  30. package/build/middleware/auth.js.map +1 -1
  31. package/build/middleware/chipset.d.ts.map +1 -1
  32. package/build/middleware/chipset.js +16 -1
  33. package/build/middleware/chipset.js.map +1 -1
  34. package/build/middleware/cleanup.d.ts +39 -7
  35. package/build/middleware/cleanup.d.ts.map +1 -1
  36. package/build/middleware/cleanup.js +148 -16
  37. package/build/middleware/cleanup.js.map +1 -1
  38. package/build/middleware/fetch-utils.d.ts +50 -7
  39. package/build/middleware/fetch-utils.d.ts.map +1 -1
  40. package/build/middleware/fetch-utils.js +127 -14
  41. package/build/middleware/fetch-utils.js.map +1 -1
  42. package/build/middleware/fs-utils.d.ts +126 -0
  43. package/build/middleware/fs-utils.d.ts.map +1 -0
  44. package/build/middleware/fs-utils.js +229 -0
  45. package/build/middleware/fs-utils.js.map +1 -0
  46. package/build/middleware/logger.d.ts +16 -0
  47. package/build/middleware/logger.d.ts.map +1 -1
  48. package/build/middleware/logger.js +18 -0
  49. package/build/middleware/logger.js.map +1 -1
  50. package/build/middleware/output-processor.d.ts.map +1 -1
  51. package/build/middleware/output-processor.js +43 -8
  52. package/build/middleware/output-processor.js.map +1 -1
  53. package/build/middleware/parse-utils.d.ts +19 -0
  54. package/build/middleware/parse-utils.d.ts.map +1 -0
  55. package/build/middleware/parse-utils.js +30 -0
  56. package/build/middleware/parse-utils.js.map +1 -0
  57. package/build/middleware/png-utils.d.ts.map +1 -1
  58. package/build/middleware/png-utils.js +33 -11
  59. package/build/middleware/png-utils.js.map +1 -1
  60. package/build/middleware/result-handle.d.ts +76 -0
  61. package/build/middleware/result-handle.d.ts.map +1 -0
  62. package/build/middleware/result-handle.js +378 -0
  63. package/build/middleware/result-handle.js.map +1 -0
  64. package/build/middleware/sanitize.d.ts +26 -0
  65. package/build/middleware/sanitize.d.ts.map +1 -1
  66. package/build/middleware/sanitize.js +43 -2
  67. package/build/middleware/sanitize.js.map +1 -1
  68. package/build/middleware/security.d.ts.map +1 -1
  69. package/build/middleware/security.js +30 -5
  70. package/build/middleware/security.js.map +1 -1
  71. package/build/middleware/ui-dump.d.ts.map +1 -1
  72. package/build/middleware/ui-dump.js +30 -10
  73. package/build/middleware/ui-dump.js.map +1 -1
  74. package/build/server.d.ts.map +1 -1
  75. package/build/server.js +34 -5
  76. package/build/server.js.map +1 -1
  77. package/build/tool-context.d.ts.map +1 -1
  78. package/build/tool-context.js +2 -0
  79. package/build/tool-context.js.map +1 -1
  80. package/build/tools/accessibility.d.ts.map +1 -1
  81. package/build/tools/accessibility.js +39 -40
  82. package/build/tools/accessibility.js.map +1 -1
  83. package/build/tools/at-commands.d.ts.map +1 -1
  84. package/build/tools/at-commands.js +31 -6
  85. package/build/tools/at-commands.js.map +1 -1
  86. package/build/tools/baseband.d.ts.map +1 -1
  87. package/build/tools/baseband.js +18 -7
  88. package/build/tools/baseband.js.map +1 -1
  89. package/build/tools/build.d.ts.map +1 -1
  90. package/build/tools/build.js +30 -8
  91. package/build/tools/build.js.map +1 -1
  92. package/build/tools/ci.d.ts.map +1 -1
  93. package/build/tools/ci.js +16 -5
  94. package/build/tools/ci.js.map +1 -1
  95. package/build/tools/control.d.ts.map +1 -1
  96. package/build/tools/control.js +19 -11
  97. package/build/tools/control.js.map +1 -1
  98. package/build/tools/device-farm.d.ts.map +1 -1
  99. package/build/tools/device-farm.js +27 -3
  100. package/build/tools/device-farm.js.map +1 -1
  101. package/build/tools/device-profiles.d.ts.map +1 -1
  102. package/build/tools/device-profiles.js +70 -8
  103. package/build/tools/device-profiles.js.map +1 -1
  104. package/build/tools/device.d.ts.map +1 -1
  105. package/build/tools/device.js +4 -2
  106. package/build/tools/device.js.map +1 -1
  107. package/build/tools/diagnostics.d.ts.map +1 -1
  108. package/build/tools/diagnostics.js +17 -9
  109. package/build/tools/diagnostics.js.map +1 -1
  110. package/build/tools/emulator.d.ts.map +1 -1
  111. package/build/tools/emulator.js +12 -13
  112. package/build/tools/emulator.js.map +1 -1
  113. package/build/tools/files.d.ts +1 -1
  114. package/build/tools/files.d.ts.map +1 -1
  115. package/build/tools/files.js +98 -56
  116. package/build/tools/files.js.map +1 -1
  117. package/build/tools/firmware-analysis.d.ts.map +1 -1
  118. package/build/tools/firmware-analysis.js +35 -12
  119. package/build/tools/firmware-analysis.js.map +1 -1
  120. package/build/tools/forwarding.d.ts.map +1 -1
  121. package/build/tools/forwarding.js +248 -6
  122. package/build/tools/forwarding.js.map +1 -1
  123. package/build/tools/health.d.ts.map +1 -1
  124. package/build/tools/health.js +14 -3
  125. package/build/tools/health.js.map +1 -1
  126. package/build/tools/input-gestures.d.ts.map +1 -1
  127. package/build/tools/input-gestures.js +65 -13
  128. package/build/tools/input-gestures.js.map +1 -1
  129. package/build/tools/logcat-watch.d.ts.map +1 -1
  130. package/build/tools/logcat-watch.js +21 -14
  131. package/build/tools/logcat-watch.js.map +1 -1
  132. package/build/tools/logs.d.ts.map +1 -1
  133. package/build/tools/logs.js +8 -4
  134. package/build/tools/logs.js.map +1 -1
  135. package/build/tools/mirroring.d.ts.map +1 -1
  136. package/build/tools/mirroring.js +21 -19
  137. package/build/tools/mirroring.js.map +1 -1
  138. package/build/tools/multi-device.d.ts.map +1 -1
  139. package/build/tools/multi-device.js +10 -0
  140. package/build/tools/multi-device.js.map +1 -1
  141. package/build/tools/network-capture.d.ts.map +1 -1
  142. package/build/tools/network-capture.js +32 -8
  143. package/build/tools/network-capture.js.map +1 -1
  144. package/build/tools/network-discovery.d.ts.map +1 -1
  145. package/build/tools/network-discovery.js +11 -2
  146. package/build/tools/network-discovery.js.map +1 -1
  147. package/build/tools/ota-monitor.d.ts.map +1 -1
  148. package/build/tools/ota-monitor.js +26 -12
  149. package/build/tools/ota-monitor.js.map +1 -1
  150. package/build/tools/packages.d.ts.map +1 -1
  151. package/build/tools/packages.js +18 -11
  152. package/build/tools/packages.js.map +1 -1
  153. package/build/tools/plugins.d.ts +18 -0
  154. package/build/tools/plugins.d.ts.map +1 -1
  155. package/build/tools/plugins.js +47 -2
  156. package/build/tools/plugins.js.map +1 -1
  157. package/build/tools/prompts.d.ts.map +1 -1
  158. package/build/tools/prompts.js +2 -0
  159. package/build/tools/prompts.js.map +1 -1
  160. package/build/tools/qemu.d.ts +6 -3
  161. package/build/tools/qemu.d.ts.map +1 -1
  162. package/build/tools/qemu.js +28 -16
  163. package/build/tools/qemu.js.map +1 -1
  164. package/build/tools/registry.d.ts.map +1 -1
  165. package/build/tools/registry.js +76 -26
  166. package/build/tools/registry.js.map +1 -1
  167. package/build/tools/regression.d.ts +37 -0
  168. package/build/tools/regression.d.ts.map +1 -1
  169. package/build/tools/regression.js +63 -22
  170. package/build/tools/regression.js.map +1 -1
  171. package/build/tools/resources.d.ts.map +1 -1
  172. package/build/tools/resources.js +62 -15
  173. package/build/tools/resources.js.map +1 -1
  174. package/build/tools/result-handles.d.ts +37 -0
  175. package/build/tools/result-handles.d.ts.map +1 -0
  176. package/build/tools/result-handles.js +286 -0
  177. package/build/tools/result-handles.js.map +1 -0
  178. package/build/tools/ril-intercept.d.ts.map +1 -1
  179. package/build/tools/ril-intercept.js +15 -18
  180. package/build/tools/ril-intercept.js.map +1 -1
  181. package/build/tools/screen-record.d.ts.map +1 -1
  182. package/build/tools/screen-record.js +34 -5
  183. package/build/tools/screen-record.js.map +1 -1
  184. package/build/tools/screenshot-diff.d.ts.map +1 -1
  185. package/build/tools/screenshot-diff.js +29 -21
  186. package/build/tools/screenshot-diff.js.map +1 -1
  187. package/build/tools/selinux-audit.d.ts.map +1 -1
  188. package/build/tools/selinux-audit.js +7 -5
  189. package/build/tools/selinux-audit.js.map +1 -1
  190. package/build/tools/sensors.d.ts.map +1 -1
  191. package/build/tools/sensors.js +26 -4
  192. package/build/tools/sensors.js.map +1 -1
  193. package/build/tools/shell.d.ts.map +1 -1
  194. package/build/tools/shell.js +9 -0
  195. package/build/tools/shell.js.map +1 -1
  196. package/build/tools/snapshot.d.ts.map +1 -1
  197. package/build/tools/snapshot.js +38 -19
  198. package/build/tools/snapshot.js.map +1 -1
  199. package/build/tools/split-apk.d.ts.map +1 -1
  200. package/build/tools/split-apk.js +48 -24
  201. package/build/tools/split-apk.js.map +1 -1
  202. package/build/tools/test-gen.d.ts.map +1 -1
  203. package/build/tools/test-gen.js +29 -10
  204. package/build/tools/test-gen.js.map +1 -1
  205. package/build/tools/testing.d.ts.map +1 -1
  206. package/build/tools/testing.js +27 -9
  207. package/build/tools/testing.js.map +1 -1
  208. package/build/tools/thermal-power.d.ts +8 -1
  209. package/build/tools/thermal-power.d.ts.map +1 -1
  210. package/build/tools/thermal-power.js +35 -10
  211. package/build/tools/thermal-power.js.map +1 -1
  212. package/build/tools/ui.d.ts.map +1 -1
  213. package/build/tools/ui.js +20 -13
  214. package/build/tools/ui.js.map +1 -1
  215. package/build/tools/wireless-firmware.d.ts.map +1 -1
  216. package/build/tools/wireless-firmware.js +21 -7
  217. package/build/tools/wireless-firmware.js.map +1 -1
  218. package/build/tools/wireless.d.ts.map +1 -1
  219. package/build/tools/wireless.js +39 -3
  220. package/build/tools/wireless.js.map +1 -1
  221. package/build/tools/workflow-market.d.ts.map +1 -1
  222. package/build/tools/workflow-market.js +67 -13
  223. package/build/tools/workflow-market.js.map +1 -1
  224. package/build/tools/workflow.d.ts.map +1 -1
  225. package/build/tools/workflow.js +77 -8
  226. package/build/tools/workflow.js.map +1 -1
  227. package/build/ws-transport.d.ts.map +1 -1
  228. package/build/ws-transport.js +21 -3
  229. package/build/ws-transport.js.map +1 -1
  230. 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":"AAAA;;;GAGG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGjD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CA+G1D"}
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"}
@@ -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
- const tempCheck = await ctx.bridge.shell('touch /sdcard/.DA_health_check && rm /sdcard/.DA_health_check && echo OK', {
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 (error) {
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":"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"}
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":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOjD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CA4pChE"}
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 { shellEscape } from "../middleware/sanitize.js";
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 '${shellEscape(escaped)}'`;
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.string().url().describe("URL to open (must be a valid URL with scheme)"),
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 '${shellEscape(url)}'`, { device: resolved.serial, ignoreExitCode: true });
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 '${shellEscape(text)}'`, { device: serial, ignoreExitCode: true });
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 '${shellEscape(text)}' 2>/dev/null`, { device: serial, ignoreExitCode: true });
187
- return { content: [{ type: "text", text: `Clipboard set (${text.length} chars). Note: if no clipper app is installed, use 'input text' as alternative.` }] };
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 compress it for reduced file size and LLM token usage. Captures at full resolution then uses device-side conversion to produce a smaller JPEG. Returns the local file path. Ideal for iterative UI testing where token cost matters.", {
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 = isOnDevice() ? "/data/local/tmp" : "/sdcard";
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.replace(/[^a-zA-Z0-9._-]/g, "_")
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 '${shellEscape(args.replace(/ /g, "%s"))}'`;
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
  }