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.
Files changed (447) hide show
  1. package/README.md +49 -149
  2. package/README.zh-CN.md +47 -147
  3. package/assets/skills/taphound-case-suite/SKILL.md +363 -0
  4. package/assets/skills/taphound-case-suite/schemas/base-flow-record.schema.json +22 -0
  5. package/assets/skills/taphound-case-suite/schemas/case-catalog.schema.json +26 -0
  6. package/assets/skills/taphound-case-suite/schemas/case-ledger.schema.json +158 -0
  7. package/assets/skills/taphound-case-suite/schemas/suite-input.schema.json +41 -0
  8. package/assets/skills/taphound-case-suite/schemas/transition.schema.json +61 -0
  9. package/assets/skills/taphound-case-suite/scripts/ledger.mjs +1207 -0
  10. package/assets/skills/taphound-case-suite/templates/base-flow-record.example.json +20 -0
  11. package/assets/skills/taphound-case-suite/templates/suite-input.example.json +27 -0
  12. package/assets/skills/taphound-case-suite/templates/transition.example.json +8 -0
  13. package/assets/skills/taphound-flash/SKILL.md +115 -0
  14. package/assets/skills/taphound-flash/scripts/flash.mjs +585 -0
  15. package/assets/skills/taphound-flash/templates/flash-plan.example.json +15 -0
  16. package/assets/skills/taphound-journey-brief-author/CONTEXT-GUIDE.md +471 -0
  17. package/assets/skills/taphound-journey-brief-author/SKILL.md +494 -0
  18. package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.md +96 -0
  19. package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.zh-CN.md +80 -0
  20. package/assets/skills/taphound-journey-brief-author/prompts/context-analyze-project.md +309 -0
  21. package/assets/skills/taphound-journey-brief-author/schemas/context-refresh-result.json +123 -0
  22. package/assets/skills/taphound-journey-brief-author/schemas/project-context-module.json +192 -0
  23. package/assets/skills/taphound-journey-brief-author/schemas/project-context.json +152 -0
  24. package/assets/skills/taphound-journey-brief-author/templates/project-context-module.example.json +58 -0
  25. package/assets/skills/taphound-journey-brief-author/templates/project-context.example.json +51 -0
  26. package/assets/skills/taphound-journey-brief-author/templates/taphound-journey-brief.template.md +63 -0
  27. package/assets/skills/taphound-journey-generator/SKILL.md +500 -0
  28. package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/prompts/check-completion.md +3 -0
  29. package/assets/skills/taphound-journey-generator/prompts/consume-journey-brief.md +52 -0
  30. package/assets/skills/taphound-journey-generator/prompts/generate-step.md +211 -0
  31. package/assets/skills/taphound-journey-generator/prompts/select-flow.md +91 -0
  32. package/assets/skills/taphound-journey-generator/references/bridge.md +35 -0
  33. package/assets/skills/taphound-journey-generator/references/mid-session.md +44 -0
  34. package/assets/skills/taphound-journey-generator/schemas/external-flow.json +37 -0
  35. package/assets/skills/taphound-journey-generator/schemas/flow.json +28 -0
  36. package/assets/skills/taphound-journey-generator/schemas/journey-source.json +25 -0
  37. package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/observe-output.json +13 -4
  38. package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/proposed-step-envelope.json +127 -5
  39. package/assets/skills/taphound-journey-generator/scripts/envelope.mjs +561 -0
  40. package/assets/skills/taphound-journey-generator/templates/external-flow.example.json +22 -0
  41. package/assets/skills/taphound-journey-generator/templates/flow.example.json +22 -0
  42. package/assets/skills/taphound-journey-generator/templates/journey-source.example.json +17 -0
  43. package/assets/skills/taphound-journey-generator/templates/taphound-journey-brief.example.md +70 -0
  44. package/assets/skills/taphound-verify-change/SKILL.md +91 -0
  45. package/assets/skills/taphound-verify-change/references/accept.md +38 -0
  46. package/assets/skills/taphound-verify-change/references/preserve.md +349 -0
  47. package/assets/skills/taphound-verify-change/schemas/workflow-manifest.schema.json +295 -0
  48. package/assets/skills/taphound-verify-change/scripts/handoff.mjs +482 -0
  49. package/assets/skills/taphound-verify-change/scripts/ui-refactor.mjs +505 -0
  50. package/dist/adapters/adb/adb-adapter.d.ts +16 -2
  51. package/dist/adapters/adb/adb-adapter.js +199 -0
  52. package/dist/adapters/adb/system-uiautomator-snapshot-provider.d.ts +13 -0
  53. package/dist/adapters/adb/system-uiautomator-snapshot-provider.js +122 -0
  54. package/dist/adapters/adb/ui-automator-parser.d.ts +3 -0
  55. package/dist/adapters/adb/ui-automator-parser.js +160 -0
  56. package/dist/adapters/adb/window-topology-parser.d.ts +2 -0
  57. package/dist/adapters/adb/window-topology-parser.js +101 -0
  58. package/dist/adapters/android-cli/android-cli-adapter.d.ts +16 -8
  59. package/dist/adapters/android-cli/android-cli-adapter.js +137 -15
  60. package/dist/adapters/android-cli/android-cli-snapshot-provider.d.ts +9 -0
  61. package/dist/adapters/android-cli/android-cli-snapshot-provider.js +99 -0
  62. package/dist/adapters/android-cli/layout-parser.js +12 -8
  63. package/dist/adapters/appium/appium-doctor.d.ts +7 -0
  64. package/dist/adapters/appium/appium-doctor.js +128 -0
  65. package/dist/adapters/appium/appium-ui-snapshot-provider.d.ts +34 -0
  66. package/dist/adapters/appium/appium-ui-snapshot-provider.js +240 -0
  67. package/dist/adapters/camera/camera-probe-adapter.d.ts +18 -0
  68. package/dist/adapters/camera/camera-probe-adapter.js +286 -0
  69. package/dist/adapters/filesystem/artifact-store.js +40 -1
  70. package/dist/adapters/filesystem/context-document-writer.d.ts +15 -0
  71. package/dist/adapters/filesystem/context-document-writer.js +112 -0
  72. package/dist/adapters/filesystem/external-flow-registry.d.ts +17 -0
  73. package/dist/adapters/filesystem/external-flow-registry.js +325 -0
  74. package/dist/adapters/filesystem/generation-session-store.d.ts +17 -11
  75. package/dist/adapters/filesystem/generation-session-store.js +281 -855
  76. package/dist/adapters/filesystem/generation-store/session-lock.d.ts +29 -0
  77. package/dist/adapters/filesystem/generation-store/session-lock.js +202 -0
  78. package/dist/adapters/filesystem/generation-store/session-transitions.d.ts +24 -0
  79. package/dist/adapters/filesystem/generation-store/session-transitions.js +340 -0
  80. package/dist/adapters/filesystem/generation-store/store-files.d.ts +50 -0
  81. package/dist/adapters/filesystem/generation-store/store-files.js +363 -0
  82. package/dist/adapters/filesystem/journey-composition-store.d.ts +23 -0
  83. package/dist/adapters/filesystem/journey-composition-store.js +151 -0
  84. package/dist/adapters/filesystem/knowledge-registry.d.ts +12 -0
  85. package/dist/adapters/filesystem/knowledge-registry.js +223 -0
  86. package/dist/adapters/filesystem/project-bound-file.js +3 -5
  87. package/dist/adapters/filesystem/project-file-inspector.d.ts +2 -0
  88. package/dist/adapters/filesystem/project-file-inspector.js +57 -28
  89. package/dist/adapters/filesystem/project-identity-inspector.d.ts +7 -0
  90. package/dist/adapters/filesystem/project-identity-inspector.js +215 -0
  91. package/dist/adapters/filesystem/project-inventory-inspector.d.ts +8 -0
  92. package/dist/adapters/filesystem/project-inventory-inspector.js +93 -0
  93. package/dist/adapters/filesystem/project-module-discoverer.d.ts +6 -0
  94. package/dist/adapters/filesystem/project-module-discoverer.js +158 -0
  95. package/dist/adapters/filesystem/skill-installer.d.ts +7 -2
  96. package/dist/adapters/filesystem/skill-installer.js +40 -11
  97. package/dist/adapters/filesystem/workspace-layout.d.ts +7 -0
  98. package/dist/adapters/filesystem/workspace-layout.js +57 -0
  99. package/dist/adapters/git/node-git-diff.d.ts +17 -0
  100. package/dist/adapters/git/node-git-diff.js +114 -0
  101. package/dist/adapters/process/node-detached-process-launcher.d.ts +6 -0
  102. package/dist/adapters/process/node-detached-process-launcher.js +96 -0
  103. package/dist/adapters/process/node-process-runner.js +11 -2
  104. package/dist/adapters/prompt/inquirer-align-prompt.d.ts +27 -0
  105. package/dist/adapters/prompt/inquirer-align-prompt.js +65 -0
  106. package/dist/adapters/prompt/inquirer-generation-prompt.js +3 -0
  107. package/dist/adapters/prompt/inquirer-recorder-prompt.d.ts +9 -1
  108. package/dist/adapters/prompt/inquirer-recorder-prompt.js +63 -0
  109. package/dist/adapters/runtime/adb-runtime-backend.d.ts +67 -0
  110. package/dist/adapters/runtime/adb-runtime-backend.js +206 -0
  111. package/dist/adapters/runtime/fake-runtime-backend.d.ts +87 -0
  112. package/dist/adapters/runtime/fake-runtime-backend.js +240 -0
  113. package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.d.ts +45 -0
  114. package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.js +153 -0
  115. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.d.ts +23 -0
  116. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.js +39 -0
  117. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.d.ts +20 -0
  118. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.js +180 -0
  119. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.d.ts +78 -0
  120. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.js +379 -0
  121. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-tools.d.ts +46 -0
  122. package/dist/adapters/runtime/session-adb-view.d.ts +53 -0
  123. package/dist/adapters/runtime/session-adb-view.js +193 -0
  124. package/dist/adapters/runtime/session-backed-ports.d.ts +21 -0
  125. package/dist/adapters/runtime/session-backed-ports.js +43 -0
  126. package/dist/adapters/runtime/shared-session-runtime-backend.d.ts +12 -0
  127. package/dist/adapters/runtime/shared-session-runtime-backend.js +64 -0
  128. package/dist/adapters/ui/auto-ui-snapshot-provider.d.ts +8 -0
  129. package/dist/adapters/ui/auto-ui-snapshot-provider.js +48 -0
  130. package/dist/adapters/ui/device-layout-temp.d.ts +6 -0
  131. package/dist/adapters/ui/device-layout-temp.js +18 -0
  132. package/dist/adapters/ui/device-ui-environment.d.ts +13 -0
  133. package/dist/adapters/ui/device-ui-environment.js +81 -0
  134. package/dist/adapters/ui/layout-normalization.d.ts +9 -0
  135. package/dist/adapters/ui/layout-normalization.js +44 -0
  136. package/dist/adapters/ui/ui-snapshot-error.d.ts +11 -0
  137. package/dist/adapters/ui/ui-snapshot-error.js +12 -0
  138. package/dist/adapters/ui/ui-snapshot-support.d.ts +15 -0
  139. package/dist/adapters/ui/ui-snapshot-support.js +12 -0
  140. package/dist/application/align/align-service.d.ts +42 -0
  141. package/dist/application/align/align-service.js +106 -0
  142. package/dist/application/assertion/expectation-evaluator.d.ts +18 -5
  143. package/dist/application/assertion/expectation-evaluator.js +156 -18
  144. package/dist/application/assertion/guarded-expectation.d.ts +45 -0
  145. package/dist/application/assertion/guarded-expectation.js +74 -0
  146. package/dist/application/checkpoint/baseline-capturer.d.ts +26 -0
  147. package/dist/application/checkpoint/baseline-capturer.js +163 -0
  148. package/dist/application/checkpoint/baseline-error.d.ts +5 -0
  149. package/dist/application/checkpoint/baseline-error.js +8 -0
  150. package/dist/application/checkpoint/baseline-service.d.ts +31 -0
  151. package/dist/application/checkpoint/baseline-service.js +147 -0
  152. package/dist/application/checkpoint/checkpoint-evaluator.d.ts +35 -0
  153. package/dist/application/checkpoint/checkpoint-evaluator.js +415 -0
  154. package/dist/application/checkpoint/regression-comparator.d.ts +18 -0
  155. package/dist/application/checkpoint/regression-comparator.js +192 -0
  156. package/dist/application/collector/logcat-collector.d.ts +22 -1
  157. package/dist/application/collector/logcat-collector.js +103 -15
  158. package/dist/application/collector/logcat-event.d.ts +8 -0
  159. package/dist/application/collector/logcat-event.js +79 -0
  160. package/dist/application/collector/logcat-stop.d.ts +2 -0
  161. package/dist/application/collector/logcat-stop.js +11 -0
  162. package/dist/application/context/context-generator.d.ts +45 -0
  163. package/dist/application/context/context-generator.js +228 -0
  164. package/dist/application/context/context-loader.d.ts +40 -0
  165. package/dist/application/context/context-loader.js +223 -0
  166. package/dist/application/context/context-refresher.d.ts +59 -0
  167. package/dist/application/context/context-refresher.js +349 -0
  168. package/dist/application/context/context-rehasher.d.ts +36 -0
  169. package/dist/application/context/context-rehasher.js +110 -0
  170. package/dist/application/context/context-validator.d.ts +23 -1
  171. package/dist/application/context/context-validator.js +116 -9
  172. package/dist/application/context/evidence-hash.d.ts +1 -0
  173. package/dist/application/context/evidence-hash.js +125 -0
  174. package/dist/application/context/evidence-match.d.ts +9 -0
  175. package/dist/application/context/evidence-match.js +8 -0
  176. package/dist/application/context/shard-identity.d.ts +2 -0
  177. package/dist/application/context/shard-identity.js +7 -0
  178. package/dist/application/contract/contract-loader.d.ts +25 -0
  179. package/dist/application/contract/contract-loader.js +68 -0
  180. package/dist/application/contract/contract-review.d.ts +25 -0
  181. package/dist/application/contract/contract-review.js +51 -0
  182. package/dist/application/contract/contract-verifier.d.ts +53 -0
  183. package/dist/application/contract/contract-verifier.js +645 -0
  184. package/dist/application/devices/resolve-device-assignments.d.ts +21 -0
  185. package/dist/application/devices/resolve-device-assignments.js +60 -0
  186. package/dist/application/diagnosis/failure-classifier.d.ts +23 -0
  187. package/dist/application/diagnosis/failure-classifier.js +179 -0
  188. package/dist/application/doctor/doctor-service.d.ts +36 -3
  189. package/dist/application/doctor/doctor-service.js +255 -25
  190. package/dist/application/generation/generation-app-preparer.d.ts +20 -0
  191. package/dist/application/generation/generation-app-preparer.js +28 -0
  192. package/dist/application/generation/generation-config-service.d.ts +21 -0
  193. package/dist/application/generation/generation-config-service.js +53 -0
  194. package/dist/application/generation/generation-confirmation-service.d.ts +14 -3
  195. package/dist/application/generation/generation-confirmation-service.js +109 -24
  196. package/dist/application/generation/generation-context-snapshot.d.ts +6 -0
  197. package/dist/application/generation/generation-context-snapshot.js +29 -0
  198. package/dist/application/generation/generation-finalizer.d.ts +15 -9
  199. package/dist/application/generation/generation-finalizer.js +167 -70
  200. package/dist/application/generation/generation-publisher.js +12 -8
  201. package/dist/application/generation/generation-recovery-service.d.ts +35 -0
  202. package/dist/application/generation/generation-recovery-service.js +144 -0
  203. package/dist/application/generation/generation-reopen-service.d.ts +15 -0
  204. package/dist/application/generation/generation-reopen-service.js +37 -0
  205. package/dist/application/generation/generation-replace-service.d.ts +32 -0
  206. package/dist/application/generation/generation-replace-service.js +116 -0
  207. package/dist/application/generation/generation-starter.d.ts +44 -4
  208. package/dist/application/generation/generation-starter.js +175 -8
  209. package/dist/application/generation/generation-step-executor.d.ts +67 -5
  210. package/dist/application/generation/generation-step-executor.js +471 -108
  211. package/dist/application/generation/proposed-step-validator.js +96 -30
  212. package/dist/application/generation/replay-policy-loader.d.ts +16 -0
  213. package/dist/application/generation/replay-policy-loader.js +29 -0
  214. package/dist/application/generation/risk-evaluator.d.ts +4 -1
  215. package/dist/application/generation/risk-evaluator.js +118 -3
  216. package/dist/application/generation/runtime-observer.d.ts +39 -7
  217. package/dist/application/generation/runtime-observer.js +193 -24
  218. package/dist/application/impact/impact-resolver.d.ts +32 -0
  219. package/dist/application/impact/impact-resolver.js +160 -0
  220. package/dist/application/init/init-service.js +26 -13
  221. package/dist/application/interaction/action-executor.d.ts +3 -2
  222. package/dist/application/interaction/action-executor.js +11 -1
  223. package/dist/application/interaction/action-target.d.ts +17 -0
  224. package/dist/application/interaction/action-target.js +40 -0
  225. package/dist/application/interaction/external-step-runner.d.ts +153 -0
  226. package/dist/application/interaction/external-step-runner.js +332 -0
  227. package/dist/application/interaction/fallback-resolver.d.ts +6 -3
  228. package/dist/application/interaction/fallback-resolver.js +7 -5
  229. package/dist/application/interaction/scroll-to-executor.d.ts +17 -3
  230. package/dist/application/interaction/scroll-to-executor.js +118 -36
  231. package/dist/application/journey/external-flow-resolver.d.ts +25 -0
  232. package/dist/application/journey/external-flow-resolver.js +22 -0
  233. package/dist/application/journey/journey-check-service.d.ts +54 -0
  234. package/dist/application/journey/journey-check-service.js +207 -0
  235. package/dist/application/journey/journey-promoter.d.ts +26 -0
  236. package/dist/application/journey/journey-promoter.js +123 -0
  237. package/dist/application/journey/journey-resolver.d.ts +42 -0
  238. package/dist/application/journey/journey-resolver.js +239 -0
  239. package/dist/application/journey/journey-retirer.d.ts +25 -0
  240. package/dist/application/journey/journey-retirer.js +87 -0
  241. package/dist/application/knowledge/anchor-resolver.d.ts +36 -0
  242. package/dist/application/knowledge/anchor-resolver.js +110 -0
  243. package/dist/application/knowledge/knowledge-loader.d.ts +16 -0
  244. package/dist/application/knowledge/knowledge-loader.js +36 -0
  245. package/dist/application/locator/layout-failure-summary.d.ts +2 -0
  246. package/dist/application/locator/layout-failure-summary.js +67 -0
  247. package/dist/application/locator/layout-traversal.d.ts +11 -0
  248. package/dist/application/locator/layout-traversal.js +18 -0
  249. package/dist/application/locator/locator-resolver.d.ts +13 -1
  250. package/dist/application/locator/locator-resolver.js +182 -18
  251. package/dist/application/observe/observe-service.d.ts +20 -0
  252. package/dist/application/observe/observe-service.js +91 -0
  253. package/dist/application/project/project-describer.d.ts +14 -6
  254. package/dist/application/project/project-describer.js +38 -5
  255. package/dist/application/recognition/screen-detector.d.ts +28 -0
  256. package/dist/application/recognition/screen-detector.js +111 -0
  257. package/dist/application/recorder/locator-selector.js +151 -17
  258. package/dist/application/recorder/recorder-service.d.ts +16 -4
  259. package/dist/application/recorder/recorder-service.js +420 -128
  260. package/dist/application/report/report-writer.js +1 -1
  261. package/dist/application/runtime/cold-launch.d.ts +28 -0
  262. package/dist/application/runtime/cold-launch.js +59 -0
  263. package/dist/application/runtime/step-runner.d.ts +49 -4
  264. package/dist/application/runtime/step-runner.js +721 -130
  265. package/dist/application/runtime/verify-runtime.d.ts +57 -7
  266. package/dist/application/runtime/verify-runtime.js +554 -174
  267. package/dist/application/ui/cached-ui-snapshot-provider.d.ts +45 -0
  268. package/dist/application/ui/cached-ui-snapshot-provider.js +111 -0
  269. package/dist/application/ui/ui-snapshot-lifecycle.d.ts +2 -0
  270. package/dist/application/ui/ui-snapshot-lifecycle.js +8 -0
  271. package/dist/application/ui/ui-stability-probe.d.ts +3 -0
  272. package/dist/application/ui/ui-stability-probe.js +8 -0
  273. package/dist/application/wait/idle-advice.d.ts +6 -0
  274. package/dist/application/wait/idle-advice.js +27 -0
  275. package/dist/application/wait/idle-profiles.d.ts +15 -0
  276. package/dist/application/wait/idle-profiles.js +75 -0
  277. package/dist/application/wait/idle-waiter.d.ts +28 -4
  278. package/dist/application/wait/idle-waiter.js +186 -15
  279. package/dist/cli/commands/align.d.ts +3 -0
  280. package/dist/cli/commands/align.js +124 -0
  281. package/dist/cli/commands/baseline.d.ts +3 -0
  282. package/dist/cli/commands/baseline.js +130 -0
  283. package/dist/cli/commands/context.js +300 -27
  284. package/dist/cli/commands/contract.d.ts +3 -0
  285. package/dist/cli/commands/contract.js +120 -0
  286. package/dist/cli/commands/doctor.js +16 -9
  287. package/dist/cli/commands/failure.d.ts +3 -0
  288. package/dist/cli/commands/failure.js +63 -0
  289. package/dist/cli/commands/generation/lifecycle-commands.d.ts +5 -0
  290. package/dist/cli/commands/generation/lifecycle-commands.js +211 -0
  291. package/dist/cli/commands/generation/session-commands.d.ts +11 -0
  292. package/dist/cli/commands/generation/session-commands.js +472 -0
  293. package/dist/cli/commands/generation/shared.d.ts +1414 -0
  294. package/dist/cli/commands/generation/shared.js +291 -0
  295. package/dist/cli/commands/generation/step-commands.d.ts +8 -0
  296. package/dist/cli/commands/generation/step-commands.js +315 -0
  297. package/dist/cli/commands/generation.js +10 -507
  298. package/dist/cli/commands/impact.d.ts +3 -0
  299. package/dist/cli/commands/impact.js +87 -0
  300. package/dist/cli/commands/init.js +13 -6
  301. package/dist/cli/commands/journey.d.ts +3 -0
  302. package/dist/cli/commands/journey.js +382 -0
  303. package/dist/cli/commands/knowledge.d.ts +3 -0
  304. package/dist/cli/commands/knowledge.js +93 -0
  305. package/dist/cli/commands/observe.d.ts +3 -0
  306. package/dist/cli/commands/observe.js +127 -0
  307. package/dist/cli/commands/project.js +2 -1
  308. package/dist/cli/commands/record.js +18 -5
  309. package/dist/cli/commands/verify.js +273 -63
  310. package/dist/cli/dependencies.d.ts +125 -1
  311. package/dist/cli/dependencies.js +450 -51
  312. package/dist/cli/diff-verification.d.ts +41 -0
  313. package/dist/cli/diff-verification.js +192 -0
  314. package/dist/cli/main.js +49 -1
  315. package/dist/cli/output.d.ts +5 -3
  316. package/dist/cli/output.js +12 -0
  317. package/dist/cli/program.js +19 -1
  318. package/dist/cli/project-root.d.ts +1 -0
  319. package/dist/cli/project-root.js +6 -0
  320. package/dist/cli/runtime-selection.d.ts +22 -0
  321. package/dist/cli/runtime-selection.js +92 -0
  322. package/dist/cli/version.d.ts +1 -0
  323. package/dist/cli/version.js +14 -0
  324. package/dist/cli/workspace-guard.d.ts +2 -0
  325. package/dist/cli/workspace-guard.js +3 -0
  326. package/dist/domain/binding-reference.d.ts +4 -0
  327. package/dist/domain/binding-reference.js +23 -0
  328. package/dist/domain/checkpoint.d.ts +404 -0
  329. package/dist/domain/checkpoint.js +297 -0
  330. package/dist/domain/config.d.ts +107 -1
  331. package/dist/domain/config.js +52 -6
  332. package/dist/domain/contract.d.ts +413 -0
  333. package/dist/domain/contract.js +196 -0
  334. package/dist/domain/external-flow.d.ts +493 -0
  335. package/dist/domain/external-flow.js +15 -0
  336. package/dist/domain/failure-classification.d.ts +176 -0
  337. package/dist/domain/failure-classification.js +169 -0
  338. package/dist/domain/failure.d.ts +2 -1
  339. package/dist/domain/failure.js +123 -0
  340. package/dist/domain/generation.d.ts +1671 -132
  341. package/dist/domain/generation.js +202 -13
  342. package/dist/domain/geometry.d.ts +13 -0
  343. package/dist/domain/geometry.js +16 -0
  344. package/dist/domain/impact.d.ts +71 -0
  345. package/dist/domain/impact.js +76 -0
  346. package/dist/domain/init.d.ts +12 -4
  347. package/dist/domain/init.js +24 -18
  348. package/dist/domain/journey-composition.d.ts +2285 -0
  349. package/dist/domain/journey-composition.js +85 -0
  350. package/dist/domain/journey-lifecycle.d.ts +10 -0
  351. package/dist/domain/journey-lifecycle.js +9 -0
  352. package/dist/domain/journey.d.ts +2419 -212
  353. package/dist/domain/journey.js +384 -14
  354. package/dist/domain/knowledge.d.ts +165 -0
  355. package/dist/domain/knowledge.js +188 -0
  356. package/dist/domain/layout.d.ts +34 -5
  357. package/dist/domain/layout.js +64 -6
  358. package/dist/domain/locator-evidence.d.ts +3 -0
  359. package/dist/domain/locator-evidence.js +36 -0
  360. package/dist/domain/logcat-event.d.ts +54 -0
  361. package/dist/domain/logcat-event.js +40 -0
  362. package/dist/domain/observation.d.ts +33 -0
  363. package/dist/domain/observation.js +18 -0
  364. package/dist/domain/project-context.d.ts +219 -1
  365. package/dist/domain/project-context.js +209 -6
  366. package/dist/domain/proposed-step.d.ts +340 -60
  367. package/dist/domain/proposed-step.js +23 -3
  368. package/dist/domain/report.d.ts +1455 -28
  369. package/dist/domain/report.js +211 -14
  370. package/dist/domain/runtime-snapshot.d.ts +58 -1
  371. package/dist/domain/runtime-snapshot.js +14 -3
  372. package/dist/domain/runtime.d.ts +50 -0
  373. package/dist/domain/runtime.js +36 -0
  374. package/dist/domain/system-app-profiles.d.ts +6 -0
  375. package/dist/domain/system-app-profiles.js +43 -0
  376. package/dist/domain/ui-backend.d.ts +28 -0
  377. package/dist/domain/ui-backend.js +33 -0
  378. package/dist/domain/ui-cache.d.ts +11 -0
  379. package/dist/domain/ui-cache.js +10 -0
  380. package/dist/domain/window-hierarchy.d.ts +84 -0
  381. package/dist/domain/window-hierarchy.js +172 -0
  382. package/dist/domain/workflow-manifest.d.ts +77 -0
  383. package/dist/domain/workflow-manifest.js +136 -0
  384. package/dist/domain/workspace.d.ts +28 -0
  385. package/dist/domain/workspace.js +72 -0
  386. package/dist/ports/adb.d.ts +34 -1
  387. package/dist/ports/align-prompt.d.ts +11 -0
  388. package/dist/ports/align-prompt.js +6 -0
  389. package/dist/ports/anchor-resolver.d.ts +30 -0
  390. package/dist/ports/anchor-resolver.js +1 -0
  391. package/dist/ports/annotated-screen-resolver.d.ts +4 -0
  392. package/dist/ports/annotated-screen-resolver.js +1 -0
  393. package/dist/ports/camera-probe.d.ts +23 -0
  394. package/dist/ports/camera-probe.js +8 -0
  395. package/dist/ports/context-document-writer.d.ts +24 -0
  396. package/dist/ports/context-document-writer.js +1 -0
  397. package/dist/ports/detached-process-launcher.d.ts +12 -0
  398. package/dist/ports/detached-process-launcher.js +1 -0
  399. package/dist/ports/external-flow-registry.d.ts +37 -0
  400. package/dist/ports/external-flow-registry.js +1 -0
  401. package/dist/ports/generation-prompt.d.ts +2 -1
  402. package/dist/ports/generation-session-store.d.ts +12 -2
  403. package/dist/ports/git-diff.d.ts +9 -0
  404. package/dist/ports/git-diff.js +1 -0
  405. package/dist/ports/journey-composition-store.d.ts +17 -0
  406. package/dist/ports/journey-composition-store.js +1 -0
  407. package/dist/ports/knowledge-registry.d.ts +20 -0
  408. package/dist/ports/knowledge-registry.js +1 -0
  409. package/dist/ports/process-runner.d.ts +7 -0
  410. package/dist/ports/project-file-inspector.d.ts +1 -0
  411. package/dist/ports/project-identity-inspector.d.ts +24 -0
  412. package/dist/ports/project-identity-inspector.js +1 -0
  413. package/dist/ports/project-inventory-inspector.d.ts +21 -0
  414. package/dist/ports/project-inventory-inspector.js +1 -0
  415. package/dist/ports/project-module-discoverer.d.ts +26 -0
  416. package/dist/ports/project-module-discoverer.js +1 -0
  417. package/dist/ports/recorder-prompt.d.ts +10 -1
  418. package/dist/ports/runtime-backend.d.ts +89 -0
  419. package/dist/ports/runtime-backend.js +13 -0
  420. package/dist/ports/runtime-capability.d.ts +12 -0
  421. package/dist/ports/runtime-capability.js +20 -0
  422. package/dist/ports/runtime-session-ports.d.ts +25 -0
  423. package/dist/ports/runtime-session-ports.js +1 -0
  424. package/dist/ports/screenshot.d.ts +11 -0
  425. package/dist/ports/screenshot.js +1 -0
  426. package/dist/ports/skill-installer.d.ts +2 -2
  427. package/dist/ports/ui-snapshot.d.ts +35 -0
  428. package/dist/ports/ui-snapshot.js +1 -0
  429. package/dist/ports/ui-stability.d.ts +19 -0
  430. package/dist/ports/ui-stability.js +1 -0
  431. package/dist/ports/workspace-layout.d.ts +4 -0
  432. package/dist/ports/workspace-layout.js +1 -0
  433. package/dist/shared/errors.d.ts +2 -0
  434. package/dist/shared/errors.js +22 -0
  435. package/dist/shared/paths.d.ts +2 -0
  436. package/dist/shared/paths.js +24 -0
  437. package/dist/shared/strings.d.ts +2 -0
  438. package/dist/shared/strings.js +16 -0
  439. package/package.json +6 -1
  440. package/assets/skills/taphound-ai-journey/GUIDE.md +0 -862
  441. package/assets/skills/taphound-ai-journey/SKILL.md +0 -377
  442. package/assets/skills/taphound-ai-journey/prompts/analyze-project.md +0 -526
  443. package/assets/skills/taphound-ai-journey/prompts/generate-step.md +0 -109
  444. package/assets/skills/taphound-ai-journey/schemas/project-context.json +0 -81
  445. package/assets/skills/taphound-ai-journey/templates/project-context.example.json +0 -49
  446. package/dist/ports/android-cli.d.ts +0 -21
  447. /package/dist/{ports/android-cli.js → adapters/runtime/mobile-mcp/mobile-mcp-tools.js} +0 -0
@@ -0,0 +1,240 @@
1
+ import { createHash } from "node:crypto";
2
+ import { performance } from "node:perf_hooks";
3
+ import { parseAppiumPageSource } from "../adb/ui-automator-parser.js";
4
+ import { readDeviceUiEnvironment } from "../ui/device-ui-environment.js";
5
+ import { UiSnapshotError } from "../ui/ui-snapshot-error.js";
6
+ import { snapshotFromCapture } from "../ui/ui-snapshot-support.js";
7
+ function loopbackEndpoint(value) {
8
+ const endpoint = new URL(value);
9
+ if (endpoint.protocol !== "http:"
10
+ || !["127.0.0.1", "localhost", "[::1]"].includes(endpoint.hostname)
11
+ || endpoint.username !== ""
12
+ || endpoint.password !== "") {
13
+ throw new Error("Appium endpoint must be an unauthenticated loopback HTTP URL");
14
+ }
15
+ return endpoint;
16
+ }
17
+ export class FetchAppiumHttpClient {
18
+ endpoint;
19
+ constructor(endpoint) {
20
+ this.endpoint = endpoint;
21
+ }
22
+ async request(input) {
23
+ const timeout = AbortSignal.timeout(Math.trunc(input.timeoutMs));
24
+ const signal = input.signal === undefined
25
+ ? timeout
26
+ : AbortSignal.any([input.signal, timeout]);
27
+ const response = await fetch(new URL(input.path.slice(1), this.endpoint), {
28
+ method: input.method,
29
+ ...(input.body === undefined
30
+ ? {}
31
+ : {
32
+ headers: { "content-type": "application/json" },
33
+ body: JSON.stringify(input.body)
34
+ }),
35
+ signal
36
+ });
37
+ const payload = await response.json();
38
+ if (!response.ok) {
39
+ throw new Error(`Appium HTTP ${String(response.status)}`);
40
+ }
41
+ return { value: payload.value };
42
+ }
43
+ }
44
+ function objectValue(value) {
45
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
46
+ throw new Error("Appium returned an invalid response");
47
+ }
48
+ return value;
49
+ }
50
+ class AppiumUiSnapshotProvider {
51
+ http;
52
+ sessionId;
53
+ environment;
54
+ descriptor;
55
+ closePromise;
56
+ lastSourceSignature;
57
+ constructor(http, sessionId, environment, descriptor) {
58
+ this.http = http;
59
+ this.sessionId = sessionId;
60
+ this.environment = environment;
61
+ this.descriptor = descriptor;
62
+ }
63
+ reset() {
64
+ this.lastSourceSignature = undefined;
65
+ }
66
+ async sample(options) {
67
+ void options.deviceSerial;
68
+ const startedAt = performance.now();
69
+ const snapshot = await this.capture({
70
+ reason: "idle",
71
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
72
+ timeoutMs: options.timeoutMs ?? 5000
73
+ });
74
+ const signature = createHash("sha256")
75
+ .update(JSON.stringify(snapshot.roots))
76
+ .digest("hex");
77
+ const previous = this.lastSourceSignature;
78
+ this.lastSourceSignature = signature;
79
+ return {
80
+ changes: previous === signature ? [] : [{ layoutSha256: signature }],
81
+ layout: snapshot.roots,
82
+ backend: "uiautomator",
83
+ durationMs: performance.now() - startedAt
84
+ };
85
+ }
86
+ async capture(options) {
87
+ if (this.closePromise !== undefined) {
88
+ throw new UiSnapshotError("UI_SNAPSHOT_FAILED", this.descriptor.id, "Appium UI snapshot provider is closed");
89
+ }
90
+ const startedAt = performance.now();
91
+ let source;
92
+ try {
93
+ source = (await this.http.request({
94
+ method: "GET",
95
+ path: `/session/${this.sessionId}/source`,
96
+ timeoutMs: options.timeoutMs,
97
+ ...(options.signal === undefined ? {} : { signal: options.signal })
98
+ })).value;
99
+ }
100
+ catch (error) {
101
+ throw new UiSnapshotError("UI_SNAPSHOT_FAILED", this.descriptor.id, `Appium page source capture failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error, terminal: true });
102
+ }
103
+ if (typeof source !== "string") {
104
+ throw new UiSnapshotError("UI_SNAPSHOT_INVALID", this.descriptor.id, "Appium returned a non-string page source");
105
+ }
106
+ let roots;
107
+ try {
108
+ roots = parseAppiumPageSource(source);
109
+ }
110
+ catch (error) {
111
+ throw new UiSnapshotError("UI_SNAPSHOT_INVALID", this.descriptor.id, "Appium returned malformed page source", { cause: error });
112
+ }
113
+ if (roots.length === 0) {
114
+ throw new UiSnapshotError("UI_SNAPSHOT_INVALID", this.descriptor.id, "Appium returned an empty layout");
115
+ }
116
+ return snapshotFromCapture({
117
+ startedAt,
118
+ roots,
119
+ backend: this.descriptor,
120
+ viewport: this.environment.viewport,
121
+ timing: {}
122
+ });
123
+ }
124
+ close() {
125
+ this.closePromise ??= this.http.request({
126
+ method: "DELETE",
127
+ path: `/session/${this.sessionId}`,
128
+ timeoutMs: 5000
129
+ }).then(() => undefined);
130
+ return this.closePromise;
131
+ }
132
+ }
133
+ export class AppiumUiSnapshotProviderFactory {
134
+ runner;
135
+ endpoint;
136
+ http;
137
+ settings;
138
+ constructor(runner, http, options = {}) {
139
+ this.runner = runner;
140
+ this.endpoint = loopbackEndpoint(options.endpoint ?? "http://127.0.0.1:4723/");
141
+ this.http = http ?? new FetchAppiumHttpClient(this.endpoint);
142
+ this.settings = {
143
+ mapTestTagToResourceId: options.mapTestTagToResourceId ?? false
144
+ };
145
+ }
146
+ async probe(timeoutMs = 2000) {
147
+ try {
148
+ const response = await this.http.request({
149
+ method: "GET",
150
+ path: "/status",
151
+ timeoutMs
152
+ });
153
+ return response.value !== undefined;
154
+ }
155
+ catch {
156
+ return false;
157
+ }
158
+ }
159
+ async open(options) {
160
+ const environment = await readDeviceUiEnvironment(this.runner, "appium-uiautomator2", options);
161
+ let provisionalSessionId;
162
+ try {
163
+ const status = objectValue((await this.http.request({
164
+ method: "GET",
165
+ path: "/status",
166
+ timeoutMs: options.timeoutMs,
167
+ ...(options.signal === undefined ? {} : { signal: options.signal })
168
+ })).value);
169
+ const build = status.build === undefined ? {} : objectValue(status.build);
170
+ const engineVersion = typeof build.version === "string"
171
+ ? build.version
172
+ : "unknown";
173
+ const created = objectValue((await this.http.request({
174
+ method: "POST",
175
+ path: "/session",
176
+ timeoutMs: options.timeoutMs,
177
+ body: {
178
+ capabilities: {
179
+ alwaysMatch: {
180
+ platformName: "Android",
181
+ "appium:automationName": "UiAutomator2",
182
+ "appium:udid": options.deviceSerial,
183
+ "appium:noReset": true,
184
+ "appium:autoLaunch": false,
185
+ "appium:autoGrantPermissions": false,
186
+ "appium:fullReset": false,
187
+ "appium:shouldTerminateApp": false
188
+ },
189
+ firstMatch: [{}]
190
+ }
191
+ },
192
+ ...(options.signal === undefined ? {} : { signal: options.signal })
193
+ })).value);
194
+ const sessionId = typeof created.sessionId === "string"
195
+ ? created.sessionId
196
+ : undefined;
197
+ if (sessionId === undefined) {
198
+ throw new Error("Appium did not return a session id");
199
+ }
200
+ provisionalSessionId = sessionId;
201
+ await this.http.request({
202
+ method: "POST",
203
+ path: `/session/${sessionId}/appium/settings`,
204
+ timeoutMs: options.timeoutMs,
205
+ body: { settings: this.settings },
206
+ ...(options.signal === undefined ? {} : { signal: options.signal })
207
+ });
208
+ const descriptor = {
209
+ id: "appium-uiautomator2",
210
+ adapterVersion: "appium-uiautomator2-v1",
211
+ engineVersion,
212
+ configSha256: createHash("sha256").update(JSON.stringify({
213
+ endpoint: this.endpoint.origin,
214
+ settings: this.settings,
215
+ capabilitiesVersion: 1
216
+ })).digest("hex")
217
+ };
218
+ const provider = new AppiumUiSnapshotProvider(this.http, sessionId, environment, descriptor);
219
+ await provider.capture({
220
+ reason: "evidence",
221
+ timeoutMs: options.timeoutMs,
222
+ ...(options.signal === undefined ? {} : { signal: options.signal })
223
+ });
224
+ provisionalSessionId = undefined;
225
+ return provider;
226
+ }
227
+ catch (error) {
228
+ if (provisionalSessionId !== undefined) {
229
+ await this.http.request({
230
+ method: "DELETE",
231
+ path: `/session/${provisionalSessionId}`,
232
+ timeoutMs: 5000
233
+ }).catch(() => undefined);
234
+ }
235
+ if (error instanceof UiSnapshotError)
236
+ throw error;
237
+ throw new UiSnapshotError("UI_BACKEND_UNAVAILABLE", "appium-uiautomator2", "Appium UiAutomator2 session could not be opened", { cause: error });
238
+ }
239
+ }
240
+ }
@@ -0,0 +1,18 @@
1
+ import { type RuntimeSessionOpener } from "../../ports/runtime-backend.js";
2
+ import type { RuntimeSessionPortViewsFactory } from "../../ports/runtime-session-ports.js";
3
+ import type { UiSnapshotProviderFactory } from "../../ports/ui-snapshot.js";
4
+ import type { CameraProbeInput, CameraProbePort, CameraProbeResult } from "../../ports/camera-probe.js";
5
+ export interface CameraProbeAdapterDeps {
6
+ sessions: RuntimeSessionOpener;
7
+ sessionPorts: RuntimeSessionPortViewsFactory;
8
+ uiSnapshots: UiSnapshotProviderFactory;
9
+ now: () => number;
10
+ sleep: (ms: number) => Promise<void>;
11
+ }
12
+ export declare class CameraProbeAdapter implements CameraProbePort {
13
+ private readonly deps;
14
+ constructor(deps: CameraProbeAdapterDeps);
15
+ probe(input: CameraProbeInput): Promise<CameraProbeResult>;
16
+ private probeWith;
17
+ private waitForStableForeground;
18
+ }
@@ -0,0 +1,286 @@
1
+ import { withRuntimeSession } from "../../ports/runtime-backend.js";
2
+ import { CameraProbeError } from "../../ports/camera-probe.js";
3
+ const SHUTTER_KEYWORDS = ["shutter", "快门", "capture", "拍照"];
4
+ const CONFIRM_KEYWORDS = [
5
+ "done",
6
+ "confirm",
7
+ "accept",
8
+ "ok",
9
+ "save",
10
+ "checkmark",
11
+ "完成",
12
+ "确认",
13
+ "接受",
14
+ "确定",
15
+ "保存"
16
+ ];
17
+ const CONFIRM_RESOURCE_ID_SUBSTRINGS = ["done", "confirm", "accept", "save", "ok"];
18
+ const SHUTTER_RESOURCE_ID_SUBSTRINGS = ["shutter"];
19
+ const IMAGE_CAPTURE_ACTION = "android.media.action.IMAGE_CAPTURE";
20
+ const CAMERA_FOREGROUND_TIMEOUT_MS = 8000;
21
+ const CAMERA_FOREGROUND_POLL_INTERVAL_MS = 500;
22
+ const LAYOUT_SETTLE_MS = 1500;
23
+ const RESOLVER_KEYWORDS = ["resolver", "chooser"];
24
+ const AOSP_CAMERA_PACKAGES = ["com.android.camera", "com.android.camera2"];
25
+ function flatten(elements) {
26
+ const result = [];
27
+ const visit = (nodes) => {
28
+ for (const node of nodes) {
29
+ result.push(node);
30
+ visit(node.children);
31
+ }
32
+ };
33
+ visit(elements);
34
+ return result;
35
+ }
36
+ function matchesAnyKeyword(value, keywords) {
37
+ const lower = value.toLowerCase();
38
+ return keywords.some((keyword) => lower.includes(keyword.toLowerCase()));
39
+ }
40
+ function resourceEntryName(resourceId) {
41
+ const separator = resourceId.lastIndexOf(":id/");
42
+ return (separator >= 0 ? resourceId.slice(separator + 4) : resourceId).toLowerCase();
43
+ }
44
+ function matchesResourceId(resourceId, token) {
45
+ const entryName = resourceEntryName(resourceId);
46
+ const tokens = entryName.split(/[^a-z0-9]+/u);
47
+ return tokens.includes(token.toLowerCase());
48
+ }
49
+ function selectByResourceIdPriority(candidates, resourceIdTokens) {
50
+ for (const token of resourceIdTokens) {
51
+ const matches = candidates.filter((node) => (node.resourceId !== undefined
52
+ && matchesResourceId(node.resourceId, token)));
53
+ if (matches.length > 0) {
54
+ return matches;
55
+ }
56
+ }
57
+ return [];
58
+ }
59
+ function buttonMatch(node) {
60
+ if (node.resourceId === undefined) {
61
+ return "no_resource_id";
62
+ }
63
+ return {
64
+ resourceId: node.resourceId,
65
+ ...(node.contentDescription === undefined
66
+ ? {}
67
+ : { contentDescription: node.contentDescription })
68
+ };
69
+ }
70
+ function findButton(layout, keywords, resourceIdSubstrings) {
71
+ const eligible = flatten(layout).filter((node) => node.enabled && node.clickable === true);
72
+ const candidates = eligible.filter((node) => (node.contentDescription !== undefined
73
+ && matchesAnyKeyword(node.contentDescription, keywords)));
74
+ if (candidates.length === 0) {
75
+ const byResourceId = selectByResourceIdPriority(eligible, resourceIdSubstrings);
76
+ if (byResourceId.length === 0) {
77
+ return "none";
78
+ }
79
+ if (byResourceId.length === 1) {
80
+ const node = byResourceId[0];
81
+ return node === undefined ? "none" : buttonMatch(node);
82
+ }
83
+ return "ambiguous";
84
+ }
85
+ if (candidates.length === 1) {
86
+ const node = candidates[0];
87
+ return node === undefined ? "none" : buttonMatch(node);
88
+ }
89
+ const byResourceId = selectByResourceIdPriority(candidates, resourceIdSubstrings);
90
+ if (byResourceId.length === 1) {
91
+ const node = byResourceId[0];
92
+ return node === undefined ? "none" : buttonMatch(node);
93
+ }
94
+ return "ambiguous";
95
+ }
96
+ function centerOf(element) {
97
+ if (element.center !== undefined) {
98
+ return element.center;
99
+ }
100
+ if (element.bounds !== undefined) {
101
+ return {
102
+ x: Math.round((element.bounds.left + element.bounds.right) / 2),
103
+ y: Math.round((element.bounds.top + element.bounds.bottom) / 2)
104
+ };
105
+ }
106
+ throw new CameraProbeError("ALIGN_SHUTTER_NOT_FOUND", "Shutter element has no center or bounds");
107
+ }
108
+ function isResolverOrChooser(packageName) {
109
+ const lower = packageName.toLowerCase();
110
+ return RESOLVER_KEYWORDS.some((keyword) => lower.includes(keyword));
111
+ }
112
+ function isAospCameraPackage(packageName) {
113
+ return AOSP_CAMERA_PACKAGES.includes(packageName);
114
+ }
115
+ function deviceIdentity(deviceSerial, signal) {
116
+ return {
117
+ packageName: "",
118
+ deviceSerial,
119
+ ...(signal === undefined ? {} : { signal })
120
+ };
121
+ }
122
+ export class CameraProbeAdapter {
123
+ deps;
124
+ constructor(deps) {
125
+ this.deps = deps;
126
+ }
127
+ probe(input) {
128
+ return withRuntimeSession(this.deps.sessions, input.deviceSerial, input.signal, (session) => this.probeWith(this.deps.sessionPorts(session).adb, input));
129
+ }
130
+ async probeWith(adb, input) {
131
+ const { deviceSerial, signal } = input;
132
+ let cameraPackage;
133
+ const uiSnapshotProvider = await this.deps.uiSnapshots.open({
134
+ deviceSerial,
135
+ timeoutMs: CAMERA_FOREGROUND_TIMEOUT_MS,
136
+ ...(signal === undefined ? {} : { signal })
137
+ });
138
+ try {
139
+ let preForeground = await adb.foregroundComponent(deviceIdentity(deviceSerial, signal));
140
+ if (isAospCameraPackage(preForeground.packageName)
141
+ || isResolverOrChooser(preForeground.packageName)) {
142
+ await adb.forceStop({
143
+ packageName: preForeground.packageName,
144
+ deviceSerial,
145
+ ...(signal === undefined ? {} : { signal })
146
+ });
147
+ preForeground = await adb.foregroundComponent(deviceIdentity(deviceSerial, signal));
148
+ }
149
+ const startResult = await adb.startActivityByIntent({
150
+ action: IMAGE_CAPTURE_ACTION,
151
+ deviceSerial,
152
+ ...(signal === undefined ? {} : { signal })
153
+ });
154
+ if (startResult.exitCode !== 0) {
155
+ throw new CameraProbeError("ALIGN_CAMERA_INTENT_FAILED", `am start exited with code ${String(startResult.exitCode)}: ${startResult.stderr || startResult.stdout}`);
156
+ }
157
+ const deadline = this.deps.now() + CAMERA_FOREGROUND_TIMEOUT_MS;
158
+ let current = preForeground;
159
+ while (this.deps.now() < deadline) {
160
+ current = await adb.foregroundComponent(deviceIdentity(deviceSerial, signal));
161
+ if (isResolverOrChooser(current.packageName)) {
162
+ throw new CameraProbeError("ALIGN_CAMERA_INTENT_FAILED", `IMAGE_CAPTURE landed on system resolver/chooser (${current.packageName}); set a default camera app`);
163
+ }
164
+ if (current.packageName !== preForeground.packageName) {
165
+ cameraPackage = current.packageName;
166
+ break;
167
+ }
168
+ await this.deps.sleep(CAMERA_FOREGROUND_POLL_INTERVAL_MS);
169
+ }
170
+ if (cameraPackage === undefined) {
171
+ throw new CameraProbeError("ALIGN_CAMERA_NOT_LAUNCHED", "Camera package did not become foreground within 8s");
172
+ }
173
+ await uiSnapshotProvider.capture({
174
+ reason: "locate",
175
+ timeoutMs: CAMERA_FOREGROUND_TIMEOUT_MS,
176
+ ...(signal === undefined ? {} : { signal })
177
+ });
178
+ await this.deps.sleep(LAYOUT_SETTLE_MS);
179
+ const initialLayout = (await uiSnapshotProvider.capture({
180
+ reason: "locate",
181
+ timeoutMs: CAMERA_FOREGROUND_TIMEOUT_MS,
182
+ ...(signal === undefined ? {} : { signal })
183
+ })).roots;
184
+ const preShutterForeground = await this.waitForStableForeground(adb, deviceSerial, signal);
185
+ if (preShutterForeground.packageName !== cameraPackage) {
186
+ throw new CameraProbeError("ALIGN_CAMERA_NOT_LAUNCHED", `Camera package left the foreground before shutter discovery: ${preShutterForeground.packageName}`);
187
+ }
188
+ const shutter = findButton(initialLayout, SHUTTER_KEYWORDS, SHUTTER_RESOURCE_ID_SUBSTRINGS);
189
+ if (shutter === "none") {
190
+ throw new CameraProbeError("ALIGN_SHUTTER_NOT_FOUND", "No enabled clickable element matched shutter contentDescription or resourceId");
191
+ }
192
+ if (shutter === "ambiguous") {
193
+ throw new CameraProbeError("ALIGN_SHUTTER_AMBIGUOUS", "Multiple shutter candidates could not be disambiguated by resourceId");
194
+ }
195
+ if (shutter === "no_resource_id") {
196
+ throw new CameraProbeError("ALIGN_SHUTTER_NO_RESOURCE_ID", "Shutter candidate has no resourceId for deterministic replay");
197
+ }
198
+ const shutterElement = flatten(initialLayout).find((node) => node.resourceId === shutter.resourceId);
199
+ if (shutterElement === undefined) {
200
+ throw new CameraProbeError("ALIGN_SHUTTER_NOT_FOUND", `Shutter resourceId ${shutter.resourceId} not present in layout`);
201
+ }
202
+ await adb.tap(centerOf(shutterElement), deviceSerial, signal);
203
+ await uiSnapshotProvider.capture({
204
+ reason: "locate",
205
+ timeoutMs: CAMERA_FOREGROUND_TIMEOUT_MS,
206
+ ...(signal === undefined ? {} : { signal })
207
+ });
208
+ await this.deps.sleep(LAYOUT_SETTLE_MS);
209
+ const postShutterLayout = (await uiSnapshotProvider.capture({
210
+ reason: "locate",
211
+ timeoutMs: CAMERA_FOREGROUND_TIMEOUT_MS,
212
+ ...(signal === undefined ? {} : { signal })
213
+ })).roots;
214
+ const postShutterForeground = await this.waitForStableForeground(adb, deviceSerial, signal);
215
+ const activityName = preShutterForeground.activity;
216
+ if (postShutterForeground.packageName !== cameraPackage) {
217
+ return {
218
+ packageName: cameraPackage,
219
+ activityName,
220
+ shutterResourceId: shutter.resourceId,
221
+ ...(shutter.contentDescription !== undefined
222
+ ? { shutterContentDescription: shutter.contentDescription }
223
+ : {})
224
+ };
225
+ }
226
+ const confirm = findButton(postShutterLayout, CONFIRM_KEYWORDS, CONFIRM_RESOURCE_ID_SUBSTRINGS);
227
+ if (confirm === "ambiguous") {
228
+ throw new CameraProbeError("ALIGN_CONFIRM_AMBIGUOUS", "Multiple confirm candidates could not be disambiguated by resourceId");
229
+ }
230
+ if (confirm === "none") {
231
+ throw new CameraProbeError("ALIGN_CONFIRM_NOT_FOUND", "Camera remained foreground after capture, but no deterministic confirm button was found");
232
+ }
233
+ if (confirm === "no_resource_id") {
234
+ throw new CameraProbeError("ALIGN_CONFIRM_NO_RESOURCE_ID", "Confirm candidate has no resourceId for deterministic replay");
235
+ }
236
+ return {
237
+ packageName: cameraPackage,
238
+ activityName,
239
+ shutterResourceId: shutter.resourceId,
240
+ ...(shutter.contentDescription !== undefined
241
+ ? { shutterContentDescription: shutter.contentDescription }
242
+ : {}),
243
+ confirmResourceId: confirm.resourceId,
244
+ ...(confirm.contentDescription !== undefined
245
+ ? { confirmContentDescription: confirm.contentDescription }
246
+ : {}),
247
+ confirmActivityName: postShutterForeground.activity
248
+ };
249
+ }
250
+ finally {
251
+ try {
252
+ await uiSnapshotProvider.close();
253
+ }
254
+ catch {
255
+ void null;
256
+ }
257
+ if (cameraPackage !== undefined) {
258
+ try {
259
+ await adb.forceStop({
260
+ packageName: cameraPackage,
261
+ deviceSerial,
262
+ ...(signal === undefined ? {} : { signal })
263
+ });
264
+ }
265
+ catch {
266
+ void null;
267
+ }
268
+ }
269
+ }
270
+ }
271
+ async waitForStableForeground(adb, deviceSerial, signal) {
272
+ const deadline = this.deps.now() + CAMERA_FOREGROUND_TIMEOUT_MS;
273
+ let previous;
274
+ while (this.deps.now() < deadline) {
275
+ const current = await adb.foregroundComponent(deviceIdentity(deviceSerial, signal));
276
+ if (previous !== undefined
277
+ && previous.packageName === current.packageName
278
+ && previous.activity === current.activity) {
279
+ return current;
280
+ }
281
+ previous = current;
282
+ await this.deps.sleep(Math.min(CAMERA_FOREGROUND_POLL_INTERVAL_MS, Math.max(0, deadline - this.deps.now())));
283
+ }
284
+ throw new CameraProbeError("ALIGN_CAMERA_NOT_LAUNCHED", "Foreground Activity did not stabilize within 8s");
285
+ }
286
+ }
@@ -1,10 +1,48 @@
1
- import { access, mkdir, mkdtemp, rename, rm, writeFile } from "node:fs/promises";
1
+ import { access, mkdir, mkdtemp, realpath, rename, rm, writeFile } from "node:fs/promises";
2
2
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
3
+ import { isErrnoException } from "../../shared/errors.js";
3
4
  function assertRunDirectoryName(name) {
4
5
  if (!/^[A-Za-z0-9._-]+$/.test(name)) {
5
6
  throw new Error("Invalid artifact run directory name");
6
7
  }
7
8
  }
9
+ async function projectedRealPath(path) {
10
+ const missing = [];
11
+ let current = resolve(path);
12
+ for (;;) {
13
+ try {
14
+ return resolve(await realpath(current), ...missing.reverse());
15
+ }
16
+ catch (error) {
17
+ if (!isErrnoException(error) || error.code !== "ENOENT") {
18
+ throw error;
19
+ }
20
+ const parent = dirname(current);
21
+ if (parent === current) {
22
+ throw error;
23
+ }
24
+ missing.push(current.slice(parent.length + 1));
25
+ current = parent;
26
+ }
27
+ }
28
+ }
29
+ async function assertArtifactAuthorityBoundary(path) {
30
+ const canonical = await projectedRealPath(path);
31
+ const segments = canonical.split(/[\\/]+/);
32
+ const workspaceIndex = segments.lastIndexOf(".taphound");
33
+ if (workspaceIndex < 0) {
34
+ return;
35
+ }
36
+ const namespace = segments[workspaceIndex + 1];
37
+ if (namespace === "build") {
38
+ return;
39
+ }
40
+ if (namespace === "local"
41
+ && segments[workspaceIndex + 2] !== undefined) {
42
+ return;
43
+ }
44
+ throw new Error(`Artifact path inside .taphound/ must stay under .taphound/build/ or .taphound/local/<id>/: ${path}`);
45
+ }
8
46
  function safePath(root, relativePath) {
9
47
  const candidate = resolve(root, relativePath);
10
48
  const fromRoot = relative(root, candidate);
@@ -65,6 +103,7 @@ class FileSystemArtifactSession {
65
103
  export class FileSystemArtifactStore {
66
104
  begin = async (baseDirectory, runDirectoryName) => {
67
105
  assertRunDirectoryName(runDirectoryName);
106
+ await assertArtifactAuthorityBoundary(baseDirectory);
68
107
  await mkdir(baseDirectory, { recursive: true });
69
108
  const temporaryDirectory = await mkdtemp(join(baseDirectory, `.${runDirectoryName}.tmp-`));
70
109
  await mkdir(join(temporaryDirectory, "steps"), { recursive: true });
@@ -0,0 +1,15 @@
1
+ import type { ContextDocumentWrite, ContextDocumentWriter } from "../../ports/context-document-writer.js";
2
+ export declare class FileSystemContextDocumentWriter implements ContextDocumentWriter {
3
+ readonly writeContextDocument: (input: {
4
+ projectRoot: string;
5
+ relativePath: string;
6
+ document: unknown;
7
+ }) => Promise<ContextDocumentWrite>;
8
+ readonly writeContextDocumentBatch: (input: {
9
+ projectRoot: string;
10
+ documents: readonly {
11
+ relativePath: string;
12
+ document: unknown;
13
+ }[];
14
+ }) => Promise<ContextDocumentWrite[]>;
15
+ }