swipium 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (387) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +295 -0
  3. package/dist/appMap/automationLink.js +74 -0
  4. package/dist/appMap/automationLink.js.map +1 -0
  5. package/dist/appMap/build.js +193 -0
  6. package/dist/appMap/build.js.map +1 -0
  7. package/dist/appMap/codeIndex.js +73 -0
  8. package/dist/appMap/codeIndex.js.map +1 -0
  9. package/dist/appMap/featureModel.js +83 -0
  10. package/dist/appMap/featureModel.js.map +1 -0
  11. package/dist/appMap/firstRunApply.js +131 -0
  12. package/dist/appMap/firstRunApply.js.map +1 -0
  13. package/dist/appMap/fsWalk.js +75 -0
  14. package/dist/appMap/fsWalk.js.map +1 -0
  15. package/dist/appMap/issues.js +227 -0
  16. package/dist/appMap/issues.js.map +1 -0
  17. package/dist/appMap/migrations.js +87 -0
  18. package/dist/appMap/migrations.js.map +1 -0
  19. package/dist/appMap/prelaunch.js +66 -0
  20. package/dist/appMap/prelaunch.js.map +1 -0
  21. package/dist/appMap/projectRegistry.js +70 -0
  22. package/dist/appMap/projectRegistry.js.map +1 -0
  23. package/dist/appMap/provenance.js +52 -0
  24. package/dist/appMap/provenance.js.map +1 -0
  25. package/dist/appMap/query.js +118 -0
  26. package/dist/appMap/query.js.map +1 -0
  27. package/dist/appMap/runtimeMerge.js +207 -0
  28. package/dist/appMap/runtimeMerge.js.map +1 -0
  29. package/dist/appMap/schema.js +65 -0
  30. package/dist/appMap/schema.js.map +1 -0
  31. package/dist/appMap/screenMatch.js +75 -0
  32. package/dist/appMap/screenMatch.js.map +1 -0
  33. package/dist/appMap/staticScan.js +614 -0
  34. package/dist/appMap/staticScan.js.map +1 -0
  35. package/dist/appMap/store.js +173 -0
  36. package/dist/appMap/store.js.map +1 -0
  37. package/dist/appMap/tsAstScan.js +184 -0
  38. package/dist/appMap/tsAstScan.js.map +1 -0
  39. package/dist/artifacts/bundletool.js +199 -0
  40. package/dist/artifacts/bundletool.js.map +1 -0
  41. package/dist/artifacts/resolve.js +388 -0
  42. package/dist/artifacts/resolve.js.map +1 -0
  43. package/dist/automationGen/appiumModel.js +129 -0
  44. package/dist/automationGen/appiumModel.js.map +1 -0
  45. package/dist/automationGen/ciEmitter.js +80 -0
  46. package/dist/automationGen/ciEmitter.js.map +1 -0
  47. package/dist/automationGen/jsEmitter.js +440 -0
  48. package/dist/automationGen/jsEmitter.js.map +1 -0
  49. package/dist/automationGen/packagePatch.js +56 -0
  50. package/dist/automationGen/packagePatch.js.map +1 -0
  51. package/dist/automationGen/projectProfile.js +317 -0
  52. package/dist/automationGen/projectProfile.js.map +1 -0
  53. package/dist/automationGen/pythonEmitter.js +386 -0
  54. package/dist/automationGen/pythonEmitter.js.map +1 -0
  55. package/dist/automationGen/readmeEmitter.js +66 -0
  56. package/dist/automationGen/readmeEmitter.js.map +1 -0
  57. package/dist/automationGen/suitePlan.js +94 -0
  58. package/dist/automationGen/suitePlan.js.map +1 -0
  59. package/dist/automationGen/validation.js +150 -0
  60. package/dist/automationGen/validation.js.map +1 -0
  61. package/dist/build/parseBuildLog.js +119 -0
  62. package/dist/build/parseBuildLog.js.map +1 -0
  63. package/dist/build/plan.js +150 -0
  64. package/dist/build/plan.js.map +1 -0
  65. package/dist/ci/preflight.js +85 -0
  66. package/dist/ci/preflight.js.map +1 -0
  67. package/dist/cli/init.js +105 -0
  68. package/dist/cli/init.js.map +1 -0
  69. package/dist/cli/scan.js +85 -0
  70. package/dist/cli/scan.js.map +1 -0
  71. package/dist/cli/suite.js +85 -0
  72. package/dist/cli/suite.js.map +1 -0
  73. package/dist/cli/verify.js +62 -0
  74. package/dist/cli/verify.js.map +1 -0
  75. package/dist/consent/consent.js +43 -0
  76. package/dist/consent/consent.js.map +1 -0
  77. package/dist/context/detect.js +156 -0
  78. package/dist/context/detect.js.map +1 -0
  79. package/dist/context/findApps.js +198 -0
  80. package/dist/context/findApps.js.map +1 -0
  81. package/dist/context/projectRoot.js +41 -0
  82. package/dist/context/projectRoot.js.map +1 -0
  83. package/dist/context/scan.js +167 -0
  84. package/dist/context/scan.js.map +1 -0
  85. package/dist/core/target.js +115 -0
  86. package/dist/core/target.js.map +1 -0
  87. package/dist/core/targetPlan.js +140 -0
  88. package/dist/core/targetPlan.js.map +1 -0
  89. package/dist/drivers/DirectDriver.js +228 -0
  90. package/dist/drivers/DirectDriver.js.map +1 -0
  91. package/dist/drivers/Driver.js +4 -0
  92. package/dist/drivers/Driver.js.map +1 -0
  93. package/dist/drivers/SimctlDriver.js +127 -0
  94. package/dist/drivers/SimctlDriver.js.map +1 -0
  95. package/dist/drivers/WdaDriver.js +250 -0
  96. package/dist/drivers/WdaDriver.js.map +1 -0
  97. package/dist/explore/candidates.js +93 -0
  98. package/dist/explore/candidates.js.map +1 -0
  99. package/dist/explore/graph.js +116 -0
  100. package/dist/explore/graph.js.map +1 -0
  101. package/dist/explore/memory.js +35 -0
  102. package/dist/explore/memory.js.map +1 -0
  103. package/dist/explore/planner.js +105 -0
  104. package/dist/explore/planner.js.map +1 -0
  105. package/dist/explore/policy.js +85 -0
  106. package/dist/explore/policy.js.map +1 -0
  107. package/dist/explore/runner.js +475 -0
  108. package/dist/explore/runner.js.map +1 -0
  109. package/dist/explore/signatures.js +35 -0
  110. package/dist/explore/signatures.js.map +1 -0
  111. package/dist/explore/suite.js +55 -0
  112. package/dist/explore/suite.js.map +1 -0
  113. package/dist/featureTesting/executionBootstrap.js +140 -0
  114. package/dist/featureTesting/executionBootstrap.js.map +1 -0
  115. package/dist/firstRun/authStateMachine.js +83 -0
  116. package/dist/firstRun/authStateMachine.js.map +1 -0
  117. package/dist/firstRun/classifyScreen.js +201 -0
  118. package/dist/firstRun/classifyScreen.js.map +1 -0
  119. package/dist/firstRun/firstRunPlanner.js +207 -0
  120. package/dist/firstRun/firstRunPlanner.js.map +1 -0
  121. package/dist/firstRun/firstRunRunner.js +331 -0
  122. package/dist/firstRun/firstRunRunner.js.map +1 -0
  123. package/dist/firstRun/generatedDataPolicy.js +215 -0
  124. package/dist/firstRun/generatedDataPolicy.js.map +1 -0
  125. package/dist/firstRun/inputPlanner.js +67 -0
  126. package/dist/firstRun/inputPlanner.js.map +1 -0
  127. package/dist/firstRun/onboardingStateMachine.js +46 -0
  128. package/dist/firstRun/onboardingStateMachine.js.map +1 -0
  129. package/dist/firstRun/paywallPolicy.js +54 -0
  130. package/dist/firstRun/paywallPolicy.js.map +1 -0
  131. package/dist/firstRun/types.js +7 -0
  132. package/dist/firstRun/types.js.map +1 -0
  133. package/dist/fixtures/catalog.js +131 -0
  134. package/dist/fixtures/catalog.js.map +1 -0
  135. package/dist/flows/discover.js +18 -0
  136. package/dist/flows/discover.js.map +1 -0
  137. package/dist/flows/generate.js +211 -0
  138. package/dist/flows/generate.js.map +1 -0
  139. package/dist/flows/lint.js +97 -0
  140. package/dist/flows/lint.js.map +1 -0
  141. package/dist/flows/pack.js +68 -0
  142. package/dist/flows/pack.js.map +1 -0
  143. package/dist/flows/run.js +738 -0
  144. package/dist/flows/run.js.map +1 -0
  145. package/dist/flows/schema.js +240 -0
  146. package/dist/flows/schema.js.map +1 -0
  147. package/dist/flows/seedExec.js +108 -0
  148. package/dist/flows/seedExec.js.map +1 -0
  149. package/dist/flows/templates.js +155 -0
  150. package/dist/flows/templates.js.map +1 -0
  151. package/dist/flows/waitGuards.js +12 -0
  152. package/dist/flows/waitGuards.js.map +1 -0
  153. package/dist/index.js +33 -0
  154. package/dist/index.js.map +1 -0
  155. package/dist/ios/signing.js +182 -0
  156. package/dist/ios/signing.js.map +1 -0
  157. package/dist/issues/classify.js +186 -0
  158. package/dist/issues/classify.js.map +1 -0
  159. package/dist/issues/fingerprint.js +130 -0
  160. package/dist/issues/fingerprint.js.map +1 -0
  161. package/dist/issues/index.js +240 -0
  162. package/dist/issues/index.js.map +1 -0
  163. package/dist/issues/metrics.js +155 -0
  164. package/dist/issues/metrics.js.map +1 -0
  165. package/dist/issues/recurrence.js +193 -0
  166. package/dist/issues/recurrence.js.map +1 -0
  167. package/dist/issues/report.js +110 -0
  168. package/dist/issues/report.js.map +1 -0
  169. package/dist/issues/reportBridge.js +119 -0
  170. package/dist/issues/reportBridge.js.map +1 -0
  171. package/dist/issues/schema.js +90 -0
  172. package/dist/issues/schema.js.map +1 -0
  173. package/dist/issues/sourceRevision.js +108 -0
  174. package/dist/issues/sourceRevision.js.map +1 -0
  175. package/dist/issues/store.js +155 -0
  176. package/dist/issues/store.js.map +1 -0
  177. package/dist/lib/android.js +278 -0
  178. package/dist/lib/android.js.map +1 -0
  179. package/dist/lib/commandTemplate.js +55 -0
  180. package/dist/lib/commandTemplate.js.map +1 -0
  181. package/dist/lib/coordSpace.js +23 -0
  182. package/dist/lib/coordSpace.js.map +1 -0
  183. package/dist/lib/device.js +87 -0
  184. package/dist/lib/device.js.map +1 -0
  185. package/dist/lib/gitignore.js +22 -0
  186. package/dist/lib/gitignore.js.map +1 -0
  187. package/dist/lib/image.js +97 -0
  188. package/dist/lib/image.js.map +1 -0
  189. package/dist/lib/logger.js +8 -0
  190. package/dist/lib/logger.js.map +1 -0
  191. package/dist/lib/metroState.js +45 -0
  192. package/dist/lib/metroState.js.map +1 -0
  193. package/dist/lib/needsInput.js +201 -0
  194. package/dist/lib/needsInput.js.map +1 -0
  195. package/dist/lib/png.js +103 -0
  196. package/dist/lib/png.js.map +1 -0
  197. package/dist/lib/redact.js +21 -0
  198. package/dist/lib/redact.js.map +1 -0
  199. package/dist/lib/result.js +87 -0
  200. package/dist/lib/result.js.map +1 -0
  201. package/dist/lib/schemaHash.js +65 -0
  202. package/dist/lib/schemaHash.js.map +1 -0
  203. package/dist/lib/sensitive.js +13 -0
  204. package/dist/lib/sensitive.js.map +1 -0
  205. package/dist/lib/simctl.js +149 -0
  206. package/dist/lib/simctl.js.map +1 -0
  207. package/dist/lib/spawn.js +144 -0
  208. package/dist/lib/spawn.js.map +1 -0
  209. package/dist/lib/wda.js +347 -0
  210. package/dist/lib/wda.js.map +1 -0
  211. package/dist/lib/wdaConfig.js +69 -0
  212. package/dist/lib/wdaConfig.js.map +1 -0
  213. package/dist/lib/wdaTune.js +58 -0
  214. package/dist/lib/wdaTune.js.map +1 -0
  215. package/dist/oracle/auth.js +22 -0
  216. package/dist/oracle/auth.js.map +1 -0
  217. package/dist/oracle/failures.js +193 -0
  218. package/dist/oracle/failures.js.map +1 -0
  219. package/dist/oracle/health.js +123 -0
  220. package/dist/oracle/health.js.map +1 -0
  221. package/dist/oracle/record.js +36 -0
  222. package/dist/oracle/record.js.map +1 -0
  223. package/dist/orchestration/envelope.js +52 -0
  224. package/dist/orchestration/envelope.js.map +1 -0
  225. package/dist/orchestration/goal.js +98 -0
  226. package/dist/orchestration/goal.js.map +1 -0
  227. package/dist/plan/plan.js +124 -0
  228. package/dist/plan/plan.js.map +1 -0
  229. package/dist/prompts/index.js +63 -0
  230. package/dist/prompts/index.js.map +1 -0
  231. package/dist/report/artifactManifest.js +44 -0
  232. package/dist/report/artifactManifest.js.map +1 -0
  233. package/dist/report/coverage.js +60 -0
  234. package/dist/report/coverage.js.map +1 -0
  235. package/dist/report/evidence.js +84 -0
  236. package/dist/report/evidence.js.map +1 -0
  237. package/dist/report/export.js +447 -0
  238. package/dist/report/export.js.map +1 -0
  239. package/dist/report/flake.js +61 -0
  240. package/dist/report/flake.js.map +1 -0
  241. package/dist/report/history.js +564 -0
  242. package/dist/report/history.js.map +1 -0
  243. package/dist/report/policy.js +98 -0
  244. package/dist/report/policy.js.map +1 -0
  245. package/dist/report/qaLevel.js +96 -0
  246. package/dist/report/qaLevel.js.map +1 -0
  247. package/dist/report/readiness.js +213 -0
  248. package/dist/report/readiness.js.map +1 -0
  249. package/dist/report/summary.js +42 -0
  250. package/dist/report/summary.js.map +1 -0
  251. package/dist/report/testCatalog.js +93 -0
  252. package/dist/report/testCatalog.js.map +1 -0
  253. package/dist/report/timing.js +37 -0
  254. package/dist/report/timing.js.map +1 -0
  255. package/dist/server.js +180 -0
  256. package/dist/server.js.map +1 -0
  257. package/dist/services/automationGenerate.js +60 -0
  258. package/dist/services/automationGenerate.js.map +1 -0
  259. package/dist/services/build.js +66 -0
  260. package/dist/services/build.js.map +1 -0
  261. package/dist/services/preflight.js +51 -0
  262. package/dist/services/preflight.js.map +1 -0
  263. package/dist/services/prepareAndroid.js +183 -0
  264. package/dist/services/prepareAndroid.js.map +1 -0
  265. package/dist/services/prepareIos.js +162 -0
  266. package/dist/services/prepareIos.js.map +1 -0
  267. package/dist/services/report.js +537 -0
  268. package/dist/services/report.js.map +1 -0
  269. package/dist/services/smoke.js +120 -0
  270. package/dist/services/smoke.js.map +1 -0
  271. package/dist/services/suiteGenerate.js +123 -0
  272. package/dist/services/suiteGenerate.js.map +1 -0
  273. package/dist/services/testSuiteKnowledge.js +150 -0
  274. package/dist/services/testSuiteKnowledge.js.map +1 -0
  275. package/dist/session/attach.js +47 -0
  276. package/dist/session/attach.js.map +1 -0
  277. package/dist/session/progress.js +56 -0
  278. package/dist/session/progress.js.map +1 -0
  279. package/dist/session/store.js +329 -0
  280. package/dist/session/store.js.map +1 -0
  281. package/dist/snapshot/overlays.js +132 -0
  282. package/dist/snapshot/overlays.js.map +1 -0
  283. package/dist/snapshot/parse.js +204 -0
  284. package/dist/snapshot/parse.js.map +1 -0
  285. package/dist/snapshot/present.js +12 -0
  286. package/dist/snapshot/present.js.map +1 -0
  287. package/dist/snapshot/settle.js +36 -0
  288. package/dist/snapshot/settle.js.map +1 -0
  289. package/dist/state/profile.js +318 -0
  290. package/dist/state/profile.js.map +1 -0
  291. package/dist/suite/compile.js +176 -0
  292. package/dist/suite/compile.js.map +1 -0
  293. package/dist/suite/lint.js +45 -0
  294. package/dist/suite/lint.js.map +1 -0
  295. package/dist/suite/pom.js +285 -0
  296. package/dist/suite/pom.js.map +1 -0
  297. package/dist/suite/testcase.js +87 -0
  298. package/dist/suite/testcase.js.map +1 -0
  299. package/dist/testSuite/generator.js +222 -0
  300. package/dist/testSuite/generator.js.map +1 -0
  301. package/dist/testSuite/history.js +44 -0
  302. package/dist/testSuite/history.js.map +1 -0
  303. package/dist/testSuite/issueLinks.js +96 -0
  304. package/dist/testSuite/issueLinks.js.map +1 -0
  305. package/dist/testSuite/merge.js +190 -0
  306. package/dist/testSuite/merge.js.map +1 -0
  307. package/dist/testSuite/schema.js +86 -0
  308. package/dist/testSuite/schema.js.map +1 -0
  309. package/dist/testSuite/store.js +116 -0
  310. package/dist/testSuite/store.js.map +1 -0
  311. package/dist/testSuite/traceability.js +55 -0
  312. package/dist/testSuite/traceability.js.map +1 -0
  313. package/dist/tools/act.js +457 -0
  314. package/dist/tools/act.js.map +1 -0
  315. package/dist/tools/agent.js +225 -0
  316. package/dist/tools/agent.js.map +1 -0
  317. package/dist/tools/appMap.js +317 -0
  318. package/dist/tools/appMap.js.map +1 -0
  319. package/dist/tools/assertVisual.js +74 -0
  320. package/dist/tools/assertVisual.js.map +1 -0
  321. package/dist/tools/automationGenerate.js +394 -0
  322. package/dist/tools/automationGenerate.js.map +1 -0
  323. package/dist/tools/capabilities.js +114 -0
  324. package/dist/tools/capabilities.js.map +1 -0
  325. package/dist/tools/clearOverlay.js +150 -0
  326. package/dist/tools/clearOverlay.js.map +1 -0
  327. package/dist/tools/detectContext.js +41 -0
  328. package/dist/tools/detectContext.js.map +1 -0
  329. package/dist/tools/doctor.js +116 -0
  330. package/dist/tools/doctor.js.map +1 -0
  331. package/dist/tools/explore.js +289 -0
  332. package/dist/tools/explore.js.map +1 -0
  333. package/dist/tools/firstRun.js +136 -0
  334. package/dist/tools/firstRun.js.map +1 -0
  335. package/dist/tools/flow.js +331 -0
  336. package/dist/tools/flow.js.map +1 -0
  337. package/dist/tools/flowGenerate.js +55 -0
  338. package/dist/tools/flowGenerate.js.map +1 -0
  339. package/dist/tools/getArtifact.js +39 -0
  340. package/dist/tools/getArtifact.js.map +1 -0
  341. package/dist/tools/health.js +41 -0
  342. package/dist/tools/health.js.map +1 -0
  343. package/dist/tools/ios.js +492 -0
  344. package/dist/tools/ios.js.map +1 -0
  345. package/dist/tools/jobs.js +20 -0
  346. package/dist/tools/jobs.js.map +1 -0
  347. package/dist/tools/network.js +50 -0
  348. package/dist/tools/network.js.map +1 -0
  349. package/dist/tools/note.js +69 -0
  350. package/dist/tools/note.js.map +1 -0
  351. package/dist/tools/plan.js +49 -0
  352. package/dist/tools/plan.js.map +1 -0
  353. package/dist/tools/prepareIosTarget.js +99 -0
  354. package/dist/tools/prepareIosTarget.js.map +1 -0
  355. package/dist/tools/prepareTarget.js +257 -0
  356. package/dist/tools/prepareTarget.js.map +1 -0
  357. package/dist/tools/report.js +42 -0
  358. package/dist/tools/report.js.map +1 -0
  359. package/dist/tools/screenRecord.js +24 -0
  360. package/dist/tools/screenRecord.js.map +1 -0
  361. package/dist/tools/screenshot.js +63 -0
  362. package/dist/tools/screenshot.js.map +1 -0
  363. package/dist/tools/smoke.js +39 -0
  364. package/dist/tools/smoke.js.map +1 -0
  365. package/dist/tools/snapshot.js +129 -0
  366. package/dist/tools/snapshot.js.map +1 -0
  367. package/dist/tools/startSession.js +169 -0
  368. package/dist/tools/startSession.js.map +1 -0
  369. package/dist/tools/state.js +87 -0
  370. package/dist/tools/state.js.map +1 -0
  371. package/dist/tools/suite.js +412 -0
  372. package/dist/tools/suite.js.map +1 -0
  373. package/dist/tools/testThis.js +774 -0
  374. package/dist/tools/testThis.js.map +1 -0
  375. package/dist/tools/wda.js +477 -0
  376. package/dist/tools/wda.js.map +1 -0
  377. package/dist/version.js +62 -0
  378. package/dist/version.js.map +1 -0
  379. package/dist/visual/ocr.js +72 -0
  380. package/dist/visual/ocr.js.map +1 -0
  381. package/dist/visual/provider.js +75 -0
  382. package/dist/visual/provider.js.map +1 -0
  383. package/docs/README.md +19 -0
  384. package/docs/assets/swipium-lockup-ink-on-light.png +0 -0
  385. package/docs/mcp-server.md +135 -0
  386. package/docs/tools.md +130 -0
  387. package/package.json +70 -0
@@ -0,0 +1,198 @@
1
+ // Mobile app discovery for low-context roots (Developer 1, roadmap §3.1 / §3.7). When a developer
2
+ // or agent points Swipium at a monorepo root, a parent directory, or any folder that is not itself
3
+ // a mobile app, this locates the candidate app(s) so `qa_test_this` can either proceed with the one
4
+ // strong candidate (recording a workaround) or ask ONE disambiguation question — instead of failing
5
+ // with NOT_MOBILE_PROJECT at a root that does in fact contain an app.
6
+ //
7
+ // Pure + bounded + side-effect free: it scans a fixed, shallow set of conventional locations and
8
+ // scores each candidate from cheap file markers. It never recurses into node_modules/build output.
9
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
10
+ import { join, relative, basename } from 'node:path';
11
+ import { detectFramework } from './detect.js';
12
+ const SKIP_DIRS = new Set([
13
+ 'node_modules', '.git', '.swipium', '.expo', '.gradle', '.idea', '.vscode',
14
+ 'build', 'dist', 'out', 'DerivedData', 'Pods', 'vendor', 'coverage', '.next', '.turbo',
15
+ ]);
16
+ /** Conventional parents whose immediate subdirectories may each hold a mobile app. */
17
+ const GLOB_PARENTS = ['apps', 'packages', 'mobile', 'examples', 'example', 'clients'];
18
+ /** Conventional direct app locations (relative to root). */
19
+ const DIRECT_DIRS = ['', 'ios', 'android', 'app', 'src-app'];
20
+ function readJson(p) {
21
+ try {
22
+ return JSON.parse(readFileSync(p, 'utf8'));
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ }
28
+ function safeMtime(p) {
29
+ try {
30
+ return statSync(p).mtimeMs;
31
+ }
32
+ catch {
33
+ return 0;
34
+ }
35
+ }
36
+ function listDirs(dir) {
37
+ try {
38
+ return readdirSync(dir, { withFileTypes: true })
39
+ .filter((e) => e.isDirectory() && !e.name.startsWith('.') && !SKIP_DIRS.has(e.name))
40
+ .map((e) => e.name);
41
+ }
42
+ catch {
43
+ return [];
44
+ }
45
+ }
46
+ /** Light, bounded check for a usable app config in `dir` (mirrors the framework signals). */
47
+ function appConfigPresent(dir, fw) {
48
+ switch (fw) {
49
+ case 'expo':
50
+ return ['app.json', 'app.config.js', 'app.config.ts'].some((f) => existsSync(join(dir, f)));
51
+ case 'bare-react-native':
52
+ return existsSync(join(dir, 'package.json'));
53
+ case 'native-android':
54
+ return ['app/build.gradle', 'app/build.gradle.kts', 'settings.gradle', 'settings.gradle.kts'].some((f) => existsSync(join(dir, f)));
55
+ case 'native-ios':
56
+ return readdirSafe(dir).some((f) => f.endsWith('.xcodeproj') || f.endsWith('.xcworkspace')) || existsSync(join(dir, 'Package.swift'));
57
+ case 'flutter':
58
+ return existsSync(join(dir, 'pubspec.yaml'));
59
+ default:
60
+ return false;
61
+ }
62
+ }
63
+ function readdirSafe(dir) {
64
+ try {
65
+ return readdirSync(dir);
66
+ }
67
+ catch {
68
+ return [];
69
+ }
70
+ }
71
+ /** Best-effort app id from cheap config markers (no APK badging — that is too heavy here). */
72
+ function lightAppId(dir, fw) {
73
+ if (fw === 'expo') {
74
+ const cfg = readJson(join(dir, 'app.json'));
75
+ const expo = (cfg?.expo ?? cfg);
76
+ const id = expo?.ios?.bundleIdentifier ?? expo?.android?.package;
77
+ if (typeof id === 'string')
78
+ return id;
79
+ }
80
+ for (const g of ['app/build.gradle', 'app/build.gradle.kts', 'android/app/build.gradle', 'android/app/build.gradle.kts']) {
81
+ const p = join(dir, g);
82
+ if (!existsSync(p))
83
+ continue;
84
+ try {
85
+ const m = readFileSync(p, 'utf8').match(/applicationId\s*[=\s]\s*["']([^"']+)["']/);
86
+ if (m)
87
+ return m[1];
88
+ }
89
+ catch {
90
+ /* unreadable */
91
+ }
92
+ }
93
+ return null;
94
+ }
95
+ /** Bounded heuristic for "an installable artifact probably exists here" (existence-only, no walk). */
96
+ function artifactPresent(dir) {
97
+ const candidates = [
98
+ join(dir, 'android', 'app', 'build', 'outputs', 'apk'),
99
+ join(dir, 'app', 'build', 'outputs', 'apk'),
100
+ join(dir, 'build', 'app', 'outputs', 'flutter-apk'),
101
+ join(dir, 'ios', 'build'),
102
+ join(dir, 'apps', 'android'),
103
+ join(dir, 'apps', 'ios'),
104
+ ];
105
+ if (candidates.some((p) => existsSync(p)))
106
+ return true;
107
+ // a top-level .apk/.ipa/.app dropped in the dir
108
+ return readdirSafe(dir).some((f) => /\.(apk|aab|ipa)$/i.test(f) || f.endsWith('.app'));
109
+ }
110
+ const LOCKFILES = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'Podfile.lock', 'Gemfile.lock', 'pubspec.lock'];
111
+ const RECENT_MS = 1000 * 60 * 60 * 24 * 60; // 60 days
112
+ /** Score one directory as a mobile-app candidate. Returns null if no framework is recognized. */
113
+ function scoreDir(root, dir, nowMs) {
114
+ const fw = detectFramework(dir);
115
+ if (fw === 'unknown')
116
+ return null;
117
+ const reasons = [`framework=${fw}`];
118
+ let score = 50; // a recognized framework is the dominant signal
119
+ const appConfig = appConfigPresent(dir, fw);
120
+ if (appConfig) {
121
+ score += 15;
122
+ reasons.push('app config present');
123
+ }
124
+ const hasArtifact = artifactPresent(dir);
125
+ if (hasArtifact) {
126
+ score += 20;
127
+ reasons.push('build artifact present');
128
+ }
129
+ const appId = lightAppId(dir, fw);
130
+ if (appId) {
131
+ score += 10;
132
+ reasons.push(`appId=${appId}`);
133
+ }
134
+ if (LOCKFILES.some((f) => existsSync(join(dir, f)))) {
135
+ score += 5;
136
+ reasons.push('lockfile present');
137
+ }
138
+ // recency: prefer the app the developer is actually working on
139
+ const mtimeMs = Math.max(safeMtime(join(dir, 'package.json')), safeMtime(join(dir, 'app.json')), safeMtime(join(dir, 'pubspec.yaml')), safeMtime(dir));
140
+ if (mtimeMs && nowMs - mtimeMs < RECENT_MS) {
141
+ score += 10;
142
+ reasons.push('recently modified');
143
+ }
144
+ return { path: dir, rel: relative(root, dir) || '.', framework: fw, score, reasons, hasArtifact, appConfig, appId, mtimeMs };
145
+ }
146
+ /**
147
+ * Discover mobile-app candidates at or under `root`. Bounded: root + a curated set of conventional
148
+ * locations + the immediate children of apps/packages/mobile/examples. Best-first, deduped by path.
149
+ *
150
+ * `nowMs` is injected (default Date.now) so callers/tests stay deterministic.
151
+ */
152
+ export function findMobileApps(root, nowMs = Date.now()) {
153
+ const searched = new Set();
154
+ const candidates = new Map();
155
+ const consider = (dir) => {
156
+ if (!dir || searched.has(dir))
157
+ return;
158
+ if (!existsSync(dir))
159
+ return;
160
+ try {
161
+ if (!statSync(dir).isDirectory())
162
+ return;
163
+ }
164
+ catch {
165
+ return;
166
+ }
167
+ searched.add(dir);
168
+ const c = scoreDir(root, dir, nowMs);
169
+ if (c && !candidates.has(c.path))
170
+ candidates.set(c.path, c);
171
+ };
172
+ // 1. direct conventional locations
173
+ for (const rel of DIRECT_DIRS)
174
+ consider(rel ? join(root, rel) : root);
175
+ // 2. immediate children of conventional parents (apps/*, packages/*, mobile/*, examples/*)
176
+ for (const parent of GLOB_PARENTS) {
177
+ const parentDir = join(root, parent);
178
+ for (const name of listDirs(parentDir))
179
+ consider(join(parentDir, name));
180
+ }
181
+ const ranked = [...candidates.values()].sort((a, b) => b.score - a.score || b.mtimeMs - a.mtimeMs || basename(a.path).localeCompare(basename(b.path)));
182
+ return { root, candidates: ranked, searchedLocations: [...searched].sort() };
183
+ }
184
+ /**
185
+ * Decide what `qa_test_this` should do with the discovery result.
186
+ * - 'adopt' → exactly one recognized app; proceed with it.
187
+ * - 'disambiguate' → ≥2 recognized apps; ask one monorepo_target question with ranked choices.
188
+ * - 'none' → no recognized app anywhere searched.
189
+ */
190
+ export function classifyDiscovery(result) {
191
+ const choices = result.candidates;
192
+ if (choices.length === 0)
193
+ return { decision: 'none', choices };
194
+ if (choices.length === 1)
195
+ return { decision: 'adopt', chosen: choices[0], choices };
196
+ return { decision: 'disambiguate', choices };
197
+ }
198
+ //# sourceMappingURL=findApps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findApps.js","sourceRoot":"","sources":["../../src/context/findApps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ // Resolve the project root WITHOUT trusting the server cwd (DESIGN §2, review #1).
2
+ // Order: explicit arg → MCP roots (the proper mechanism) → needs-input.
3
+ import { existsSync, statSync } from 'node:fs';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { isAbsolute } from 'node:path';
6
+ export async function resolveProjectRoot(server, explicit) {
7
+ // 1) explicit arg wins (must be an absolute, existing directory)
8
+ if (explicit && explicit.trim()) {
9
+ const p = explicit.trim();
10
+ if (!isAbsolute(p)) {
11
+ return { source: 'none', hint: `projectRoot must be an absolute path, got "${p}".` };
12
+ }
13
+ if (existsSync(p) && statSync(p).isDirectory()) {
14
+ return { root: p, source: 'arg' };
15
+ }
16
+ return { source: 'none', hint: `Path not found or not a directory: ${p}` };
17
+ }
18
+ // 2) MCP roots (workspace the client exposed)
19
+ try {
20
+ const caps = server.server.getClientCapabilities?.();
21
+ if (caps?.roots) {
22
+ const res = await server.server.listRoots();
23
+ const fileRoot = res.roots?.find((r) => typeof r.uri === 'string' && r.uri.startsWith('file://'));
24
+ if (fileRoot) {
25
+ const p = fileURLToPath(fileRoot.uri);
26
+ if (existsSync(p) && statSync(p).isDirectory()) {
27
+ return { root: p, source: 'mcp-roots' };
28
+ }
29
+ }
30
+ }
31
+ }
32
+ catch {
33
+ // client doesn't support roots, or the call failed — fall through to needs-input
34
+ }
35
+ // 3) ask
36
+ return {
37
+ source: 'none',
38
+ hint: 'No workspace root exposed by the client. Call qa_start_session with projectRoot="/absolute/path/to/app".',
39
+ };
40
+ }
41
+ //# sourceMappingURL=projectRoot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectRoot.js","sourceRoot":"","sources":["../../src/context/projectRoot.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,wEAAwE;AAExE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AASvC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAiB,EAAE,QAAiB;IAC3E,iEAAiE;IACjE,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;YACnB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,8CAA8C,CAAC,IAAI,EAAE,CAAC;QACvF,CAAC;QACD,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YAC/C,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACpC,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,sCAAsC,CAAC,EAAE,EAAE,CAAC;IAC7E,CAAC;IAED,8CAA8C;IAC9C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE,EAAE,CAAC;QACrD,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;YAChB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YAC5C,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;YAClG,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACtC,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;oBAC/C,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;gBAC1C,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,iFAAiF;IACnF,CAAC;IAED,SAAS;IACT,OAAO;QACL,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,0GAA0G;KACjH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,167 @@
1
+ // scanProject (PHASE3-PLAN §3.1) — the data behind `swipium scan` and `.swipium/config.json`.
2
+ // Extends detectContext() with the higher-level signals a developer needs to answer
3
+ // "what can I test right now?": app id, whether Metro is likely needed, whether a fresh
4
+ // start is safe, whether auth is likely, install state (best-effort), a recommended budget
5
+ // profile, and an overall ready | partial | blocked verdict. Best-effort and side-effect free.
6
+ import { createHash } from 'node:crypto';
7
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { detectContext } from './detect.js';
10
+ import { apkPackageId, adbDevices } from '../lib/android.js';
11
+ import { run } from '../lib/spawn.js';
12
+ import { bundleIdFromApp } from '../artifacts/resolve.js';
13
+ const AUTH_DEP = /(^|[/@])(auth|next-auth|expo-auth-session|react-native-app-auth|amazon-cognito|@clerk|supabase|firebase)/i;
14
+ const HARD_BLOCKER = /adb not found|could not identify|no online device|no prebuilt (apk|app artifact|android apk|ios app artifact)|no build artifact/i;
15
+ function isDebugRN(fw) {
16
+ return fw === 'expo' || fw === 'bare-react-native';
17
+ }
18
+ function readJson(p) {
19
+ try {
20
+ return JSON.parse(readFileSync(p, 'utf8'));
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ /** Best-effort appId from app config files (Expo / Android gradle) before falling back to the APK. */
27
+ function appIdFromSource(root, fw) {
28
+ if (fw === 'expo') {
29
+ const cfg = readJson(join(root, 'app.json'));
30
+ const expo = (cfg?.expo ?? cfg);
31
+ const pkg = expo?.ios?.bundleIdentifier ?? expo?.android?.package;
32
+ if (typeof pkg === 'string')
33
+ return { appId: pkg, source: 'app.json' };
34
+ }
35
+ // gradle applicationId (best-effort regex over the app module's build.gradle[.kts])
36
+ for (const g of ['app/build.gradle', 'app/build.gradle.kts', 'android/app/build.gradle', 'android/app/build.gradle.kts']) {
37
+ const p = join(root, g);
38
+ if (!existsSync(p))
39
+ continue;
40
+ const m = readFileSync(p, 'utf8').match(/applicationId\s*[=\s]\s*["']([^"']+)["']/);
41
+ if (m)
42
+ return { appId: m[1], source: 'gradle' };
43
+ }
44
+ return { appId: null, source: null };
45
+ }
46
+ function detectAuth(root) {
47
+ const pkg = readJson(join(root, 'package.json'));
48
+ const deps = pkg ? { ...pkg.dependencies, ...pkg.devDependencies } : {};
49
+ const signals = Object.keys(deps).filter((d) => AUTH_DEP.test(d));
50
+ return { likely: signals.length > 0, signals };
51
+ }
52
+ /** Is `appId` installed on `serial`? Best-effort; short timeout. */
53
+ async function pmInstalled(serial, appId) {
54
+ try {
55
+ const r = await run('adb', ['-s', serial, 'shell', 'pm', 'list', 'packages', appId], { timeoutMs: 6000 });
56
+ return r.stdout.includes(`package:${appId}`);
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ function hashArtifact(path) {
63
+ try {
64
+ const h = createHash('sha256');
65
+ const st = statSync(path);
66
+ if (st.isFile()) {
67
+ h.update(readFileSync(path));
68
+ }
69
+ else if (st.isDirectory()) {
70
+ const walk = (dir, prefix = '') => {
71
+ for (const name of readdirSync(dir).sort()) {
72
+ const full = join(dir, name);
73
+ const rel = prefix ? `${prefix}/${name}` : name;
74
+ const child = statSync(full);
75
+ if (child.isDirectory())
76
+ walk(full, rel);
77
+ else if (child.isFile()) {
78
+ h.update(rel);
79
+ h.update('\0');
80
+ h.update(readFileSync(full));
81
+ }
82
+ }
83
+ };
84
+ walk(path);
85
+ }
86
+ else {
87
+ return null;
88
+ }
89
+ return `sha256:${h.digest('hex')}`;
90
+ }
91
+ catch {
92
+ return null;
93
+ }
94
+ }
95
+ function artifactHashes(ctx) {
96
+ const refs = [
97
+ ...ctx.artifacts.apks.map((path) => ({ path, type: 'apk' })),
98
+ ...ctx.artifacts.ipas.map((path) => ({ path, type: 'ipa' })),
99
+ ...ctx.artifacts.appBundles.map((path) => ({ path, type: 'app' })),
100
+ ];
101
+ return refs.flatMap((ref) => {
102
+ const artifactHash = hashArtifact(ref.path);
103
+ return artifactHash ? [{ ...ref, artifactHash }] : [];
104
+ });
105
+ }
106
+ export async function scanProject(root) {
107
+ const ctx = await detectContext(root);
108
+ // app id: source config first (authoritative for the project), else the prebuilt APK.
109
+ let { appId, source: appIdSource } = appIdFromSource(root, ctx.framework);
110
+ if (!appId && ctx.artifacts.apks[0]) {
111
+ const fromApk = await apkPackageId(ctx.artifacts.apks[0]);
112
+ if (fromApk) {
113
+ appId = fromApk;
114
+ appIdSource = 'apk-badging';
115
+ }
116
+ }
117
+ if (!appId && ctx.artifacts.appBundles[0]) {
118
+ const fromApp = bundleIdFromApp(ctx.artifacts.appBundles[0]);
119
+ if (fromApp) {
120
+ appId = fromApp;
121
+ appIdSource = 'Info.plist';
122
+ }
123
+ }
124
+ const { likely: likelyAuth, signals: authSignals } = detectAuth(root);
125
+ const metroNeed = isDebugRN(ctx.framework) ? 'likely' : 'no';
126
+ const freshStartSafe = !isDebugRN(ctx.framework);
127
+ // install state: only meaningful with exactly one online device + a known appId.
128
+ let installed = null;
129
+ if (appId && ctx.devices.androidOnline.length === 1) {
130
+ installed = await pmInstalled(ctx.devices.androidOnline[0], appId);
131
+ }
132
+ else if (appId && ctx.toolchain.adb && ctx.devices.androidOnline.length === 0) {
133
+ // adbDevices may have changed since detect; one cheap recheck before giving up
134
+ const live = await adbDevices();
135
+ if (live.length === 1)
136
+ installed = await pmInstalled(live[0], appId);
137
+ }
138
+ // missing items: detect blockers, but a known-installed app removes the "no APK" blocker.
139
+ const missing = ctx.blockers.filter((b) => !(installed === true && /no prebuilt apk/i.test(b)));
140
+ const hard = missing.filter((b) => HARD_BLOCKER.test(b) && !(installed === true && /no prebuilt apk/i.test(b)));
141
+ const readiness = hard.length ? 'blocked' : missing.length ? 'partial' : 'ready';
142
+ const needsBootOrInstall = (ctx.devices.androidOnline.length === 0 && ctx.devices.avds.length > 0) || (installed === false && ctx.artifacts.apks.length > 0);
143
+ const recommendedProfile = needsBootOrInstall ? 'install_smoke' : likelyAuth ? 'login_smoke' : 'guardrail';
144
+ return {
145
+ schemaVersion: 1,
146
+ generatedAt: new Date().toISOString(),
147
+ projectRoot: root,
148
+ framework: ctx.framework,
149
+ monorepo: ctx.monorepo,
150
+ appId,
151
+ appIdSource,
152
+ apks: ctx.artifacts.apks,
153
+ ipas: ctx.artifacts.ipas,
154
+ appBundles: ctx.artifacts.appBundles,
155
+ artifactHashes: artifactHashes(ctx),
156
+ metroNeed,
157
+ freshStartSafe,
158
+ likelyAuth,
159
+ authSignals,
160
+ installed,
161
+ recommendedProfile,
162
+ readiness,
163
+ missing,
164
+ devices: ctx.devices,
165
+ };
166
+ }
167
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/context/scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ // Shared target resolution (PHASE3-PLAN §2.3 core extraction). Both qa_act and the flow
2
+ // runner resolve a target the SAME way — extracting it here is the "one IR, no drift" rule:
3
+ // a ref | selector | coords becomes a tappable point with the resolved @eN (for obstruction
4
+ // checks) and secure-field awareness (for redaction). Selector resolution re-snapshots and
5
+ // updates session.lastSnapshot, exactly as before.
6
+ import { parseSnapshot, signature } from '../snapshot/parse.js';
7
+ import { isSecureNode } from '../lib/redact.js';
8
+ import { createHash } from 'node:crypto';
9
+ export function resourceIdAliases(id) {
10
+ const raw = id.trim();
11
+ const short = raw.includes('/') ? raw.slice(raw.lastIndexOf('/') + 1) : raw.replace(/^id\//, '');
12
+ const pkgLess = raw.includes(':id/') ? raw.slice(raw.indexOf(':id/') + 4) : short;
13
+ return new Set([raw, short, `id/${short}`, pkgLess].filter(Boolean));
14
+ }
15
+ export function resourceIdMatches(candidateShort, candidateFull, target) {
16
+ const wanted = resourceIdAliases(target);
17
+ const cands = [candidateShort, candidateFull].filter((x) => !!x);
18
+ return cands.some((c) => [...resourceIdAliases(c)].some((alias) => wanted.has(alias)));
19
+ }
20
+ export function boundsBucket(bounds) {
21
+ if (!bounds)
22
+ return undefined;
23
+ const bucket = (n) => Math.floor(n / 50) * 50;
24
+ return `${bucket(bounds[0])},${bucket(bounds[1])},${bucket(bounds[2])},${bucket(bounds[3])}`;
25
+ }
26
+ export function center(b) {
27
+ return { x: Math.round((b[0] + b[2]) / 2), y: Math.round((b[1] + b[3]) / 2) };
28
+ }
29
+ export function setsEqual(a, b) {
30
+ if (a.size !== b.size)
31
+ return false;
32
+ for (const x of a)
33
+ if (!b.has(x))
34
+ return false;
35
+ return true;
36
+ }
37
+ function screenSignatureFor(parsed) {
38
+ const sigs = parsed.elements.map(signature);
39
+ if (!sigs.length)
40
+ return undefined;
41
+ return createHash('sha1').update(sigs.sort().join('\n')).digest('hex').slice(0, 16);
42
+ }
43
+ function norm(value) {
44
+ return (value ?? '').trim().toLowerCase();
45
+ }
46
+ function applyResourceIdHints(candidates, target, rawFor) {
47
+ let narrowed = candidates;
48
+ const tryNarrow = (fn) => {
49
+ const next = narrowed.filter(fn);
50
+ if (next.length)
51
+ narrowed = next;
52
+ };
53
+ if (target.packageName) {
54
+ tryNarrow((candidate) => rawFor(candidate)?.id?.startsWith(`${target.packageName}:id/`) === true);
55
+ }
56
+ if (target.className) {
57
+ const wanted = norm(target.className);
58
+ tryNarrow((candidate) => {
59
+ const raw = rawFor(candidate);
60
+ const cls = norm(raw?.cls);
61
+ return cls === wanted || cls.endsWith(`.${wanted}`) || norm(candidate.role) === wanted;
62
+ });
63
+ }
64
+ if (target.textHint) {
65
+ const wanted = norm(target.textHint);
66
+ tryNarrow((candidate) => {
67
+ const raw = rawFor(candidate);
68
+ return [candidate.text, candidate.label, raw?.text, raw?.desc].some((value) => norm(value) === wanted);
69
+ });
70
+ }
71
+ if (target.boundsHint) {
72
+ tryNarrow((candidate) => boundsBucket(rawFor(candidate)?.bounds ?? candidate.bounds) === target.boundsHint);
73
+ }
74
+ return narrowed;
75
+ }
76
+ export async function resolveTarget(session, target) {
77
+ if (target?.x != null && target?.y != null)
78
+ return { x: target.x, y: target.y, via: 'coords' };
79
+ if (target?.ref) {
80
+ const node = session.lastSnapshot?.fullByRef.get(target.ref);
81
+ if (!node)
82
+ return { error: `No ${target.ref} in the latest snapshot — run qa_snapshot first (refs invalidate after navigation).` };
83
+ return { ...center(node.bounds), via: target.ref, ref: target.ref, textLen: node.text.length, secure: isSecureNode(node) };
84
+ }
85
+ if (target?.text || target?.id) {
86
+ const parsed = parseSnapshot(await session.driver.dumpXml());
87
+ session.lastSnapshot = { fullByRef: parsed.fullByRef, signatures: new Set(parsed.elements.map(signature)), allNodes: parsed.allNodes };
88
+ const screenMismatch = target.screenSignature && screenSignatureFor(parsed) !== target.screenSignature;
89
+ const t = target.text?.toLowerCase();
90
+ const idCands = target.id
91
+ ? parsed.elements.filter((e) => {
92
+ const raw = e.ref ? parsed.fullByRef.get(e.ref) : undefined;
93
+ return resourceIdMatches(e.id, raw?.id, target.id);
94
+ })
95
+ : [];
96
+ const textCands = !target.id && t
97
+ ? parsed.elements.filter((e) => e.text?.toLowerCase().includes(t) || e.label?.toLowerCase().includes(t))
98
+ : [];
99
+ let cands = target.id ? idCands : textCands;
100
+ if (target.id && cands.length > 1 && target.index == null) {
101
+ cands = applyResourceIdHints(cands, target, (candidate) => parsed.fullByRef.get(candidate.ref));
102
+ }
103
+ if (!cands.length) {
104
+ return { error: `No element matched selector ${JSON.stringify(target)}${screenMismatch ? ' (recorded screen signature differs from current screen)' : ''}.` };
105
+ }
106
+ if (target.id && cands.length > 1 && target.index == null) {
107
+ const labels = cands.map((e) => `${e.ref}:${e.role}:${e.text ?? e.label ?? e.id ?? 'unlabeled'}`).join(', ');
108
+ return { error: `AMBIGUOUS_SELECTOR: resource-id ${target.id} matched ${cands.length} elements (${labels}); add text/class/bounds hints or an explicit index${screenMismatch ? ' (recorded screen signature differs from current screen)' : ''}.` };
109
+ }
110
+ const el = cands[target.index ?? 0] ?? cands[0];
111
+ return { ...center(el.bounds), via: `selector(${el.ref})`, ref: el.ref, textLen: el.text?.length, secure: el.secure };
112
+ }
113
+ return { error: 'No target provided. Use { ref } | { text|id } | { x, y }.' };
114
+ }
115
+ //# sourceMappingURL=target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"target.js","sourceRoot":"","sources":["../../src/core/target.ts"],"names":[],"mappings":"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"}