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.
- package/.agents/skills/mcp-builder/SKILL.md +44 -64
- package/.agents/skills/mcp-builder/references/build-flags.md +44 -43
- package/.agents/skills/mcp-builder/references/diagnostics-schema.md +4 -1
- package/.agents/skills/mcp-builder/references/toolchain-details.md +49 -62
- package/.agents/skills/spec-review/references/spec-review-checklist.md +7 -0
- package/.agents/skills/test-authoring/SKILL.md +7 -6
- package/.agents/skills/test-authoring/references/repo-test-layout.md +3 -1
- package/.agents/skills/test-authoring/references/test-authoring-workflow.md +16 -7
- package/.agents/skills/test-authoring/references/test-quality-checklist.md +2 -1
- package/.cursor-plugin/marketplace.json +1 -1
- package/.github/copilot-instructions.md +14 -36
- package/.github/workflows/ci.yml +4 -4
- package/AGENTS.md +12 -3
- package/CONTRIBUTING.md +79 -0
- package/README.md +218 -129
- package/agent-plugin/.antigravity-plugin/README.md +48 -0
- package/agent-plugin/.antigravity-plugin/plugin.json +18 -0
- package/agent-plugin/.claude-plugin/plugin.json +2 -2
- package/agent-plugin/.codex-plugin/plugin.json +1 -1
- package/agent-plugin/.cursor-plugin/plugin.json +2 -2
- package/agent-plugin/.mcp.json +1 -1
- package/agent-plugin/README.md +50 -11
- package/agent-plugin/cursor/.cursor-plugin/plugin.json +23 -0
- package/agent-plugin/cursor/LICENSE +201 -0
- package/agent-plugin/cursor/README.md +9 -0
- package/agent-plugin/cursor/skills/mobile-debug-mcp/SKILL.md +253 -0
- package/agent-plugin/mcp.json +1 -1
- package/agent-plugin/mcp_config.json +13 -0
- package/agent-plugin/plugin.json +1 -1
- package/agent-plugin/skills/mobile-debug-mcp/SKILL.md +62 -17
- package/dist/contracts/execution-controls.js +24 -0
- package/dist/contracts/selector.js +8 -0
- package/dist/contracts/target.js +15 -0
- package/dist/contracts/ui.js +1 -0
- package/dist/debugger/android.js +2 -1
- package/dist/debugger/diagnostics.js +54 -0
- package/dist/debugger/guard.js +4 -4
- package/dist/debugger/ios-worker.js +62 -0
- package/dist/debugger/ios.js +57 -0
- package/dist/debugger/protocol.js +33 -0
- package/dist/debugger/sessions.js +133 -74
- package/dist/debugger/worker.js +59 -46
- package/dist/interact/adjustment/adjustment-context.js +41 -0
- package/dist/interact/adjustment/adjustment-gestures.js +55 -0
- package/dist/interact/adjustment/adjustment-plan.js +18 -0
- package/dist/interact/adjustment/adjustment-resolution.js +127 -0
- package/dist/interact/adjustment/adjustment-result.js +19 -0
- package/dist/interact/adjustment/adjustment-semantic.js +64 -0
- package/dist/interact/adjustment/adjustment-verification.js +35 -0
- package/dist/interact/adjustment/adjustment.js +52 -0
- package/dist/interact/adjustment/control-geometry.js +87 -0
- package/dist/interact/adjustment/control-values.js +136 -0
- package/dist/interact/adjustment/slider-labels.js +84 -0
- package/dist/interact/assertions/screen-assertion.js +37 -0
- package/dist/interact/assertions/state-assertion-context.js +35 -0
- package/dist/interact/assertions/state-assertion.js +76 -0
- package/dist/interact/assertions/state-comparison.js +141 -0
- package/dist/interact/assertions/visibility-assertion.js +34 -0
- package/dist/interact/conditional-action.js +12 -7
- package/dist/interact/index.js +77 -3092
- package/dist/interact/input-service.js +1 -0
- package/dist/interact/interaction-trace.js +21 -0
- package/dist/interact/model.js +1 -0
- package/dist/interact/observation.js +1 -0
- package/dist/interact/targeting/candidate-ancestor.js +58 -0
- package/dist/interact/targeting/candidate-result.js +23 -0
- package/dist/interact/targeting/candidate-scoring.js +50 -0
- package/dist/interact/targeting/element-helpers.js +216 -0
- package/dist/interact/targeting/find-element.js +65 -0
- package/dist/interact/targeting/nearby-control.js +56 -0
- package/dist/interact/targeting/resolved-elements.js +21 -0
- package/dist/interact/targeting/tap-target.js +17 -0
- package/dist/interact/targeting/target-scope.js +104 -0
- package/dist/interact/waits/legacy-log-wait.js +38 -0
- package/dist/interact/waits/legacy-wait-observations.js +103 -0
- package/dist/interact/waits/legacy-wait-state.js +34 -0
- package/dist/interact/waits/legacy-wait.js +48 -0
- package/dist/interact/waits/screen-wait.js +49 -0
- package/dist/interact/waits/ui-change-helpers.js +157 -0
- package/dist/interact/waits/ui-change-state.js +18 -0
- package/dist/interact/waits/ui-change-wait.js +77 -0
- package/dist/interact/waits/ui-wait-condition.js +56 -0
- package/dist/interact/waits/ui-wait-state.js +38 -0
- package/dist/interact/waits/ui-wait.js +106 -0
- package/dist/manage/android.js +55 -51
- package/dist/manage/index.js +38 -75
- package/dist/manage/ios/build-types.js +1 -0
- package/dist/manage/ios/build.js +210 -0
- package/dist/manage/ios/project-discovery.js +29 -0
- package/dist/manage/ios/scheme.js +18 -0
- package/dist/manage/ios.js +44 -256
- package/dist/observe/android/context.js +1 -0
- package/dist/observe/android/current-screen.js +55 -0
- package/dist/observe/android/fingerprint.js +13 -0
- package/dist/observe/android/log-stream.js +132 -0
- package/dist/observe/android/logs.js +107 -0
- package/dist/observe/android/screenshot.js +70 -0
- package/dist/observe/android/ui-tree.js +107 -0
- package/dist/observe/android.js +16 -520
- package/dist/observe/evidence.js +114 -75
- package/dist/observe/index.js +16 -263
- package/dist/observe/ios/context.js +16 -0
- package/dist/observe/ios/fingerprint.js +12 -0
- package/dist/observe/ios/log-stream.js +105 -0
- package/dist/observe/ios/logs.js +168 -0
- package/dist/observe/ios/screenshot.js +22 -0
- package/dist/observe/ios/ui-elements.js +241 -0
- package/dist/observe/ios/ui-tree.js +137 -0
- package/dist/observe/ios-trace/artifact.js +63 -0
- package/dist/observe/ios-trace/capabilities.js +86 -0
- package/dist/observe/ios-trace/native.js +93 -0
- package/dist/observe/ios-trace/notification.js +35 -0
- package/dist/observe/ios-trace/process.js +98 -0
- package/dist/observe/ios-trace/sessions.js +244 -0
- package/dist/observe/ios.js +17 -715
- package/dist/observe/logs/model.js +1 -0
- package/dist/{network → observe/network}/index.js +2 -2
- package/dist/observe/readiness-service.js +5 -3
- package/dist/observe/readiness.js +33 -14
- package/dist/observe/screenshot-encoding.js +41 -0
- package/dist/observe/snapshot-collection.js +79 -0
- package/dist/observe/snapshot-logs.js +30 -0
- package/dist/observe/snapshot-metadata.js +43 -31
- package/dist/observe/snapshot-semantic.js +115 -0
- package/dist/observe/system-trace-error.js +7 -0
- package/dist/observe/system-trace-router.js +81 -0
- package/dist/observe/system-trace.js +26 -21
- package/dist/runtime/action-results.js +270 -0
- package/dist/runtime/clock.js +4 -0
- package/dist/server/common.js +1 -256
- package/dist/server/definitions/assert.js +162 -0
- package/dist/server/definitions/diagnostic.js +118 -0
- package/dist/server/definitions/input.js +274 -0
- package/dist/server/definitions/manage.js +194 -0
- package/dist/server/definitions/observe.js +149 -0
- package/dist/server/definitions/target.js +138 -0
- package/dist/server/definitions/wait.js +105 -0
- package/dist/server/handlers/action-execution.js +163 -0
- package/dist/server/handlers/correlation.js +23 -0
- package/dist/server/handlers/debugger-handlers.js +67 -0
- package/dist/server/handlers/debugger-response.js +9 -0
- package/dist/server/handlers/diagnostic.js +77 -0
- package/dist/server/handlers/handler-types.js +1 -0
- package/dist/server/handlers/input.js +154 -0
- package/dist/server/handlers/interact.js +113 -0
- package/dist/server/handlers/journey-assertions.js +35 -0
- package/dist/server/handlers/journey.js +147 -0
- package/dist/server/handlers/manage.js +119 -0
- package/dist/server/handlers/observe.js +95 -0
- package/dist/server/handlers/payload.js +4 -0
- package/dist/server/handlers/tool-handlers.js +170 -0
- package/dist/server/schemas/condition-schema.js +2 -0
- package/dist/server/{debugger-schema.js → schemas/debugger-schema.js} +20 -12
- package/dist/server/{journey-schema.js → schemas/journey-schema.js} +7 -22
- package/dist/server/schemas/system-trace-schema.js +22 -0
- package/dist/server/tool-definitions.js +58 -1135
- package/dist/server-core.js +2 -2
- package/dist/server.js +4 -4
- package/dist/system/android-status.js +101 -0
- package/dist/system/android.js +37 -27
- package/dist/system/index.js +13 -439
- package/dist/system/ios-status.js +132 -0
- package/dist/system/ios.js +11 -8
- package/dist/system/model.js +1 -0
- package/dist/system/probes.js +105 -0
- package/dist/system/status-summary.js +43 -0
- package/dist/system/unsupported-status.js +52 -0
- package/dist/utils/android/commands.js +89 -0
- package/dist/utils/android/gradle.js +139 -0
- package/dist/utils/android/logs.js +103 -0
- package/dist/utils/android/ui-tree.js +233 -0
- package/dist/utils/android/utils.js +6 -564
- package/dist/utils/cli/idb/idb-helper.js +4 -6
- package/dist/utils/cli/idb/install-idb.js +13 -9
- package/dist/utils/cli/ios/preflight-ios.js +42 -38
- package/dist/utils/ios/utils.js +48 -46
- package/dist/utils/java.js +65 -49
- package/dist/utils/resolve-device.js +4 -2
- package/dist/utils/ui/index.js +26 -26
- package/docs/CHANGELOG.md +67 -1
- package/docs/README.md +38 -0
- package/docs/agent-plugin-installation-verification.md +39 -0
- package/docs/benchmarks/README.md +18 -0
- package/docs/benchmarks/interaction-latency/README.md +14 -0
- package/docs/benchmarks/interaction-latency/final.md +4 -1
- package/docs/benchmarks/interaction-latency/ios-validation.md +2 -0
- package/docs/benchmarks/ui-tree-performance/README.md +24 -16
- package/docs/benchmarks/ui-tree-performance/comparison.md +11 -10
- package/docs/code-structure.md +120 -0
- package/docs/specs/001-state-verification/spec.md +16 -14
- package/docs/specs/002-richer-element-identity/spec.md +12 -10
- package/docs/specs/003-wait-and-synchronization-reliability/spec.md +12 -10
- package/docs/specs/004-action-verification-routing/spec.md +3 -3
- package/docs/specs/005-unified-action-execution-and-verification-model/spec.md +2 -0
- package/docs/specs/006-runtime-action-instrumentation-and-binding-layer/spec.md +2 -0
- package/docs/specs/007-actionability-resolution-and-executable-target-selection/spec.md +3 -1
- package/docs/specs/008-adjustable-control-support-and-semantic-value-manipulation/spec.md +3 -4
- package/docs/specs/009-semantic-control-modeling-for-custom-and-composite-controls/spec.md +2 -0
- package/docs/specs/010-verification-stabilization-and-temporal-convergence/spec.md +2 -2
- package/docs/specs/011-recovery-and-replanning-for-failed-or-ambiguous-interaction-flows/spec.md +2 -2
- package/docs/specs/011.1-recovery-contract-types-and-runtime-wiring-spec/spec.md +2 -0
- package/docs/specs/012-action-trace-and-xecution-observability/spec.md +2 -3
- package/docs/specs/013-wait-and-synchronization-reliability/spec.md +19 -19
- package/docs/specs/014-actionability-resolution/spec.md +20 -18
- package/docs/specs/015-adjustable-control-support/spec.md +17 -15
- package/docs/specs/016-environment-tools/spec.md +20 -20
- package/docs/specs/018-interaction-latency-and-batched-journeys/spec.md +2 -0
- package/docs/specs/019-ui-tree-observation-performance/spec.md +3 -1
- package/docs/specs/020-conditional-actions/intent.md +5 -3
- package/docs/specs/020-conditional-actions/plan.md +8 -9
- package/docs/specs/020-conditional-actions/spec.md +4 -2
- package/docs/specs/021-app-readiness-signals/intent.md +4 -2
- package/docs/specs/021-app-readiness-signals/plan.md +7 -6
- package/docs/specs/021-app-readiness-signals/spec.md +3 -1
- package/docs/specs/022-correlated-evidence/intent.md +4 -4
- package/docs/specs/022-correlated-evidence/plan.md +10 -11
- package/docs/specs/022-correlated-evidence/spec.md +3 -1
- package/docs/specs/023-android-system-traces/intent.md +3 -2
- package/docs/specs/023-android-system-traces/plan.md +5 -7
- package/docs/specs/023-android-system-traces/spec.md +3 -1
- package/docs/specs/024-android-heap-dump-capture/intent.md +3 -1
- package/docs/specs/025-android-runtime-debugger/intent.md +4 -3
- package/docs/specs/025-android-runtime-debugger/plan.md +3 -1
- package/docs/specs/025-android-runtime-debugger/spec.md +4 -3
- package/docs/specs/026-code-structure-and-quality-gates/health-plan.md +88 -0
- package/docs/specs/026-code-structure-and-quality-gates/intent.md +47 -0
- package/docs/specs/026-code-structure-and-quality-gates/plan.md +191 -0
- package/docs/specs/026-code-structure-and-quality-gates/spec.md +138 -0
- package/docs/specs/026-code-structure-and-quality-gates/verification-final.json +200 -0
- package/docs/specs/026-code-structure-and-quality-gates/verification-thin-facade.json +154 -0
- package/docs/specs/026-code-structure-and-quality-gates/verification.json +136 -0
- package/docs/specs/027-debugger-failure-diagnostics/intent.md +43 -0
- package/docs/specs/027-debugger-failure-diagnostics/plan.md +86 -0
- package/docs/specs/027-debugger-failure-diagnostics/spec.md +133 -0
- package/docs/specs/028-compact-debugger-responses/intent.md +41 -0
- package/docs/specs/028-compact-debugger-responses/plan.md +112 -0
- package/docs/specs/028-compact-debugger-responses/spec.md +108 -0
- package/docs/specs/029-kotlin-locals-validation/report.md +119 -0
- package/docs/specs/030-android-coroutine-validation/evidence.json +475 -0
- package/docs/specs/030-android-coroutine-validation/intent.md +47 -0
- package/docs/specs/030-android-coroutine-validation/plan.md +147 -0
- package/docs/specs/030-android-coroutine-validation/report.md +240 -0
- package/docs/specs/030-android-coroutine-validation/spec.md +107 -0
- package/docs/specs/031-ios-system-traces/intent.md +38 -0
- package/docs/specs/031-ios-system-traces/investigation.md +92 -0
- package/docs/specs/031-ios-system-traces/late-run-failure-evidence.json +32 -0
- package/docs/specs/031-ios-system-traces/plan.md +138 -0
- package/docs/specs/031-ios-system-traces/pr102-monitor-evidence.json +22 -0
- package/docs/specs/031-ios-system-traces/simulator-evidence.json +47 -0
- package/docs/specs/031-ios-system-traces/spec.md +135 -0
- package/docs/specs/032-ios-runtime-debugger/evidence.json +176 -0
- package/docs/specs/032-ios-runtime-debugger/intent.md +38 -0
- package/docs/specs/032-ios-runtime-debugger/plan.md +95 -0
- package/docs/specs/032-ios-runtime-debugger/report.md +205 -0
- package/docs/specs/032-ios-runtime-debugger/spec.md +153 -0
- package/docs/tools/TOOLS.md +31 -18
- package/docs/tools/{evidence.md → correlated-evidence.md} +9 -1
- package/docs/tools/debugger.md +120 -4
- package/docs/tools/interact.md +42 -20
- package/docs/tools/manage.md +25 -44
- package/docs/tools/observe.md +55 -23
- package/docs/tools/system-traces.md +89 -14
- package/docs/tools/system.md +12 -5
- package/package.json +12 -8
- package/scripts/agent-plugin.mjs +55 -5
- package/scripts/architecture-graph.mjs +69 -0
- package/scripts/check-architecture.mjs +47 -0
- package/scripts/code-quality-baseline.json +1 -0
- package/scripts/code-quality.mjs +161 -0
- package/src/contracts/execution-controls.ts +26 -0
- package/src/contracts/selector.ts +17 -0
- package/src/contracts/target.ts +19 -0
- package/src/contracts/ui.ts +94 -0
- package/src/debugger/android.ts +3 -2
- package/src/debugger/diagnostics.ts +43 -0
- package/src/debugger/guard.ts +10 -5
- package/src/debugger/ios-worker.ts +49 -0
- package/src/debugger/ios.ts +45 -0
- package/{worker → src/debugger/java}/DebuggerWorker.java +9 -3
- package/src/debugger/lldb/breakpoints.py +51 -0
- package/src/debugger/lldb/inspection.py +109 -0
- package/src/debugger/lldb/worker.py +203 -0
- package/src/debugger/protocol.ts +37 -0
- package/src/debugger/sessions.ts +326 -155
- package/src/debugger/worker.ts +57 -41
- package/src/interact/adjustment/adjustment-context.ts +83 -0
- package/src/interact/adjustment/adjustment-gestures.ts +48 -0
- package/src/interact/adjustment/adjustment-plan.ts +23 -0
- package/src/interact/adjustment/adjustment-resolution.ts +104 -0
- package/src/interact/adjustment/adjustment-result.ts +34 -0
- package/src/interact/adjustment/adjustment-semantic.ts +53 -0
- package/src/interact/adjustment/adjustment-verification.ts +60 -0
- package/src/interact/adjustment/adjustment.ts +49 -0
- package/src/interact/adjustment/control-geometry.ts +120 -0
- package/src/interact/adjustment/control-values.ts +141 -0
- package/src/interact/adjustment/slider-labels.ts +84 -0
- package/src/interact/assertions/screen-assertion.ts +39 -0
- package/src/interact/assertions/state-assertion-context.ts +48 -0
- package/src/interact/assertions/state-assertion.ts +64 -0
- package/src/interact/assertions/state-comparison.ts +168 -0
- package/src/interact/assertions/visibility-assertion.ts +41 -0
- package/src/interact/conditional-action.ts +18 -12
- package/src/interact/index.ts +89 -3494
- package/src/interact/input-service.ts +11 -0
- package/src/interact/interaction-trace.ts +36 -0
- package/src/interact/model.ts +109 -0
- package/src/interact/observation.ts +7 -0
- package/src/interact/targeting/candidate-ancestor.ts +49 -0
- package/src/interact/targeting/candidate-result.ts +24 -0
- package/src/interact/targeting/candidate-scoring.ts +42 -0
- package/src/interact/targeting/element-helpers.ts +243 -0
- package/src/interact/targeting/find-element.ts +47 -0
- package/src/interact/targeting/nearby-control.ts +46 -0
- package/src/interact/targeting/resolved-elements.ts +20 -0
- package/src/interact/targeting/tap-target.ts +22 -0
- package/src/interact/targeting/target-scope.ts +121 -0
- package/src/interact/waits/legacy-log-wait.ts +30 -0
- package/src/interact/waits/legacy-wait-observations.ts +55 -0
- package/src/interact/waits/legacy-wait-state.ts +48 -0
- package/src/interact/waits/legacy-wait.ts +34 -0
- package/src/interact/waits/screen-wait.ts +37 -0
- package/src/interact/waits/ui-change-helpers.ts +184 -0
- package/src/interact/waits/ui-change-state.ts +26 -0
- package/src/interact/waits/ui-change-wait.ts +59 -0
- package/src/interact/waits/ui-wait-condition.ts +52 -0
- package/src/interact/waits/ui-wait-state.ts +55 -0
- package/src/interact/waits/ui-wait.ts +84 -0
- package/src/manage/android.ts +56 -50
- package/src/manage/index.ts +63 -93
- package/src/manage/ios/build-types.ts +14 -0
- package/src/manage/ios/build.ts +230 -0
- package/src/manage/ios/project-discovery.ts +26 -0
- package/src/manage/ios/scheme.ts +16 -0
- package/src/manage/ios.ts +67 -298
- package/src/observe/android/context.ts +1 -0
- package/src/observe/android/current-screen.ts +63 -0
- package/src/observe/android/fingerprint.ts +13 -0
- package/src/observe/android/log-stream.ts +127 -0
- package/src/observe/android/logs.ts +110 -0
- package/src/observe/android/screenshot.ts +79 -0
- package/src/observe/android/ui-tree.ts +113 -0
- package/src/observe/android.ts +17 -531
- package/src/observe/evidence.ts +122 -70
- package/src/observe/index.ts +24 -286
- package/src/observe/ios/context.ts +22 -0
- package/src/observe/ios/fingerprint.ts +12 -0
- package/src/observe/ios/log-stream.ts +103 -0
- package/src/observe/ios/logs.ts +179 -0
- package/src/observe/ios/screenshot.ts +26 -0
- package/src/observe/ios/ui-elements.ts +266 -0
- package/src/observe/ios/ui-tree.ts +143 -0
- package/src/observe/ios-trace/artifact.ts +55 -0
- package/src/observe/ios-trace/capabilities.ts +74 -0
- package/src/observe/ios-trace/native.ts +72 -0
- package/src/observe/ios-trace/notification.ts +30 -0
- package/src/observe/ios-trace/process.ts +82 -0
- package/src/observe/ios-trace/sessions.ts +204 -0
- package/src/observe/ios.ts +18 -749
- package/src/observe/logs/model.ts +11 -0
- package/src/{network → observe/network}/index.ts +2 -2
- package/src/observe/readiness-service.ts +6 -4
- package/src/observe/readiness.ts +42 -21
- package/src/observe/screenshot-encoding.ts +32 -0
- package/src/observe/snapshot-collection.ts +69 -0
- package/src/observe/snapshot-logs.ts +26 -0
- package/src/observe/snapshot-metadata.ts +51 -31
- package/src/observe/snapshot-semantic.ts +153 -0
- package/src/observe/system-trace-error.ts +3 -0
- package/src/observe/system-trace-router.ts +61 -0
- package/src/observe/system-trace.ts +28 -19
- package/src/runtime/action-results.ts +376 -0
- package/src/runtime/clock.ts +9 -0
- package/src/server/common.ts +9 -359
- package/src/server/definitions/assert.ts +164 -0
- package/src/server/definitions/diagnostic.ts +120 -0
- package/src/server/definitions/input.ts +276 -0
- package/src/server/definitions/manage.ts +196 -0
- package/src/server/definitions/observe.ts +151 -0
- package/src/server/definitions/target.ts +140 -0
- package/src/server/definitions/wait.ts +107 -0
- package/src/server/handlers/action-execution.ts +164 -0
- package/src/server/handlers/correlation.ts +20 -0
- package/src/server/handlers/debugger-handlers.ts +58 -0
- package/src/server/handlers/debugger-response.ts +7 -0
- package/src/server/handlers/diagnostic.ts +86 -0
- package/src/server/handlers/handler-types.ts +7 -0
- package/src/server/handlers/input.ts +173 -0
- package/src/server/handlers/interact.ts +135 -0
- package/src/server/handlers/journey-assertions.ts +39 -0
- package/src/server/handlers/journey.ts +132 -0
- package/src/server/handlers/manage.ts +141 -0
- package/src/server/handlers/observe.ts +116 -0
- package/src/server/handlers/payload.ts +36 -0
- package/src/server/handlers/tool-handlers.ts +170 -0
- package/src/server/schemas/condition-schema.ts +2 -0
- package/src/server/schemas/debugger-schema.ts +46 -0
- package/src/server/{journey-schema.ts → schemas/journey-schema.ts} +7 -22
- package/src/server/schemas/system-trace-schema.ts +23 -0
- package/src/server/tool-definitions.ts +67 -1143
- package/src/server-core.ts +2 -2
- package/src/server.ts +4 -4
- package/src/system/android-status.ts +124 -0
- package/src/system/android.ts +33 -21
- package/src/system/index.ts +21 -525
- package/src/system/ios-status.ts +147 -0
- package/src/system/ios.ts +14 -10
- package/src/system/model.ts +41 -0
- package/src/system/probes.ts +114 -0
- package/src/system/status-summary.ts +47 -0
- package/src/system/unsupported-status.ts +55 -0
- package/src/types.ts +16 -106
- package/src/utils/android/commands.ts +81 -0
- package/src/utils/android/gradle.ts +132 -0
- package/src/utils/android/logs.ts +103 -0
- package/src/utils/android/ui-tree.ts +239 -0
- package/src/utils/android/utils.ts +16 -556
- package/src/utils/cli/idb/check-idb.ts +4 -4
- package/src/utils/cli/idb/idb-helper.ts +6 -8
- package/src/utils/cli/idb/install-idb.ts +15 -9
- package/src/utils/cli/ios/preflight-ios.ts +39 -31
- package/src/utils/cli/ios/run-ios-ui-tree-tap.ts +4 -4
- package/src/utils/ios/utils.ts +60 -61
- package/src/utils/java.ts +62 -43
- package/src/utils/resolve-device.ts +9 -6
- package/src/utils/ui/index.ts +28 -28
- package/test/device/automated/debugger/coroutine-lifecycle.ts +27 -0
- package/test/device/automated/debugger/coroutine-support.ts +140 -0
- package/test/device/automated/debugger/coroutine.android.test.ts +169 -0
- package/test/device/automated/debugger/kotlin.android.test.ts +127 -0
- package/test/device/automated/debugger/runtime.android.test.ts +19 -3
- package/test/device/automated/debugger/runtime.ios.test.ts +221 -0
- package/test/device/automated/interact/interaction.android.test.ts +56 -0
- package/test/device/automated/observe/ios-system-trace.smoke.ts +119 -0
- package/test/device/automated/observe/system-trace.smoke.ts +1 -1
- package/test/device/manual/interact/modul8_selector_tap.manual.ts +1 -1
- package/test/fixtures/contracts/tool-definitions.json +3696 -0
- package/test/fixtures/coroutine-app/MainActivity.kt +100 -0
- package/test/fixtures/coroutine-app/README.md +48 -0
- package/test/fixtures/coroutine-app/build.ts +35 -0
- package/test/fixtures/debugger-app/MainActivity.java +21 -0
- package/test/fixtures/debugger-app/MainActivity.kt +32 -0
- package/test/fixtures/debugger-app/README.md +52 -0
- package/test/fixtures/debugger-app/build.ts +25 -15
- package/test/fixtures/ios-debugger-app/Main.swift +95 -0
- package/test/fixtures/ios-debugger-app/README.md +27 -0
- package/test/fixtures/ios-debugger-app/build.ts +21 -0
- package/test/fixtures/latency-journey-app/README.md +2 -0
- package/test/fixtures/readiness-app/README.md +2 -0
- package/test/helpers/ios-trace-shutdown.ts +29 -0
- package/test/unit/debugger/coroutine-lifecycle.test.ts +43 -0
- package/test/unit/debugger/diagnostic-sessions.test.ts +67 -0
- package/test/unit/debugger/diagnostics.test.ts +80 -0
- package/test/unit/debugger/guards.test.ts +4 -0
- package/test/unit/debugger/handle-guards.test.ts +88 -0
- package/test/unit/debugger/ios-preparation.test.ts +57 -0
- package/test/unit/debugger/ios.test.ts +104 -0
- package/test/unit/debugger/lldb-worker.py +166 -0
- package/test/unit/debugger/lldb-worker.test.ts +10 -0
- package/test/unit/debugger/preparation.test.ts +32 -0
- package/test/unit/debugger/protocol.test.ts +15 -0
- package/test/unit/debugger/sessions.test.ts +1 -1
- package/test/unit/interact/adjustment_service.test.ts +42 -0
- package/test/unit/interact/adjustment_verification.test.ts +35 -0
- package/test/unit/interact/clock_dependencies.test.ts +63 -0
- package/test/unit/interact/element_helpers.test.ts +19 -0
- package/test/unit/interact/legacy_wait_service.test.ts +17 -0
- package/test/unit/interact/state_comparison.test.ts +20 -0
- package/test/unit/interact/target_scope.test.ts +35 -0
- package/test/unit/network/get_network_activity.test.ts +1 -1
- package/test/unit/observe/ios-trace-native.test.ts +90 -0
- package/test/unit/observe/ios-trace-sessions.test.ts +131 -0
- package/test/unit/observe/readiness.test.ts +1 -1
- package/test/unit/observe/screenshot-encoding.test.ts +30 -0
- package/test/unit/observe/snapshot-logs.test.ts +14 -0
- package/test/unit/observe/system-trace-router.test.ts +49 -0
- package/test/unit/quality/agent-plugin-export.test.ts +38 -0
- package/test/unit/quality/architecture.test.ts +60 -0
- package/test/unit/quality/code-quality.test.ts +58 -0
- package/test/unit/quality/interaction_boundaries.test.ts +24 -0
- package/test/unit/server/conditional_actions.test.ts +1 -1
- package/test/unit/server/conditional_schema.test.ts +2 -2
- package/test/unit/server/debugger-response.test.ts +63 -0
- package/test/unit/server/system-trace.test.ts +12 -3
- package/test/unit/server/tool_definitions_parity.test.ts +8 -0
- package/dist/server/debugger-handlers.js +0 -38
- package/dist/server/system-trace-schema.js +0 -17
- package/dist/server/tool-handlers.js +0 -1000
- package/docs/ROADMAP.md +0 -646
- package/docs/benchmarks/ui-tree-performance/bridge-probe.json +0 -29
- package/docs/benchmarks/ui-tree-performance/initial-baseline.json +0 -59560
- package/docs/specs/baseline-spec-v0.md +0 -312
- package/docs/specs/mcp-tooling-spec-v1.md +0 -446
- package/src/server/debugger-handlers.ts +0 -29
- package/src/server/debugger-schema.ts +0 -37
- package/src/server/system-trace-schema.ts +0 -17
- package/src/server/tool-handlers.ts +0 -1008
- /package/dist/{server/condition-schema.js → contracts/condition.js} +0 -0
- /package/dist/server/{readiness-schema.js → schemas/readiness-schema.js} +0 -0
- /package/src/{server/condition-schema.ts → contracts/condition.ts} +0 -0
- /package/src/server/{readiness-schema.ts → schemas/readiness-schema.ts} +0 -0
|
@@ -90,8 +90,8 @@ When a visible target may still be loading or transitioning, use
|
|
|
90
90
|
`get_app_readiness` reports loading, transitioning, settled, or unknown with
|
|
91
91
|
supporting evidence. A single tree cannot establish settled state. Settled means
|
|
92
92
|
normalized foreground UI stability, not proven responsiveness, app identity, or
|
|
93
|
-
backend completion. Managed Android debugger sessions can establish
|
|
94
|
-
`debugger_paused`; external-debugger
|
|
93
|
+
backend completion. Managed Android and supported iOS simulator debugger sessions can establish
|
|
94
|
+
`debugger_paused`; external-debugger pause detection remains unsupported.
|
|
95
95
|
Unresponsive detection is unsupported. Timeouts and screenshots alone do not
|
|
96
96
|
establish either state.
|
|
97
97
|
|
|
@@ -118,7 +118,7 @@ For behaviour outside the app's foreground screen, inspect the relevant system
|
|
|
118
118
|
surface or device state. For content that cannot be established visually, such
|
|
119
119
|
as audio output, use a suitable direct check or state that it was not verified.
|
|
120
120
|
|
|
121
|
-
## Investigate slow
|
|
121
|
+
## Investigate slow mobile journeys
|
|
122
122
|
|
|
123
123
|
Use system tracing for a reproducible slow or stalled interaction when UI timing,
|
|
124
124
|
state and logs do not explain it. Avoid recording traces for routine navigation.
|
|
@@ -126,12 +126,19 @@ Check that the connected server exposes the tracing tools; an older installed
|
|
|
126
126
|
server may not have them. If absent or unsupported, report that limit and retain
|
|
127
127
|
available UI/log evidence rather than assuming capture is possible.
|
|
128
128
|
|
|
129
|
-
1. Call `get_system_trace_capabilities` for the explicit
|
|
130
|
-
`supported` and missing prerequisites. iOS
|
|
129
|
+
1. Call `get_system_trace_capabilities` for the explicit device and check
|
|
130
|
+
`supported` and missing prerequisites. iOS requires macOS/full Xcode, an explicit
|
|
131
|
+
booted simulator UDID and a running development `appId` for start. Only Time
|
|
132
|
+
Profiler is initially supported; physical iOS capture is unsupported. Discovery
|
|
133
|
+
lists installed templates separately and does not prove profiling permissions,
|
|
134
|
+
symbols, scheduling data or hang detection.
|
|
131
135
|
2. Establish a fresh baseline and a journey with an observable outcome assertion.
|
|
132
136
|
Choose `durationMs` to cover the journey and its observations (default 30,000;
|
|
133
137
|
supported range 1,000–120,000). Android tree reads can take several seconds.
|
|
134
|
-
3. Call `start_system_trace
|
|
138
|
+
3. Call `start_system_trace` (iOS: include `appId`, optionally
|
|
139
|
+
`template: "Time Profiler"`), check `state: "recording"` and retain its
|
|
140
|
+
`trace_id`. iOS waits for native recording-start notification; uncertain startup
|
|
141
|
+
fails without automatic retry. Pass the returned value as
|
|
135
142
|
`systemTraceId` to `run_journey` on the same device.
|
|
136
143
|
4. Stop in cleanup with `stop_system_trace({ traceId })` when retaining diagnostic
|
|
137
144
|
evidence, including after a failed journey. Use `cancel_system_trace` when
|
|
@@ -143,9 +150,12 @@ available UI/log evidence rather than assuming capture is possible.
|
|
|
143
150
|
Report the local `artifact.path`, device, journey outcome, `correlation_ids` and
|
|
144
151
|
journey trace coverage. A completed capture is not proof the app outcome passed.
|
|
145
152
|
Partial coverage and unsynchronised host/device clocks limit conclusions; linked
|
|
146
|
-
IDs do not establish causality.
|
|
147
|
-
|
|
148
|
-
artifacts
|
|
153
|
+
IDs do not establish causality. Perfetto files and Instruments `.trace` bundles
|
|
154
|
+
can contain sensitive system metadata, paths, symbols, logs and signposts even
|
|
155
|
+
with iOS app attachment. Keep these artifacts local unless sharing is authorised,
|
|
156
|
+
and remove temporary artifacts when no longer needed. The iOS artifact includes
|
|
157
|
+
TOC/table validation and sample row count; zero rows do not prove inactivity.
|
|
158
|
+
The profiled app remains pinned if the journey leaves it.
|
|
149
159
|
|
|
150
160
|
Capture tools do not provide automatic CPU, frame or blocked-thread analysis.
|
|
151
161
|
If a suitable analyser is available, inspect the artifact before making a cause
|
|
@@ -153,21 +163,32 @@ claim. Otherwise report capture verified and analysis unrun, and provide the
|
|
|
153
163
|
artifact for further investigation. Nonempty output alone does not establish
|
|
154
164
|
valid trace contents or a performance diagnosis.
|
|
155
165
|
|
|
156
|
-
## Investigate
|
|
166
|
+
## Investigate runtime behaviour
|
|
157
167
|
|
|
158
168
|
Use runtime debugging to test a concrete execution hypothesis when UI and logs
|
|
159
169
|
are insufficient. Check that the connected server exposes the debugger tools;
|
|
160
|
-
an older installed server may lack them
|
|
161
|
-
|
|
170
|
+
an older installed server may lack them or advertise only Android in the platform
|
|
171
|
+
schema. Android requires JDK 17+ and a running debuggable app. When the connected
|
|
172
|
+
server advertises iOS debugging, it requires a booted simulator and Xcode's
|
|
173
|
+
Swift-enabled LLDB/Python. Physical iOS devices and native-specific inspection
|
|
174
|
+
remain unsupported. Keep ordinary
|
|
162
175
|
navigation and UI verification on the existing interaction path.
|
|
163
176
|
|
|
164
|
-
|
|
177
|
+
For routine debugger calls, opt into `responseMode: "compact"` to omit repeated
|
|
178
|
+
`device`, `target` and `capabilities`. Default `debug` preserves full output;
|
|
179
|
+
attach, capabilities and state remain full in either mode. Use `get_debugger_state`
|
|
180
|
+
to recover the baseline. Errors, diagnostics, IDs, stop/execution state, limits,
|
|
181
|
+
truncation and cleanup remain available. Compact output is not privacy redaction
|
|
182
|
+
and does not establish execution recovery.
|
|
183
|
+
|
|
184
|
+
1. Call `get_debugger_capabilities` with the advertised `platform` (`android` or `ios`), explicit `deviceId`
|
|
165
185
|
and `appId`. Inspect `supported`, prerequisites and candidate processes. Provide
|
|
166
186
|
`processId` when ambiguous; general system health does not prove attach support.
|
|
167
187
|
2. Call `attach_debugger`, retain `debug_session_id`, then use it as the
|
|
168
188
|
`debugSessionId` input. Set a source breakpoint with `set_debugger_breakpoint`
|
|
169
|
-
using
|
|
170
|
-
`
|
|
189
|
+
using Android binary `className` or iOS absolute `sourceFile` (exactly one),
|
|
190
|
+
plus 1-based executable `line`. For iOS use the exact simulator UDID. Check its
|
|
191
|
+
status: `pending` waits for class/image loading; actual locations establish binding.
|
|
171
192
|
3. Trigger the reproduction and call `wait_for_debugger_event`, carrying forward
|
|
172
193
|
`next_cursor` as `afterSequence`. A stopped event supplies `stop_id` and
|
|
173
194
|
`thread_id`. Empty event waits do not mean execution stopped; report overflow
|
|
@@ -176,12 +197,28 @@ navigation and UI verification on the existing interaction path.
|
|
|
176
197
|
`get_debugger_locals` with `stopId`/`frameId` from the returned frame. Inspection
|
|
177
198
|
is shallow and bounded; check availability and truncation. ART may return
|
|
178
199
|
`EMPTY_OR_UNAVAILABLE_VARIABLE_METADATA`: an empty table does not prove that
|
|
179
|
-
the frame has no locals.
|
|
200
|
+
the frame has no locals. On Kotlin assignment lines, the local may not yet be
|
|
201
|
+
initialized or in its emitted variable scope; compare a stop after assignment
|
|
202
|
+
and compiler metadata before diagnosing a worker defect.
|
|
203
|
+
For Swift async work, inspect `async_execution_inspection` and `async_stepping`
|
|
204
|
+
capabilities. Breakpoints on either side of await establish separate execution
|
|
205
|
+
observations; a step acknowledgement does not prove continuation following.
|
|
206
|
+
Inspect the subsequent source stop. Swift aggregate values (including strings)
|
|
207
|
+
may supply type summaries, and non-live locals may be unavailable. No suspended
|
|
208
|
+
task enumeration or complete logical async stack is promised.
|
|
209
|
+
Values can contain private data; report only what is
|
|
180
210
|
needed for the investigation.
|
|
181
211
|
5. Use `step_debugger` (`kind: into/over/out`) or `resume_debugger` with the current
|
|
182
212
|
`stopId`. Acknowledgement is not proof of a later stop. Discard all old stop,
|
|
183
213
|
thread and frame IDs after execution resumes; acquire fresh IDs from events.
|
|
184
|
-
6.
|
|
214
|
+
6. On operation failure, inspect `error.diagnostic.stage`, `.reason` and `.recovery`
|
|
215
|
+
when present. These are bounded classifications and static guidance, not raw
|
|
216
|
+
worker output or proof of root cause. Capability probes may return ordered
|
|
217
|
+
`diagnostics` (platform prerequisites, then target) while retaining `success: true` and
|
|
218
|
+
`supported: false`. Terminal state retains sanitized failure diagnostics.
|
|
219
|
+
Timeout/cancellation can leave execution uncertain: reconcile state and cleanup
|
|
220
|
+
before another state-changing command; never automatically retry attach/step/resume.
|
|
221
|
+
7. Always finish with `detach_debugger`, including after failed investigations.
|
|
185
222
|
Inspect `cleanup.status`; retry incomplete detach rather than starting another
|
|
186
223
|
session. Use `get_debugger_state` to reconcile an uncertain command outcome;
|
|
187
224
|
do not automatically repeat attach, step or resume.
|
|
@@ -200,6 +237,14 @@ traversal, coroutine reconstruction or heap capture. A successful debugger comma
|
|
|
200
237
|
establishes only its reported execution/inspection evidence, not the visible app
|
|
201
238
|
outcome or the cause of a bug.
|
|
202
239
|
|
|
240
|
+
For Kotlin coroutines, use explicit source breakpoints before and after suspension
|
|
241
|
+
and verify each observed location. Thread-based stepping can stop in generated
|
|
242
|
+
state-machine code; it does not promise to follow a coroutine across dispatchers.
|
|
243
|
+
Inspect compiler line/local metadata when source bindings or values are unclear.
|
|
244
|
+
Shallow locals do not expose spilled continuation fields, suspended coroutine
|
|
245
|
+
inventories, parent/child identities or logical asynchronous stacks. Do not enable
|
|
246
|
+
debug probes or add app instrumentation without a separately agreed design.
|
|
247
|
+
|
|
203
248
|
## Report evidence precisely
|
|
204
249
|
|
|
205
250
|
State the device, actions taken, observed result, evidence source, and any
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const verificationModes = ['none', 'light', 'full'];
|
|
3
|
+
export const responseModes = ['compact', 'debug'];
|
|
4
|
+
export const verificationModeSchema = z.enum(verificationModes);
|
|
5
|
+
export const responseModeSchema = z.enum(responseModes);
|
|
6
|
+
export const verificationModeJson = { type: 'string', enum: [...verificationModes] };
|
|
7
|
+
export const responseModeJson = { type: 'string', enum: [...responseModes] };
|
|
8
|
+
export function interactionControls(args) {
|
|
9
|
+
const verificationMode = (typeof args.verificationMode === 'string' ? args.verificationMode : undefined) ?? 'full';
|
|
10
|
+
const responseMode = (typeof args.responseMode === 'string' ? args.responseMode : undefined) ?? 'debug';
|
|
11
|
+
if (!verificationModes.includes(verificationMode) || !responseModes.includes(responseMode))
|
|
12
|
+
throw new Error('INVALID_ARGUMENT');
|
|
13
|
+
for (const key of ['verificationMode', 'responseMode'])
|
|
14
|
+
if (args[key] !== undefined && typeof args[key] !== 'string')
|
|
15
|
+
throw new Error('INVALID_ARGUMENT');
|
|
16
|
+
for (const key of ['actionTimeoutMs', 'verificationTimeoutMs'])
|
|
17
|
+
if (args[key] !== undefined && (typeof args[key] !== 'number' || !Number.isInteger(args[key])))
|
|
18
|
+
throw new Error('INVALID_ARGUMENT');
|
|
19
|
+
const actionTimeoutMs = (typeof args.actionTimeoutMs === 'number' && Number.isFinite(args.actionTimeoutMs) ? args.actionTimeoutMs : undefined);
|
|
20
|
+
const verificationTimeoutMs = (typeof args.verificationTimeoutMs === 'number' && Number.isFinite(args.verificationTimeoutMs) ? args.verificationTimeoutMs : undefined);
|
|
21
|
+
return { verificationMode, responseMode,
|
|
22
|
+
actionTimeoutMs: actionTimeoutMs === undefined ? undefined : Math.max(100, Math.min(30000, actionTimeoutMs)),
|
|
23
|
+
verificationTimeoutMs: verificationTimeoutMs === undefined ? 3000 : Math.max(100, Math.min(60000, verificationTimeoutMs)) };
|
|
24
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const selectorSchema = z.object({
|
|
3
|
+
text: z.string().min(1).optional(), resource_id: z.string().min(1).optional(),
|
|
4
|
+
accessibility_id: z.string().min(1).optional(), contains: z.boolean().optional()
|
|
5
|
+
}).strict().refine(value => !!(value.text || value.resource_id || value.accessibility_id), 'Selector requires a target');
|
|
6
|
+
export const selectorJson = { type: 'object', additionalProperties: false, properties: {
|
|
7
|
+
text: { type: 'string', minLength: 1 }, resource_id: { type: 'string', minLength: 1 }, accessibility_id: { type: 'string', minLength: 1 }, contains: { type: 'boolean' }
|
|
8
|
+
}, anyOf: ['text', 'resource_id', 'accessibility_id'].map(key => ({ required: [key] })) };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { selectorSchema as selector, selectorJson } from './selector.js';
|
|
3
|
+
const pollIntervalMs = z.number().int().min(50).max(1000).optional();
|
|
4
|
+
export const waitForSchema = z.object({
|
|
5
|
+
condition: z.enum(['exists', 'visible', 'clickable']).optional(),
|
|
6
|
+
timeoutMs: z.number().int().min(100).max(10000).optional(), pollIntervalMs,
|
|
7
|
+
match: z.object({ index: z.number().int().nonnegative().optional() }).strict().optional()
|
|
8
|
+
}).strict();
|
|
9
|
+
export const targetFields = { selector: selector.optional(), elementId: z.string().min(1).optional() };
|
|
10
|
+
export const exactlyOneTarget = (value) => Number(!!value.selector) + Number(!!value.elementId) === 1;
|
|
11
|
+
export const tapElementControlsSchema = z.object({
|
|
12
|
+
elementId: z.string().min(1).optional(), selector: selector.optional(), waitFor: waitForSchema.optional()
|
|
13
|
+
}).strict().refine(value => Number(!!value.elementId) + Number(!!value.selector) === 1 && (!value.elementId || !value.waitFor));
|
|
14
|
+
export const targetJson = { selector: selectorJson, elementId: { type: 'string', minLength: 1 } };
|
|
15
|
+
export const oneTargetJson = [{ required: ['selector'], not: { required: ['elementId'] } }, { required: ['elementId'], not: { required: ['selector'] } }];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/debugger/android.js
CHANGED
|
@@ -41,7 +41,8 @@ export class AndroidDebugNative {
|
|
|
41
41
|
try {
|
|
42
42
|
jdwpOutput = (await exec(resolveAdbCmd(), ['-s', device, 'jdwp'], { timeout: debuggerRemaining(500), signal: debuggerSignal(), maxBuffer: 1024 * 1024 })).stdout;
|
|
43
43
|
}
|
|
44
|
-
catch (
|
|
44
|
+
catch (error) {
|
|
45
|
+
const e = error;
|
|
45
46
|
if (debuggerSignal()?.aborted)
|
|
46
47
|
throw new DebuggerError('DEBUGGER_CANCELLED');
|
|
47
48
|
debuggerRemaining(1);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Only catalog values cross the debugger error boundary. Never inspect exception text. */
|
|
2
|
+
export const stages = ['prerequisite', 'compilation', 'worker_start', 'ipc', 'target_resolution', 'forwarding', 'attach', 'command', 'transport', 'cleanup', 'unknown'];
|
|
3
|
+
export const reasons = ['unavailable', 'unsupported', 'spawn_failed', 'process_exited', 'invalid_message', 'message_limit', 'write_failed', 'connection_refused', 'transport_lost', 'timeout', 'cancelled', 'operation_failed', 'unknown'];
|
|
4
|
+
const recovery = {
|
|
5
|
+
prerequisite: 'Check JDK 17+ availability, including java, javac and jdk.jdi.',
|
|
6
|
+
compilation: 'Check the JDK compiler and worker source/cache availability.',
|
|
7
|
+
worker_start: 'Check Java worker launch availability. Inspect session state and cleanup before attaching again.',
|
|
8
|
+
ipc: 'Inspect session state and cleanup. Detach before attempting a fresh attach.',
|
|
9
|
+
target_resolution: 'Check the explicit device and running debuggable app/process.',
|
|
10
|
+
forwarding: 'Check the explicit device and debugger forwarding availability.',
|
|
11
|
+
attach: 'Check the running debuggable target and whether another debugger is attached. Inspect cleanup before a fresh attach.',
|
|
12
|
+
command: 'Inspect debugger session state and cleanup before another state-changing command.',
|
|
13
|
+
transport: 'Inspect session state and cleanup. Detach before attempting a fresh attach; execution state may be unknown.',
|
|
14
|
+
cleanup: 'Retry detach_debugger and inspect cleanup.remaining_resources. Do not kill the app or ADB server.',
|
|
15
|
+
unknown: 'Inspect debugger session state and cleanup; the failure cause is unknown.'
|
|
16
|
+
};
|
|
17
|
+
export function diagnostic(stage, reason) {
|
|
18
|
+
const guidance = reason === 'timeout' || reason === 'cancelled'
|
|
19
|
+
? 'Execution may be uncertain. Inspect session state and cleanup before another state-changing command.' : recovery[stage];
|
|
20
|
+
return { stage, reason, recovery: guidance };
|
|
21
|
+
}
|
|
22
|
+
export function parseDiagnostic(value) {
|
|
23
|
+
if (!value || typeof value !== 'object')
|
|
24
|
+
throw new Error('INVALID_WORKER_DIAGNOSTIC');
|
|
25
|
+
const { stage, reason } = value;
|
|
26
|
+
if (!stages.includes(stage) || !reasons.includes(reason))
|
|
27
|
+
throw new Error('INVALID_WORKER_DIAGNOSTIC');
|
|
28
|
+
return diagnostic(stage, reason);
|
|
29
|
+
}
|
|
30
|
+
export class DebuggerError extends Error {
|
|
31
|
+
code;
|
|
32
|
+
diagnostic;
|
|
33
|
+
constructor(code, diagnostic) {
|
|
34
|
+
super(code);
|
|
35
|
+
this.code = code;
|
|
36
|
+
this.diagnostic = diagnostic;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const validationCodes = new Set(['INVALID_ARGUMENT', 'DEBUGGER_BUSY', 'DEBUGGER_NOT_FOUND', 'DEBUGGER_PAUSED', 'DEBUGGER_NOT_PAUSED', 'DEBUGGER_STALE_REFERENCE']);
|
|
40
|
+
export function classify(error, stage, fallback, reason = 'unknown') {
|
|
41
|
+
const existing = error instanceof DebuggerError ? error : new DebuggerError(fallback);
|
|
42
|
+
if (existing.diagnostic || validationCodes.has(existing.code))
|
|
43
|
+
return existing;
|
|
44
|
+
const knownReason = existing.code === 'DEBUGGER_TIMEOUT' ? 'timeout' : existing.code === 'DEBUGGER_CANCELLED' ? 'cancelled' : reason;
|
|
45
|
+
return new DebuggerError(existing.code, diagnostic(stage, knownReason));
|
|
46
|
+
}
|
|
47
|
+
export async function atStage(stage, fallback, operation) {
|
|
48
|
+
try {
|
|
49
|
+
return await operation();
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
throw classify(error, stage, fallback);
|
|
53
|
+
}
|
|
54
|
+
}
|
package/dist/debugger/guard.js
CHANGED
|
@@ -9,16 +9,16 @@ export function withDebuggerGuard(platform, device, operation) {
|
|
|
9
9
|
// Synchronous checks and native submission share a JS turn with pause updates.
|
|
10
10
|
export function checkDebuggerGuard(dispatch = false) {
|
|
11
11
|
const current = context.getStore();
|
|
12
|
-
if (!current
|
|
12
|
+
if (!current)
|
|
13
13
|
return;
|
|
14
|
-
if (pauseProvider(current.device))
|
|
14
|
+
if (pauseProvider(current.device, current.platform))
|
|
15
15
|
throw new DebuggerError('DEBUGGER_PAUSED');
|
|
16
16
|
if (dispatch)
|
|
17
17
|
current.dispatched = true;
|
|
18
18
|
}
|
|
19
19
|
export function guardDispatched() { return context.getStore()?.dispatched ?? false; }
|
|
20
|
-
export function pausedReadiness(device) {
|
|
21
|
-
const paused = pauseProvider(device);
|
|
20
|
+
export function pausedReadiness(device, platform = 'android') {
|
|
21
|
+
const paused = pauseProvider(device, platform);
|
|
22
22
|
if (!paused)
|
|
23
23
|
return null;
|
|
24
24
|
return { state: 'debugger_paused', scope: 'device_ui', basis: 'observed',
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { getXcrunCmd } from '../utils/ios/utils.js';
|
|
3
|
+
import { iosExec } from './ios.js';
|
|
4
|
+
import { DebuggerProcess } from './worker.js';
|
|
5
|
+
import { DebuggerError } from './diagnostics.js';
|
|
6
|
+
export const iosCapabilities = { attach: true, breakpoints: true, stacks: true, locals: true, step: true, resume: true,
|
|
7
|
+
evaluation: false, mutation: false, coroutines: false, native: false, async_execution_inspection: true, async_stepping: true };
|
|
8
|
+
export const iosLimitations = ['IOS_SIMULATOR_ONLY', 'DEBUG_METADATA_REQUIRED', 'SHALLOW_LOCALS', 'ALL_THREADS_SUSPEND', 'DEVICE_WIDE_UI_GUARD', 'ASYNC_STEPPING_TOOLCHAIN_DEPENDENT', 'AGGREGATE_VALUES_TYPE_ONLY', 'NO_TASK_ENUMERATION'];
|
|
9
|
+
export const iosWorkerSource = fileURLToPath(new URL('../../src/debugger/lldb/worker.py', import.meta.url));
|
|
10
|
+
let prepared;
|
|
11
|
+
export function prepareIOSWorker() {
|
|
12
|
+
prepared ??= (async () => {
|
|
13
|
+
if (process.platform !== 'darwin')
|
|
14
|
+
throw new DebuggerError('DEBUGGER_UNSUPPORTED');
|
|
15
|
+
const pythonPath = await iosExec(getXcrunCmd(), ['lldb', '-P']);
|
|
16
|
+
const python = await iosExec(getXcrunCmd(), ['--find', 'python3']);
|
|
17
|
+
// Ignore user Python startup/customisation and LLDB init files.
|
|
18
|
+
const env = { PATH: process.env.PATH, HOME: process.env.HOME, PYTHONPATH: pythonPath, PYTHONNOUSERSITE: '1' };
|
|
19
|
+
const ready = await iosExec(python, ['-s', '-B', '-c', 'import lldb; d=lldb.SBDebugger.Create(False); print("ready" if d.IsValid() else "unavailable"); lldb.SBDebugger.Destroy(d)'], env);
|
|
20
|
+
if (ready !== 'ready')
|
|
21
|
+
throw new DebuggerError('DEBUGGER_UNSUPPORTED');
|
|
22
|
+
return { python, env };
|
|
23
|
+
})().catch(error => { prepared = undefined; throw error; });
|
|
24
|
+
return prepared;
|
|
25
|
+
}
|
|
26
|
+
export async function createIOSWorker(onEvent) {
|
|
27
|
+
const { python, env } = await prepareIOSWorker();
|
|
28
|
+
let released = false;
|
|
29
|
+
const worker = new DebuggerProcess(python, '', event => {
|
|
30
|
+
if (event.event === 'terminated' && ['process_exit', 'detached', 'pause_limit', 'idle_limit', 'session_limit'].includes(String(event.reason)))
|
|
31
|
+
released = true;
|
|
32
|
+
onEvent(event);
|
|
33
|
+
}, { args: ['-u', '-s', '-B', iosWorkerSource], env });
|
|
34
|
+
return new IOSWorker(worker, () => released);
|
|
35
|
+
}
|
|
36
|
+
/** A dead worker is not proof that its owned suspension was released. */
|
|
37
|
+
export class IOSWorker {
|
|
38
|
+
worker;
|
|
39
|
+
released;
|
|
40
|
+
detached = false;
|
|
41
|
+
constructor(worker, released) {
|
|
42
|
+
this.worker = worker;
|
|
43
|
+
this.released = released;
|
|
44
|
+
}
|
|
45
|
+
async call(command, args = {}, timeout = 10000) {
|
|
46
|
+
const result = await this.worker.call(command, args, timeout);
|
|
47
|
+
if (command === 'detach' && result.detached === true)
|
|
48
|
+
this.detached = true;
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
async close() {
|
|
52
|
+
if (!this.detached && !this.released()) {
|
|
53
|
+
try {
|
|
54
|
+
await this.call('detach', {}, 1000);
|
|
55
|
+
}
|
|
56
|
+
catch { /* Report uncertainty after bounded transport shutdown. */ }
|
|
57
|
+
}
|
|
58
|
+
await this.worker.close();
|
|
59
|
+
if (!this.detached && !this.released())
|
|
60
|
+
throw new DebuggerError('DEBUGGER_CLEANUP_FAILED');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { realpath } from 'node:fs/promises';
|
|
5
|
+
import { getXcrunCmd } from '../utils/ios/utils.js';
|
|
6
|
+
import { DebuggerError, debuggerRemaining, debuggerSignal, nativeError } from './worker.js';
|
|
7
|
+
const exec = promisify(execFile);
|
|
8
|
+
export async function iosExec(file, args, env) {
|
|
9
|
+
try {
|
|
10
|
+
return (await exec(file, args, { timeout: debuggerRemaining(5000), signal: debuggerSignal(), maxBuffer: 1024 * 1024, ...(env ? { env } : {}) })).stdout.trim();
|
|
11
|
+
}
|
|
12
|
+
catch (e) {
|
|
13
|
+
throw nativeError(e, 'DEVICE_UNAVAILABLE');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class IOSDebugNative {
|
|
17
|
+
run;
|
|
18
|
+
constructor(run = iosExec) {
|
|
19
|
+
this.run = run;
|
|
20
|
+
}
|
|
21
|
+
sim(args) { return this.run(getXcrunCmd(), ['simctl', ...args]); }
|
|
22
|
+
async candidates(device, app) {
|
|
23
|
+
if (!/^[A-Fa-f0-9]{8}(?:-[A-Fa-f0-9]{4}){3}-[A-Fa-f0-9]{12}$/.test(device) || !/^[A-Za-z0-9][A-Za-z0-9.-]+$/.test(app))
|
|
24
|
+
throw new DebuggerError('INVALID_ARGUMENT');
|
|
25
|
+
const inventory = JSON.parse(await this.sim(['list', 'devices', 'booted', '--json']));
|
|
26
|
+
if (!Object.values(inventory.devices).flat().some(d => d.udid === device && d.state === 'Booted'))
|
|
27
|
+
throw new DebuggerError('DEBUGGER_UNSUPPORTED');
|
|
28
|
+
const services = await this.sim(['spawn', device, 'launchctl', 'list']);
|
|
29
|
+
const pids = this.processes(services, app);
|
|
30
|
+
if (!pids.length)
|
|
31
|
+
return [];
|
|
32
|
+
const container = await this.sim(['get_app_container', device, app, 'app']);
|
|
33
|
+
const executable = await this.run('/usr/bin/plutil', ['-extract', 'CFBundleExecutable', 'raw', '-o', '-', path.join(container, 'Info.plist')]);
|
|
34
|
+
if (path.basename(executable) !== executable || !executable)
|
|
35
|
+
throw new DebuggerError('PROCESS_NOT_FOUND');
|
|
36
|
+
const executablePath = await realpath(path.join(container, executable));
|
|
37
|
+
return Promise.all(pids.map(pid => this.identity(device, { app_id: app, pid, process_name: executable, executable_path: executablePath, start_identity: '', uid: null })));
|
|
38
|
+
}
|
|
39
|
+
processes(services, app) {
|
|
40
|
+
return services.split('\n').flatMap(line => {
|
|
41
|
+
const [pid, , label] = line.trim().split(/\s+/);
|
|
42
|
+
return /^\d+$/.test(pid) && label?.startsWith(`UIKitApplication:${app}[`) ? [Number(pid)] : [];
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async identity(device, target) {
|
|
46
|
+
const services = await this.sim(['spawn', device, 'launchctl', 'list']);
|
|
47
|
+
if (!this.processes(services, target.app_id).includes(target.pid))
|
|
48
|
+
throw new DebuggerError('PROCESS_CHANGED');
|
|
49
|
+
const command = await this.run('/bin/ps', ['-ww', '-p', String(target.pid), '-o', 'comm=']);
|
|
50
|
+
const started = await this.run('/bin/ps', ['-p', String(target.pid), '-o', 'lstart=']);
|
|
51
|
+
if (command !== target.executable_path || !started || (target.start_identity && started !== target.start_identity))
|
|
52
|
+
throw new DebuggerError('PROCESS_CHANGED');
|
|
53
|
+
return { ...target, start_identity: started };
|
|
54
|
+
}
|
|
55
|
+
async forward() { throw new DebuggerError('DEBUGGER_UNSUPPORTED'); }
|
|
56
|
+
async removeForward() { throw new DebuggerError('DEBUGGER_UNSUPPORTED'); }
|
|
57
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { parseDiagnostic } from './diagnostics.js';
|
|
2
|
+
const workerCodes = new Set(['INVALID_ARGUMENT', 'DEBUGGER_BUSY', 'DEBUGGER_NOT_FOUND', 'DEBUGGER_NOT_PAUSED', 'DEBUGGER_STALE_REFERENCE', 'DEBUGGER_DISCONNECTED', 'DEBUGGER_WORKER_FAILED']);
|
|
3
|
+
function record(value) {
|
|
4
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
export function parseWorkerMessage(value) {
|
|
7
|
+
if (!record(value))
|
|
8
|
+
throw new Error('INVALID_WORKER_MESSAGE');
|
|
9
|
+
if (value.event) {
|
|
10
|
+
if (typeof value.event !== 'string')
|
|
11
|
+
throw new Error('INVALID_WORKER_EVENT');
|
|
12
|
+
for (const field of ['reason', 'stop_id']) {
|
|
13
|
+
if (value[field] !== undefined && typeof value[field] !== 'string')
|
|
14
|
+
throw new Error('INVALID_WORKER_EVENT');
|
|
15
|
+
}
|
|
16
|
+
const event = { ...value };
|
|
17
|
+
if (value.diagnostic !== undefined)
|
|
18
|
+
event.diagnostic = parseDiagnostic(value.diagnostic);
|
|
19
|
+
return { kind: 'event', value: event };
|
|
20
|
+
}
|
|
21
|
+
if (typeof value.id !== 'number' || !Number.isSafeInteger(value.id) || value.id < 1)
|
|
22
|
+
throw new Error('INVALID_WORKER_REPLY');
|
|
23
|
+
if (value.error) {
|
|
24
|
+
if (!record(value.error) || (value.error.code !== undefined && typeof value.error.code !== 'string'))
|
|
25
|
+
throw new Error('INVALID_WORKER_ERROR');
|
|
26
|
+
if (value.error.code !== undefined && !workerCodes.has(value.error.code))
|
|
27
|
+
throw new Error('INVALID_WORKER_ERROR');
|
|
28
|
+
return { kind: 'reply', id: value.id, result: {}, error: { ...(value.error.code === undefined ? {} : { code: value.error.code }), ...(value.error.diagnostic === undefined ? {} : { diagnostic: parseDiagnostic(value.error.diagnostic) }) } };
|
|
29
|
+
}
|
|
30
|
+
if (!record(value.result))
|
|
31
|
+
throw new Error('INVALID_WORKER_RESULT');
|
|
32
|
+
return { kind: 'reply', id: value.id, result: value.result };
|
|
33
|
+
}
|