mobile-debug-mcp 0.34.0 → 0.36.0

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 (500) hide show
  1. package/.agents/skills/mcp-builder/SKILL.md +44 -64
  2. package/.agents/skills/mcp-builder/references/build-flags.md +44 -43
  3. package/.agents/skills/mcp-builder/references/diagnostics-schema.md +4 -1
  4. package/.agents/skills/mcp-builder/references/toolchain-details.md +49 -62
  5. package/.agents/skills/spec-review/references/spec-review-checklist.md +7 -0
  6. package/.agents/skills/test-authoring/SKILL.md +7 -6
  7. package/.agents/skills/test-authoring/references/repo-test-layout.md +3 -1
  8. package/.agents/skills/test-authoring/references/test-authoring-workflow.md +16 -7
  9. package/.agents/skills/test-authoring/references/test-quality-checklist.md +2 -1
  10. package/.cursor-plugin/marketplace.json +1 -1
  11. package/.github/copilot-instructions.md +14 -36
  12. package/.github/workflows/ci.yml +4 -4
  13. package/AGENTS.md +12 -3
  14. package/CONTRIBUTING.md +79 -0
  15. package/README.md +218 -129
  16. package/agent-plugin/.antigravity-plugin/README.md +48 -0
  17. package/agent-plugin/.antigravity-plugin/plugin.json +18 -0
  18. package/agent-plugin/.claude-plugin/plugin.json +2 -2
  19. package/agent-plugin/.codex-plugin/plugin.json +1 -1
  20. package/agent-plugin/.cursor-plugin/plugin.json +2 -2
  21. package/agent-plugin/.mcp.json +1 -1
  22. package/agent-plugin/README.md +50 -11
  23. package/agent-plugin/cursor/.cursor-plugin/plugin.json +23 -0
  24. package/agent-plugin/cursor/LICENSE +201 -0
  25. package/agent-plugin/cursor/README.md +9 -0
  26. package/agent-plugin/cursor/skills/mobile-debug-mcp/SKILL.md +253 -0
  27. package/agent-plugin/mcp.json +1 -1
  28. package/agent-plugin/mcp_config.json +13 -0
  29. package/agent-plugin/plugin.json +1 -1
  30. package/agent-plugin/skills/mobile-debug-mcp/SKILL.md +62 -17
  31. package/dist/contracts/execution-controls.js +24 -0
  32. package/dist/contracts/selector.js +8 -0
  33. package/dist/contracts/target.js +15 -0
  34. package/dist/contracts/ui.js +1 -0
  35. package/dist/debugger/android.js +2 -1
  36. package/dist/debugger/diagnostics.js +54 -0
  37. package/dist/debugger/guard.js +4 -4
  38. package/dist/debugger/ios-worker.js +62 -0
  39. package/dist/debugger/ios.js +57 -0
  40. package/dist/debugger/protocol.js +33 -0
  41. package/dist/debugger/sessions.js +133 -74
  42. package/dist/debugger/worker.js +59 -46
  43. package/dist/interact/adjustment/adjustment-context.js +41 -0
  44. package/dist/interact/adjustment/adjustment-gestures.js +55 -0
  45. package/dist/interact/adjustment/adjustment-plan.js +18 -0
  46. package/dist/interact/adjustment/adjustment-resolution.js +127 -0
  47. package/dist/interact/adjustment/adjustment-result.js +19 -0
  48. package/dist/interact/adjustment/adjustment-semantic.js +64 -0
  49. package/dist/interact/adjustment/adjustment-verification.js +35 -0
  50. package/dist/interact/adjustment/adjustment.js +52 -0
  51. package/dist/interact/adjustment/control-geometry.js +87 -0
  52. package/dist/interact/adjustment/control-values.js +136 -0
  53. package/dist/interact/adjustment/slider-labels.js +84 -0
  54. package/dist/interact/assertions/screen-assertion.js +37 -0
  55. package/dist/interact/assertions/state-assertion-context.js +35 -0
  56. package/dist/interact/assertions/state-assertion.js +76 -0
  57. package/dist/interact/assertions/state-comparison.js +141 -0
  58. package/dist/interact/assertions/visibility-assertion.js +34 -0
  59. package/dist/interact/conditional-action.js +12 -7
  60. package/dist/interact/index.js +77 -3092
  61. package/dist/interact/input-service.js +1 -0
  62. package/dist/interact/interaction-trace.js +21 -0
  63. package/dist/interact/model.js +1 -0
  64. package/dist/interact/observation.js +1 -0
  65. package/dist/interact/targeting/candidate-ancestor.js +58 -0
  66. package/dist/interact/targeting/candidate-result.js +23 -0
  67. package/dist/interact/targeting/candidate-scoring.js +50 -0
  68. package/dist/interact/targeting/element-helpers.js +216 -0
  69. package/dist/interact/targeting/find-element.js +65 -0
  70. package/dist/interact/targeting/nearby-control.js +56 -0
  71. package/dist/interact/targeting/resolved-elements.js +21 -0
  72. package/dist/interact/targeting/tap-target.js +17 -0
  73. package/dist/interact/targeting/target-scope.js +104 -0
  74. package/dist/interact/waits/legacy-log-wait.js +38 -0
  75. package/dist/interact/waits/legacy-wait-observations.js +103 -0
  76. package/dist/interact/waits/legacy-wait-state.js +34 -0
  77. package/dist/interact/waits/legacy-wait.js +48 -0
  78. package/dist/interact/waits/screen-wait.js +49 -0
  79. package/dist/interact/waits/ui-change-helpers.js +157 -0
  80. package/dist/interact/waits/ui-change-state.js +18 -0
  81. package/dist/interact/waits/ui-change-wait.js +77 -0
  82. package/dist/interact/waits/ui-wait-condition.js +56 -0
  83. package/dist/interact/waits/ui-wait-state.js +38 -0
  84. package/dist/interact/waits/ui-wait.js +106 -0
  85. package/dist/manage/android.js +55 -51
  86. package/dist/manage/index.js +38 -75
  87. package/dist/manage/ios/build-types.js +1 -0
  88. package/dist/manage/ios/build.js +210 -0
  89. package/dist/manage/ios/project-discovery.js +29 -0
  90. package/dist/manage/ios/scheme.js +18 -0
  91. package/dist/manage/ios.js +44 -256
  92. package/dist/observe/android/context.js +1 -0
  93. package/dist/observe/android/current-screen.js +55 -0
  94. package/dist/observe/android/fingerprint.js +13 -0
  95. package/dist/observe/android/log-stream.js +132 -0
  96. package/dist/observe/android/logs.js +107 -0
  97. package/dist/observe/android/screenshot.js +70 -0
  98. package/dist/observe/android/ui-tree.js +107 -0
  99. package/dist/observe/android.js +16 -520
  100. package/dist/observe/evidence.js +114 -75
  101. package/dist/observe/index.js +16 -263
  102. package/dist/observe/ios/context.js +16 -0
  103. package/dist/observe/ios/fingerprint.js +12 -0
  104. package/dist/observe/ios/log-stream.js +105 -0
  105. package/dist/observe/ios/logs.js +168 -0
  106. package/dist/observe/ios/screenshot.js +22 -0
  107. package/dist/observe/ios/ui-elements.js +241 -0
  108. package/dist/observe/ios/ui-tree.js +137 -0
  109. package/dist/observe/ios-trace/artifact.js +63 -0
  110. package/dist/observe/ios-trace/capabilities.js +86 -0
  111. package/dist/observe/ios-trace/native.js +93 -0
  112. package/dist/observe/ios-trace/notification.js +35 -0
  113. package/dist/observe/ios-trace/process.js +98 -0
  114. package/dist/observe/ios-trace/sessions.js +244 -0
  115. package/dist/observe/ios.js +17 -715
  116. package/dist/observe/logs/model.js +1 -0
  117. package/dist/{network → observe/network}/index.js +2 -2
  118. package/dist/observe/readiness-service.js +5 -3
  119. package/dist/observe/readiness.js +33 -14
  120. package/dist/observe/screenshot-encoding.js +41 -0
  121. package/dist/observe/snapshot-collection.js +79 -0
  122. package/dist/observe/snapshot-logs.js +30 -0
  123. package/dist/observe/snapshot-metadata.js +43 -31
  124. package/dist/observe/snapshot-semantic.js +115 -0
  125. package/dist/observe/system-trace-error.js +7 -0
  126. package/dist/observe/system-trace-router.js +81 -0
  127. package/dist/observe/system-trace.js +26 -21
  128. package/dist/runtime/action-results.js +270 -0
  129. package/dist/runtime/clock.js +4 -0
  130. package/dist/server/common.js +1 -256
  131. package/dist/server/definitions/assert.js +162 -0
  132. package/dist/server/definitions/diagnostic.js +118 -0
  133. package/dist/server/definitions/input.js +274 -0
  134. package/dist/server/definitions/manage.js +194 -0
  135. package/dist/server/definitions/observe.js +149 -0
  136. package/dist/server/definitions/target.js +138 -0
  137. package/dist/server/definitions/wait.js +105 -0
  138. package/dist/server/handlers/action-execution.js +163 -0
  139. package/dist/server/handlers/correlation.js +23 -0
  140. package/dist/server/handlers/debugger-handlers.js +67 -0
  141. package/dist/server/handlers/debugger-response.js +9 -0
  142. package/dist/server/handlers/diagnostic.js +77 -0
  143. package/dist/server/handlers/handler-types.js +1 -0
  144. package/dist/server/handlers/input.js +154 -0
  145. package/dist/server/handlers/interact.js +113 -0
  146. package/dist/server/handlers/journey-assertions.js +35 -0
  147. package/dist/server/handlers/journey.js +147 -0
  148. package/dist/server/handlers/manage.js +119 -0
  149. package/dist/server/handlers/observe.js +95 -0
  150. package/dist/server/handlers/payload.js +4 -0
  151. package/dist/server/handlers/tool-handlers.js +170 -0
  152. package/dist/server/schemas/condition-schema.js +2 -0
  153. package/dist/server/{debugger-schema.js → schemas/debugger-schema.js} +20 -12
  154. package/dist/server/{journey-schema.js → schemas/journey-schema.js} +7 -22
  155. package/dist/server/schemas/system-trace-schema.js +22 -0
  156. package/dist/server/tool-definitions.js +58 -1135
  157. package/dist/server-core.js +2 -2
  158. package/dist/server.js +4 -4
  159. package/dist/system/android-status.js +101 -0
  160. package/dist/system/android.js +37 -27
  161. package/dist/system/index.js +13 -439
  162. package/dist/system/ios-status.js +132 -0
  163. package/dist/system/ios.js +11 -8
  164. package/dist/system/model.js +1 -0
  165. package/dist/system/probes.js +105 -0
  166. package/dist/system/status-summary.js +43 -0
  167. package/dist/system/unsupported-status.js +52 -0
  168. package/dist/utils/android/commands.js +89 -0
  169. package/dist/utils/android/gradle.js +139 -0
  170. package/dist/utils/android/logs.js +103 -0
  171. package/dist/utils/android/ui-tree.js +233 -0
  172. package/dist/utils/android/utils.js +6 -564
  173. package/dist/utils/cli/idb/idb-helper.js +4 -6
  174. package/dist/utils/cli/idb/install-idb.js +13 -9
  175. package/dist/utils/cli/ios/preflight-ios.js +42 -38
  176. package/dist/utils/ios/utils.js +48 -46
  177. package/dist/utils/java.js +65 -49
  178. package/dist/utils/resolve-device.js +4 -2
  179. package/dist/utils/ui/index.js +26 -26
  180. package/docs/CHANGELOG.md +67 -1
  181. package/docs/README.md +38 -0
  182. package/docs/agent-plugin-installation-verification.md +39 -0
  183. package/docs/benchmarks/README.md +18 -0
  184. package/docs/benchmarks/interaction-latency/README.md +14 -0
  185. package/docs/benchmarks/interaction-latency/final.md +4 -1
  186. package/docs/benchmarks/interaction-latency/ios-validation.md +2 -0
  187. package/docs/benchmarks/ui-tree-performance/README.md +24 -16
  188. package/docs/benchmarks/ui-tree-performance/comparison.md +11 -10
  189. package/docs/code-structure.md +120 -0
  190. package/docs/specs/001-state-verification/spec.md +16 -14
  191. package/docs/specs/002-richer-element-identity/spec.md +12 -10
  192. package/docs/specs/003-wait-and-synchronization-reliability/spec.md +12 -10
  193. package/docs/specs/004-action-verification-routing/spec.md +3 -3
  194. package/docs/specs/005-unified-action-execution-and-verification-model/spec.md +2 -0
  195. package/docs/specs/006-runtime-action-instrumentation-and-binding-layer/spec.md +2 -0
  196. package/docs/specs/007-actionability-resolution-and-executable-target-selection/spec.md +3 -1
  197. package/docs/specs/008-adjustable-control-support-and-semantic-value-manipulation/spec.md +3 -4
  198. package/docs/specs/009-semantic-control-modeling-for-custom-and-composite-controls/spec.md +2 -0
  199. package/docs/specs/010-verification-stabilization-and-temporal-convergence/spec.md +2 -2
  200. package/docs/specs/011-recovery-and-replanning-for-failed-or-ambiguous-interaction-flows/spec.md +2 -2
  201. package/docs/specs/011.1-recovery-contract-types-and-runtime-wiring-spec/spec.md +2 -0
  202. package/docs/specs/012-action-trace-and-xecution-observability/spec.md +2 -3
  203. package/docs/specs/013-wait-and-synchronization-reliability/spec.md +19 -19
  204. package/docs/specs/014-actionability-resolution/spec.md +20 -18
  205. package/docs/specs/015-adjustable-control-support/spec.md +17 -15
  206. package/docs/specs/016-environment-tools/spec.md +20 -20
  207. package/docs/specs/018-interaction-latency-and-batched-journeys/spec.md +2 -0
  208. package/docs/specs/019-ui-tree-observation-performance/spec.md +3 -1
  209. package/docs/specs/020-conditional-actions/intent.md +5 -3
  210. package/docs/specs/020-conditional-actions/plan.md +8 -9
  211. package/docs/specs/020-conditional-actions/spec.md +4 -2
  212. package/docs/specs/021-app-readiness-signals/intent.md +4 -2
  213. package/docs/specs/021-app-readiness-signals/plan.md +7 -6
  214. package/docs/specs/021-app-readiness-signals/spec.md +3 -1
  215. package/docs/specs/022-correlated-evidence/intent.md +4 -4
  216. package/docs/specs/022-correlated-evidence/plan.md +10 -11
  217. package/docs/specs/022-correlated-evidence/spec.md +3 -1
  218. package/docs/specs/023-android-system-traces/intent.md +3 -2
  219. package/docs/specs/023-android-system-traces/plan.md +5 -7
  220. package/docs/specs/023-android-system-traces/spec.md +3 -1
  221. package/docs/specs/024-android-heap-dump-capture/intent.md +3 -1
  222. package/docs/specs/025-android-runtime-debugger/intent.md +4 -3
  223. package/docs/specs/025-android-runtime-debugger/plan.md +3 -1
  224. package/docs/specs/025-android-runtime-debugger/spec.md +4 -3
  225. package/docs/specs/026-code-structure-and-quality-gates/health-plan.md +88 -0
  226. package/docs/specs/026-code-structure-and-quality-gates/intent.md +47 -0
  227. package/docs/specs/026-code-structure-and-quality-gates/plan.md +191 -0
  228. package/docs/specs/026-code-structure-and-quality-gates/spec.md +138 -0
  229. package/docs/specs/026-code-structure-and-quality-gates/verification-final.json +200 -0
  230. package/docs/specs/026-code-structure-and-quality-gates/verification-thin-facade.json +154 -0
  231. package/docs/specs/026-code-structure-and-quality-gates/verification.json +136 -0
  232. package/docs/specs/027-debugger-failure-diagnostics/intent.md +43 -0
  233. package/docs/specs/027-debugger-failure-diagnostics/plan.md +86 -0
  234. package/docs/specs/027-debugger-failure-diagnostics/spec.md +133 -0
  235. package/docs/specs/028-compact-debugger-responses/intent.md +41 -0
  236. package/docs/specs/028-compact-debugger-responses/plan.md +112 -0
  237. package/docs/specs/028-compact-debugger-responses/spec.md +108 -0
  238. package/docs/specs/029-kotlin-locals-validation/report.md +119 -0
  239. package/docs/specs/030-android-coroutine-validation/evidence.json +475 -0
  240. package/docs/specs/030-android-coroutine-validation/intent.md +47 -0
  241. package/docs/specs/030-android-coroutine-validation/plan.md +147 -0
  242. package/docs/specs/030-android-coroutine-validation/report.md +240 -0
  243. package/docs/specs/030-android-coroutine-validation/spec.md +107 -0
  244. package/docs/specs/031-ios-system-traces/intent.md +38 -0
  245. package/docs/specs/031-ios-system-traces/investigation.md +92 -0
  246. package/docs/specs/031-ios-system-traces/late-run-failure-evidence.json +32 -0
  247. package/docs/specs/031-ios-system-traces/plan.md +138 -0
  248. package/docs/specs/031-ios-system-traces/pr102-monitor-evidence.json +22 -0
  249. package/docs/specs/031-ios-system-traces/simulator-evidence.json +47 -0
  250. package/docs/specs/031-ios-system-traces/spec.md +135 -0
  251. package/docs/specs/032-ios-runtime-debugger/evidence.json +176 -0
  252. package/docs/specs/032-ios-runtime-debugger/intent.md +38 -0
  253. package/docs/specs/032-ios-runtime-debugger/plan.md +95 -0
  254. package/docs/specs/032-ios-runtime-debugger/report.md +205 -0
  255. package/docs/specs/032-ios-runtime-debugger/spec.md +153 -0
  256. package/docs/tools/TOOLS.md +31 -18
  257. package/docs/tools/{evidence.md → correlated-evidence.md} +9 -1
  258. package/docs/tools/debugger.md +120 -4
  259. package/docs/tools/interact.md +42 -20
  260. package/docs/tools/manage.md +25 -44
  261. package/docs/tools/observe.md +55 -23
  262. package/docs/tools/system-traces.md +89 -14
  263. package/docs/tools/system.md +12 -5
  264. package/package.json +12 -8
  265. package/scripts/agent-plugin.mjs +55 -5
  266. package/scripts/architecture-graph.mjs +69 -0
  267. package/scripts/check-architecture.mjs +47 -0
  268. package/scripts/code-quality-baseline.json +1 -0
  269. package/scripts/code-quality.mjs +161 -0
  270. package/src/contracts/execution-controls.ts +26 -0
  271. package/src/contracts/selector.ts +17 -0
  272. package/src/contracts/target.ts +19 -0
  273. package/src/contracts/ui.ts +94 -0
  274. package/src/debugger/android.ts +3 -2
  275. package/src/debugger/diagnostics.ts +43 -0
  276. package/src/debugger/guard.ts +10 -5
  277. package/src/debugger/ios-worker.ts +49 -0
  278. package/src/debugger/ios.ts +45 -0
  279. package/{worker → src/debugger/java}/DebuggerWorker.java +9 -3
  280. package/src/debugger/lldb/breakpoints.py +51 -0
  281. package/src/debugger/lldb/inspection.py +109 -0
  282. package/src/debugger/lldb/worker.py +203 -0
  283. package/src/debugger/protocol.ts +37 -0
  284. package/src/debugger/sessions.ts +326 -155
  285. package/src/debugger/worker.ts +57 -41
  286. package/src/interact/adjustment/adjustment-context.ts +83 -0
  287. package/src/interact/adjustment/adjustment-gestures.ts +48 -0
  288. package/src/interact/adjustment/adjustment-plan.ts +23 -0
  289. package/src/interact/adjustment/adjustment-resolution.ts +104 -0
  290. package/src/interact/adjustment/adjustment-result.ts +34 -0
  291. package/src/interact/adjustment/adjustment-semantic.ts +53 -0
  292. package/src/interact/adjustment/adjustment-verification.ts +60 -0
  293. package/src/interact/adjustment/adjustment.ts +49 -0
  294. package/src/interact/adjustment/control-geometry.ts +120 -0
  295. package/src/interact/adjustment/control-values.ts +141 -0
  296. package/src/interact/adjustment/slider-labels.ts +84 -0
  297. package/src/interact/assertions/screen-assertion.ts +39 -0
  298. package/src/interact/assertions/state-assertion-context.ts +48 -0
  299. package/src/interact/assertions/state-assertion.ts +64 -0
  300. package/src/interact/assertions/state-comparison.ts +168 -0
  301. package/src/interact/assertions/visibility-assertion.ts +41 -0
  302. package/src/interact/conditional-action.ts +18 -12
  303. package/src/interact/index.ts +89 -3494
  304. package/src/interact/input-service.ts +11 -0
  305. package/src/interact/interaction-trace.ts +36 -0
  306. package/src/interact/model.ts +109 -0
  307. package/src/interact/observation.ts +7 -0
  308. package/src/interact/targeting/candidate-ancestor.ts +49 -0
  309. package/src/interact/targeting/candidate-result.ts +24 -0
  310. package/src/interact/targeting/candidate-scoring.ts +42 -0
  311. package/src/interact/targeting/element-helpers.ts +243 -0
  312. package/src/interact/targeting/find-element.ts +47 -0
  313. package/src/interact/targeting/nearby-control.ts +46 -0
  314. package/src/interact/targeting/resolved-elements.ts +20 -0
  315. package/src/interact/targeting/tap-target.ts +22 -0
  316. package/src/interact/targeting/target-scope.ts +121 -0
  317. package/src/interact/waits/legacy-log-wait.ts +30 -0
  318. package/src/interact/waits/legacy-wait-observations.ts +55 -0
  319. package/src/interact/waits/legacy-wait-state.ts +48 -0
  320. package/src/interact/waits/legacy-wait.ts +34 -0
  321. package/src/interact/waits/screen-wait.ts +37 -0
  322. package/src/interact/waits/ui-change-helpers.ts +184 -0
  323. package/src/interact/waits/ui-change-state.ts +26 -0
  324. package/src/interact/waits/ui-change-wait.ts +59 -0
  325. package/src/interact/waits/ui-wait-condition.ts +52 -0
  326. package/src/interact/waits/ui-wait-state.ts +55 -0
  327. package/src/interact/waits/ui-wait.ts +84 -0
  328. package/src/manage/android.ts +56 -50
  329. package/src/manage/index.ts +63 -93
  330. package/src/manage/ios/build-types.ts +14 -0
  331. package/src/manage/ios/build.ts +230 -0
  332. package/src/manage/ios/project-discovery.ts +26 -0
  333. package/src/manage/ios/scheme.ts +16 -0
  334. package/src/manage/ios.ts +67 -298
  335. package/src/observe/android/context.ts +1 -0
  336. package/src/observe/android/current-screen.ts +63 -0
  337. package/src/observe/android/fingerprint.ts +13 -0
  338. package/src/observe/android/log-stream.ts +127 -0
  339. package/src/observe/android/logs.ts +110 -0
  340. package/src/observe/android/screenshot.ts +79 -0
  341. package/src/observe/android/ui-tree.ts +113 -0
  342. package/src/observe/android.ts +17 -531
  343. package/src/observe/evidence.ts +122 -70
  344. package/src/observe/index.ts +24 -286
  345. package/src/observe/ios/context.ts +22 -0
  346. package/src/observe/ios/fingerprint.ts +12 -0
  347. package/src/observe/ios/log-stream.ts +103 -0
  348. package/src/observe/ios/logs.ts +179 -0
  349. package/src/observe/ios/screenshot.ts +26 -0
  350. package/src/observe/ios/ui-elements.ts +266 -0
  351. package/src/observe/ios/ui-tree.ts +143 -0
  352. package/src/observe/ios-trace/artifact.ts +55 -0
  353. package/src/observe/ios-trace/capabilities.ts +74 -0
  354. package/src/observe/ios-trace/native.ts +72 -0
  355. package/src/observe/ios-trace/notification.ts +30 -0
  356. package/src/observe/ios-trace/process.ts +82 -0
  357. package/src/observe/ios-trace/sessions.ts +204 -0
  358. package/src/observe/ios.ts +18 -749
  359. package/src/observe/logs/model.ts +11 -0
  360. package/src/{network → observe/network}/index.ts +2 -2
  361. package/src/observe/readiness-service.ts +6 -4
  362. package/src/observe/readiness.ts +42 -21
  363. package/src/observe/screenshot-encoding.ts +32 -0
  364. package/src/observe/snapshot-collection.ts +69 -0
  365. package/src/observe/snapshot-logs.ts +26 -0
  366. package/src/observe/snapshot-metadata.ts +51 -31
  367. package/src/observe/snapshot-semantic.ts +153 -0
  368. package/src/observe/system-trace-error.ts +3 -0
  369. package/src/observe/system-trace-router.ts +61 -0
  370. package/src/observe/system-trace.ts +28 -19
  371. package/src/runtime/action-results.ts +376 -0
  372. package/src/runtime/clock.ts +9 -0
  373. package/src/server/common.ts +9 -359
  374. package/src/server/definitions/assert.ts +164 -0
  375. package/src/server/definitions/diagnostic.ts +120 -0
  376. package/src/server/definitions/input.ts +276 -0
  377. package/src/server/definitions/manage.ts +196 -0
  378. package/src/server/definitions/observe.ts +151 -0
  379. package/src/server/definitions/target.ts +140 -0
  380. package/src/server/definitions/wait.ts +107 -0
  381. package/src/server/handlers/action-execution.ts +164 -0
  382. package/src/server/handlers/correlation.ts +20 -0
  383. package/src/server/handlers/debugger-handlers.ts +58 -0
  384. package/src/server/handlers/debugger-response.ts +7 -0
  385. package/src/server/handlers/diagnostic.ts +86 -0
  386. package/src/server/handlers/handler-types.ts +7 -0
  387. package/src/server/handlers/input.ts +173 -0
  388. package/src/server/handlers/interact.ts +135 -0
  389. package/src/server/handlers/journey-assertions.ts +39 -0
  390. package/src/server/handlers/journey.ts +132 -0
  391. package/src/server/handlers/manage.ts +141 -0
  392. package/src/server/handlers/observe.ts +116 -0
  393. package/src/server/handlers/payload.ts +36 -0
  394. package/src/server/handlers/tool-handlers.ts +170 -0
  395. package/src/server/schemas/condition-schema.ts +2 -0
  396. package/src/server/schemas/debugger-schema.ts +46 -0
  397. package/src/server/{journey-schema.ts → schemas/journey-schema.ts} +7 -22
  398. package/src/server/schemas/system-trace-schema.ts +23 -0
  399. package/src/server/tool-definitions.ts +67 -1143
  400. package/src/server-core.ts +2 -2
  401. package/src/server.ts +4 -4
  402. package/src/system/android-status.ts +124 -0
  403. package/src/system/android.ts +33 -21
  404. package/src/system/index.ts +21 -525
  405. package/src/system/ios-status.ts +147 -0
  406. package/src/system/ios.ts +14 -10
  407. package/src/system/model.ts +41 -0
  408. package/src/system/probes.ts +114 -0
  409. package/src/system/status-summary.ts +47 -0
  410. package/src/system/unsupported-status.ts +55 -0
  411. package/src/types.ts +16 -106
  412. package/src/utils/android/commands.ts +81 -0
  413. package/src/utils/android/gradle.ts +132 -0
  414. package/src/utils/android/logs.ts +103 -0
  415. package/src/utils/android/ui-tree.ts +239 -0
  416. package/src/utils/android/utils.ts +16 -556
  417. package/src/utils/cli/idb/check-idb.ts +4 -4
  418. package/src/utils/cli/idb/idb-helper.ts +6 -8
  419. package/src/utils/cli/idb/install-idb.ts +15 -9
  420. package/src/utils/cli/ios/preflight-ios.ts +39 -31
  421. package/src/utils/cli/ios/run-ios-ui-tree-tap.ts +4 -4
  422. package/src/utils/ios/utils.ts +60 -61
  423. package/src/utils/java.ts +62 -43
  424. package/src/utils/resolve-device.ts +9 -6
  425. package/src/utils/ui/index.ts +28 -28
  426. package/test/device/automated/debugger/coroutine-lifecycle.ts +27 -0
  427. package/test/device/automated/debugger/coroutine-support.ts +140 -0
  428. package/test/device/automated/debugger/coroutine.android.test.ts +169 -0
  429. package/test/device/automated/debugger/kotlin.android.test.ts +127 -0
  430. package/test/device/automated/debugger/runtime.android.test.ts +19 -3
  431. package/test/device/automated/debugger/runtime.ios.test.ts +221 -0
  432. package/test/device/automated/interact/interaction.android.test.ts +56 -0
  433. package/test/device/automated/observe/ios-system-trace.smoke.ts +119 -0
  434. package/test/device/automated/observe/system-trace.smoke.ts +1 -1
  435. package/test/device/manual/interact/modul8_selector_tap.manual.ts +1 -1
  436. package/test/fixtures/contracts/tool-definitions.json +3696 -0
  437. package/test/fixtures/coroutine-app/MainActivity.kt +100 -0
  438. package/test/fixtures/coroutine-app/README.md +48 -0
  439. package/test/fixtures/coroutine-app/build.ts +35 -0
  440. package/test/fixtures/debugger-app/MainActivity.java +21 -0
  441. package/test/fixtures/debugger-app/MainActivity.kt +32 -0
  442. package/test/fixtures/debugger-app/README.md +52 -0
  443. package/test/fixtures/debugger-app/build.ts +25 -15
  444. package/test/fixtures/ios-debugger-app/Main.swift +95 -0
  445. package/test/fixtures/ios-debugger-app/README.md +27 -0
  446. package/test/fixtures/ios-debugger-app/build.ts +21 -0
  447. package/test/fixtures/latency-journey-app/README.md +2 -0
  448. package/test/fixtures/readiness-app/README.md +2 -0
  449. package/test/helpers/ios-trace-shutdown.ts +29 -0
  450. package/test/unit/debugger/coroutine-lifecycle.test.ts +43 -0
  451. package/test/unit/debugger/diagnostic-sessions.test.ts +67 -0
  452. package/test/unit/debugger/diagnostics.test.ts +80 -0
  453. package/test/unit/debugger/guards.test.ts +4 -0
  454. package/test/unit/debugger/handle-guards.test.ts +88 -0
  455. package/test/unit/debugger/ios-preparation.test.ts +57 -0
  456. package/test/unit/debugger/ios.test.ts +104 -0
  457. package/test/unit/debugger/lldb-worker.py +166 -0
  458. package/test/unit/debugger/lldb-worker.test.ts +10 -0
  459. package/test/unit/debugger/preparation.test.ts +32 -0
  460. package/test/unit/debugger/protocol.test.ts +15 -0
  461. package/test/unit/debugger/sessions.test.ts +1 -1
  462. package/test/unit/interact/adjustment_service.test.ts +42 -0
  463. package/test/unit/interact/adjustment_verification.test.ts +35 -0
  464. package/test/unit/interact/clock_dependencies.test.ts +63 -0
  465. package/test/unit/interact/element_helpers.test.ts +19 -0
  466. package/test/unit/interact/legacy_wait_service.test.ts +17 -0
  467. package/test/unit/interact/state_comparison.test.ts +20 -0
  468. package/test/unit/interact/target_scope.test.ts +35 -0
  469. package/test/unit/network/get_network_activity.test.ts +1 -1
  470. package/test/unit/observe/ios-trace-native.test.ts +90 -0
  471. package/test/unit/observe/ios-trace-sessions.test.ts +131 -0
  472. package/test/unit/observe/readiness.test.ts +1 -1
  473. package/test/unit/observe/screenshot-encoding.test.ts +30 -0
  474. package/test/unit/observe/snapshot-logs.test.ts +14 -0
  475. package/test/unit/observe/system-trace-router.test.ts +49 -0
  476. package/test/unit/quality/agent-plugin-export.test.ts +38 -0
  477. package/test/unit/quality/architecture.test.ts +60 -0
  478. package/test/unit/quality/code-quality.test.ts +58 -0
  479. package/test/unit/quality/interaction_boundaries.test.ts +24 -0
  480. package/test/unit/server/conditional_actions.test.ts +1 -1
  481. package/test/unit/server/conditional_schema.test.ts +2 -2
  482. package/test/unit/server/debugger-response.test.ts +63 -0
  483. package/test/unit/server/system-trace.test.ts +12 -3
  484. package/test/unit/server/tool_definitions_parity.test.ts +8 -0
  485. package/dist/server/debugger-handlers.js +0 -38
  486. package/dist/server/system-trace-schema.js +0 -17
  487. package/dist/server/tool-handlers.js +0 -1000
  488. package/docs/ROADMAP.md +0 -646
  489. package/docs/benchmarks/ui-tree-performance/bridge-probe.json +0 -29
  490. package/docs/benchmarks/ui-tree-performance/initial-baseline.json +0 -59560
  491. package/docs/specs/baseline-spec-v0.md +0 -312
  492. package/docs/specs/mcp-tooling-spec-v1.md +0 -446
  493. package/src/server/debugger-handlers.ts +0 -29
  494. package/src/server/debugger-schema.ts +0 -37
  495. package/src/server/system-trace-schema.ts +0 -17
  496. package/src/server/tool-handlers.ts +0 -1008
  497. /package/dist/{server/condition-schema.js → contracts/condition.js} +0 -0
  498. /package/dist/server/{readiness-schema.js → schemas/readiness-schema.js} +0 -0
  499. /package/src/{server/condition-schema.ts → contracts/condition.ts} +0 -0
  500. /package/src/server/{readiness-schema.ts → schemas/readiness-schema.ts} +0 -0
@@ -6,7 +6,7 @@ import { getIdbCmd, isIDBInstalled, commandWhich } from './idb-helper.js'
6
6
  const IDB_PKG = 'fb-idb'
7
7
 
8
8
  function runCommand(cmd: string, args: string[]): number {
9
- const res = spawnSync(cmd, args, { stdio: 'inherit' as any })
9
+ const res = spawnSync(cmd, args, { stdio: 'inherit' })
10
10
  return typeof res.status === 'number' ? res.status : 1
11
11
  }
12
12
 
@@ -42,14 +42,7 @@ async function main() {
42
42
  console.log('Auto-install enabled (MCP_AUTO_INSTALL_IDB=true or CI=true)')
43
43
  }
44
44
 
45
- const attempts: { name: string; cmd: string; args: string[] }[] = []
46
- if (commandWhich('pipx')) attempts.push({ name: 'pipx', cmd: 'pipx', args: ['install', IDB_PKG] })
47
- if (commandWhich('pip') || commandWhich('python3')) attempts.push({ name: 'pip', cmd: commandWhich('pip') ? 'pip' : 'python3', args: commandWhich('pip') ? ['install', '--user', IDB_PKG] : ['-m', 'pip', 'install', '--user', IDB_PKG] })
48
-
49
- // Add brew as a fallback on macOS if present (best-effort)
50
- if (process.platform === 'darwin' && commandWhich('brew')) {
51
- attempts.push({ name: 'brew', cmd: 'brew', args: ['install', 'idb'] })
52
- }
45
+ const attempts = installerCandidates()
53
46
 
54
47
  if (attempts.length === 0) {
55
48
  console.error('No installer tool (pipx/pip/brew) detected. Please install pipx or pip and re-run.')
@@ -88,3 +81,16 @@ if (scriptPath === process.argv[1]) {
88
81
  }
89
82
 
90
83
  export { main }
84
+
85
+ function installerCandidates() {
86
+ const attempts: { name: string; cmd: string; args: string[] }[] = []
87
+ if (commandWhich('pipx')) attempts.push({ name: 'pipx', cmd: 'pipx', args: ['install', IDB_PKG] })
88
+ if (commandWhich('pip') || commandWhich('python3')) attempts.push({ name: 'pip', cmd: commandWhich('pip') ? 'pip' : 'python3', args: commandWhich('pip') ? ['install', '--user', IDB_PKG] : ['-m', 'pip', 'install', '--user', IDB_PKG] })
89
+
90
+ // Add brew as a fallback on macOS if present (best-effort)
91
+ if (process.platform === 'darwin' && commandWhich('brew')) {
92
+ attempts.push({ name: 'brew', cmd: 'brew', args: ['install', 'idb'] })
93
+ }
94
+
95
+ return attempts
96
+ }
@@ -9,22 +9,21 @@ async function exists(p: string) {
9
9
  }
10
10
 
11
11
  async function findFirst(root: string, patterns: string[], maxDepth = 4): Promise<string | null> {
12
- const queue: Array<{dir:string, depth:number}> = [{dir: root, depth:0}]
12
+ const queue: Array<{ dir: string, depth: number }> = [{ dir: root, depth: 0 }]
13
13
  while (queue.length) {
14
- const {dir, depth} = queue.shift()!
14
+ const { dir, depth } = queue.shift()!
15
15
  try {
16
16
  console.error('DEBUG findFirst: reading dir', dir, 'depth', depth)
17
17
  const ents: fs.Dirent[] = await fs.promises.readdir(dir, { withFileTypes: true })
18
- console.error('DEBUG findFirst: entries', ents.map(e=>e.name))
18
+ console.error('DEBUG findFirst: entries', ents.map(e => e.name))
19
19
  for (const e of ents) {
20
20
  const full = path.join(dir, e.name)
21
- for (const p of patterns) {
22
- if (e.name.endsWith(p)) {
23
- console.error('DEBUG findFirst: matched', full)
24
- return full
25
- }
21
+ const matched = patterns.some(pattern => e.name.endsWith(pattern))
22
+ if (matched) {
23
+ console.error('DEBUG findFirst: matched', full)
24
+ return full
26
25
  }
27
- if (e.isDirectory() && depth < maxDepth) queue.push({dir: full, depth: depth+1})
26
+ if (e.isDirectory() && depth < maxDepth) queue.push({ dir: full, depth: depth + 1 })
28
27
  }
29
28
  } catch (err) {
30
29
  console.error('DEBUG findFirst: read failed for', dir, err)
@@ -46,18 +45,7 @@ function startCompanionIfNeeded(companionPath: string | null, udid: string | nul
46
45
  }
47
46
 
48
47
  async function main() {
49
- const args = process.argv.slice(2)
50
- let projectArg: string | undefined
51
- let udid: string | undefined
52
- let startCompanion = false
53
- let kmpBuild: any = null
54
- for (let i=0;i<args.length;i++) {
55
- const a = args[i]
56
- if (a === '--project' && args[i+1]) { projectArg = args[i+1]; i++ }
57
- else if (a === '--udid' && args[i+1]) { udid = args[i+1]; i++ }
58
- else if (a === '--start-companion') startCompanion = true
59
- else if (!projectArg) projectArg = a
60
- }
48
+ const { projectArg, udid, startCompanion, kmpBuild } = argumentsForPreflight(process.argv.slice(2))
61
49
 
62
50
  const cwd = process.cwd()
63
51
  const projectRoot = projectArg ? path.resolve(projectArg) : cwd
@@ -98,13 +86,10 @@ async function main() {
98
86
  const companionPath = commandWhich('idb_companion')
99
87
  const companionAvailable = !!companionPath
100
88
 
101
- const suggestions: string[] = []
102
- if (!projectFile) suggestions.push('Provide correct project path or ensure .xcodeproj/.xcworkspace exists in project dir')
103
- if (!kmpFramework) suggestions.push('Run KMP Gradle task to produce Shared.framework before xcodebuild (e.g., :shared:embedAndSignAppleFrameworkForXcode)')
104
- if (!idbAvailable) suggestions.push('Ensure idb is in PATH or set MCP_IDB_PATH / IDB_PATH')
105
- if (!companionAvailable) suggestions.push('Install idb_companion and ensure it is in PATH')
89
+ const suggestions = preflightSuggestions(projectFile, kmpFramework, idbAvailable, companionAvailable)
106
90
 
107
- const result: any = {
91
+ const companionInfo: { cmd: string | null; installed: boolean; start?: ReturnType<typeof startCompanionIfNeeded> } = { cmd: companionPath, installed: companionAvailable }
92
+ const result = {
108
93
  ok: !!projectFile && !!idbAvailable,
109
94
  project: {
110
95
  root: projectRoot,
@@ -120,10 +105,7 @@ async function main() {
120
105
  cmd: idbPath,
121
106
  installed: idbAvailable
122
107
  },
123
- idb_companion: {
124
- cmd: companionPath,
125
- installed: companionAvailable
126
- },
108
+ idb_companion: companionInfo,
127
109
  suggestions
128
110
  }
129
111
 
@@ -142,3 +124,29 @@ main().catch(e => {
142
124
  console.log(JSON.stringify({ ok: false, error: e instanceof Error ? e.message : String(e) }, null, 2))
143
125
  process.exit(2)
144
126
  })
127
+
128
+ function argumentsForPreflight(args: string[]) {
129
+ let projectArg: string | undefined
130
+ let udid: string | undefined
131
+ let startCompanion = false
132
+ const kmpBuild = null
133
+ for (let i = 0; i < args.length; i++) {
134
+ const a = args[i]
135
+ if (a === '--project' && args[i + 1]) { projectArg = args[i + 1]; i++ }
136
+ else if (a === '--udid' && args[i + 1]) { udid = args[i + 1]; i++ }
137
+ else if (a === '--start-companion') startCompanion = true
138
+ else if (!projectArg) projectArg = a
139
+ }
140
+
141
+ return { projectArg, udid, startCompanion, kmpBuild }
142
+ }
143
+
144
+ function preflightSuggestions(projectFile: string | null, kmpFramework: string | null, idbAvailable: boolean, companionAvailable: boolean) {
145
+ const suggestions: string[] = []
146
+ if (!projectFile) suggestions.push('Provide correct project path or ensure .xcodeproj/.xcworkspace exists in project dir')
147
+ if (!kmpFramework) suggestions.push('Run KMP Gradle task to produce Shared.framework before xcodebuild (e.g., :shared:embedAndSignAppleFrameworkForXcode)')
148
+ if (!idbAvailable) suggestions.push('Ensure idb is in PATH or set MCP_IDB_PATH / IDB_PATH')
149
+ if (!companionAvailable) suggestions.push('Install idb_companion and ensure it is in PATH')
150
+
151
+ return suggestions
152
+ }
@@ -7,7 +7,7 @@ async function main() {
7
7
  const interact = new iOSInteract();
8
8
 
9
9
  console.log('Fetching UI tree...');
10
- const tree = await obs.getUITree(deviceId as any);
10
+ const tree = await obs.getUITree(deviceId);
11
11
  if (tree.error) {
12
12
  console.error('getUITree error:', tree.error);
13
13
  process.exit(2);
@@ -18,12 +18,12 @@ async function main() {
18
18
  process.exit(3);
19
19
  }
20
20
 
21
- const clickable = tree.elements.find((e: any) => e.clickable) || tree.elements[0];
21
+ const clickable = tree.elements.find((e: import('../../../types.js').UIElement) => e.clickable) || tree.elements[0];
22
22
  console.log('Using element:', clickable.text || '(no text)', 'clickable=', clickable.clickable, 'center=', clickable.center);
23
- const [x,y] = clickable.center || [0,0];
23
+ const [x, y] = clickable.center || [0, 0];
24
24
 
25
25
  console.log(`Tapping at ${x},${y}...`);
26
- const res = await interact.tap(x, y, deviceId as any);
26
+ const res = await interact.tap(x, y, deviceId);
27
27
  console.log('Tap result:', res);
28
28
 
29
29
  if (res.success) process.exit(0);
@@ -1,3 +1,4 @@
1
+ import { checkDebuggerGuard } from '../../debugger/guard.js'
1
2
  import { remainingBudget } from '../operation-budget.js'
2
3
  import { execFile, spawn, execSync, spawnSync } from "child_process"
3
4
  import { DeviceInfo } from "../../types.js"
@@ -19,17 +20,14 @@ export function getConfiguredIdbPath(): string | undefined {
19
20
  for (const p of cfgPaths) {
20
21
  if (!p) continue
21
22
  try {
22
- if (fs.existsSync(p)) {
23
- const raw = fs.readFileSync(p, 'utf8')
24
- const json = JSON.parse(raw)
25
- if (json) {
26
- if (json.idbPath) return json.idbPath
27
- if (json.IDB_PATH) return json.IDB_PATH
28
- }
29
- }
30
- } catch {}
23
+ if (!fs.existsSync(p)) continue
24
+ const raw = fs.readFileSync(p, 'utf8')
25
+ const json = JSON.parse(raw)
26
+ if (json?.idbPath) return json.idbPath
27
+ if (json?.IDB_PATH) return json.IDB_PATH
28
+ } catch { }
31
29
  }
32
- } catch {}
30
+ } catch { }
33
31
  return undefined
34
32
  }
35
33
 
@@ -38,13 +36,13 @@ export function getIdbCmd() {
38
36
  if (cfg) return cfg
39
37
  if (process.env.IDB_PATH) return process.env.IDB_PATH
40
38
  try {
41
- const p = execSync('which idb', { stdio: ['ignore','pipe','ignore'] }).toString().trim()
39
+ const p = execSync('which idb', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()
42
40
  if (p) return p
43
- } catch {}
41
+ } catch { }
44
42
  try {
45
- const p2 = execSync('command -v idb', { stdio: ['ignore','pipe','ignore'] }).toString().trim()
43
+ const p2 = execSync('command -v idb', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()
46
44
  if (p2) return p2
47
- } catch {}
45
+ } catch { }
48
46
  // check common user locations
49
47
  const common = [
50
48
  `${process.env.HOME}/Library/Python/3.9/bin/idb`,
@@ -53,7 +51,7 @@ export function getIdbCmd() {
53
51
  '/usr/local/bin/idb',
54
52
  ]
55
53
  for (const c of common) {
56
- try { execSync(`test -x ${c}`, { stdio: ['ignore','pipe','ignore'] }); return c } catch {}
54
+ try { execSync(`test -x ${c}`, { stdio: ['ignore', 'pipe', 'ignore'] }); return c } catch { }
57
55
  }
58
56
  return 'idb'
59
57
  }
@@ -62,7 +60,7 @@ export async function isIDBInstalled(): Promise<boolean> {
62
60
  const cmd = getIdbCmd()
63
61
  if (!cmd) return false
64
62
  try {
65
- execSync(`${cmd} list-targets --json`, { stdio: ['ignore','pipe','pipe'], timeout: remainingBudget(5000), killSignal: 'SIGKILL' })
63
+ execSync(`${cmd} list-targets --json`, { stdio: ['ignore', 'pipe', 'pipe'], timeout: remainingBudget(5000), killSignal: 'SIGKILL' })
66
64
  return true
67
65
  } catch (e: unknown) {
68
66
  console.debug(`[isIDBInstalled] idb probe failed for '${cmd}': ${e instanceof Error ? e.message : String(e)}`)
@@ -88,8 +86,9 @@ export function execCommand(args: string[], deviceId: string = "booted"): Promis
88
86
  return new Promise((resolve, reject) => {
89
87
  // Use spawn for better stream control and consistency with Android implementation
90
88
  const budgetMs = remainingBudget()
89
+ checkDebuggerGuard()
91
90
  const child = spawn(getXcrunCmd(), args)
92
-
91
+
93
92
  let stdout = ''
94
93
  let stderr = ''
95
94
 
@@ -106,8 +105,8 @@ export function execCommand(args: string[], deviceId: string = "booted"): Promis
106
105
  }
107
106
 
108
107
  const DEFAULT_XCRUN_LOG_TIMEOUT = parseInt(process.env.MCP_XCRUN_LOG_TIMEOUT || '', 10) || 30000 // env (ms) or default 30s
109
- const DEFAULT_XCRUN_CMD_TIMEOUT = parseInt(process.env.MCP_XCRUN_TIMEOUT || '', 10) || 60000 // env (ms) or default 60s
110
- const timeoutMs = budgetMs || (args.includes('log') ? DEFAULT_XCRUN_LOG_TIMEOUT : DEFAULT_XCRUN_CMD_TIMEOUT) // choose appropriate timeout
108
+ const DEFAULT_XCRUN_CMD_TIMEOUT = parseInt(process.env.MCP_XCRUN_TIMEOUT || '', 10) || 60000 // env (ms) or default 60s
109
+ const timeoutMs = budgetMs || (args.includes('log') ? DEFAULT_XCRUN_LOG_TIMEOUT : DEFAULT_XCRUN_CMD_TIMEOUT) // choose appropriate timeout
111
110
  const timeout = setTimeout(() => {
112
111
  child.kill()
113
112
  reject(new Error(`Command timed out after ${timeoutMs}ms: ${getXcrunCmd()} ${args.join(' ')}`))
@@ -135,12 +134,13 @@ export function execCommandWithDiagnostics(args: string[], deviceId: string = "b
135
134
  const DEFAULT_XCRUN_LOG_TIMEOUT = parseInt(process.env.MCP_XCRUN_LOG_TIMEOUT || '', 10) || 30000
136
135
  const DEFAULT_XCRUN_CMD_TIMEOUT = parseInt(process.env.MCP_XCRUN_TIMEOUT || '', 10) || 60000
137
136
  const timeoutMs = budgetMs || (args.includes('log') ? DEFAULT_XCRUN_LOG_TIMEOUT : DEFAULT_XCRUN_CMD_TIMEOUT)
138
- const res = spawnSync(getXcrunCmd(), args, { encoding: 'utf8', timeout: timeoutMs }) as any
137
+ checkDebuggerGuard()
138
+ const res = spawnSync(getXcrunCmd(), args, { encoding: 'utf8', timeout: timeoutMs })
139
139
  const runResult = {
140
140
  exitCode: typeof res.status === 'number' ? res.status : null,
141
141
  stdout: res.stdout || '',
142
142
  stderr: res.stderr || '',
143
- envSnapshot: makeEnvSnapshot(['PATH','IDB_PATH','JAVA_HOME','HOME']),
143
+ envSnapshot: makeEnvSnapshot(['PATH', 'IDB_PATH', 'JAVA_HOME', 'HOME']),
144
144
  command: getXcrunCmd(),
145
145
  args,
146
146
  deviceId
@@ -168,17 +168,17 @@ function parseRuntimeName(runtime: string): string {
168
168
  try {
169
169
  const parts = runtime.split('.')
170
170
  const lastPart = parts[parts.length - 1] // e.g. "iOS-17-0"
171
-
171
+
172
172
  // Split by hyphen to separate OS from version numbers
173
173
  // e.g. "iOS-17-0" -> ["iOS", "17", "0"]
174
174
  const segments = lastPart.split('-');
175
-
175
+
176
176
  if (segments.length > 1) {
177
- const os = segments[0]; // "iOS"
178
- const version = segments.slice(1).join('.'); // "17.0"
179
- return `${os} ${version}`;
177
+ const os = segments[0]; // "iOS"
178
+ const version = segments.slice(1).join('.'); // "17.0"
179
+ return `${os} ${version}`;
180
180
  }
181
-
181
+
182
182
  return lastPart;
183
183
  } catch {
184
184
  return runtime
@@ -220,18 +220,17 @@ export async function getIOSDeviceMetadata(deviceId: string = "booted"): Promise
220
220
 
221
221
  for (const runtime in devicesMap) {
222
222
  const devices = devicesMap[runtime]
223
- if (Array.isArray(devices)) {
224
- for (const device of devices) {
225
- if (deviceId === "booted" || device.udid === deviceId) {
226
- resolve({
227
- platform: "ios",
228
- id: device.udid,
229
- osVersion: parseRuntimeName(runtime),
230
- model: device.name,
231
- simulator: true,
232
- })
233
- return
234
- }
223
+ if (!Array.isArray(devices)) continue
224
+ for (const device of devices) {
225
+ if (deviceId === "booted" || device.udid === deviceId) {
226
+ resolve({
227
+ platform: "ios",
228
+ id: device.udid,
229
+ osVersion: parseRuntimeName(runtime),
230
+ model: device.name,
231
+ simulator: true,
232
+ })
233
+ return
235
234
  }
236
235
  }
237
236
  }
@@ -264,34 +263,34 @@ export async function listIOSDevices(appId?: string): Promise<DeviceInfo[]> {
264
263
  const bmap = bdata.devices || {}
265
264
  for (const rt in bmap) {
266
265
  const devs = bmap[rt]
267
- if (Array.isArray(devs)) for (const d of devs) bootedSet.add(d.udid)
266
+ if (!Array.isArray(devs)) continue
267
+ for (const d of devs) bootedSet.add(d.udid)
268
268
  }
269
- } catch {}
269
+ } catch { }
270
270
  }
271
271
 
272
272
  for (const runtime in devicesMap) {
273
273
  const devices = devicesMap[runtime]
274
- if (Array.isArray(devices)) {
275
- for (const device of devices) {
276
- const info: any = {
277
- platform: 'ios',
278
- id: device.udid,
279
- osVersion: parseRuntimeName(runtime),
280
- model: device.name,
281
- simulator: true,
282
- booted: bootedSet.has(device.udid)
283
- }
274
+ if (!Array.isArray(devices)) continue
275
+ for (const device of devices) {
276
+ const info: DeviceInfo = {
277
+ platform: 'ios',
278
+ id: device.udid,
279
+ osVersion: parseRuntimeName(runtime),
280
+ model: device.name,
281
+ simulator: true,
282
+ booted: bootedSet.has(device.udid)
283
+ }
284
284
 
285
- if (appId) {
286
- // check if installed
287
- const p = execCommand(['simctl', 'get_app_container', device.udid, appId, 'data'], device.udid)
288
- .then(() => { info.appInstalled = true })
289
- .catch(() => { info.appInstalled = false })
290
- .then(() => { out.push(info) })
291
- checks.push(p)
292
- } else {
293
- out.push(info)
294
- }
285
+ if (appId) {
286
+ // check if installed
287
+ const p = execCommand(['simctl', 'get_app_container', device.udid, appId, 'data'], device.udid)
288
+ .then(() => { info.appInstalled = true })
289
+ .catch(() => { info.appInstalled = false })
290
+ .then(() => { out.push(info) })
291
+ checks.push(p)
292
+ } else {
293
+ out.push(info)
295
294
  }
296
295
  }
297
296
  }
package/src/utils/java.ts CHANGED
@@ -35,27 +35,8 @@ export async function detectJavaHome(options: { quiet?: boolean } = {}): Promise
35
35
  debug('[java.detect] ANDROID_STUDIO_JDK present but java -version did not match expected versions')
36
36
  }
37
37
 
38
- // 2) Android Studio JBR candidates (prefer these over JAVA_HOME)
39
- const jbrCandidates = [
40
- '/Applications/Android Studio.app/Contents/jbr',
41
- '/Applications/Android Studio.app/Contents/jbr/Contents/Home',
42
- '/Applications/Android Studio Preview.app/Contents/jbr',
43
- '/Applications/Android Studio Preview.app/Contents/jbr/Contents/Home',
44
- '/Applications/Android Studio Preview 2022.3.app/Contents/jbr',
45
- '/Applications/Android Studio Preview 2022.3.app/Contents/jbr/Contents/Home',
46
- '/Applications/Android Studio Preview 2023.1.app/Contents/jbr',
47
- '/Applications/Android Studio Preview 2023.1.app/Contents/jbr/Contents/Home'
48
- ]
49
- for (const p of jbrCandidates) {
50
- const javaBin = path.join(p, 'bin', 'java')
51
- if (existsSync(javaBin)) {
52
- const v = javaVersionOf(javaBin)
53
- if (isJavaVersionAcceptable(v)) {
54
- debug('[java.detect] Found Android Studio JBR at:', p)
55
- return p
56
- }
57
- }
58
- }
38
+ const jbr = findStudioJava(debug)
39
+ if (jbr) return jbr
59
40
 
60
41
  // 3) If JAVA_HOME set, validate it (accept 17 or 21)
61
42
  if (process.env.JAVA_HOME) {
@@ -86,31 +67,69 @@ export async function detectJavaHome(options: { quiet?: boolean } = {}): Promise
86
67
  if (out21) return out21
87
68
  } catch (e: unknown) { debug('[java.detect] /usr/libexec/java_home -v 21 failed: ' + String(e)) }
88
69
 
89
- // 6) macOS common JDK locations
90
- try {
91
- const homes = execSync('ls -1 /Library/Java/JavaVirtualMachines || true', { stdio: ['ignore', 'pipe', 'inherit'] }).toString().split(/\r?\n/).filter(Boolean)
92
- for (const h of homes) {
93
- if (h.toLowerCase().includes('17') || h.toLowerCase().includes('jdk-17') || h.toLowerCase().includes('21') || h.toLowerCase().includes('jdk-21')) {
94
- const candidate = `/Library/Java/JavaVirtualMachines/${h}/Contents/Home`
95
- return candidate
96
- }
97
- }
98
- } catch (e: unknown) { debug('[java.detect] listing /Library/Java/JavaVirtualMachines failed: ' + String(e)) }
70
+ const commonHome = findMacJavaHome(debug)
71
+ if (commonHome) return commonHome
99
72
 
100
- // 7) Linux locations
101
- const linuxCandidates = [
102
- '/usr/lib/jvm/java-17-openjdk-amd64',
103
- '/usr/lib/jvm/java-17-openjdk',
104
- '/usr/lib/jvm/zulu17',
105
- '/usr/lib/jvm/temurin-17-jdk',
106
- '/usr/lib/jvm/temurin-21-jdk',
107
- '/usr/lib/jvm/java-21-openjdk-amd64'
108
- ]
109
- for (const p of linuxCandidates) {
110
- try { if (existsSync(p)) return p } catch (e: unknown) { debug(`[java.detect] checking linux candidate ${p} failed: ${String(e)}`) }
111
- }
73
+ return findLinuxJavaHome(debug)
112
74
  } catch (e: unknown) {
113
75
  debug('[java.detect] error detecting java home:', e instanceof Error ? e.message : String(e))
114
76
  }
115
77
  return undefined
116
78
  }
79
+
80
+ function findStudioJava(debug: (...args: unknown[]) => void): string | undefined {
81
+ // 2) Android Studio JBR candidates (prefer these over JAVA_HOME)
82
+ const jbrCandidates = [
83
+ '/Applications/Android Studio.app/Contents/jbr',
84
+ '/Applications/Android Studio.app/Contents/jbr/Contents/Home',
85
+ '/Applications/Android Studio Preview.app/Contents/jbr',
86
+ '/Applications/Android Studio Preview.app/Contents/jbr/Contents/Home',
87
+ '/Applications/Android Studio Preview 2022.3.app/Contents/jbr',
88
+ '/Applications/Android Studio Preview 2022.3.app/Contents/jbr/Contents/Home',
89
+ '/Applications/Android Studio Preview 2023.1.app/Contents/jbr',
90
+ '/Applications/Android Studio Preview 2023.1.app/Contents/jbr/Contents/Home'
91
+ ]
92
+ for (const p of jbrCandidates) {
93
+ const javaBin = path.join(p, 'bin', 'java')
94
+ if (existsSync(javaBin)) {
95
+ const v = javaVersionOf(javaBin)
96
+ if (isJavaVersionAcceptable(v)) {
97
+ debug('[java.detect] Found Android Studio JBR at:', p)
98
+ return p
99
+ }
100
+ }
101
+ }
102
+
103
+ return undefined
104
+ }
105
+
106
+ function findMacJavaHome(debug: (...args: unknown[]) => void): string | undefined {
107
+ // 6) macOS common JDK locations
108
+ try {
109
+ const homes = execSync('ls -1 /Library/Java/JavaVirtualMachines || true', { stdio: ['ignore', 'pipe', 'inherit'] }).toString().split(/\r?\n/).filter(Boolean)
110
+ for (const h of homes) {
111
+ if (h.toLowerCase().includes('17') || h.toLowerCase().includes('jdk-17') || h.toLowerCase().includes('21') || h.toLowerCase().includes('jdk-21')) {
112
+ const candidate = `/Library/Java/JavaVirtualMachines/${h}/Contents/Home`
113
+ return candidate
114
+ }
115
+ }
116
+ } catch (e: unknown) { debug('[java.detect] listing /Library/Java/JavaVirtualMachines failed: ' + String(e)) }
117
+
118
+ return undefined
119
+ }
120
+
121
+ function findLinuxJavaHome(debug: (...args: unknown[]) => void): string | undefined {
122
+ // 7) Linux locations
123
+ const linuxCandidates = [
124
+ '/usr/lib/jvm/java-17-openjdk-amd64',
125
+ '/usr/lib/jvm/java-17-openjdk',
126
+ '/usr/lib/jvm/zulu17',
127
+ '/usr/lib/jvm/temurin-17-jdk',
128
+ '/usr/lib/jvm/temurin-21-jdk',
129
+ '/usr/lib/jvm/java-21-openjdk-amd64'
130
+ ]
131
+ for (const p of linuxCandidates) {
132
+ try { if (existsSync(p)) return p } catch (e: unknown) { debug(`[java.detect] checking linux candidate ${p} failed: ${String(e)}`) }
133
+ }
134
+ return undefined
135
+ }
@@ -16,7 +16,7 @@ export interface ResolveOptions {
16
16
 
17
17
  function parseNumericVersion(v: string): number {
18
18
  if (!v) return 0
19
- const m = v.match(/(\d+)(?:[\.\-](\d+))?/)
19
+ const m = v.match(/(\d+)(?:[\.\-](\d+))?/)
20
20
  if (!m) return 0
21
21
  const major = parseInt(m[1], 10) || 0
22
22
  const minor = parseInt(m[2] || "0", 10) || 0
@@ -82,13 +82,17 @@ async function resolveDevice(opts: ResolveOptions): Promise<DeviceInfo> {
82
82
  return found
83
83
  }
84
84
 
85
+ return selectDevice(devices, platform, appId, prefer)
86
+ }
87
+
88
+ function selectDevice(devices: DeviceInfo[], platform: string, appId?: string, prefer?: ResolveOptions['prefer']): DeviceInfo {
85
89
  let candidates = devices.slice()
86
90
 
87
91
  if (prefer === "physical") candidates = candidates.filter(d => !d.simulator)
88
92
  if (prefer === "emulator") candidates = candidates.filter(d => d.simulator)
89
93
 
90
94
  if (appId) {
91
- const installed = candidates.filter(d => (d as any).appInstalled)
95
+ const installed = candidates.filter(d => d.appInstalled)
92
96
  if (installed.length > 0) candidates = installed
93
97
  }
94
98
 
@@ -103,7 +107,7 @@ async function resolveDevice(opts: ResolveOptions): Promise<DeviceInfo> {
103
107
 
104
108
  // Prefer booted iOS simulators if present
105
109
  if (platform === 'ios') {
106
- const booted = candidates.filter((d: any) => !!d.booted)
110
+ const booted = candidates.filter((d: DeviceInfo) => !!d.booted)
107
111
  if (booted.length === 1) return booted[0]
108
112
  if (booted.length > 1) return booted[0] // if multiple booted, pick the first
109
113
  }
@@ -113,9 +117,8 @@ async function resolveDevice(opts: ResolveOptions): Promise<DeviceInfo> {
113
117
  return candidates[0]
114
118
  }
115
119
 
116
- const list = candidates.map(d => ({ id: d.id, platform: d.platform, osVersion: d.osVersion, model: d.model, simulator: d.simulator, appInstalled: (d as any).appInstalled }))
117
- const err = new Error(`Multiple matching devices found: ${JSON.stringify(list, null, 2)}`)
118
- ;(err as any).devices = list
120
+ const list = candidates.map(d => ({ id: d.id, platform: d.platform, osVersion: d.osVersion, model: d.model, simulator: d.simulator, appInstalled: d.appInstalled }))
121
+ const err = Object.assign(new Error(`Multiple matching devices found: ${JSON.stringify(list, null, 2)}`), { devices: list })
119
122
  throw err
120
123
  }
121
124