taphound 0.2.0-dev.1 → 0.2.0-dev.11
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/README.md +49 -149
- package/README.zh-CN.md +47 -147
- package/assets/skills/taphound-case-suite/SKILL.md +363 -0
- package/assets/skills/taphound-case-suite/schemas/base-flow-record.schema.json +22 -0
- package/assets/skills/taphound-case-suite/schemas/case-catalog.schema.json +26 -0
- package/assets/skills/taphound-case-suite/schemas/case-ledger.schema.json +158 -0
- package/assets/skills/taphound-case-suite/schemas/suite-input.schema.json +41 -0
- package/assets/skills/taphound-case-suite/schemas/transition.schema.json +61 -0
- package/assets/skills/taphound-case-suite/scripts/ledger.mjs +1207 -0
- package/assets/skills/taphound-case-suite/templates/base-flow-record.example.json +20 -0
- package/assets/skills/taphound-case-suite/templates/suite-input.example.json +27 -0
- package/assets/skills/taphound-case-suite/templates/transition.example.json +8 -0
- package/assets/skills/taphound-flash/SKILL.md +115 -0
- package/assets/skills/taphound-flash/scripts/flash.mjs +585 -0
- package/assets/skills/taphound-flash/templates/flash-plan.example.json +15 -0
- package/assets/skills/taphound-journey-brief-author/CONTEXT-GUIDE.md +471 -0
- package/assets/skills/taphound-journey-brief-author/SKILL.md +494 -0
- package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.md +96 -0
- package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.zh-CN.md +80 -0
- package/assets/skills/taphound-journey-brief-author/prompts/context-analyze-project.md +309 -0
- package/assets/skills/taphound-journey-brief-author/schemas/context-refresh-result.json +123 -0
- package/assets/skills/taphound-journey-brief-author/schemas/project-context-module.json +192 -0
- package/assets/skills/taphound-journey-brief-author/schemas/project-context.json +152 -0
- package/assets/skills/taphound-journey-brief-author/templates/project-context-module.example.json +58 -0
- package/assets/skills/taphound-journey-brief-author/templates/project-context.example.json +51 -0
- package/assets/skills/taphound-journey-brief-author/templates/taphound-journey-brief.template.md +63 -0
- package/assets/skills/taphound-journey-generator/SKILL.md +500 -0
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/prompts/check-completion.md +3 -0
- package/assets/skills/taphound-journey-generator/prompts/consume-journey-brief.md +52 -0
- package/assets/skills/taphound-journey-generator/prompts/generate-step.md +211 -0
- package/assets/skills/taphound-journey-generator/prompts/select-flow.md +91 -0
- package/assets/skills/taphound-journey-generator/references/bridge.md +35 -0
- package/assets/skills/taphound-journey-generator/references/mid-session.md +44 -0
- package/assets/skills/taphound-journey-generator/schemas/external-flow.json +37 -0
- package/assets/skills/taphound-journey-generator/schemas/flow.json +28 -0
- package/assets/skills/taphound-journey-generator/schemas/journey-source.json +25 -0
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/observe-output.json +13 -4
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/proposed-step-envelope.json +127 -5
- package/assets/skills/taphound-journey-generator/scripts/envelope.mjs +561 -0
- package/assets/skills/taphound-journey-generator/templates/external-flow.example.json +22 -0
- package/assets/skills/taphound-journey-generator/templates/flow.example.json +22 -0
- package/assets/skills/taphound-journey-generator/templates/journey-source.example.json +17 -0
- package/assets/skills/taphound-journey-generator/templates/taphound-journey-brief.example.md +70 -0
- package/assets/skills/taphound-verify-change/SKILL.md +91 -0
- package/assets/skills/taphound-verify-change/references/accept.md +38 -0
- package/assets/skills/taphound-verify-change/references/preserve.md +349 -0
- package/assets/skills/taphound-verify-change/schemas/workflow-manifest.schema.json +295 -0
- package/assets/skills/taphound-verify-change/scripts/handoff.mjs +482 -0
- package/assets/skills/taphound-verify-change/scripts/ui-refactor.mjs +505 -0
- package/dist/adapters/adb/adb-adapter.d.ts +16 -2
- package/dist/adapters/adb/adb-adapter.js +199 -0
- package/dist/adapters/adb/system-uiautomator-snapshot-provider.d.ts +13 -0
- package/dist/adapters/adb/system-uiautomator-snapshot-provider.js +122 -0
- package/dist/adapters/adb/ui-automator-parser.d.ts +3 -0
- package/dist/adapters/adb/ui-automator-parser.js +160 -0
- package/dist/adapters/adb/window-topology-parser.d.ts +2 -0
- package/dist/adapters/adb/window-topology-parser.js +101 -0
- package/dist/adapters/android-cli/android-cli-adapter.d.ts +16 -8
- package/dist/adapters/android-cli/android-cli-adapter.js +137 -15
- package/dist/adapters/android-cli/android-cli-snapshot-provider.d.ts +9 -0
- package/dist/adapters/android-cli/android-cli-snapshot-provider.js +99 -0
- package/dist/adapters/android-cli/layout-parser.js +12 -8
- package/dist/adapters/appium/appium-doctor.d.ts +7 -0
- package/dist/adapters/appium/appium-doctor.js +128 -0
- package/dist/adapters/appium/appium-ui-snapshot-provider.d.ts +34 -0
- package/dist/adapters/appium/appium-ui-snapshot-provider.js +240 -0
- package/dist/adapters/camera/camera-probe-adapter.d.ts +18 -0
- package/dist/adapters/camera/camera-probe-adapter.js +286 -0
- package/dist/adapters/filesystem/artifact-store.js +40 -1
- package/dist/adapters/filesystem/context-document-writer.d.ts +15 -0
- package/dist/adapters/filesystem/context-document-writer.js +112 -0
- package/dist/adapters/filesystem/external-flow-registry.d.ts +17 -0
- package/dist/adapters/filesystem/external-flow-registry.js +325 -0
- package/dist/adapters/filesystem/generation-session-store.d.ts +17 -11
- package/dist/adapters/filesystem/generation-session-store.js +281 -855
- package/dist/adapters/filesystem/generation-store/session-lock.d.ts +29 -0
- package/dist/adapters/filesystem/generation-store/session-lock.js +202 -0
- package/dist/adapters/filesystem/generation-store/session-transitions.d.ts +24 -0
- package/dist/adapters/filesystem/generation-store/session-transitions.js +340 -0
- package/dist/adapters/filesystem/generation-store/store-files.d.ts +50 -0
- package/dist/adapters/filesystem/generation-store/store-files.js +363 -0
- package/dist/adapters/filesystem/journey-composition-store.d.ts +23 -0
- package/dist/adapters/filesystem/journey-composition-store.js +151 -0
- package/dist/adapters/filesystem/knowledge-registry.d.ts +12 -0
- package/dist/adapters/filesystem/knowledge-registry.js +223 -0
- package/dist/adapters/filesystem/project-bound-file.js +3 -5
- package/dist/adapters/filesystem/project-file-inspector.d.ts +2 -0
- package/dist/adapters/filesystem/project-file-inspector.js +57 -28
- package/dist/adapters/filesystem/project-identity-inspector.d.ts +7 -0
- package/dist/adapters/filesystem/project-identity-inspector.js +215 -0
- package/dist/adapters/filesystem/project-inventory-inspector.d.ts +8 -0
- package/dist/adapters/filesystem/project-inventory-inspector.js +93 -0
- package/dist/adapters/filesystem/project-module-discoverer.d.ts +6 -0
- package/dist/adapters/filesystem/project-module-discoverer.js +158 -0
- package/dist/adapters/filesystem/skill-installer.d.ts +7 -2
- package/dist/adapters/filesystem/skill-installer.js +40 -11
- package/dist/adapters/filesystem/workspace-layout.d.ts +7 -0
- package/dist/adapters/filesystem/workspace-layout.js +57 -0
- package/dist/adapters/git/node-git-diff.d.ts +17 -0
- package/dist/adapters/git/node-git-diff.js +114 -0
- package/dist/adapters/process/node-detached-process-launcher.d.ts +6 -0
- package/dist/adapters/process/node-detached-process-launcher.js +96 -0
- package/dist/adapters/process/node-process-runner.js +11 -2
- package/dist/adapters/prompt/inquirer-align-prompt.d.ts +27 -0
- package/dist/adapters/prompt/inquirer-align-prompt.js +65 -0
- package/dist/adapters/prompt/inquirer-generation-prompt.js +3 -0
- package/dist/adapters/prompt/inquirer-recorder-prompt.d.ts +9 -1
- package/dist/adapters/prompt/inquirer-recorder-prompt.js +63 -0
- package/dist/adapters/runtime/adb-runtime-backend.d.ts +67 -0
- package/dist/adapters/runtime/adb-runtime-backend.js +206 -0
- package/dist/adapters/runtime/fake-runtime-backend.d.ts +87 -0
- package/dist/adapters/runtime/fake-runtime-backend.js +240 -0
- package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.d.ts +45 -0
- package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.js +153 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.d.ts +23 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.js +39 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.d.ts +20 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.js +180 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.d.ts +78 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.js +379 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-tools.d.ts +46 -0
- package/dist/adapters/runtime/session-adb-view.d.ts +53 -0
- package/dist/adapters/runtime/session-adb-view.js +193 -0
- package/dist/adapters/runtime/session-backed-ports.d.ts +21 -0
- package/dist/adapters/runtime/session-backed-ports.js +43 -0
- package/dist/adapters/runtime/shared-session-runtime-backend.d.ts +12 -0
- package/dist/adapters/runtime/shared-session-runtime-backend.js +64 -0
- package/dist/adapters/ui/auto-ui-snapshot-provider.d.ts +8 -0
- package/dist/adapters/ui/auto-ui-snapshot-provider.js +48 -0
- package/dist/adapters/ui/device-layout-temp.d.ts +6 -0
- package/dist/adapters/ui/device-layout-temp.js +18 -0
- package/dist/adapters/ui/device-ui-environment.d.ts +13 -0
- package/dist/adapters/ui/device-ui-environment.js +81 -0
- package/dist/adapters/ui/layout-normalization.d.ts +9 -0
- package/dist/adapters/ui/layout-normalization.js +44 -0
- package/dist/adapters/ui/ui-snapshot-error.d.ts +11 -0
- package/dist/adapters/ui/ui-snapshot-error.js +12 -0
- package/dist/adapters/ui/ui-snapshot-support.d.ts +15 -0
- package/dist/adapters/ui/ui-snapshot-support.js +12 -0
- package/dist/application/align/align-service.d.ts +42 -0
- package/dist/application/align/align-service.js +106 -0
- package/dist/application/assertion/expectation-evaluator.d.ts +18 -5
- package/dist/application/assertion/expectation-evaluator.js +156 -18
- package/dist/application/assertion/guarded-expectation.d.ts +45 -0
- package/dist/application/assertion/guarded-expectation.js +74 -0
- package/dist/application/checkpoint/baseline-capturer.d.ts +26 -0
- package/dist/application/checkpoint/baseline-capturer.js +163 -0
- package/dist/application/checkpoint/baseline-error.d.ts +5 -0
- package/dist/application/checkpoint/baseline-error.js +8 -0
- package/dist/application/checkpoint/baseline-service.d.ts +31 -0
- package/dist/application/checkpoint/baseline-service.js +147 -0
- package/dist/application/checkpoint/checkpoint-evaluator.d.ts +35 -0
- package/dist/application/checkpoint/checkpoint-evaluator.js +415 -0
- package/dist/application/checkpoint/regression-comparator.d.ts +18 -0
- package/dist/application/checkpoint/regression-comparator.js +192 -0
- package/dist/application/collector/logcat-collector.d.ts +22 -1
- package/dist/application/collector/logcat-collector.js +103 -15
- package/dist/application/collector/logcat-event.d.ts +8 -0
- package/dist/application/collector/logcat-event.js +79 -0
- package/dist/application/collector/logcat-stop.d.ts +2 -0
- package/dist/application/collector/logcat-stop.js +11 -0
- package/dist/application/context/context-generator.d.ts +45 -0
- package/dist/application/context/context-generator.js +228 -0
- package/dist/application/context/context-loader.d.ts +40 -0
- package/dist/application/context/context-loader.js +223 -0
- package/dist/application/context/context-refresher.d.ts +59 -0
- package/dist/application/context/context-refresher.js +349 -0
- package/dist/application/context/context-rehasher.d.ts +36 -0
- package/dist/application/context/context-rehasher.js +110 -0
- package/dist/application/context/context-validator.d.ts +23 -1
- package/dist/application/context/context-validator.js +116 -9
- package/dist/application/context/evidence-hash.d.ts +1 -0
- package/dist/application/context/evidence-hash.js +125 -0
- package/dist/application/context/evidence-match.d.ts +9 -0
- package/dist/application/context/evidence-match.js +8 -0
- package/dist/application/context/shard-identity.d.ts +2 -0
- package/dist/application/context/shard-identity.js +7 -0
- package/dist/application/contract/contract-loader.d.ts +25 -0
- package/dist/application/contract/contract-loader.js +68 -0
- package/dist/application/contract/contract-review.d.ts +25 -0
- package/dist/application/contract/contract-review.js +51 -0
- package/dist/application/contract/contract-verifier.d.ts +53 -0
- package/dist/application/contract/contract-verifier.js +645 -0
- package/dist/application/devices/resolve-device-assignments.d.ts +21 -0
- package/dist/application/devices/resolve-device-assignments.js +60 -0
- package/dist/application/diagnosis/failure-classifier.d.ts +23 -0
- package/dist/application/diagnosis/failure-classifier.js +179 -0
- package/dist/application/doctor/doctor-service.d.ts +36 -3
- package/dist/application/doctor/doctor-service.js +255 -25
- package/dist/application/generation/generation-app-preparer.d.ts +20 -0
- package/dist/application/generation/generation-app-preparer.js +28 -0
- package/dist/application/generation/generation-config-service.d.ts +21 -0
- package/dist/application/generation/generation-config-service.js +53 -0
- package/dist/application/generation/generation-confirmation-service.d.ts +14 -3
- package/dist/application/generation/generation-confirmation-service.js +109 -24
- package/dist/application/generation/generation-context-snapshot.d.ts +6 -0
- package/dist/application/generation/generation-context-snapshot.js +29 -0
- package/dist/application/generation/generation-finalizer.d.ts +15 -9
- package/dist/application/generation/generation-finalizer.js +167 -70
- package/dist/application/generation/generation-publisher.js +12 -8
- package/dist/application/generation/generation-recovery-service.d.ts +35 -0
- package/dist/application/generation/generation-recovery-service.js +144 -0
- package/dist/application/generation/generation-reopen-service.d.ts +15 -0
- package/dist/application/generation/generation-reopen-service.js +37 -0
- package/dist/application/generation/generation-replace-service.d.ts +32 -0
- package/dist/application/generation/generation-replace-service.js +116 -0
- package/dist/application/generation/generation-starter.d.ts +44 -4
- package/dist/application/generation/generation-starter.js +175 -8
- package/dist/application/generation/generation-step-executor.d.ts +67 -5
- package/dist/application/generation/generation-step-executor.js +471 -108
- package/dist/application/generation/proposed-step-validator.js +96 -30
- package/dist/application/generation/replay-policy-loader.d.ts +16 -0
- package/dist/application/generation/replay-policy-loader.js +29 -0
- package/dist/application/generation/risk-evaluator.d.ts +4 -1
- package/dist/application/generation/risk-evaluator.js +118 -3
- package/dist/application/generation/runtime-observer.d.ts +39 -7
- package/dist/application/generation/runtime-observer.js +193 -24
- package/dist/application/impact/impact-resolver.d.ts +32 -0
- package/dist/application/impact/impact-resolver.js +160 -0
- package/dist/application/init/init-service.js +26 -13
- package/dist/application/interaction/action-executor.d.ts +3 -2
- package/dist/application/interaction/action-executor.js +11 -1
- package/dist/application/interaction/action-target.d.ts +17 -0
- package/dist/application/interaction/action-target.js +40 -0
- package/dist/application/interaction/external-step-runner.d.ts +153 -0
- package/dist/application/interaction/external-step-runner.js +332 -0
- package/dist/application/interaction/fallback-resolver.d.ts +6 -3
- package/dist/application/interaction/fallback-resolver.js +7 -5
- package/dist/application/interaction/scroll-to-executor.d.ts +17 -3
- package/dist/application/interaction/scroll-to-executor.js +118 -36
- package/dist/application/journey/external-flow-resolver.d.ts +25 -0
- package/dist/application/journey/external-flow-resolver.js +22 -0
- package/dist/application/journey/journey-check-service.d.ts +54 -0
- package/dist/application/journey/journey-check-service.js +207 -0
- package/dist/application/journey/journey-promoter.d.ts +26 -0
- package/dist/application/journey/journey-promoter.js +123 -0
- package/dist/application/journey/journey-resolver.d.ts +42 -0
- package/dist/application/journey/journey-resolver.js +239 -0
- package/dist/application/journey/journey-retirer.d.ts +25 -0
- package/dist/application/journey/journey-retirer.js +87 -0
- package/dist/application/knowledge/anchor-resolver.d.ts +36 -0
- package/dist/application/knowledge/anchor-resolver.js +110 -0
- package/dist/application/knowledge/knowledge-loader.d.ts +16 -0
- package/dist/application/knowledge/knowledge-loader.js +36 -0
- package/dist/application/locator/layout-failure-summary.d.ts +2 -0
- package/dist/application/locator/layout-failure-summary.js +67 -0
- package/dist/application/locator/layout-traversal.d.ts +11 -0
- package/dist/application/locator/layout-traversal.js +18 -0
- package/dist/application/locator/locator-resolver.d.ts +13 -1
- package/dist/application/locator/locator-resolver.js +182 -18
- package/dist/application/observe/observe-service.d.ts +20 -0
- package/dist/application/observe/observe-service.js +91 -0
- package/dist/application/project/project-describer.d.ts +14 -6
- package/dist/application/project/project-describer.js +38 -5
- package/dist/application/recognition/screen-detector.d.ts +28 -0
- package/dist/application/recognition/screen-detector.js +111 -0
- package/dist/application/recorder/locator-selector.js +151 -17
- package/dist/application/recorder/recorder-service.d.ts +16 -4
- package/dist/application/recorder/recorder-service.js +420 -128
- package/dist/application/report/report-writer.js +1 -1
- package/dist/application/runtime/cold-launch.d.ts +28 -0
- package/dist/application/runtime/cold-launch.js +59 -0
- package/dist/application/runtime/step-runner.d.ts +49 -4
- package/dist/application/runtime/step-runner.js +721 -130
- package/dist/application/runtime/verify-runtime.d.ts +57 -7
- package/dist/application/runtime/verify-runtime.js +554 -174
- package/dist/application/ui/cached-ui-snapshot-provider.d.ts +45 -0
- package/dist/application/ui/cached-ui-snapshot-provider.js +111 -0
- package/dist/application/ui/ui-snapshot-lifecycle.d.ts +2 -0
- package/dist/application/ui/ui-snapshot-lifecycle.js +8 -0
- package/dist/application/ui/ui-stability-probe.d.ts +3 -0
- package/dist/application/ui/ui-stability-probe.js +8 -0
- package/dist/application/wait/idle-advice.d.ts +6 -0
- package/dist/application/wait/idle-advice.js +27 -0
- package/dist/application/wait/idle-profiles.d.ts +15 -0
- package/dist/application/wait/idle-profiles.js +75 -0
- package/dist/application/wait/idle-waiter.d.ts +28 -4
- package/dist/application/wait/idle-waiter.js +186 -15
- package/dist/cli/commands/align.d.ts +3 -0
- package/dist/cli/commands/align.js +124 -0
- package/dist/cli/commands/baseline.d.ts +3 -0
- package/dist/cli/commands/baseline.js +130 -0
- package/dist/cli/commands/context.js +300 -27
- package/dist/cli/commands/contract.d.ts +3 -0
- package/dist/cli/commands/contract.js +120 -0
- package/dist/cli/commands/doctor.js +16 -9
- package/dist/cli/commands/failure.d.ts +3 -0
- package/dist/cli/commands/failure.js +63 -0
- package/dist/cli/commands/generation/lifecycle-commands.d.ts +5 -0
- package/dist/cli/commands/generation/lifecycle-commands.js +211 -0
- package/dist/cli/commands/generation/session-commands.d.ts +11 -0
- package/dist/cli/commands/generation/session-commands.js +472 -0
- package/dist/cli/commands/generation/shared.d.ts +1414 -0
- package/dist/cli/commands/generation/shared.js +291 -0
- package/dist/cli/commands/generation/step-commands.d.ts +8 -0
- package/dist/cli/commands/generation/step-commands.js +315 -0
- package/dist/cli/commands/generation.js +10 -507
- package/dist/cli/commands/impact.d.ts +3 -0
- package/dist/cli/commands/impact.js +87 -0
- package/dist/cli/commands/init.js +13 -6
- package/dist/cli/commands/journey.d.ts +3 -0
- package/dist/cli/commands/journey.js +382 -0
- package/dist/cli/commands/knowledge.d.ts +3 -0
- package/dist/cli/commands/knowledge.js +93 -0
- package/dist/cli/commands/observe.d.ts +3 -0
- package/dist/cli/commands/observe.js +127 -0
- package/dist/cli/commands/project.js +2 -1
- package/dist/cli/commands/record.js +18 -5
- package/dist/cli/commands/verify.js +273 -63
- package/dist/cli/dependencies.d.ts +125 -1
- package/dist/cli/dependencies.js +450 -51
- package/dist/cli/diff-verification.d.ts +41 -0
- package/dist/cli/diff-verification.js +192 -0
- package/dist/cli/main.js +49 -1
- package/dist/cli/output.d.ts +5 -3
- package/dist/cli/output.js +12 -0
- package/dist/cli/program.js +19 -1
- package/dist/cli/project-root.d.ts +1 -0
- package/dist/cli/project-root.js +6 -0
- package/dist/cli/runtime-selection.d.ts +22 -0
- package/dist/cli/runtime-selection.js +92 -0
- package/dist/cli/version.d.ts +1 -0
- package/dist/cli/version.js +14 -0
- package/dist/cli/workspace-guard.d.ts +2 -0
- package/dist/cli/workspace-guard.js +3 -0
- package/dist/domain/binding-reference.d.ts +4 -0
- package/dist/domain/binding-reference.js +23 -0
- package/dist/domain/checkpoint.d.ts +404 -0
- package/dist/domain/checkpoint.js +297 -0
- package/dist/domain/config.d.ts +107 -1
- package/dist/domain/config.js +52 -6
- package/dist/domain/contract.d.ts +413 -0
- package/dist/domain/contract.js +196 -0
- package/dist/domain/external-flow.d.ts +493 -0
- package/dist/domain/external-flow.js +15 -0
- package/dist/domain/failure-classification.d.ts +176 -0
- package/dist/domain/failure-classification.js +169 -0
- package/dist/domain/failure.d.ts +2 -1
- package/dist/domain/failure.js +123 -0
- package/dist/domain/generation.d.ts +1671 -132
- package/dist/domain/generation.js +202 -13
- package/dist/domain/geometry.d.ts +13 -0
- package/dist/domain/geometry.js +16 -0
- package/dist/domain/impact.d.ts +71 -0
- package/dist/domain/impact.js +76 -0
- package/dist/domain/init.d.ts +12 -4
- package/dist/domain/init.js +24 -18
- package/dist/domain/journey-composition.d.ts +2285 -0
- package/dist/domain/journey-composition.js +85 -0
- package/dist/domain/journey-lifecycle.d.ts +10 -0
- package/dist/domain/journey-lifecycle.js +9 -0
- package/dist/domain/journey.d.ts +2419 -212
- package/dist/domain/journey.js +384 -14
- package/dist/domain/knowledge.d.ts +165 -0
- package/dist/domain/knowledge.js +188 -0
- package/dist/domain/layout.d.ts +34 -5
- package/dist/domain/layout.js +64 -6
- package/dist/domain/locator-evidence.d.ts +3 -0
- package/dist/domain/locator-evidence.js +36 -0
- package/dist/domain/logcat-event.d.ts +54 -0
- package/dist/domain/logcat-event.js +40 -0
- package/dist/domain/observation.d.ts +33 -0
- package/dist/domain/observation.js +18 -0
- package/dist/domain/project-context.d.ts +219 -1
- package/dist/domain/project-context.js +209 -6
- package/dist/domain/proposed-step.d.ts +340 -60
- package/dist/domain/proposed-step.js +23 -3
- package/dist/domain/report.d.ts +1455 -28
- package/dist/domain/report.js +211 -14
- package/dist/domain/runtime-snapshot.d.ts +58 -1
- package/dist/domain/runtime-snapshot.js +14 -3
- package/dist/domain/runtime.d.ts +50 -0
- package/dist/domain/runtime.js +36 -0
- package/dist/domain/system-app-profiles.d.ts +6 -0
- package/dist/domain/system-app-profiles.js +43 -0
- package/dist/domain/ui-backend.d.ts +28 -0
- package/dist/domain/ui-backend.js +33 -0
- package/dist/domain/ui-cache.d.ts +11 -0
- package/dist/domain/ui-cache.js +10 -0
- package/dist/domain/window-hierarchy.d.ts +84 -0
- package/dist/domain/window-hierarchy.js +172 -0
- package/dist/domain/workflow-manifest.d.ts +77 -0
- package/dist/domain/workflow-manifest.js +136 -0
- package/dist/domain/workspace.d.ts +28 -0
- package/dist/domain/workspace.js +72 -0
- package/dist/ports/adb.d.ts +34 -1
- package/dist/ports/align-prompt.d.ts +11 -0
- package/dist/ports/align-prompt.js +6 -0
- package/dist/ports/anchor-resolver.d.ts +30 -0
- package/dist/ports/anchor-resolver.js +1 -0
- package/dist/ports/annotated-screen-resolver.d.ts +4 -0
- package/dist/ports/annotated-screen-resolver.js +1 -0
- package/dist/ports/camera-probe.d.ts +23 -0
- package/dist/ports/camera-probe.js +8 -0
- package/dist/ports/context-document-writer.d.ts +24 -0
- package/dist/ports/context-document-writer.js +1 -0
- package/dist/ports/detached-process-launcher.d.ts +12 -0
- package/dist/ports/detached-process-launcher.js +1 -0
- package/dist/ports/external-flow-registry.d.ts +37 -0
- package/dist/ports/external-flow-registry.js +1 -0
- package/dist/ports/generation-prompt.d.ts +2 -1
- package/dist/ports/generation-session-store.d.ts +12 -2
- package/dist/ports/git-diff.d.ts +9 -0
- package/dist/ports/git-diff.js +1 -0
- package/dist/ports/journey-composition-store.d.ts +17 -0
- package/dist/ports/journey-composition-store.js +1 -0
- package/dist/ports/knowledge-registry.d.ts +20 -0
- package/dist/ports/knowledge-registry.js +1 -0
- package/dist/ports/process-runner.d.ts +7 -0
- package/dist/ports/project-file-inspector.d.ts +1 -0
- package/dist/ports/project-identity-inspector.d.ts +24 -0
- package/dist/ports/project-identity-inspector.js +1 -0
- package/dist/ports/project-inventory-inspector.d.ts +21 -0
- package/dist/ports/project-inventory-inspector.js +1 -0
- package/dist/ports/project-module-discoverer.d.ts +26 -0
- package/dist/ports/project-module-discoverer.js +1 -0
- package/dist/ports/recorder-prompt.d.ts +10 -1
- package/dist/ports/runtime-backend.d.ts +89 -0
- package/dist/ports/runtime-backend.js +13 -0
- package/dist/ports/runtime-capability.d.ts +12 -0
- package/dist/ports/runtime-capability.js +20 -0
- package/dist/ports/runtime-session-ports.d.ts +25 -0
- package/dist/ports/runtime-session-ports.js +1 -0
- package/dist/ports/screenshot.d.ts +11 -0
- package/dist/ports/screenshot.js +1 -0
- package/dist/ports/skill-installer.d.ts +2 -2
- package/dist/ports/ui-snapshot.d.ts +35 -0
- package/dist/ports/ui-snapshot.js +1 -0
- package/dist/ports/ui-stability.d.ts +19 -0
- package/dist/ports/ui-stability.js +1 -0
- package/dist/ports/workspace-layout.d.ts +4 -0
- package/dist/ports/workspace-layout.js +1 -0
- package/dist/shared/errors.d.ts +2 -0
- package/dist/shared/errors.js +22 -0
- package/dist/shared/paths.d.ts +2 -0
- package/dist/shared/paths.js +24 -0
- package/dist/shared/strings.d.ts +2 -0
- package/dist/shared/strings.js +16 -0
- package/package.json +6 -1
- package/assets/skills/taphound-ai-journey/GUIDE.md +0 -862
- package/assets/skills/taphound-ai-journey/SKILL.md +0 -377
- package/assets/skills/taphound-ai-journey/prompts/analyze-project.md +0 -526
- package/assets/skills/taphound-ai-journey/prompts/generate-step.md +0 -109
- package/assets/skills/taphound-ai-journey/schemas/project-context.json +0 -81
- package/assets/skills/taphound-ai-journey/templates/project-context.example.json +0 -49
- package/dist/ports/android-cli.d.ts +0 -21
- /package/dist/{ports/android-cli.js → adapters/runtime/mobile-mcp/mobile-mcp-tools.js} +0 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { normalizeBounds, normalizeResourceId } from "../../ui/layout-normalization.js";
|
|
2
|
+
import { MobileMcpToolError } from "./mobile-mcp-errors.js";
|
|
3
|
+
const DEVICES_TOOL = "mobile_list_available_devices";
|
|
4
|
+
const SCREEN_SIZE_TOOL = "mobile_get_screen_size";
|
|
5
|
+
const ELEMENTS_TOOL = "mobile_list_elements_on_screen";
|
|
6
|
+
const ANDROID_PACKAGE_PATTERN = /^[A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z][A-Za-z0-9_]*)+$/;
|
|
7
|
+
export function assertMobileMcpToolText(toolName, text, expected) {
|
|
8
|
+
if (text !== expected) {
|
|
9
|
+
throw new MobileMcpToolError(toolName, `unexpected response from ${toolName}: expected "${expected}", received "${text}"`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function parseMobileMcpDevices(text) {
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = JSON.parse(text);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
throw new MobileMcpToolError(DEVICES_TOOL, `${DEVICES_TOOL} returned malformed JSON`, { cause: error });
|
|
19
|
+
}
|
|
20
|
+
if (parsed === null
|
|
21
|
+
|| typeof parsed !== "object"
|
|
22
|
+
|| !Array.isArray(parsed.devices)) {
|
|
23
|
+
throw new MobileMcpToolError(DEVICES_TOOL, `${DEVICES_TOOL} response is missing the devices array`);
|
|
24
|
+
}
|
|
25
|
+
const devices = parsed.devices;
|
|
26
|
+
return devices.map((entry) => {
|
|
27
|
+
if (entry === null
|
|
28
|
+
|| typeof entry !== "object"
|
|
29
|
+
|| typeof entry.id !== "string"
|
|
30
|
+
|| typeof entry.platform !== "string") {
|
|
31
|
+
throw new MobileMcpToolError(DEVICES_TOOL, `${DEVICES_TOOL} returned an invalid device entry`);
|
|
32
|
+
}
|
|
33
|
+
const record = entry;
|
|
34
|
+
return {
|
|
35
|
+
id: record.id,
|
|
36
|
+
platform: record.platform,
|
|
37
|
+
...(typeof record.name === "string" ? { name: record.name } : {}),
|
|
38
|
+
...(typeof record.type === "string" ? { type: record.type } : {}),
|
|
39
|
+
...(typeof record.version === "string" ? { version: record.version } : {}),
|
|
40
|
+
...(typeof record.state === "string" ? { state: record.state } : {}),
|
|
41
|
+
...(typeof record.model === "string" ? { model: record.model } : {})
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export function parseMobileMcpScreenSize(text) {
|
|
46
|
+
const match = /^Screen size is (\d+)x(\d+) pixels$/.exec(text);
|
|
47
|
+
if (match === null) {
|
|
48
|
+
throw new MobileMcpToolError(SCREEN_SIZE_TOOL, `${SCREEN_SIZE_TOOL} returned an unrecognized response: "${text}"`);
|
|
49
|
+
}
|
|
50
|
+
const width = Number(match[1]);
|
|
51
|
+
const height = Number(match[2]);
|
|
52
|
+
if (width <= 0 || height <= 0) {
|
|
53
|
+
throw new MobileMcpToolError(SCREEN_SIZE_TOOL, `${SCREEN_SIZE_TOOL} returned a non-positive screen size`);
|
|
54
|
+
}
|
|
55
|
+
return { width, height };
|
|
56
|
+
}
|
|
57
|
+
export function parseMobileMcpElements(text) {
|
|
58
|
+
const prefix = "Found these elements on screen: ";
|
|
59
|
+
if (!text.startsWith(prefix)) {
|
|
60
|
+
throw new MobileMcpToolError(ELEMENTS_TOOL, `${ELEMENTS_TOOL} returned an unrecognized response: "${text}"`);
|
|
61
|
+
}
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(text.slice(prefix.length));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw new MobileMcpToolError(ELEMENTS_TOOL, `${ELEMENTS_TOOL} returned a malformed element payload`, { cause: error });
|
|
68
|
+
}
|
|
69
|
+
if (!Array.isArray(parsed)) {
|
|
70
|
+
throw new MobileMcpToolError(ELEMENTS_TOOL, `${ELEMENTS_TOOL} returned a non-array element payload`);
|
|
71
|
+
}
|
|
72
|
+
return parsed.map((entry) => {
|
|
73
|
+
if (entry === null
|
|
74
|
+
|| typeof entry !== "object"
|
|
75
|
+
|| !isElementRect(entry.coordinates)) {
|
|
76
|
+
throw new MobileMcpToolError(ELEMENTS_TOOL, `${ELEMENTS_TOOL} returned an invalid element entry`);
|
|
77
|
+
}
|
|
78
|
+
const record = entry;
|
|
79
|
+
return {
|
|
80
|
+
coordinates: record.coordinates,
|
|
81
|
+
...(typeof record.type === "string" ? { type: record.type } : {}),
|
|
82
|
+
...(typeof record.text === "string" ? { text: record.text } : {}),
|
|
83
|
+
...(typeof record.label === "string" ? { label: record.label } : {}),
|
|
84
|
+
...(typeof record.name === "string" ? { name: record.name } : {}),
|
|
85
|
+
...(typeof record.value === "string" ? { value: record.value } : {}),
|
|
86
|
+
...(typeof record.identifier === "string"
|
|
87
|
+
? { identifier: record.identifier }
|
|
88
|
+
: {}),
|
|
89
|
+
...(record.focused === true ? { focused: true } : {})
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function isElementRect(value) {
|
|
94
|
+
if (value === null || typeof value !== "object") {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const record = value;
|
|
98
|
+
return (Number.isInteger(record.x)
|
|
99
|
+
&& Number.isInteger(record.y)
|
|
100
|
+
&& Number.isInteger(record.width)
|
|
101
|
+
&& Number.isInteger(record.height)
|
|
102
|
+
&& record.x >= 0
|
|
103
|
+
&& record.y >= 0
|
|
104
|
+
&& record.width >= 0
|
|
105
|
+
&& record.height >= 0);
|
|
106
|
+
}
|
|
107
|
+
export function parseMobileMcpAppPackages(text) {
|
|
108
|
+
const packages = [];
|
|
109
|
+
const seen = new Set();
|
|
110
|
+
for (const match of text.matchAll(/\(([^()]+)\)/g)) {
|
|
111
|
+
const candidate = match[1] ?? "";
|
|
112
|
+
if (!ANDROID_PACKAGE_PATTERN.test(candidate) || seen.has(candidate)) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
seen.add(candidate);
|
|
116
|
+
packages.push(candidate);
|
|
117
|
+
}
|
|
118
|
+
return packages;
|
|
119
|
+
}
|
|
120
|
+
export function mapMobileMcpElement(element, index) {
|
|
121
|
+
const { coordinates } = element;
|
|
122
|
+
const bounds = normalizeBounds({
|
|
123
|
+
left: coordinates.x,
|
|
124
|
+
top: coordinates.y,
|
|
125
|
+
right: coordinates.x + coordinates.width,
|
|
126
|
+
bottom: coordinates.y + coordinates.height
|
|
127
|
+
});
|
|
128
|
+
const center = bounds === undefined
|
|
129
|
+
? undefined
|
|
130
|
+
: {
|
|
131
|
+
x: Math.floor((bounds.left + bounds.right) / 2),
|
|
132
|
+
y: Math.floor((bounds.top + bounds.bottom) / 2)
|
|
133
|
+
};
|
|
134
|
+
const resourceId = element.identifier === undefined
|
|
135
|
+
? undefined
|
|
136
|
+
: normalizeResourceId(element.identifier);
|
|
137
|
+
return {
|
|
138
|
+
id: `mobile-mcp-${String(index)}`,
|
|
139
|
+
...(resourceId === undefined ? {} : { resourceId }),
|
|
140
|
+
...(element.text === undefined || element.text === ""
|
|
141
|
+
? {}
|
|
142
|
+
: { text: element.text }),
|
|
143
|
+
...(element.label === undefined || element.label === ""
|
|
144
|
+
? {}
|
|
145
|
+
: { contentDescription: element.label }),
|
|
146
|
+
...(element.focused === true ? { focused: true } : {}),
|
|
147
|
+
enabled: true,
|
|
148
|
+
...(bounds === undefined || center === undefined
|
|
149
|
+
? {}
|
|
150
|
+
: { bounds, center }),
|
|
151
|
+
children: []
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export function mapMobileMcpSwipe(from, to) {
|
|
155
|
+
const dx = to.x - from.x;
|
|
156
|
+
const dy = to.y - from.y;
|
|
157
|
+
if (dx === 0 && dy === 0) {
|
|
158
|
+
throw new MobileMcpToolError("mobile_swipe_on_screen", "swipe requires movement: from and to coordinates are identical");
|
|
159
|
+
}
|
|
160
|
+
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
161
|
+
return {
|
|
162
|
+
direction: dx > 0 ? "right" : "left",
|
|
163
|
+
x: from.x,
|
|
164
|
+
y: from.y,
|
|
165
|
+
distance: Math.abs(dx)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
direction: dy > 0 ? "down" : "up",
|
|
170
|
+
x: from.x,
|
|
171
|
+
y: from.y,
|
|
172
|
+
distance: Math.abs(dy)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
export function mobileMcpSwipeResponse(mapping) {
|
|
176
|
+
return [
|
|
177
|
+
`Swiped ${mapping.direction} ${String(mapping.distance)} pixels`,
|
|
178
|
+
`from coordinates: ${String(mapping.x)}, ${String(mapping.y)}`
|
|
179
|
+
].join(" ");
|
|
180
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Point } from "../../../domain/geometry.js";
|
|
2
|
+
import type { DisplayViewport } from "../../../domain/geometry.js";
|
|
3
|
+
import type { DeviceInfo, RuntimeBackendDescriptor, RuntimeCapabilities } from "../../../domain/runtime.js";
|
|
4
|
+
import type { UiBackendDescriptor } from "../../../domain/ui-backend.js";
|
|
5
|
+
import type { CommandResult } from "../../../ports/process-runner.js";
|
|
6
|
+
import type { OpenRuntimeSessionOptions, OpenRuntimeUiSnapshotsOptions, RuntimeAppQuery, RuntimeBackend, RuntimeLaunchOptions, RuntimeScreenshotOptions, RuntimeSession } from "../../../ports/runtime-backend.js";
|
|
7
|
+
import type { CaptureUiSnapshotOptions, UiSnapshot, UiSnapshotProvider } from "../../../ports/ui-snapshot.js";
|
|
8
|
+
import type { UiStabilityObservation, UiStabilityProbe, UiStabilitySampleOptions } from "../../../ports/ui-stability.js";
|
|
9
|
+
import type { MobileMcpTools } from "./mobile-mcp-tools.js";
|
|
10
|
+
export declare const MOBILE_MCP_RUNTIME_ADAPTER_VERSION = "mobile-mcp-runtime-v1";
|
|
11
|
+
export declare const MOBILE_MCP_UI_ADAPTER_VERSION = "mobile-mcp-layout-v1";
|
|
12
|
+
export declare const DEFAULT_MOBILE_MCP_SNAPSHOT_TIMEOUT_MS = 10000;
|
|
13
|
+
export declare function mobileMcpRuntimeCapabilities(): RuntimeCapabilities;
|
|
14
|
+
export declare function mobileMcpUiBackendDescriptor(engineVersion: string | undefined): UiBackendDescriptor;
|
|
15
|
+
export declare class MobileMcpSnapshotProvider implements UiSnapshotProvider {
|
|
16
|
+
private readonly tools;
|
|
17
|
+
private readonly deviceSerial;
|
|
18
|
+
readonly descriptor: UiBackendDescriptor;
|
|
19
|
+
private readonly viewport;
|
|
20
|
+
private closed;
|
|
21
|
+
constructor(tools: MobileMcpTools, deviceSerial: string, descriptor: UiBackendDescriptor, viewport: DisplayViewport);
|
|
22
|
+
capture(options: CaptureUiSnapshotOptions): Promise<UiSnapshot>;
|
|
23
|
+
close(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export declare class MobileMcpUiStabilityProbe implements UiStabilityProbe {
|
|
26
|
+
private readonly tools;
|
|
27
|
+
private readonly signatures;
|
|
28
|
+
constructor(tools: MobileMcpTools);
|
|
29
|
+
reset(): void;
|
|
30
|
+
sample(options: UiStabilitySampleOptions): Promise<UiStabilityObservation>;
|
|
31
|
+
}
|
|
32
|
+
export interface MobileMcpRuntimeSessionDependencies {
|
|
33
|
+
tools: MobileMcpTools;
|
|
34
|
+
deviceSerial: string;
|
|
35
|
+
descriptor: RuntimeBackendDescriptor;
|
|
36
|
+
}
|
|
37
|
+
export declare class MobileMcpRuntimeSession implements RuntimeSession {
|
|
38
|
+
private readonly dependencies;
|
|
39
|
+
readonly descriptor: RuntimeBackendDescriptor;
|
|
40
|
+
readonly deviceSerial: string;
|
|
41
|
+
readonly currentActivity: undefined;
|
|
42
|
+
readonly deviceIdentity: undefined;
|
|
43
|
+
readonly foregroundComponent: undefined;
|
|
44
|
+
readonly appProcesses: undefined;
|
|
45
|
+
readonly windowTopology: undefined;
|
|
46
|
+
readonly startLogcat: undefined;
|
|
47
|
+
readonly dumpLogcat: undefined;
|
|
48
|
+
readonly annotatedScreens: undefined;
|
|
49
|
+
readonly startActivityByIntent: undefined;
|
|
50
|
+
readonly uiStability: UiStabilityProbe;
|
|
51
|
+
private uiSnapshotProvider;
|
|
52
|
+
constructor(dependencies: MobileMcpRuntimeSessionDependencies);
|
|
53
|
+
openUiSnapshots(options?: OpenRuntimeUiSnapshotsOptions): Promise<UiSnapshotProvider>;
|
|
54
|
+
private openSnapshotProvider;
|
|
55
|
+
isInstalled(app: RuntimeAppQuery): Promise<boolean>;
|
|
56
|
+
launchApp(options: RuntimeLaunchOptions): Promise<CommandResult>;
|
|
57
|
+
forceStop(app: RuntimeAppQuery): Promise<CommandResult>;
|
|
58
|
+
tap(point: Point, signal?: AbortSignal): Promise<CommandResult>;
|
|
59
|
+
longClick(point: Point, durationMs: number, signal?: AbortSignal): Promise<CommandResult>;
|
|
60
|
+
swipe(from: Point, to: Point, _durationMs: number, signal?: AbortSignal): Promise<CommandResult>;
|
|
61
|
+
back(signal?: AbortSignal): Promise<CommandResult>;
|
|
62
|
+
inputText(text: string, signal?: AbortSignal): Promise<CommandResult>;
|
|
63
|
+
captureScreenshot(options: RuntimeScreenshotOptions): Promise<CommandResult>;
|
|
64
|
+
private runToolAction;
|
|
65
|
+
close(): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
export interface MobileMcpRuntimeBackendDependencies {
|
|
68
|
+
createTools: () => MobileMcpTools;
|
|
69
|
+
engineVersion?: string | undefined;
|
|
70
|
+
}
|
|
71
|
+
export declare class MobileMcpRuntimeBackend implements RuntimeBackend {
|
|
72
|
+
private readonly dependencies;
|
|
73
|
+
readonly descriptor: RuntimeBackendDescriptor;
|
|
74
|
+
readonly capabilities: RuntimeCapabilities;
|
|
75
|
+
constructor(dependencies: MobileMcpRuntimeBackendDependencies);
|
|
76
|
+
listDevices(signal?: AbortSignal): Promise<readonly DeviceInfo[]>;
|
|
77
|
+
openSession(options: OpenRuntimeSessionOptions): Promise<RuntimeSession>;
|
|
78
|
+
}
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdtemp, mkdir, rename, copyFile, rm, unlink } from "node:fs/promises";
|
|
3
|
+
import { dirname, extname, join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { performance } from "node:perf_hooks";
|
|
6
|
+
import { snapshotFromCapture } from "../../ui/ui-snapshot-support.js";
|
|
7
|
+
import { UiSnapshotError } from "../../ui/ui-snapshot-error.js";
|
|
8
|
+
import { MobileMcpToolError } from "./mobile-mcp-errors.js";
|
|
9
|
+
import { assertMobileMcpToolText, mapMobileMcpElement, mapMobileMcpSwipe, mobileMcpSwipeResponse, parseMobileMcpAppPackages, parseMobileMcpDevices, parseMobileMcpElements, parseMobileMcpScreenSize } from "./mobile-mcp-responses.js";
|
|
10
|
+
export const MOBILE_MCP_RUNTIME_ADAPTER_VERSION = "mobile-mcp-runtime-v1";
|
|
11
|
+
export const MOBILE_MCP_UI_ADAPTER_VERSION = "mobile-mcp-layout-v1";
|
|
12
|
+
export const DEFAULT_MOBILE_MCP_SNAPSHOT_TIMEOUT_MS = 10_000;
|
|
13
|
+
const SCREENSHOT_EXTENSIONS = [".png", ".jpg", ".jpeg"];
|
|
14
|
+
export function mobileMcpRuntimeCapabilities() {
|
|
15
|
+
return {
|
|
16
|
+
layoutSnapshot: true,
|
|
17
|
+
screenshot: true,
|
|
18
|
+
annotatedScreens: false,
|
|
19
|
+
frameStatsIdle: false,
|
|
20
|
+
logs: false,
|
|
21
|
+
processDiscovery: false,
|
|
22
|
+
windowTopology: false,
|
|
23
|
+
intentStart: false,
|
|
24
|
+
foregroundActivity: false
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function mobileMcpUiBackendDescriptor(engineVersion) {
|
|
28
|
+
const config = { source: "mobile_list_elements_on_screen", version: 1 };
|
|
29
|
+
return {
|
|
30
|
+
id: "mobile-mcp",
|
|
31
|
+
adapterVersion: MOBILE_MCP_UI_ADAPTER_VERSION,
|
|
32
|
+
...(engineVersion === undefined ? {} : { engineVersion }),
|
|
33
|
+
configSha256: createHash("sha256")
|
|
34
|
+
.update(JSON.stringify(config))
|
|
35
|
+
.digest("hex")
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function errorMessage(error) {
|
|
39
|
+
if (error instanceof Error) {
|
|
40
|
+
return error.message;
|
|
41
|
+
}
|
|
42
|
+
return String(error);
|
|
43
|
+
}
|
|
44
|
+
function successResult(stdout, durationMs) {
|
|
45
|
+
return {
|
|
46
|
+
exitCode: 0,
|
|
47
|
+
signal: null,
|
|
48
|
+
stdout,
|
|
49
|
+
stderr: "",
|
|
50
|
+
durationMs,
|
|
51
|
+
timedOut: false,
|
|
52
|
+
cancelled: false
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function failureResult(stderr, durationMs, cancelled) {
|
|
56
|
+
return {
|
|
57
|
+
exitCode: 1,
|
|
58
|
+
signal: null,
|
|
59
|
+
stdout: "",
|
|
60
|
+
stderr,
|
|
61
|
+
durationMs,
|
|
62
|
+
timedOut: false,
|
|
63
|
+
cancelled
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export class MobileMcpSnapshotProvider {
|
|
67
|
+
tools;
|
|
68
|
+
deviceSerial;
|
|
69
|
+
descriptor;
|
|
70
|
+
viewport;
|
|
71
|
+
closed = false;
|
|
72
|
+
constructor(tools, deviceSerial, descriptor, viewport) {
|
|
73
|
+
this.tools = tools;
|
|
74
|
+
this.deviceSerial = deviceSerial;
|
|
75
|
+
this.descriptor = descriptor;
|
|
76
|
+
this.viewport = viewport;
|
|
77
|
+
}
|
|
78
|
+
async capture(options) {
|
|
79
|
+
if (this.closed) {
|
|
80
|
+
throw new UiSnapshotError("UI_SNAPSHOT_FAILED", this.descriptor.id, "UI snapshot provider is closed");
|
|
81
|
+
}
|
|
82
|
+
const startedAt = performance.now();
|
|
83
|
+
let elements;
|
|
84
|
+
try {
|
|
85
|
+
const text = await this.tools.listElements(this.deviceSerial, {
|
|
86
|
+
timeoutMs: options.timeoutMs,
|
|
87
|
+
...(options.signal === undefined ? {} : { signal: options.signal })
|
|
88
|
+
});
|
|
89
|
+
elements = parseMobileMcpElements(text);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (error instanceof UiSnapshotError) {
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
throw new UiSnapshotError("UI_SNAPSHOT_FAILED", this.descriptor.id, errorMessage(error), { cause: error });
|
|
96
|
+
}
|
|
97
|
+
if (elements.length === 0) {
|
|
98
|
+
throw new UiSnapshotError("UI_SNAPSHOT_INVALID", this.descriptor.id, "mobile-mcp returned an empty layout");
|
|
99
|
+
}
|
|
100
|
+
const roots = elements.map(mapMobileMcpElement);
|
|
101
|
+
return snapshotFromCapture({
|
|
102
|
+
startedAt,
|
|
103
|
+
roots,
|
|
104
|
+
backend: this.descriptor,
|
|
105
|
+
viewport: this.viewport,
|
|
106
|
+
timing: {}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
close() {
|
|
110
|
+
this.closed = true;
|
|
111
|
+
return Promise.resolve();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export class MobileMcpUiStabilityProbe {
|
|
115
|
+
tools;
|
|
116
|
+
signatures = new Map();
|
|
117
|
+
constructor(tools) {
|
|
118
|
+
this.tools = tools;
|
|
119
|
+
}
|
|
120
|
+
reset() {
|
|
121
|
+
this.signatures.clear();
|
|
122
|
+
}
|
|
123
|
+
async sample(options) {
|
|
124
|
+
const startedAt = performance.now();
|
|
125
|
+
const text = await this.tools.listElements(options.deviceSerial, {
|
|
126
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
127
|
+
...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs })
|
|
128
|
+
});
|
|
129
|
+
const layout = parseMobileMcpElements(text).map(mapMobileMcpElement);
|
|
130
|
+
const signature = createHash("sha256")
|
|
131
|
+
.update(JSON.stringify(layout))
|
|
132
|
+
.digest("hex");
|
|
133
|
+
const key = options.deviceSerial;
|
|
134
|
+
const previous = this.signatures.get(key);
|
|
135
|
+
this.signatures.set(key, signature);
|
|
136
|
+
return {
|
|
137
|
+
changes: previous === signature
|
|
138
|
+
? []
|
|
139
|
+
: [{ layoutSha256: signature }],
|
|
140
|
+
layout,
|
|
141
|
+
backend: "mobileMcp",
|
|
142
|
+
durationMs: performance.now() - startedAt
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
export class MobileMcpRuntimeSession {
|
|
147
|
+
dependencies;
|
|
148
|
+
descriptor;
|
|
149
|
+
deviceSerial;
|
|
150
|
+
currentActivity;
|
|
151
|
+
deviceIdentity;
|
|
152
|
+
foregroundComponent;
|
|
153
|
+
appProcesses;
|
|
154
|
+
windowTopology;
|
|
155
|
+
startLogcat;
|
|
156
|
+
dumpLogcat;
|
|
157
|
+
annotatedScreens;
|
|
158
|
+
startActivityByIntent;
|
|
159
|
+
uiStability;
|
|
160
|
+
uiSnapshotProvider;
|
|
161
|
+
constructor(dependencies) {
|
|
162
|
+
this.dependencies = dependencies;
|
|
163
|
+
this.descriptor = dependencies.descriptor;
|
|
164
|
+
this.deviceSerial = dependencies.deviceSerial;
|
|
165
|
+
this.uiStability = new MobileMcpUiStabilityProbe(dependencies.tools);
|
|
166
|
+
}
|
|
167
|
+
openUiSnapshots(options) {
|
|
168
|
+
if (options?.backend !== undefined && options.backend !== "auto") {
|
|
169
|
+
return Promise.reject(new UiSnapshotError("UI_SNAPSHOT_FAILED", "mobile-mcp", `UI backend "${options.backend}" is not available through the mobile-mcp runtime backend`));
|
|
170
|
+
}
|
|
171
|
+
if (this.uiSnapshotProvider !== undefined) {
|
|
172
|
+
return this.uiSnapshotProvider;
|
|
173
|
+
}
|
|
174
|
+
const opened = this.openSnapshotProvider(options);
|
|
175
|
+
opened.catch(() => {
|
|
176
|
+
if (this.uiSnapshotProvider === opened) {
|
|
177
|
+
this.uiSnapshotProvider = undefined;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
this.uiSnapshotProvider = opened;
|
|
181
|
+
return opened;
|
|
182
|
+
}
|
|
183
|
+
async openSnapshotProvider(options) {
|
|
184
|
+
try {
|
|
185
|
+
const text = await this.dependencies.tools.getScreenSize(this.deviceSerial, {
|
|
186
|
+
timeoutMs: options?.timeoutMs
|
|
187
|
+
?? DEFAULT_MOBILE_MCP_SNAPSHOT_TIMEOUT_MS,
|
|
188
|
+
...(options?.signal === undefined ? {} : { signal: options.signal })
|
|
189
|
+
});
|
|
190
|
+
const size = parseMobileMcpScreenSize(text);
|
|
191
|
+
return new MobileMcpSnapshotProvider(this.dependencies.tools, this.deviceSerial, mobileMcpUiBackendDescriptor(this.dependencies.tools.serverVersion()), {
|
|
192
|
+
width: size.width,
|
|
193
|
+
height: size.height,
|
|
194
|
+
rotation: 0,
|
|
195
|
+
coordinateSpace: "physicalDisplayPixels"
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
if (error instanceof UiSnapshotError) {
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
throw new UiSnapshotError("UI_SNAPSHOT_FAILED", "mobile-mcp", errorMessage(error), { cause: error });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
isInstalled(app) {
|
|
206
|
+
return this.dependencies.tools
|
|
207
|
+
.listApps(this.deviceSerial, {
|
|
208
|
+
...(app.signal === undefined ? {} : { signal: app.signal }),
|
|
209
|
+
...(app.timeoutMs === undefined ? {} : { timeoutMs: app.timeoutMs })
|
|
210
|
+
})
|
|
211
|
+
.then((text) => (parseMobileMcpAppPackages(text).includes(app.packageName)));
|
|
212
|
+
}
|
|
213
|
+
launchApp(options) {
|
|
214
|
+
return this.runToolAction(options.signal, async () => {
|
|
215
|
+
const text = await this.dependencies.tools.launchApp(this.deviceSerial, options.packageName, {
|
|
216
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
217
|
+
...(options.timeoutMs === undefined
|
|
218
|
+
? {}
|
|
219
|
+
: { timeoutMs: options.timeoutMs })
|
|
220
|
+
});
|
|
221
|
+
assertMobileMcpToolText("mobile_launch_app", text, `Launched app ${options.packageName}`);
|
|
222
|
+
return text;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
forceStop(app) {
|
|
226
|
+
return this.runToolAction(app.signal, async () => {
|
|
227
|
+
const text = await this.dependencies.tools.terminateApp(this.deviceSerial, app.packageName, {
|
|
228
|
+
...(app.signal === undefined ? {} : { signal: app.signal }),
|
|
229
|
+
...(app.timeoutMs === undefined ? {} : { timeoutMs: app.timeoutMs })
|
|
230
|
+
});
|
|
231
|
+
assertMobileMcpToolText("mobile_terminate_app", text, `Terminated app ${app.packageName}`);
|
|
232
|
+
return text;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
tap(point, signal) {
|
|
236
|
+
return this.runToolAction(signal, async () => {
|
|
237
|
+
const text = await this.dependencies.tools.tap(this.deviceSerial, point.x, point.y, signal === undefined ? undefined : { signal });
|
|
238
|
+
assertMobileMcpToolText("mobile_click_on_screen_at_coordinates", text, `Clicked on screen at coordinates: ${String(point.x)}, ${String(point.y)}`);
|
|
239
|
+
return text;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
longClick(point, durationMs, signal) {
|
|
243
|
+
return this.runToolAction(signal, async () => {
|
|
244
|
+
if (!Number.isInteger(durationMs) || durationMs < 1 || durationMs > 10_000) {
|
|
245
|
+
throw new MobileMcpToolError("mobile_long_press_on_screen_at_coordinates", `long press duration must be an integer between 1 and 10000 ms, received ${String(durationMs)}`);
|
|
246
|
+
}
|
|
247
|
+
const text = await this.dependencies.tools.longPress(this.deviceSerial, point.x, point.y, durationMs, signal === undefined ? undefined : { signal });
|
|
248
|
+
assertMobileMcpToolText("mobile_long_press_on_screen_at_coordinates", text, [
|
|
249
|
+
"Long pressed on screen at coordinates:",
|
|
250
|
+
`${String(point.x)}, ${String(point.y)}`,
|
|
251
|
+
`for ${String(durationMs)}ms`
|
|
252
|
+
].join(" "));
|
|
253
|
+
return text;
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
swipe(from, to, _durationMs, signal) {
|
|
257
|
+
return this.runToolAction(signal, async () => {
|
|
258
|
+
const mapping = mapMobileMcpSwipe(from, to);
|
|
259
|
+
const text = await this.dependencies.tools.swipe(this.deviceSerial, mapping.direction, mapping.x, mapping.y, mapping.distance, signal === undefined ? undefined : { signal });
|
|
260
|
+
assertMobileMcpToolText("mobile_swipe_on_screen", text, mobileMcpSwipeResponse(mapping));
|
|
261
|
+
return text;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
back(signal) {
|
|
265
|
+
return this.runToolAction(signal, async () => {
|
|
266
|
+
const text = await this.dependencies.tools.pressButton(this.deviceSerial, "BACK", signal === undefined ? undefined : { signal });
|
|
267
|
+
assertMobileMcpToolText("mobile_press_button", text, "Pressed the button: BACK");
|
|
268
|
+
return text;
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
inputText(text, signal) {
|
|
272
|
+
return this.runToolAction(signal, async () => {
|
|
273
|
+
const response = await this.dependencies.tools.typeKeys(this.deviceSerial, text, false, signal === undefined ? undefined : { signal });
|
|
274
|
+
assertMobileMcpToolText("mobile_type_keys", response, `Typed text: ${text}`);
|
|
275
|
+
return response;
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
captureScreenshot(options) {
|
|
279
|
+
if (options.annotate === true) {
|
|
280
|
+
return Promise.resolve(failureResult("the mobile-mcp runtime backend cannot annotate screenshots", 0, false));
|
|
281
|
+
}
|
|
282
|
+
const extension = extname(options.outputPath).toLowerCase();
|
|
283
|
+
if (!SCREENSHOT_EXTENSIONS.includes(extension)) {
|
|
284
|
+
return Promise.resolve(failureResult(`screenshot output must end with ${SCREENSHOT_EXTENSIONS.join(", ")}, received "${options.outputPath}"`, 0, false));
|
|
285
|
+
}
|
|
286
|
+
return this.runToolAction(options.signal, async () => {
|
|
287
|
+
const tempDir = await mkdtemp(join(tmpdir(), "taphound-mobile-mcp-"));
|
|
288
|
+
let tempPath;
|
|
289
|
+
try {
|
|
290
|
+
tempPath = join(tempDir, `screenshot${extension}`);
|
|
291
|
+
const text = await this.dependencies.tools.saveScreenshot(this.deviceSerial, tempPath, {
|
|
292
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
293
|
+
...(options.timeoutMs === undefined
|
|
294
|
+
? {}
|
|
295
|
+
: { timeoutMs: options.timeoutMs })
|
|
296
|
+
});
|
|
297
|
+
assertMobileMcpToolText("mobile_save_screenshot", text, `Screenshot saved to: ${tempPath}`);
|
|
298
|
+
await mkdir(dirname(options.outputPath), { recursive: true });
|
|
299
|
+
try {
|
|
300
|
+
await rename(tempPath, options.outputPath);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
await copyFile(tempPath, options.outputPath);
|
|
304
|
+
await unlink(tempPath).catch(() => { });
|
|
305
|
+
}
|
|
306
|
+
return `Screenshot saved to: ${options.outputPath}`;
|
|
307
|
+
}
|
|
308
|
+
finally {
|
|
309
|
+
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
async runToolAction(signal, invoke) {
|
|
314
|
+
const startedAt = performance.now();
|
|
315
|
+
try {
|
|
316
|
+
const stdout = await invoke();
|
|
317
|
+
return successResult(stdout, performance.now() - startedAt);
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
return failureResult(errorMessage(error), performance.now() - startedAt, signal?.aborted === true);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async close() {
|
|
324
|
+
const provider = this.uiSnapshotProvider;
|
|
325
|
+
this.uiSnapshotProvider = undefined;
|
|
326
|
+
if (provider !== undefined) {
|
|
327
|
+
await provider.then((snapshotProvider) => snapshotProvider.close(), () => undefined);
|
|
328
|
+
}
|
|
329
|
+
await this.dependencies.tools.close();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
export class MobileMcpRuntimeBackend {
|
|
333
|
+
dependencies;
|
|
334
|
+
descriptor;
|
|
335
|
+
capabilities;
|
|
336
|
+
constructor(dependencies) {
|
|
337
|
+
this.dependencies = dependencies;
|
|
338
|
+
const capabilities = mobileMcpRuntimeCapabilities();
|
|
339
|
+
this.capabilities = capabilities;
|
|
340
|
+
this.descriptor = {
|
|
341
|
+
id: "mobile-mcp",
|
|
342
|
+
adapterVersion: MOBILE_MCP_RUNTIME_ADAPTER_VERSION,
|
|
343
|
+
...(dependencies.engineVersion === undefined
|
|
344
|
+
? {}
|
|
345
|
+
: { engineVersion: dependencies.engineVersion }),
|
|
346
|
+
configSha256: createHash("sha256").update(JSON.stringify({
|
|
347
|
+
id: "mobile-mcp",
|
|
348
|
+
adapterVersion: MOBILE_MCP_RUNTIME_ADAPTER_VERSION,
|
|
349
|
+
...(dependencies.engineVersion === undefined
|
|
350
|
+
? {}
|
|
351
|
+
: { engineVersion: dependencies.engineVersion }),
|
|
352
|
+
capabilities
|
|
353
|
+
})).digest("hex"),
|
|
354
|
+
capabilities
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
async listDevices(signal) {
|
|
358
|
+
const tools = this.dependencies.createTools();
|
|
359
|
+
try {
|
|
360
|
+
const text = await tools.listDevices(signal === undefined ? undefined : { signal });
|
|
361
|
+
return parseMobileMcpDevices(text)
|
|
362
|
+
.filter((device) => (device.platform === "android" && device.state === "online"))
|
|
363
|
+
.map((device) => ({
|
|
364
|
+
serial: device.id,
|
|
365
|
+
status: "device"
|
|
366
|
+
}));
|
|
367
|
+
}
|
|
368
|
+
finally {
|
|
369
|
+
await tools.close();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
openSession(options) {
|
|
373
|
+
return Promise.resolve(new MobileMcpRuntimeSession({
|
|
374
|
+
tools: this.dependencies.createTools(),
|
|
375
|
+
deviceSerial: options.deviceSerial,
|
|
376
|
+
descriptor: this.descriptor
|
|
377
|
+
}));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type MobileMcpSwipeDirection = "up" | "down" | "left" | "right";
|
|
2
|
+
export interface MobileMcpDeviceEntry {
|
|
3
|
+
id: string;
|
|
4
|
+
name?: string | undefined;
|
|
5
|
+
platform: string;
|
|
6
|
+
type?: string | undefined;
|
|
7
|
+
version?: string | undefined;
|
|
8
|
+
state?: string | undefined;
|
|
9
|
+
model?: string | undefined;
|
|
10
|
+
}
|
|
11
|
+
export interface MobileMcpElementRect {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
}
|
|
17
|
+
export interface MobileMcpElement {
|
|
18
|
+
type?: string | undefined;
|
|
19
|
+
text?: string | undefined;
|
|
20
|
+
label?: string | undefined;
|
|
21
|
+
name?: string | undefined;
|
|
22
|
+
value?: string | undefined;
|
|
23
|
+
identifier?: string | undefined;
|
|
24
|
+
coordinates: MobileMcpElementRect;
|
|
25
|
+
focused?: boolean | undefined;
|
|
26
|
+
}
|
|
27
|
+
export interface MobileMcpToolCallOptions {
|
|
28
|
+
signal?: AbortSignal | undefined;
|
|
29
|
+
timeoutMs?: number | undefined;
|
|
30
|
+
}
|
|
31
|
+
export interface MobileMcpTools {
|
|
32
|
+
listDevices(options?: MobileMcpToolCallOptions): Promise<string>;
|
|
33
|
+
getScreenSize(device: string, options?: MobileMcpToolCallOptions): Promise<string>;
|
|
34
|
+
listElements(device: string, options?: MobileMcpToolCallOptions): Promise<string>;
|
|
35
|
+
listApps(device: string, options?: MobileMcpToolCallOptions): Promise<string>;
|
|
36
|
+
launchApp(device: string, packageName: string, options?: MobileMcpToolCallOptions): Promise<string>;
|
|
37
|
+
terminateApp(device: string, packageName: string, options?: MobileMcpToolCallOptions): Promise<string>;
|
|
38
|
+
tap(device: string, x: number, y: number, options?: MobileMcpToolCallOptions): Promise<string>;
|
|
39
|
+
longPress(device: string, x: number, y: number, durationMs: number, options?: MobileMcpToolCallOptions): Promise<string>;
|
|
40
|
+
swipe(device: string, direction: MobileMcpSwipeDirection, x?: number, y?: number, distance?: number, options?: MobileMcpToolCallOptions): Promise<string>;
|
|
41
|
+
pressButton(device: string, button: string, options?: MobileMcpToolCallOptions): Promise<string>;
|
|
42
|
+
typeKeys(device: string, text: string, submit: boolean, options?: MobileMcpToolCallOptions): Promise<string>;
|
|
43
|
+
saveScreenshot(device: string, saveTo: string, options?: MobileMcpToolCallOptions): Promise<string>;
|
|
44
|
+
serverVersion(): string | undefined;
|
|
45
|
+
close(): Promise<void>;
|
|
46
|
+
}
|