agent-device 0.18.3 → 0.19.1

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 (219) hide show
  1. package/README.md +5 -1
  2. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.18.3.apk → agent-device-android-multitouch-helper-0.19.1.apk} +0 -0
  3. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.1.apk.sha256 +1 -0
  4. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.18.3.manifest.json → agent-device-android-multitouch-helper-0.19.1.manifest.json} +4 -4
  5. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.18.3.apk → agent-device-android-snapshot-helper-0.19.1.apk} +0 -0
  6. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.1.apk.sha256 +1 -0
  7. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.18.3.manifest.json → agent-device-android-snapshot-helper-0.19.1.manifest.json} +6 -6
  8. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift +28 -0
  9. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +33 -127
  10. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +630 -177
  11. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +155 -0
  12. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +67 -0
  13. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +173 -122
  14. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +172 -0
  15. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +64 -0
  16. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +76 -182
  17. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +92 -51
  18. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +213 -137
  19. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +142 -0
  20. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +208 -46
  21. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +82 -11
  22. package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +74 -0
  23. package/dist/src/agent-browser-tool.js +1 -0
  24. package/dist/src/android-adb.d.ts +63 -234
  25. package/dist/src/android-adb.js +1 -1
  26. package/dist/src/android-input-ownership.js +6 -0
  27. package/dist/src/android.js +1 -5
  28. package/dist/src/app-lifecycle.js +7 -0
  29. package/dist/src/apple-runner-options.js +1 -0
  30. package/dist/src/apps.js +5 -11
  31. package/dist/src/artifacts.d.ts +4 -3
  32. package/dist/src/artifacts.js +1 -1
  33. package/dist/src/audio-probe.js +1 -1
  34. package/dist/src/batch.d.ts +2 -177
  35. package/dist/src/batch.js +1 -1
  36. package/dist/src/cli-help.js +178 -60
  37. package/dist/src/cli-output.js +1 -1
  38. package/dist/src/cli-runner.js +2 -0
  39. package/dist/src/cli.js +12 -41
  40. package/dist/src/client-metro-companion.js +1 -0
  41. package/dist/src/client.js +3 -0
  42. package/dist/src/command-metadata.js +1 -0
  43. package/dist/src/command-schema.js +22 -0
  44. package/dist/src/command-surface.js +1 -1
  45. package/dist/src/contracts.d.ts +2 -213
  46. package/dist/src/contracts.js +1 -1
  47. package/dist/src/debug-symbols.js +5 -3
  48. package/dist/src/device-input-state.js +2 -0
  49. package/dist/src/devices.js +1 -1
  50. package/dist/src/devices2.js +1 -0
  51. package/dist/src/devices3.js +1 -0
  52. package/dist/src/find.js +1 -1
  53. package/dist/src/finders.d.ts +2 -81
  54. package/dist/src/finders.js +1 -1
  55. package/dist/src/generic.js +1 -1
  56. package/dist/src/handler-utils.js +1 -1
  57. package/dist/src/helper.js +2 -0
  58. package/dist/src/host-process.js +2 -0
  59. package/dist/src/index.d.ts +1444 -1786
  60. package/dist/src/index.js +1 -1
  61. package/dist/src/input-actions.js +3 -1
  62. package/dist/src/input-actions2.js +1 -0
  63. package/dist/src/install-source.d.ts +13 -14
  64. package/dist/src/install-source.js +1 -1
  65. package/dist/src/install-source2.js +1 -0
  66. package/dist/src/interaction-snapshot.js +1 -0
  67. package/dist/src/interaction.js +1 -1
  68. package/dist/src/interactor.js +2 -1
  69. package/dist/src/internal/bin.d.ts +1 -1
  70. package/dist/src/internal/bin.js +2 -5
  71. package/dist/src/internal/companion-tunnel.d.ts +1 -1
  72. package/dist/src/internal/companion-tunnel.js +1 -1
  73. package/dist/src/internal/daemon.d.ts +1 -1
  74. package/dist/src/internal/daemon.js +18 -1
  75. package/dist/src/internal/png-worker.d.ts +29 -26
  76. package/dist/src/internal/png-worker.js +1 -1
  77. package/dist/src/internal/update-check-entry.d.ts +1 -1
  78. package/dist/src/internal/update-check-entry.js +1 -1
  79. package/dist/src/io.d.ts +2 -85
  80. package/dist/src/io.js +1 -1
  81. package/dist/src/lease.js +1 -1
  82. package/dist/src/linux.js +1 -1
  83. package/dist/src/manifest.js +1 -0
  84. package/dist/src/metro.d.ts +69 -113
  85. package/dist/src/metro.js +1 -1
  86. package/dist/src/metro2.js +1 -0
  87. package/dist/src/notifications.js +1 -1
  88. package/dist/src/output-path.js +1 -0
  89. package/dist/src/path-resolution.js +1 -0
  90. package/dist/src/png-worker-contract.js +1 -0
  91. package/dist/src/points.js +4 -0
  92. package/dist/src/project-runtime.js +1 -0
  93. package/dist/src/provider.js +1 -1
  94. package/dist/src/providers.js +1 -0
  95. package/dist/src/react-native.js +1 -1
  96. package/dist/src/record-trace-recording.js +2 -5
  97. package/dist/src/record-trace.js +1 -1
  98. package/dist/src/recording-provider.js +1 -1
  99. package/dist/src/registry.js +38 -0
  100. package/dist/src/remote-config.d.ts +2 -83
  101. package/dist/src/reporting.js +9 -0
  102. package/dist/src/rolldown-runtime.js +1 -0
  103. package/dist/src/runner-client.js +1 -0
  104. package/dist/src/runner-provider.js +1 -0
  105. package/dist/src/runner-sequence.js +1 -1
  106. package/dist/src/runtime-transport.js +1 -0
  107. package/dist/src/runtime.js +3 -0
  108. package/dist/src/scoped-provider.js +1 -0
  109. package/dist/src/sdk-android-adb.d.ts +144 -0
  110. package/dist/src/sdk-android-adb.js +4 -0
  111. package/dist/src/sdk-app-inventory.d.ts +4 -0
  112. package/dist/src/sdk-app-inventory.js +1 -0
  113. package/dist/src/sdk-batch-runner.d.ts +39 -0
  114. package/dist/src/sdk-batch-runner.js +2 -0
  115. package/dist/src/sdk-batch.js +2 -0
  116. package/dist/src/sdk-contracts.d.ts +386 -0
  117. package/dist/src/sdk-contracts.js +2 -0
  118. package/dist/src/sdk-finders.d.ts +60 -0
  119. package/dist/src/sdk-finders.js +1 -0
  120. package/dist/src/sdk-io.d.ts +78 -0
  121. package/dist/src/sdk-io.js +1 -0
  122. package/dist/src/sdk-remote-config.d.ts +139 -0
  123. package/dist/src/sdk-remote-config.js +1 -0
  124. package/dist/src/sdk-selectors.js +1 -0
  125. package/dist/src/selector-runtime.js +1 -1
  126. package/dist/src/selectors.d.ts +38 -137
  127. package/dist/src/selectors.js +1 -1
  128. package/dist/src/selectors2.js +1 -0
  129. package/dist/src/server.js +3 -2
  130. package/dist/src/session.js +10 -11
  131. package/dist/src/setting-state.js +1 -0
  132. package/dist/src/simctl.js +1 -0
  133. package/dist/src/simulator.js +1 -2
  134. package/dist/src/snapshot-diagnostics.js +3 -0
  135. package/dist/src/snapshot-helper.js +5 -0
  136. package/dist/src/snapshot-quality.js +1 -0
  137. package/dist/src/snapshot.js +1 -2
  138. package/dist/src/snapshot2.js +2 -0
  139. package/dist/src/{5560.js → timeouts.js} +1 -1
  140. package/dist/src/tool-provider.js +3 -0
  141. package/dist/src/tool-provider2.js +1 -0
  142. package/dist/src/ttl-memo.js +1 -0
  143. package/dist/src/tv-remote.js +14 -0
  144. package/dist/src/update-check.js +1 -0
  145. package/dist/src/url.js +1 -0
  146. package/dist/src/version.js +1 -0
  147. package/dist/src/web.js +1 -1
  148. package/dist/src/xml.js +2 -0
  149. package/package.json +13 -19
  150. package/server.json +2 -2
  151. package/skills/agent-device/SKILL.md +8 -7
  152. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.18.3.apk.sha256 +0 -1
  153. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.18.3.apk.sha256 +0 -1
  154. package/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +0 -443
  155. package/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +0 -169
  156. package/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +0 -220
  157. package/dist/src/1404.js +0 -1
  158. package/dist/src/1986.js +0 -1
  159. package/dist/src/2748.js +0 -1
  160. package/dist/src/2948.js +0 -30
  161. package/dist/src/3006.js +0 -1
  162. package/dist/src/314.js +0 -1
  163. package/dist/src/3340.js +0 -8
  164. package/dist/src/4418.js +0 -1
  165. package/dist/src/4428.js +0 -3
  166. package/dist/src/485.js +0 -1
  167. package/dist/src/4917.js +0 -1
  168. package/dist/src/495.js +0 -5
  169. package/dist/src/5448.js +0 -3
  170. package/dist/src/5484.js +0 -1
  171. package/dist/src/5643.js +0 -1
  172. package/dist/src/587.js +0 -1
  173. package/dist/src/5913.js +0 -4
  174. package/dist/src/6849.js +0 -1
  175. package/dist/src/6918.js +0 -1
  176. package/dist/src/6967.js +0 -1
  177. package/dist/src/760.js +0 -1
  178. package/dist/src/7871.js +0 -1
  179. package/dist/src/8184.js +0 -1
  180. package/dist/src/8225.js +0 -1
  181. package/dist/src/8391.js +0 -1
  182. package/dist/src/8404.js +0 -2
  183. package/dist/src/8414.js +0 -2
  184. package/dist/src/8875.js +0 -4
  185. package/dist/src/893.js +0 -1
  186. package/dist/src/8936.js +0 -1
  187. package/dist/src/9414.js +0 -1
  188. package/dist/src/9581.js +0 -22
  189. package/dist/src/9612.js +0 -1
  190. package/dist/src/9624.js +0 -1
  191. package/dist/src/9722.js +0 -39
  192. package/dist/src/devices~1.js +0 -1
  193. package/dist/src/devices~2.js +0 -1
  194. package/dist/src/input-actions~1.js +0 -1
  195. package/dist/src/rslib-runtime.js +0 -1
  196. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +0 -0
  197. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +0 -0
  198. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
  199. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcshareddata/xcschemes/AgentDeviceRunner.xcscheme +0 -0
  200. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +0 -0
  201. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests.entitlements +0 -0
  202. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +0 -0
  203. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +0 -0
  204. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.h +0 -0
  205. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.m +0 -0
  206. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +0 -0
  207. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +0 -0
  208. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +0 -0
  209. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +0 -0
  210. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Exceptions.swift +0 -0
  211. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +0 -0
  212. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +0 -0
  213. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +0 -0
  214. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +0 -0
  215. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +0 -0
  216. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan +0 -0
  217. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +0 -0
  218. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/RecordingScripts/recording-resize.swift +0 -0
  219. /package/{apple-runner → dist/apple-runner}/AgentDeviceRunner/RecordingScripts/recording-trim.swift +0 -0
@@ -52,8 +52,45 @@ final class RunnerTests: XCTestCase {
52
52
  let maxRecordingFps = 120
53
53
  var needsPostSnapshotInteractionDelay = false
54
54
  var needsFirstInteractionDelay = false
55
+ var runnerAccessibilityHealth: RunnerAccessibilityHealth = .unknown
55
56
  var activeRecording: ScreenRecorder?
56
57
  let commandJournal = RunnerCommandJournal()
58
+ // Coalesces duplicate transport sends of the same commandId onto the single in-flight
59
+ // execution instead of enqueueing them again behind it (#1105 capture pileup).
60
+ let inFlightCommandLock = NSLock()
61
+ var inFlightCommandIds: Set<String> = []
62
+ var inFlightCommandWaiters: [String: [((data: Data, shouldFinish: Bool)) -> Void]] = [:]
63
+ // Tracks main-queue work abandoned by the execution watchdog so new main-thread commands
64
+ // fail fast as busy instead of queueing behind work that cannot be cancelled (#1105).
65
+ let mainThreadWorkLock = NSLock()
66
+ var abandonedMainThreadWorkCount = 0
67
+ var abandonedMainThreadWorkSince: Date?
68
+ // Past this age the runner stops claiming "busy, retry soon" and reports itself wedged so
69
+ // the daemon recycles it — the only cure once the main thread is stuck for good.
70
+ let mainThreadWedgeThreshold: TimeInterval = 120
71
+ // Sticky per-bundle hint: after an XCTest-backed snapshot tier ground past its slice (or a
72
+ // snapshot was abandoned by the watchdog), later capture plans avoid the XCTest accessibility
73
+ // channel when an independent recovery backend exists, or use a bounded XCTest probe when it
74
+ // does not, for the same screen class (#1105/#1156).
75
+ let snapshotXCTestChannelPenaltyLock = NSLock()
76
+ var snapshotXCTestChannelPenaltyBundleId: String?
77
+ var snapshotXCTestChannelPenaltyUntil = Date.distantPast
78
+ let snapshotXCTestChannelPenaltyDuration: TimeInterval = 120
79
+ // Bluesky-class screens can grind ~4-8s before an XCTest-backed snapshot tier fails; anything
80
+ // past this threshold marks the screen hostile so the next capture uses non-XCTest recovery.
81
+ let snapshotXCTestSlowCaptureThreshold: TimeInterval = 3
82
+ // The blocking XCTest tree snapshot XPC runs on the main thread with this slice so a
83
+ // content-dependent grind (#1105: seconds to minutes on live Bluesky screens) cannot pin
84
+ // the capture plan. On timeout the XPC keeps grinding on main; while any abandoned
85
+ // tree capture is outstanding, plans skip XCTest-backed tiers (tree, query sweep) until the
86
+ // abandoned work drains.
87
+ let treeCaptureLock = NSLock()
88
+ var abandonedTreeCaptureCount = 0
89
+ let treeCaptureSliceBudget: TimeInterval = 8
90
+ // Observability for the record(_:) suppression below: how many AX-broken-screen snapshot
91
+ // issues this session muted, so wedge investigations see the volume without grepping logs.
92
+ let suppressedIssueLock = NSLock()
93
+ var suppressedAxSnapshotIssueCount = 0
57
94
  let interactiveTypes: Set<XCUIElement.ElementType> = [
58
95
  .button,
59
96
  .cell,
@@ -88,6 +125,43 @@ final class RunnerTests: XCTestCase {
88
125
  continueAfterFailure = true
89
126
  }
90
127
 
128
+ /// True for the one recorded-issue class the runner deliberately mutes: an AX-server error
129
+ /// (`kAXError*`) inside a "Failed to get matching snapshot" fetch. The kAXError token
130
+ /// intentionally covers kAXErrorIllegalArgument and its sibling AX server codes (e.g.
131
+ /// kAXErrorCannotComplete): any AX-server rejection inside a matching-snapshot fetch is the
132
+ /// same capture-plan noise the plan already classifies and recovers from. The timeout
133
+ /// variant ("Failed to get matching snapshot: Timed out while evaluating UI query.") carries
134
+ /// no kAXError token and MUST keep recording — it signals a genuinely hung query, exactly
135
+ /// the pathology XCTEST_RECORDED_FAILURE must stay able to see.
136
+ static func isSuppressedAxSnapshotIssueDescription(_ description: String) -> Bool {
137
+ description.contains("Failed to get matching snapshot") && description.contains("kAXError")
138
+ }
139
+
140
+ /// On AX-broken screens (deep RN trees, #758/#1105) XCUIApplication queries record
141
+ /// "Failed to get matching snapshot: ... kAXError..." issues; XCTest tears the whole test
142
+ /// case down once a few accumulate, killing the long-lived runner right after the command
143
+ /// completes and forcing a ~25s restart per capture. This override is deliberately
144
+ /// suite-global (all commands, not just snapshot capture): tap-triggered element queries on
145
+ /// the same screens record the same noise and would still tear the runner down, and command
146
+ /// outcomes stay honest through their own error paths — only this issue side-channel is
147
+ /// muted. Everything else still records (and still drives XCTEST_RECORDED_FAILURE).
148
+ override func record(_ issue: XCTIssue) {
149
+ let description = issue.compactDescription
150
+ if Self.isSuppressedAxSnapshotIssueDescription(description) {
151
+ suppressedIssueLock.lock()
152
+ suppressedAxSnapshotIssueCount += 1
153
+ let count = suppressedAxSnapshotIssueCount
154
+ suppressedIssueLock.unlock()
155
+ NSLog(
156
+ "AGENT_DEVICE_RUNNER_AX_SNAPSHOT_ISSUE_SUPPRESSED count=%ld description=%@",
157
+ count,
158
+ description
159
+ )
160
+ return
161
+ }
162
+ super.record(issue)
163
+ }
164
+
91
165
  @MainActor
92
166
  func testCommand() throws {
93
167
  if RunnerEnv.isTruthy("AGENT_DEVICE_RUNNER_NOOP_STARTUP") {
@@ -0,0 +1 @@
1
+ import{t as e}from"./rolldown-runtime.js";import{F as t,I as n}from"./sdk-contracts.js";import{T as r,j as i,v as a}from"./sdk-android-adb.js";import{t as o}from"./timeouts.js";import{c as s,i as c,n as l,o as u,t as d}from"./host-process.js";import f from"node:crypto";import p from"node:fs";import m from"node:os";import h from"node:path";async function g(e){let{lockDirPath:n,owner:r}=e,i=h.join(n,`owner.json`),a=Date.now()+(e.timeoutMs??3e4),s=e.pollMs??100,c=e.ownerGraceMs??5e3,l=e.description??`process lock`;for(p.mkdirSync(h.dirname(n),{recursive:!0});Date.now()<a;)try{p.mkdirSync(n),_(i,r);let e=!1;return async()=>{e||(e=!0,p.rmSync(n,{recursive:!0,force:!0}))}}catch(e){let t=e;if(t.code!==`EEXIST`)throw t;if(ee(n,i,c))continue;await o(s)}throw new t(`COMMAND_FAILED`,`Timed out waiting for ${l}`,{lockDirPath:n,...y(n,i)})}function _(e,t){let n=`${e}.${process.pid}.${Date.now()}.tmp`;p.writeFileSync(n,JSON.stringify(t),`utf8`),p.renameSync(n,e)}function ee(e,t,n){let r=null;try{r=p.statSync(e)}catch{return!0}let i=v(t);return i?b(i)?!1:(p.rmSync(e,{recursive:!0,force:!0}),!0):Date.now()-r.mtimeMs<n?!1:(p.rmSync(e,{recursive:!0,force:!0}),!0)}function v(e){try{return JSON.parse(p.readFileSync(e,`utf8`))}catch{return null}}function y(e,t){let n=Date.now(),r=v(t),i;try{i=Math.max(0,Math.round(n-p.statSync(e).mtimeMs))}catch{}return{...i===void 0?{}:{lockAgeMs:i},...r?{ownerPid:r.pid,ownerStartTime:r.startTime,ownerAgeMs:Math.max(0,Math.round(n-r.acquiredAtMs))}:{}}}function b(e){return!Number.isInteger(e.pid)||e.pid<=0||!l(e.pid)?!1:e.startTime?u(e.pid)===e.startTime:!0}const x=new Map;function S(e){let t=f.createHash(`sha256`);return t.update(h.resolve(e.stateDir)),t.update(`\0`),t.update(h.resolve(e.installDir)),`--agent-device-managed-web=${t.digest(`hex`).slice(0,16)}`}function C(e,t){let n=S(t),r=e?.trim();return r?k(r).includes(n)?r:`${r},${n}`:n}function w(e){return A(e.AGENT_BROWSER_IDLE_TIMEOUT_MS)??A(e.AGENT_DEVICE_WEB_IDLE_TIMEOUT_MS)??3e5}function te(e,t){if(e.pid!==process.pid&&j(e.command)){if(e.command.includes(S(t)))return{process:e,reason:`launch-marker`};if(M(t).some(t=>re(e,t)))return{process:e,reason:`managed-browser-home`}}}function T(e,t){let n=e.flatMap(e=>{let n=te(e,t);return n?[n]:[]});return{count:n.length,pids:n.map(e=>e.process.pid),processes:n}}async function E(e){return T(await O(),e)}async function D(e,t,n={}){let r=N([...n.openWebSessionNames??[]]);if(r.length>0)return P(`open-web-session`,{openWebSessionNames:r});let a=w(process.env),o=I(e);if(o!==void 0&&Date.now()-o<a)return P(`recent-browser-activity`,{idleTimeoutMs:a,latestActivityMs:o});let c=await O(),l=T(c,e),u=d(l.processes.map(e=>e.process.pid),c).map(e=>e.pid),f={...l,signalPids:u};return l.count===0?f:(i({level:`warn`,phase:`web_agent_browser_orphan_cleanup`,data:{reason:t,count:l.count,pids:l.pids,signalPids:u,stateDir:e.stateDir,installDir:e.installDir,matchReasons:l.processes.map(e=>e.reason)}}),await s({pids:u,termTimeoutMs:1500,killTimeoutMs:1e3}),f)}async function ne(e,t={}){let n=Date.now(),r=h.resolve(e.stateDir),i=x.get(r);if(!(i!==void 0&&n-i<3e4))return x.set(r,n),await D(e,`provider-startup`,t)}async function O(){return await r(async()=>await c({timeoutMs:1500}))}function k(e){return e.split(/[,\n]/).map(e=>e.trim()).filter(Boolean)}function A(e){if(e===void 0||e.trim()===``)return;let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0?t:void 0}function j(e){return/\b(?:Google Chrome for Testing|Chrome for Testing|Chromium|chrome|chrome\.exe|chromium|chromium-browser|headless_shell)\b/i.test(e)}function M(e){return N([h.join(e.homeDir,`.agent-browser`,`browsers`),h.join(e.runtimeHomeDir,`.agent-browser`,`browsers`)])}function N(e){return[...new Set(e.filter(e=>e.length>0))]}function P(e,t){return{count:0,pids:[],processes:[],signalPids:[],skipped:{reason:e,...t}}}function re(e,t){let n=F(e.command),r=`${F(h.resolve(t)).replace(/\/+$/,``)}/`;return n.includes(r)}function F(e){return e.replace(/\\/g,`/`)}function I(e){let t=L(e.socketDir).map(e=>R(e)).filter(e=>e!==void 0);return t.length>0?Math.max(...t):void 0}function L(e){try{return p.readdirSync(e).map(t=>h.join(e,t))}catch{return[]}}function R(e){try{return p.statSync(e).mtimeMs}catch{return}}var z=e({doctorManagedAgentBrowser:()=>K,getManagedAgentBrowserStatus:()=>q,mapManagedAgentBrowserError:()=>$,resolveAgentBrowserTool:()=>W,setupManagedAgentBrowser:()=>G});const B=`0.27.1`,V=`agent-browser`,H=5*6e4,U=6e4;async function W(e){let t=q(e);if(t.installed)return J(t);throw Q(t)}async function G(e){let t=q(e);ce(t.nodeMajor);let n=await g({lockDirPath:h.join(t.installDir,`..`,`.agent-browser-install.lock`),owner:{pid:process.pid,startTime:u(process.pid),acquiredAtMs:Date.now()},timeoutMs:H,description:`managed agent-browser setup`});try{let t=q(e);return t.installed?t:(p.mkdirSync(t.installDir,{recursive:!0}),await Y(t),await X(t,[`install`],{timeoutMs:H}),await X(t,[`doctor`,`--offline`,`--quick`],{timeoutMs:U}),ae(t),q(e))}finally{await n()}}async function K(e){let t=q(e);if(!t.installed)throw Q(t);return{status:t,...await X(t,[`doctor`,`--offline`,`--quick`],{timeoutMs:U,allowFailure:!0})}}function q(e){let t=e.stateDir??process.env.AGENT_DEVICE_STATE_DIR??pe(),n=h.join(t,`tools`,`agent-browser`,B),r=se(n),i=h.join(n,`home`),a=le(n),o=ue(n),s=fe(r)&&oe(n),c=Number.parseInt(process.versions.node.split(`.`)[0]??`0`,10);return{version:B,stateDir:t,installDir:n,binaryPath:r,homeDir:i,runtimeHomeDir:a,socketDir:o,installed:s,nodeMajor:c,nodeSupported:c>=24}}function J(e){if(!e.installed)throw Q(e);return{command:e.binaryPath,env:Z(e,process.env)}}async function Y(e){let t=h.join(e.installDir,`package`);p.mkdirSync(t,{recursive:!0}),await a(`npm`,[`install`,`--prefix`,t,`--no-audit`,`--no-fund`,`--no-save`,`${V}@${B}`],{env:process.env,timeoutMs:H})}async function X(e,t,n){return await a(e.binaryPath,t,{allowFailure:n.allowFailure,env:Z(e,process.env),timeoutMs:n.timeoutMs})}function Z(e,t){return p.mkdirSync(e.homeDir,{recursive:!0}),ie(e),p.mkdirSync(e.socketDir,{recursive:!0}),{...t,HOME:e.runtimeHomeDir,AGENT_BROWSER_SOCKET_DIR:e.socketDir,AGENT_BROWSER_IDLE_TIMEOUT_MS:String(w(t)),AGENT_BROWSER_ARGS:C(t.AGENT_BROWSER_ARGS,e)}}function ie(e){if(e.runtimeHomeDir!==e.homeDir){p.mkdirSync(h.dirname(e.runtimeHomeDir),{recursive:!0});try{let t=p.lstatSync(e.runtimeHomeDir);if(t.isSymbolicLink()&&p.readlinkSync(e.runtimeHomeDir)===e.homeDir||t.isDirectory())return;t.isSymbolicLink()&&p.unlinkSync(e.runtimeHomeDir)}catch(e){if(!de(e))throw e}try{p.symlinkSync(e.homeDir,e.runtimeHomeDir,`dir`)}catch{p.mkdirSync(e.runtimeHomeDir,{recursive:!0})}}}function ae(e){p.writeFileSync(h.join(e.installDir,`manifest.json`),JSON.stringify({package:V,version:B,node:process.version,installedAt:new Date().toISOString()},null,2),`utf8`)}function oe(e){return p.existsSync(h.join(e,`manifest.json`))}function se(e){let t=h.join(e,`package`);return h.join(t,`node_modules`,`.bin`,process.platform===`win32`?`agent-browser.cmd`:`agent-browser`)}function Q(e){return new t(`TOOL_MISSING`,`Managed web browser backend is not installed.`,{version:B,installDir:e.installDir,hint:e.nodeSupported===!1?`Web automation requires Node 24+; current Node is ${process.version}.`:"Run `agent-device web setup` to install the managed web backend."})}function ce(e){if(!(e>=24))throw new t(`UNSUPPORTED_OPERATION`,`Web automation requires Node 24 or newer.`,{currentNode:process.version,requiredNodeMajor:24,hint:`Run agent-device with Node 24+ for web setup and web automation.`})}function le(e){if(process.platform===`win32`)return h.join(e,`home`);let t=f.createHash(`sha1`).update(e).digest(`hex`).slice(0,12);return h.join(m.tmpdir(),`agent-device-web`,t)}function ue(e){let t=f.createHash(`sha1`).update(e).digest(`hex`).slice(0,12);return h.join(m.tmpdir(),`adw`,t)}function de(e){return typeof e==`object`&&!!e&&`code`in e&&e.code===`ENOENT`}function fe(e){try{return p.accessSync(e,p.constants.X_OK),!0}catch{return!1}}function pe(){return h.join(process.env.HOME??process.cwd(),`.agent-device`)}function $(e){let r=n(e);return r.code===`TOOL_MISSING`?new t(r.code,r.message,{...r.details??{},hint:typeof r.details?.hint==`string`?r.details.hint:"Run `agent-device web setup` to install the managed web backend."}):r}export{D as a,g as c,W as i,q as n,ne as o,$ as r,E as s,z as t};
@@ -1,234 +1,63 @@
1
- import type { Readable } from 'node:stream';
2
- import { StdioOptions } from 'node:child_process';
3
- import type { Writable } from 'node:stream';
4
-
5
- /**
6
- * Runs device-scoped adb arguments after the device serial has already been selected.
7
- * Implementations must be safe to call concurrently for one request.
8
- */
9
- export declare type AndroidAdbExecutor = (args: string[], options?: AndroidAdbExecutorOptions) => Promise<AndroidAdbExecutorResult>;
10
-
11
- export declare type AndroidAdbExecutorOptions = Pick<ExecOptions, 'allowFailure' | 'timeoutMs' | 'binaryStdout' | 'stdin' | 'signal'>;
12
-
13
- declare type AndroidAdbExecutorResult = Pick<ExecResult, 'exitCode' | 'stdout' | 'stderr' | 'stdoutBuffer'>;
14
-
15
- /**
16
- * Installs an APK path. Implementations are responsible for honoring semantic
17
- * install options such as replace/test/downgrade/grant-permissions.
18
- */
19
- declare type AndroidAdbInstaller = (apkPath: string, options?: AndroidAdbInstallOptions) => Promise<AndroidAdbExecutorResult>;
20
-
21
- declare type AndroidAdbInstallOptions = AndroidAdbTransferOptions & {
22
- replace?: boolean;
23
- allowTestPackages?: boolean;
24
- allowDowngrade?: boolean;
25
- grantPermissions?: boolean;
26
- };
27
-
28
- declare type AndroidAdbProcess = {
29
- pid?: number;
30
- stdin: Writable | null;
31
- stdout: Readable | null;
32
- stderr: Readable | null;
33
- killed: boolean;
34
- kill(signal?: NodeJS.Signals | number): boolean;
35
- once(event: 'exit' | 'close', listener: (code: number | null, signal: NodeJS.Signals | null) => void): unknown;
36
- on(event: 'error', listener: (error: Error) => void): unknown;
37
- on(event: 'exit' | 'close', listener: (code: number | null, signal: NodeJS.Signals | null) => void): unknown;
38
- };
39
-
40
- declare type AndroidAdbProvider = {
41
- /**
42
- * Fallback executor for device-scoped adb arguments. Providers may omit explicit
43
- * methods to keep the legacy exec-shaped pull/install fallback.
44
- */
45
- exec: AndroidAdbExecutor;
46
- spawn?: AndroidAdbSpawner;
47
- reverse?: AndroidPortReverseProvider;
48
- pull?: AndroidAdbPuller;
49
- install?: AndroidAdbInstaller;
50
- installBundle?: AndroidBundleInstaller;
51
- text?: AndroidTextInjector;
52
- touch?: AndroidTouchInjector;
53
- };
54
-
55
- declare type AndroidAdbPuller = (remotePath: string, localPath: string, options?: AndroidAdbTransferOptions) => Promise<AndroidAdbExecutorResult>;
56
-
57
- declare type AndroidAdbSpawner = (args: string[], options?: ExecBackgroundOptions) => AndroidAdbProcess;
58
-
59
- declare type AndroidAdbTransferOptions = AndroidAdbExecutorOptions;
60
-
61
- declare type AndroidAppListFilter = AppsFilter;
62
-
63
- declare type AndroidAppListOptions = {
64
- filter?: AndroidAppListFilter;
65
- target?: AndroidAppListTarget;
66
- };
67
-
68
- declare type AndroidAppListTarget = 'mobile' | 'tv' | 'auto';
69
-
70
- declare type AndroidBundleInstaller = (bundlePath: string, options: {
71
- mode: string;
72
- }) => Promise<void>;
73
-
74
- declare type AndroidForegroundApp = {
75
- package?: string;
76
- activity?: string;
77
- };
78
-
79
- declare type AndroidInputOwner = 'app' | 'ime' | 'unknown';
80
-
81
- declare type AndroidKeyboardDismissResult = AndroidKeyboardState & {
82
- attempts: number;
83
- wasVisible: boolean;
84
- dismissed: boolean;
85
- };
86
-
87
- declare type AndroidKeyboardState = {
88
- visible: boolean;
89
- inputType?: string;
90
- type?: AndroidKeyboardType;
91
- inputMethodPackage?: string;
92
- focusedPackage?: string;
93
- focusedResourceId?: string;
94
- inputOwner: AndroidInputOwner;
95
- };
96
-
97
- declare type AndroidKeyboardType = 'text' | 'number' | 'email' | 'phone' | 'password' | 'datetime' | 'unknown';
98
-
99
- declare type AndroidLogcatCaptureOptions = {
100
- lines?: number;
101
- timeoutMs?: number;
102
- signal?: AbortSignal;
103
- };
104
-
105
- declare type AndroidOpenAppWithAdbOptions = {
106
- activity?: string;
107
- category?: string;
108
- };
109
-
110
- export declare type AndroidPortReverseEndpoint = `tcp:${number}` | `localabstract:${string}`;
111
-
112
- declare type AndroidPortReverseMapping = {
113
- local: AndroidPortReverseEndpoint;
114
- remote: AndroidPortReverseEndpoint;
115
- ownerId?: string;
116
- };
117
-
118
- declare type AndroidPortReverseOptions = {
119
- signal?: AbortSignal;
120
- timeoutMs?: number;
121
- };
122
-
123
- declare type AndroidPortReverseProvider = {
124
- ensure(mapping: AndroidPortReverseMapping, options?: AndroidPortReverseOptions): Promise<void>;
125
- remove(local: AndroidPortReverseEndpoint, options?: AndroidPortReverseOptions): Promise<void>;
126
- removeAllOwned(ownerId: string, options?: AndroidPortReverseOptions): Promise<void>;
127
- list?(options?: AndroidPortReverseOptions): Promise<AndroidPortReverseMapping[]>;
128
- };
129
-
130
- declare type AndroidTextInjectionRequest = {
131
- action: AndroidTextInputAction;
132
- text: string;
133
- delayMs?: number;
134
- /**
135
- * Present only for fill. Providers must make this target the focused/replaced
136
- * input for the request, not inject into an unrelated currently focused field.
137
- */
138
- target?: {
139
- x: number;
140
- y: number;
141
- };
142
- };
143
-
144
- declare type AndroidTextInjector = (request: AndroidTextInjectionRequest) => Promise<void>;
145
-
146
- declare type AndroidTextInputAction = 'type' | 'fill';
147
-
148
- declare type AndroidTouchGestureRequest = {
149
- kind: 'swipe';
150
- x1: number;
151
- y1: number;
152
- x2: number;
153
- y2: number;
154
- durationMs?: number;
155
- } | {
156
- kind: 'pinch';
157
- x: number;
158
- y: number;
159
- scale: number;
160
- durationMs?: number;
161
- } | {
162
- kind: 'rotate';
163
- x: number;
164
- y: number;
165
- degrees: number;
166
- durationMs?: number;
167
- } | {
168
- kind: 'transform';
169
- x: number;
170
- y: number;
171
- dx: number;
172
- dy: number;
173
- scale: number;
174
- degrees: number;
175
- durationMs?: number;
176
- };
177
-
178
- declare type AndroidTouchInjector = (request: AndroidTouchGestureRequest) => Promise<Record<string, unknown> | void>;
179
-
180
- declare type AppsFilter = 'user-installed' | 'all';
181
-
182
- export declare function captureAndroidLogcatWithAdb(adb: AndroidAdbExecutor, options?: AndroidLogcatCaptureOptions): Promise<string>;
183
-
184
- export declare function createAndroidPortReverseManager(provider: AndroidAdbProvider | AndroidAdbExecutor): AndroidPortReverseProvider;
185
-
186
- export declare function dismissAndroidKeyboardWithAdb(adb: AndroidAdbExecutor): Promise<AndroidKeyboardDismissResult>;
187
-
188
- declare type ExecBackgroundOptions = ExecOptions & {
189
- /**
190
- * Capture stdout/stderr into the wait result when the child has piped stdio.
191
- * Set false when the caller owns, ignores, or forwards the streams.
192
- */
193
- captureOutput?: boolean;
194
- stdio?: StdioOptions;
195
- };
196
-
197
- declare type ExecOptions = {
198
- cwd?: string;
199
- env?: NodeJS.ProcessEnv;
200
- allowFailure?: boolean;
201
- binaryStdout?: boolean;
202
- stdin?: string | Buffer;
203
- timeoutMs?: number;
204
- detached?: boolean;
205
- signal?: AbortSignal;
206
- /** Max stdout/stderr bytes for synchronous runs (default Node ~1MB). */
207
- maxBuffer?: number;
208
- };
209
-
210
- declare type ExecResult = {
211
- stdout: string;
212
- stderr: string;
213
- exitCode: number;
214
- stdoutBuffer?: Buffer;
215
- };
216
-
217
- export declare function forceStopAndroidAppWithAdb(adb: AndroidAdbExecutor, packageName: string): Promise<void>;
218
-
219
- export declare function getAndroidAppStateWithAdb(adb: AndroidAdbExecutor): Promise<AndroidForegroundApp>;
220
-
221
- export declare function getAndroidKeyboardStatusWithAdb(adb: AndroidAdbExecutor): Promise<AndroidKeyboardState>;
222
-
223
- export declare function listAndroidAppsWithAdb(adb: AndroidAdbExecutor, options?: AndroidAppListOptions): Promise<Array<{
224
- package: string;
225
- name: string;
226
- }>>;
227
-
228
- export declare function openAndroidAppWithAdb(adb: AndroidAdbExecutor, packageName: string, options?: AndroidOpenAppWithAdbOptions): Promise<void>;
229
-
230
- export declare function readAndroidClipboardWithAdb(adb: AndroidAdbExecutor): Promise<string>;
231
-
232
- export declare function writeAndroidClipboardWithAdb(adb: AndroidAdbExecutor, text: string): Promise<void>;
233
-
234
- export { }
1
+ import { i as createAndroidPortReverseManager, n as AndroidAdbExecutorOptions, r as AndroidPortReverseEndpoint, t as AndroidAdbExecutor } from "./sdk-android-adb.js";
2
+ import { t as AppsFilter } from "./sdk-app-inventory.js";
3
+ import fs from "node:fs";
4
+ //#region src/platforms/android/app-parsers.d.ts
5
+ type AndroidForegroundApp = {
6
+ package?: string;
7
+ activity?: string;
8
+ };
9
+ //#endregion
10
+ //#region src/platforms/android/app-helpers.d.ts
11
+ type AndroidAppListFilter = AppsFilter;
12
+ type AndroidAppListTarget = 'mobile' | 'tv' | 'auto';
13
+ type AndroidAppListOptions = {
14
+ filter?: AndroidAppListFilter;
15
+ target?: AndroidAppListTarget;
16
+ };
17
+ declare function listAndroidAppsWithAdb(adb: AndroidAdbExecutor, options?: AndroidAppListOptions): Promise<Array<{
18
+ package: string;
19
+ name: string;
20
+ }>>;
21
+ declare function getAndroidAppStateWithAdb(adb: AndroidAdbExecutor): Promise<AndroidForegroundApp>;
22
+ //#endregion
23
+ //#region src/platforms/android/app-control.d.ts
24
+ type AndroidOpenAppWithAdbOptions = {
25
+ activity?: string;
26
+ category?: string;
27
+ };
28
+ declare function forceStopAndroidAppWithAdb(adb: AndroidAdbExecutor, packageName: string): Promise<void>;
29
+ declare function openAndroidAppWithAdb(adb: AndroidAdbExecutor, packageName: string, options?: AndroidOpenAppWithAdbOptions): Promise<void>;
30
+ //#endregion
31
+ //#region src/platforms/android/logcat.d.ts
32
+ type AndroidLogcatCaptureOptions = {
33
+ lines?: number;
34
+ timeoutMs?: number;
35
+ signal?: AbortSignal;
36
+ };
37
+ declare function captureAndroidLogcatWithAdb(adb: AndroidAdbExecutor, options?: AndroidLogcatCaptureOptions): Promise<string>;
38
+ //#endregion
39
+ //#region src/core/android-input-ownership.d.ts
40
+ type AndroidInputOwner = 'app' | 'ime' | 'unknown';
41
+ //#endregion
42
+ //#region src/platforms/android/device-input-state.d.ts
43
+ type AndroidKeyboardType = 'text' | 'number' | 'email' | 'phone' | 'password' | 'datetime' | 'unknown';
44
+ type AndroidKeyboardState = {
45
+ visible: boolean;
46
+ inputType?: string;
47
+ type?: AndroidKeyboardType;
48
+ inputMethodPackage?: string;
49
+ focusedPackage?: string;
50
+ focusedResourceId?: string;
51
+ inputOwner: AndroidInputOwner;
52
+ };
53
+ type AndroidKeyboardDismissResult = AndroidKeyboardState & {
54
+ attempts: number;
55
+ wasVisible: boolean;
56
+ dismissed: boolean;
57
+ };
58
+ declare function getAndroidKeyboardStatusWithAdb(adb: AndroidAdbExecutor): Promise<AndroidKeyboardState>;
59
+ declare function dismissAndroidKeyboardWithAdb(adb: AndroidAdbExecutor): Promise<AndroidKeyboardDismissResult>;
60
+ declare function readAndroidClipboardWithAdb(adb: AndroidAdbExecutor): Promise<string>;
61
+ declare function writeAndroidClipboardWithAdb(adb: AndroidAdbExecutor, text: string): Promise<void>;
62
+ //#endregion
63
+ export { type AndroidAdbExecutor, type AndroidAdbExecutorOptions, type AndroidPortReverseEndpoint, captureAndroidLogcatWithAdb, createAndroidPortReverseManager, dismissAndroidKeyboardWithAdb, forceStopAndroidAppWithAdb, getAndroidAppStateWithAdb, getAndroidKeyboardStatusWithAdb, listAndroidAppsWithAdb, openAndroidAppWithAdb, readAndroidClipboardWithAdb, writeAndroidClipboardWithAdb };
@@ -1 +1 @@
1
- import{AppError as t}from"./485.js";import{resolveAppsFilter as e}from"./2748.js";import{parseAndroidForegroundApp as i,parseAndroidLaunchablePackages as a,parseAndroidUserInstalledPackages as r,inferAndroidAppName as o,parseAndroidLaunchComponent as n,isAmStartError as d}from"./3340.js";let l="android.intent.category.LAUNCHER",s="android.intent.category.LEANBACK_LAUNCHER";async function c(t,i={}){let a=await A(t,i.target??"auto");return("user-installed"===e(i.filter)?(await p(t)).filter(t=>a.has(t)):Array.from(a)).map(t=>({package:t,name:o(t)})).sort((t,e)=>t.package.localeCompare(e.package))}async function u(t){let e=await f(t,[["shell","dumpsys","window","windows"],["shell","dumpsys","window"]]);if(e)return e;let i=await f(t,[["shell","dumpsys","activity","activities"],["shell","dumpsys","activity"]]);return i||{}}async function A(t,e){return new Set((await Promise.all((function(t){switch(t){case"mobile":return[l];case"tv":return[s];default:return[l,s]}})(e).map(async e=>{var i;let r=await t(["shell","cmd","package","query-activities","--brief","-a","android.intent.action.MAIN","-c",e],{allowFailure:!0});return 0===r.exitCode?0===(i=r.stdout).trim().length?[]:a(i):[]}))).flat())}async function p(e){let i=await e(["shell","pm","list","packages","-3"],{allowFailure:!0});if(0!==i.exitCode)throw new t("COMMAND_FAILED","Failed to list Android user-installed apps",{stdout:i.stdout,stderr:i.stderr,exitCode:i.exitCode});return r(i.stdout)}async function f(t,e){for(let a of e){let e=i((await t(a,{allowFailure:!0})).stdout??"");if(e)return e}return null}let w="android.intent.category.LAUNCHER",m="android.intent.category.DEFAULT";async function _(t,e){await t(["shell","am","force-stop",e])}async function h(t,e,i=[w]){for(let a of i){let i=await t(["shell","cmd","package","resolve-activity","--brief","-a","android.intent.action.MAIN","-c",a,e],{allowFailure:!0});if(0!==i.exitCode)continue;let r=n(i.stdout);if(r)return r}return null}async function y(e,i,a={}){let r=a.category??w;if(a.activity){var o,n;return void await e(["shell","am","start","-W","-a","android.intent.action.MAIN","-c",m,"-c",r,"-n",(o=i,(n=a.activity).includes("/")?n:`${o}/${n.startsWith(".")?n:`.${n}`}`)])}let l=await e(["shell","am","start","-W","-a","android.intent.action.MAIN","-c",m,"-c",r,"-p",i],{allowFailure:!0});if(0===l.exitCode&&!d(l.stdout,l.stderr))return;let s=await h(e,i,[r]);if(!s)throw new t("COMMAND_FAILED",`Failed to resolve Android launch component for ${i}`,{stdout:l.stdout,stderr:l.stderr,exitCode:l.exitCode});await e(["shell","am","start","-W","-a","android.intent.action.MAIN","-c",m,"-c",r,"-n",s])}export{captureAndroidLogcatWithAdb,createAndroidPortReverseManager}from"./3340.js";export{dismissAndroidKeyboardWithAdb,getAndroidKeyboardStatusWithAdb,readAndroidClipboardWithAdb,writeAndroidClipboardWithAdb}from"./9624.js";export{_ as forceStopAndroidAppWithAdb,u as getAndroidAppStateWithAdb,c as listAndroidAppsWithAdb,y as openAndroidAppWithAdb};
1
+ import{F as e}from"./sdk-contracts.js";import{a as t,n}from"./sdk-android-adb.js";import{r}from"./sdk-app-inventory.js";import{a as i,g as a,h as o,m as s,o as c,u as l}from"./app-lifecycle.js";import{s as u}from"./android-input-ownership.js";import{c as d,i as f,n as p,o as m}from"./device-input-state.js";const h=`android.intent.category.LAUNCHER`,g=`android.intent.category.LEANBACK_LAUNCHER`;async function _(e,t={}){let n=await y(e,t.target??`auto`);return(r(t.filter)===`user-installed`?(await C(e)).filter(e=>n.has(e)):Array.from(n)).map(e=>({package:e,name:i(e)})).sort((e,t)=>e.package.localeCompare(t.package))}async function v(e){return await w(e,[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`]])||await w(e,[[`shell`,`dumpsys`,`activity`,`activities`],[`shell`,`dumpsys`,`activity`]])||{}}async function y(e,t){let n=(await Promise.all(b(t).map(async t=>{let n=await e(x(t),{allowFailure:!0});return n.exitCode===0?S(n.stdout):[]}))).flat();return new Set(n)}function b(e){switch(e){case`mobile`:return[h];case`tv`:return[g];default:return[h,g]}}function x(e){return[`shell`,`cmd`,`package`,`query-activities`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,e]}function S(e){return e.trim().length===0?[]:o(e)}async function C(e){let t=await e([`shell`,`pm`,`list`,`packages`,`-3`],{allowFailure:!0});if(t.exitCode!==0)throw n(`Failed to list Android user-installed apps`,t);return a(t.stdout)}async function w(e,t){for(let n of t){let t=s((await e(n,{allowFailure:!0})).stdout??``);if(t)return t}return null}const T=`android.intent.category.LAUNCHER`,E=`android.intent.category.DEFAULT`;async function D(e,t){await e([`shell`,`am`,`force-stop`,t])}async function O(e,t,n=[T]){for(let r of n){let n=await e([`shell`,`cmd`,`package`,`resolve-activity`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,r,t],{allowFailure:!0});if(n.exitCode!==0)continue;let i=l(n.stdout);if(i)return i}return null}async function k(t,n,r={}){let i=r.category??T;if(r.activity){await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,E,`-c`,i,`-n`,A(n,r.activity)]);return}let a=await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,E,`-c`,i,`-p`,n],{allowFailure:!0});if(a.exitCode===0&&!c(a.stdout,a.stderr))return;let o=await O(t,n,[i]);if(!o)throw new e(`COMMAND_FAILED`,`Failed to resolve Android launch component for ${n}`,{stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,E,`-c`,i,`-n`,o])}function A(e,t){return t.includes(`/`)?t:`${e}/${t.startsWith(`.`)?t:`.${t}`}`}export{u as captureAndroidLogcatWithAdb,t as createAndroidPortReverseManager,p as dismissAndroidKeyboardWithAdb,D as forceStopAndroidAppWithAdb,v as getAndroidAppStateWithAdb,f as getAndroidKeyboardStatusWithAdb,_ as listAndroidAppsWithAdb,k as openAndroidAppWithAdb,m as readAndroidClipboardWithAdb,d as writeAndroidClipboardWithAdb};
@@ -0,0 +1,6 @@
1
+ import{t as e}from"./rolldown-runtime.js";import{F as t,I as n}from"./sdk-contracts.js";import{b as r,c as i,h as a,j as o,n as s,r as c,v as l,w as u}from"./sdk-android-adb.js";import{t as d}from"./timeouts.js";import{n as f,t as p}from"./manifest.js";import{n as m,t as h}from"./install-source2.js";import"node:fs";import g from"node:path";function ee(e){let t=e.trim();if(!t||/\s/.test(t))return!1;let n=/^([A-Za-z][A-Za-z0-9+.-]*):(.+)$/.exec(t);if(!n)return!1;let r=n[1]?.toLowerCase(),i=n[2]??``;return r===`http`||r===`https`||r===`ws`||r===`wss`||r===`ftp`||r===`ftps`?i.startsWith(`//`):!0}function _(e){let t=e.trim().split(`:`)[0]?.toLowerCase();return t===`http`||t===`https`}function te(e,t){let n=e?.trim();if(n)return n;if(_(t))return`com.apple.mobilesafari`}function ne(e={}){let t=e.ttlMs??3e4,n=e.nowMs??Date.now,r=new Map,i=e=>{let t=y(e);for(let e of r.keys())e.startsWith(t)&&r.delete(e)};return{get(e,t){let i=v(e,t),a=r.get(i);if(a){if(a.expiresAtMs<=n()){r.delete(i);return}return a.value}},set(e,i,a){return r.set(v(e,i),{value:a,expiresAtMs:n()+t}),a},clear(e){i(e)},async invalidateWhile(e,t){i(e);try{return await t()}finally{i(e)}}}}function v(e,t){return[e.platform,e.deviceId,e.variant??``,t.trim().toLowerCase()].join(`\0`)}function y(e){return[e.platform,e.deviceId,``].join(`\0`)}function b(e){return[`1`,`true`,`yes`,`on`].includes((e??``).trim().toLowerCase())}const x={attempts:3,baseDelayMs:200,maxDelayMs:2e3,jitter:.2};var S=class e{startedAtMs;expiresAtMs;constructor(e,t){this.startedAtMs=e,this.expiresAtMs=e+Math.max(0,t)}static fromTimeoutMs(t,n=Date.now()){return new e(n,t)}remainingMs(e=Date.now()){return Math.max(0,this.expiresAtMs-e)}elapsedMs(e=Date.now()){return Math.max(0,e-this.startedAtMs)}isExpired(e=Date.now()){return this.remainingMs(e)<=0}};async function C(e,n={},r={}){let i={maxAttempts:n.maxAttempts??x.attempts,baseDelayMs:n.baseDelayMs??x.baseDelayMs,maxDelayMs:n.maxDelayMs??x.maxDelayMs,jitter:n.jitter??x.jitter,shouldRetry:n.shouldRetry},a;for(let n=1;n<=i.maxAttempts;n+=1){if(r.signal?.aborted)throw new t(`COMMAND_FAILED`,`request canceled`,{reason:`request_canceled`});if(r.deadline?.isExpired()&&n>1)break;try{let t=await e({attempt:n,maxAttempts:i.maxAttempts,deadline:r.deadline});return r.onEvent?.({phase:r.phase,event:`succeeded`,attempt:n,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs()}),w({phase:r.phase,event:`succeeded`,attempt:n,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs()}),t}catch(e){a=e;let t=r.classifyReason?.(e),o={phase:r.phase,event:`attempt_failed`,attempt:n,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:t};if(r.onEvent?.(o),w(o),n>=i.maxAttempts||i.shouldRetry&&!i.shouldRetry(e,n))break;let s=ie(i.baseDelayMs,i.maxDelayMs,i.jitter,n),c=r.deadline?Math.min(s,r.deadline.remainingMs()):s;if(c<=0)break;let l={phase:r.phase,event:`retry_scheduled`,attempt:n,maxAttempts:i.maxAttempts,delayMs:c,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:t};r.onEvent?.(l),w(l),await ae(c,r.signal)}}let o={phase:r.phase,event:`exhausted`,attempt:i.maxAttempts,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:r.classifyReason?.(a)};throw r.onEvent?.(o),w(o),a||new t(`COMMAND_FAILED`,`retry failed`)}async function re(e,t={}){return C(()=>e(),{maxAttempts:t.attempts,baseDelayMs:t.baseDelayMs,maxDelayMs:t.maxDelayMs,jitter:t.jitter,shouldRetry:t.shouldRetry})}function ie(e,t,n,r){let i=Math.min(t,e*2**(r-1)),a=i*n;return Math.max(0,i+(Math.random()*2-1)*a)}function ae(e,t){return new Promise(n=>{if(t?.aborted){n();return}let r=!1,i=()=>{r||(r=!0,t&&t.removeEventListener(`abort`,o),n())},a=setTimeout(i,e);function o(){clearTimeout(a),i()}t&&t.addEventListener(`abort`,o,{once:!0})})}function w(e){o({level:e.event===`attempt_failed`||e.event===`exhausted`?`warn`:`debug`,phase:`retry`,data:{...e}})}function T(e){return e?.trim()||void 0}function oe(e){return T(e)}function E(e){return new Set(e.split(/[\s,]+/).map(e=>e.trim()).filter(Boolean))}function D(e,t=process.env){let n=T(e)??T(t.AGENT_DEVICE_ANDROID_DEVICE_ALLOWLIST);if(n)return E(n)}const se=new Set([`IOS_BOOT_TIMEOUT`,`IOS_RUNNER_CONNECT_TIMEOUT`,`IOS_TOOL_MISSING`,`ANDROID_BOOT_TIMEOUT`,`ADB_TRANSPORT_UNAVAILABLE`,`CI_RESOURCE_STARVATION_SUSPECTED`]);function ce(e){return se.has(e.toUpperCase())}function O(e){let t=e.error?n(e.error):null,r=e.context?.platform,i=e.context?.phase;if(t?.code===`TOOL_MISSING`)return r===`android`?`ADB_TRANSPORT_UNAVAILABLE`:`IOS_TOOL_MISSING`;let a=t?.details??{},o=typeof a.message==`string`?a.message:void 0,s=typeof a.stdout==`string`?a.stdout:void 0,c=typeof a.stderr==`string`?a.stderr:void 0,l=a.boot&&typeof a.boot==`object`?a.boot:null,u=a.bootstatus&&typeof a.bootstatus==`object`?a.bootstatus:null,d=[e.message,t?.message,e.stdout,e.stderr,o,s,c,typeof l?.stdout==`string`?l.stdout:void 0,typeof l?.stderr==`string`?l.stderr:void 0,typeof u?.stdout==`string`?u.stdout:void 0,typeof u?.stderr==`string`?u.stderr:void 0].filter(Boolean).join(`
2
+ `).toLowerCase();return r===`ios`&&(d.includes(`runner did not accept connection`)||i===`connect`&&(d.includes(`timed out`)||d.includes(`timeout`)||d.includes(`econnrefused`)||d.includes(`connection refused`)||d.includes(`fetch failed`)||d.includes(`socket hang up`)))?`IOS_RUNNER_CONNECT_TIMEOUT`:r===`ios`&&i===`boot`&&(d.includes(`timed out`)||d.includes(`timeout`))?`IOS_BOOT_TIMEOUT`:r===`android`&&i===`boot`&&(d.includes(`timed out`)||d.includes(`timeout`))?`ANDROID_BOOT_TIMEOUT`:d.includes(`resource temporarily unavailable`)||d.includes(`killed: 9`)||d.includes(`cannot allocate memory`)||d.includes(`system is low on memory`)?`CI_RESOURCE_STARVATION_SUSPECTED`:r===`android`&&(d.includes(`device not found`)||d.includes(`no devices`)||d.includes(`device offline`)||d.includes(`offline`)||d.includes(`unauthorized`)||d.includes(`not authorized`)||d.includes(`unable to locate device`)||d.includes(`invalid device`))?`ADB_TRANSPORT_UNAVAILABLE`:t?.code===`COMMAND_FAILED`||d.length>0?`BOOT_COMMAND_FAILED`:`UNKNOWN`}function k(e){switch(e){case`IOS_BOOT_TIMEOUT`:return`Retry simulator boot and inspect simctl bootstatus logs; in CI reduce parallel jobs or use a larger runner.`;case`IOS_RUNNER_CONNECT_TIMEOUT`:return`Retry runner startup, inspect xcodebuild logs, and verify simulator responsiveness before command execution.`;case`ANDROID_BOOT_TIMEOUT`:return`Retry emulator startup and verify sys.boot_completed reaches 1; consider increasing startup budget in CI.`;case`ADB_TRANSPORT_UNAVAILABLE`:return`Check adb server/device transport (adb devices -l), restart adb, and ensure the target device is online and authorized.`;case`CI_RESOURCE_STARVATION_SUSPECTED`:return`CI machine may be resource constrained; reduce parallel jobs or use a larger runner.`;case`IOS_TOOL_MISSING`:return`Xcode command-line tools are missing or not in PATH; run xcode-select --install and verify xcrun works.`;case`BOOT_COMMAND_FAILED`:return`Inspect command stderr/stdout for the failing boot phase and retry after environment validation.`;default:return`Retry once and inspect verbose logs for the failing phase.`}}var le=e({ensureAndroidEmulatorBooted:()=>Se,listAndroidDevices:()=>U,parseAndroidAvdList:()=>G,parseAndroidEmulatorAvdNameOutput:()=>I,parseAndroidFeatureListForTv:()=>H,parseAndroidTargetFromCharacteristics:()=>V,resolveAndroidAvdName:()=>K,resolveAndroidEmulatorAvdName:()=>B,waitForAndroidBoot:()=>J});const A=1e4,ue=[`android.software.leanback`,`android.software.leanback_only`,`android.hardware.type.television`];async function j(e,t,n){let r=Array(e.length),i=0,a=Math.min(t,e.length);return await Promise.all(Array.from({length:a},async()=>{for(;i<e.length;){let t=i;i+=1,r[t]=await n(e[t])}})),r}function M(e){return`${e.stdout}\n${e.stderr}`}function N(e,t){return[`-s`,e,...t]}function P(e){return e.startsWith(`emulator-`)}function F(e){return e.toLowerCase().replace(/_/g,` `).replace(/\s+/g,` `).trim()}function I(e){let t=e.split(`
3
+ `).map(e=>e.trim()).filter(e=>e.length>0);if(t.length!==0)return t.at(-1)===`OK`&&t.pop(),t.join(`
4
+ `).trim()||void 0}async function L(e,t=A){return l(`adb`,N(e,[`shell`,`getprop`,`sys.boot_completed`]),{allowFailure:!0,timeoutMs:t})}async function R(e,t){let n=t.replace(/_/g,` `).trim();if(!P(e))return n||e;let r=await B(e);return r?r.replace(/_/g,` `):n||e}async function z(e,t,r){try{return await r(`adb`,N(e,t),{allowFailure:!0,timeoutMs:1e4})}catch(e){if(de(n(e)))return;throw e}}function de(e){return e.code===`COMMAND_FAILED`&&typeof e.details?.timeoutMs==`number`}async function B(e,t=l){for(let n of[`ro.boot.qemu.avd_name`,`persist.sys.avd_name`]){let r=await z(e,[`shell`,`getprop`,n],t);if(!r)continue;let i=r.stdout.trim();if(r.exitCode===0&&i.length>0)return i}let n=await z(e,[`emu`,`avd`,`name`],t);if(!n)return;let r=I(n.stdout);if(n.exitCode===0&&r)return r}function V(e){let t=e.toLowerCase();return t.includes(`tv`)||t.includes(`leanback`)?`tv`:null}function H(e){return/feature:android\.(software\.leanback(_only)?|hardware\.type\.television)\b/i.test(e)}async function fe(e,t){let n=M(await l(`adb`,N(e,[`shell`,`cmd`,`package`,`has-feature`,t]),{allowFailure:!0,timeoutMs:A})).toLowerCase();return n.includes(`true`)?!0:n.includes(`false`)?!1:null}async function pe(e){return(await j(ue,2,async t=>await fe(e,t))).some(e=>e===!0)}async function me(e){return V(M(await l(`adb`,N(e,[`shell`,`getprop`,`ro.build.characteristics`]),{allowFailure:!0,timeoutMs:A})))===`tv`||await pe(e)||H(M(await l(`adb`,N(e,[`shell`,`pm`,`list`,`features`]),{allowFailure:!0,timeoutMs:A})))?`tv`:`mobile`}async function U(e={}){if(await f(),!await u(`adb`))throw new t(`TOOL_MISSING`,`adb not found in PATH`);let n=e.serialAllowlist??D(void 0),r=await j((await W()).filter(e=>!n||n.has(e.serial)),3,async({serial:e,rawModel:t})=>{let[n,r,i]=await Promise.all([R(e,t),xe(e),me(e)]);return{platform:`android`,id:e,name:n,kind:P(e)?`emulator`:`device`,target:i,booted:r}});return[...r,...await ge(r)]}function he(e){return e.split(`
5
+ `).map(e=>e.trim()).filter(e=>e.length>0&&!e.startsWith(`List of devices`)).map(e=>e.split(/\s+/)).flatMap(e=>{let t=e[0];return t===void 0||e[1]!==`device`?[]:[{serial:t,rawModel:(e.find(e=>e.startsWith(`model:`))??``).replace(`model:`,``)}]})}async function W(){try{return he((await l(`adb`,[`devices`,`-l`],{timeoutMs:A})).stdout)}catch(e){throw c(e)}}function G(e){return e.split(`
6
+ `).map(e=>e.trim()).filter(e=>e.length>0)}function K(e,t){let n=e.find(e=>e===t);if(n)return n;let r=F(t);return e.find(e=>F(e)===r)}async function q(){return G(a(await l(`emulator`,[`-list-avds`],{allowFailure:!0,timeoutMs:A}),`Failed to list Android emulator AVDs`,{hint:`Verify Android emulator tooling is installed and available in PATH.`}).stdout)}async function ge(e){let t=await q().catch(()=>[]),n=new Set(e.filter(e=>e.kind===`emulator`).map(e=>F(e.name)));return t.filter(e=>!n.has(F(e))).map(e=>({platform:`android`,id:e,name:e,kind:`emulator`,target:_e(e),booted:!1}))}function _e(e){return/\b(tv|television)\b/i.test(F(e))?`tv`:`mobile`}function ve(e,t,n){let r=F(t);return e.find(e=>e.platform!==`android`||e.kind!==`emulator`||n&&e.id!==n?!1:F(e.name)===r)}async function ye(e){let n=Date.now(),r=Math.min(1e3,Math.max(50,Math.floor(e.timeoutMs/20)));for(;Date.now()-n<e.timeoutMs;){try{let t=await be(e.avdName,e.serial);if(t)return{platform:`android`,id:t,name:e.avdName,kind:`emulator`,target:`mobile`,booted:!1}}catch{}await d(r)}throw new t(`COMMAND_FAILED`,`Android emulator did not appear in time`,{avdName:e.avdName,serial:e.serial,timeoutMs:e.timeoutMs,hint:`Check emulator logs and verify the AVD can start from command line.`})}async function be(e,t){let n=F(e),r=(await W()).filter(e=>t&&e.serial!==t?!1:P(e.serial));for(let e of r)if(F(e.rawModel)===n||F(await R(e.serial,e.rawModel))===n)return e.serial}async function xe(e){try{return(await L(e)).stdout.trim()===`1`}catch{return!1}}async function Se(e){await f();let n=e.avdName.trim();if(!n)throw new t(`INVALID_ARGS`,`Android emulator boot requires a non-empty AVD name.`);let i=e.timeoutMs??12e4;if(!await u(`adb`))throw new t(`TOOL_MISSING`,`adb not found in PATH`);if(!await u(`emulator`))throw new t(`TOOL_MISSING`,`emulator not found in PATH`);let a=await q(),o=K(a,n);if(!o)throw new t(`DEVICE_NOT_FOUND`,`No Android emulator AVD named ${e.avdName}`,{requestedAvdName:n,availableAvds:a,hint:"Run `emulator -list-avds` and pass an existing AVD name to --device."});let s=Date.now(),c=ve(await U(),o,e.serial),l=c&&P(c.id)?c:void 0;if(!l){let t=[`-avd`,o];e.headless&&t.push(`-no-window`,`-no-audio`),r(`emulator`,t)}let d=l??await ye({avdName:o,serial:e.serial,timeoutMs:i}),p=Date.now()-s,m=Math.max(1e3,i-p);await J(d.id,m);let h=(await U()).find(e=>e.id===d.id);return h?{...h,name:o,booted:!0}:{...d,name:o,booted:!0}}async function J(e,r=6e4){let i=r,a=S.fromTimeoutMs(i),o=Math.min(1e3,Math.max(50,Math.floor(i/20))),s=Math.max(1,Math.ceil(i/o)),c,l=!1;try{await C(async({deadline:n})=>{if(n?.isExpired())throw l=!0,new t(`COMMAND_FAILED`,`Android boot deadline exceeded`,{serial:e,timeoutMs:r,elapsedMs:a.elapsedMs(),message:`timeout`});let o=Math.max(1e3,n?.remainingMs()??i),s=await L(e,Math.min(o,A));if(c=s,s.stdout.trim()!==`1`)throw new t(`COMMAND_FAILED`,`Android device is still booting`,{serial:e,stdout:s.stdout,stderr:s.stderr,exitCode:s.exitCode})},{maxAttempts:s,baseDelayMs:o,maxDelayMs:o,jitter:0,shouldRetry:e=>{let t=O({error:e,stdout:c?.stdout,stderr:c?.stderr,context:{platform:`android`,phase:`boot`}});return t!==`ADB_TRANSPORT_UNAVAILABLE`&&t!==`ANDROID_BOOT_TIMEOUT`}},{deadline:a,phase:`boot`,classifyReason:e=>O({error:e,stdout:c?.stdout,stderr:c?.stderr,context:{platform:`android`,phase:`boot`}})})}catch(r){let o=n(r),s=c?.stdout,u=c?.stderr,d=c?.exitCode,f=O({error:r,stdout:s,stderr:u,context:{platform:`android`,phase:`boot`}});f===`BOOT_COMMAND_FAILED`&&o.message===`Android device is still booting`&&(f=`ANDROID_BOOT_TIMEOUT`);let p={serial:e,timeoutMs:i,elapsedMs:a.elapsedMs(),reason:f,hint:k(f),stdout:s,stderr:u,exitCode:d};throw l||f===`ANDROID_BOOT_TIMEOUT`?new t(`COMMAND_FAILED`,`Android device did not finish booting in time`,p):o.code===`TOOL_MISSING`?new t(`TOOL_MISSING`,o.message,{...p,...o.details??{}}):f===`ADB_TRANSPORT_UNAVAILABLE`?new t(`COMMAND_FAILED`,o.message,{...p,...o.details??{}}):new t(o.code,o.message,{...p,...o.details??{}},o.cause)}}async function Ce(e,t,n){return await i(e)(t,n)}function we(e){return{platform:`android`,id:e,name:e,kind:e.startsWith(`emulator-`)?`emulator`:`device`,booted:!0}}function Te(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`no shell command implementation`)||n.includes(`unknown command`)}var Ee=e({prepareAndroidInstallArtifact:()=>Y});async function Y(e,t){let n=e.kind===`url`&&h(e.url),r=await m({source:e,isInstallablePath:(e,t)=>t.isFile()&&De(e),installableLabel:`Android installable (.apk or .aab)`,allowArchiveExtraction:e.kind!==`url`||n,signal:t?.signal});try{let e=t?.resolveIdentity===!1?{}:await Oe(r.installablePath);return{archivePath:r.archivePath,installablePath:r.installablePath,packageName:e.packageName,cleanup:r.cleanup}}catch(e){throw await r.cleanup(),e}}function De(e){let t=g.extname(e).toLowerCase();return t===`.apk`||t===`.aab`}async function Oe(e){let t=g.extname(e).toLowerCase();return t!==`.apk`&&t!==`.aab`?{}:{packageName:await p(e)}}async function ke(e,t={}){let n=[`logcat`,`-d`,`-v`,`time`];t.lines!==void 0&&n.push(`-t`,String(Math.max(1,Math.floor(t.lines))));let r=await e(n,{allowFailure:!0,timeoutMs:t.timeoutMs,signal:t.signal});if(r.exitCode!==0)throw s(`Failed to capture Android logcat`,r);return r.stdout}function X(e,n={}){if(!e.spawn)throw new t(`UNSUPPORTED_OPERATION`,`Android ADB provider does not support streams`,{capability:`adb.spawn`});let r=[`logcat`,`-v`,`time`];n.pid&&r.push(`--pid`,n.pid);let i=e.spawn(r,{stdio:[`ignore`,`pipe`,`pipe`],signal:n.signal});return n.output&&i.stdout&&i.stdout.pipe(n.output,{end:!1}),i}const Z=new Set([`com.google.android.inputmethod.latin`,`com.samsung.android.honeyboard`,`com.touchtype.swiftkey`,`com.microsoft.swiftkey`]);function Q(e){let t=$(e.packageName),n=(e.resourceId??``).toLowerCase(),r=$(e.activeInputMethodPackage);if(t&&r&&t===r)return{inputMethodOwned:!0,source:`active-input-method`};if(r&&n.startsWith(`${r}:id/`))return{inputMethodOwned:!0,source:`active-input-method-resource`};if(t&&Z.has(t))return{inputMethodOwned:!0,source:`known-ime-package`};for(let e of Z)if(n.startsWith(`${e}:id/`))return{inputMethodOwned:!0,source:`known-ime-resource`};return{inputMethodOwned:!1,source:`app`}}function Ae(e,t,n){return Q({packageName:e,resourceId:t,activeInputMethodPackage:n}).inputMethodOwned}function je(e){return Q(e).inputMethodOwned}function Me(e){let t=$(e);return!!(t&&Z.has(t))}function Ne(e){let t=(e??``).toLowerCase();for(let e of Z)if(t.startsWith(`${e}:id/`))return!0;return!1}function Pe(e,t,n){return!e&&!t?`unknown`:Ae(e,t,n)?`ime`:`app`}function Fe(e){for(let t of[/\bmCurMethodId=([^\s]+)/i,/\bmCurId=([^\s]+)/i,/\bmCurrentInputMethodId=([^\s]+)/i,/\bcurMethodId=([^\s]+)/i]){let n=Ie(e.match(t)?.[1]);if(n)return n}}function Ie(e){let t=(e??``).trim();if(t)return $((t.split(`/`)[0]??``).match(/[a-zA-Z0-9_.]+/)?.[0])}function $(e){return(e??``).trim().toLowerCase()||void 0}export{b as C,ee as D,ne as E,_ as O,S,re as T,O as _,Ne as a,D as b,X as c,we as d,Te as f,k as g,J as h,Me as i,te as k,Ee as l,le as m,Q as n,Fe as o,Ce as p,je as r,ke as s,Pe as t,Y as u,ce as v,C as w,oe as x,E as y};
@@ -1,5 +1 @@
1
- import e from"node:crypto";import t from"node:fs/promises";import r from"node:path";import{promises as a}from"node:fs";import{normalizeError as i,AppError as n}from"./485.js";import{withDiagnosticTimer as o,emitDiagnostic as s}from"./8875.js";import{readVersion as d,findProjectRoot as l}from"./1404.js";import{openAndroidDevice as u,runAndroidAdb as c,resolveAndroidApp as p,resolveAndroidTouchInjector as m,closeAndroidApp as h,openAndroidApp as f,resolveAndroidAdbProvider as w,resolveAndroidAdbExecutor as _,installAndroidAdbPackage as A}from"./3340.js";import{appSwitcherAndroid as g,homeAndroid as y,scrollAndroid as N,longPressAndroid as v,typeAndroid as I,getAndroidScreenSize as M,pressAndroid as C,focusAndroid as k,swipeAndroid as x,rotateAndroid as S,fillAndroid as D,backAndroid as E}from"./input-actions.js";import{parseInstrumentationRecords as R,readAndroidHelperManifestInteger as b,readInstrumentationResultNumber as F,snapshotAndroid as L,readAndroidHelperManifestLiteral as O}from"./9722.js";import{requireLocationCoordinates as P,snapshotCaptureAnnotationsFrom as T}from"./2948.js";import{parseAppearanceAction as U,summarizeCommandAttemptFailures as $,parseSettingState as G,parsePermissionTarget as K,parsePermissionAction as V}from"./8225.js";import{writeAndroidClipboardText as H,readAndroidClipboardText as B}from"./9624.js";import{sleep as q}from"./5560.js";let j="android-multitouch-helper-v1",z="ANDROID_MULTITOUCH_HELPER_NO_FINAL_RESULT",J="ANDROID_MULTITOUCH_HELPER_REPORTED_FAILURE";async function X(e,t){let r=await et(e,{kind:"swipe",...t});if(r)return r;try{return await er(e,{kind:"swipe",...t})}catch(r){return s({level:"warn",phase:"android_swipe_helper_fallback",data:{error:i(r).message}}),await x(e,t.x1,t.y1,t.x2,t.y2,t.durationMs),{backend:"adb-input-swipe-fallback"}}}async function Q(e,t){if(!Number.isFinite(t.scale)||t.scale<=0)throw new n("INVALID_ARGS","gesture pinch requires scale > 0");let r=await Z(e,t.x,t.y);return await ee(e,{kind:"pinch",x:r.x,y:r.y,scale:t.scale,durationMs:t.durationMs})}async function W(e,t){if(!Number.isFinite(t.degrees))throw new n("INVALID_ARGS","gesture rotate requires finite degrees");if(void 0!==t.velocity&&(!Number.isFinite(t.velocity)||0===t.velocity))throw new n("INVALID_ARGS","gesture rotate velocity must be a non-zero number");let r=await Z(e,t.x,t.y),a=t.degrees;return await ee(e,{kind:"rotate",x:r.x,y:r.y,degrees:a,durationMs:t.durationMs})}async function Y(e,t){if(!Number.isFinite(t.scale)||t.scale<=0)throw new n("INVALID_ARGS","gesture transform requires scale > 0");if(!Number.isFinite(t.degrees))throw new n("INVALID_ARGS","gesture transform requires finite degrees");if(![t.x,t.y,t.dx,t.dy].every(Number.isFinite))throw new n("INVALID_ARGS","gesture transform requires finite x y dx dy");return await ee(e,{kind:"transform",x:t.x,y:t.y,dx:t.dx,dy:t.dy,scale:t.scale,degrees:t.degrees,durationMs:t.durationMs})}async function Z(e,t,r){if(void 0!==t&&void 0!==r)return{x:t,y:r};let a=await M(e);return{x:Math.round(a.width/2),y:Math.round(a.height/2)}}async function ee(e,t){let r=await et(e,t);return r||await er(e,t)}async function et(e,t){let r=m(e);if(r)return{backend:"provider-native-touch",...await r(t)??{}}}async function er(e,t){let r=_(e),a=await ei(),i=w(e),n=await o("android_multitouch_helper_install",async()=>{var t;return await en({adb:r,adbProvider:i,artifact:a,deviceKey:(t=e,`${t.platform}:${t.id}`)})},{packageName:a.manifest.packageName,versionCode:a.manifest.versionCode});s({phase:"android_multitouch_helper_install_decision",data:n});let d=await o("android_multitouch_helper_gesture",async()=>await ea({adb:r,request:function(e){var t;let r=Math.round(void 0!==(t=e).durationMs?t.durationMs:"swipe"===t.kind||"pinch"===t.kind?300:Math.min(Math.max(300,16*Math.ceil(Math.abs(t.degrees)/3)),2400));switch(e.kind){case"swipe":return{kind:"swipe",x1:Math.round(e.x1),y1:Math.round(e.y1),x2:Math.round(e.x2),y2:Math.round(e.y2),durationMs:r};case"pinch":return{kind:"pinch",x:Math.round(e.x),y:Math.round(e.y),scale:e.scale,radius:160,durationMs:r};case"rotate":return{kind:"rotate",x:Math.round(e.x),y:Math.round(e.y),degrees:e.degrees,radius:160,durationMs:r};case"transform":return{kind:"transform",x:Math.round(e.x),y:Math.round(e.y),dx:Math.round(e.dx),dy:Math.round(e.dy),scale:e.scale,degrees:e.degrees,durationMs:r}}}(t),packageName:a.manifest.packageName,instrumentationRunner:a.manifest.instrumentationRunner}),{packageName:a.manifest.packageName,version:a.manifest.version});return{backend:"android-multitouch-helper",helperVersion:a.manifest.version,installReason:n.reason,...d}}async function ea(e){let t,r=Buffer.from(JSON.stringify({protocol:j,...e.request})).toString("base64"),a=await e.adb(["shell","am","instrument","-w","-e","payloadBase64",r,e.instrumentationRunner],{allowFailure:!0,timeoutMs:45e3});try{t=function(e){let t=R(e).results.find(e=>e.agentDeviceProtocol===j);if(!t)throw new n(z,"Android multi-touch helper did not return a final result");if("true"!==t.ok){var r;throw new n(J,(r=t).message&&"null"!==r.message?r.message:r.errorType||"Android multi-touch helper returned an error",{errorType:t.errorType,helper:t})}return{kind:t.kind,helperApiVersion:t.helperApiVersion,injectedEvents:F(t.injectedEvents),elapsedMs:F(t.elapsedMs)}}(`${a.stdout}
2
- ${a.stderr}`)}catch(e){if(e instanceof n){if(e.code===J)throw new n("COMMAND_FAILED",e.message,e.details,e);if(e.code!==z)throw e}throw new n("COMMAND_FAILED",0===a.exitCode?"Android multi-touch helper output could not be parsed":"Android multi-touch helper failed before returning parseable output",{stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode},e)}if(0!==a.exitCode)throw new n("COMMAND_FAILED","Android multi-touch helper failed",{stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode,helper:t});return t}async function ei(){let e=d(),a=r.join(l(),"android-multitouch-helper","dist"),o=r.join(a,`agent-device-android-multitouch-helper-${e}.manifest.json`);try{let e=function(e){var t,r;if(!e||"object"!=typeof e||Array.isArray(e))throw new n("INVALID_ARGS","Android multi-touch helper manifest must be an object.");return{name:ec(e.name,"name","android-multitouch-helper"),version:eu(e.version,"version"),assetName:eu(e.assetName,"assetName"),sha256:function(e){let t=eu(e,"sha256").trim().toLowerCase();if(64!==t.length||!/^[0-9a-f]+$/.test(t))throw new n("INVALID_ARGS","Android multi-touch helper manifest sha256 must be a 64-character hex string.");return t}(e.sha256),packageName:ec(e.packageName,"packageName","com.callstack.agentdevice.multitouchhelper"),versionCode:(t=e.versionCode,r="versionCode",b(t,r,"multi-touch helper")),instrumentationRunner:ec(e.instrumentationRunner,"instrumentationRunner","com.callstack.agentdevice.multitouchhelper/.MultiTouchInstrumentation"),statusProtocol:ec(e.statusProtocol,"statusProtocol",j)}}(JSON.parse(await t.readFile(o,"utf8"))),i=r.join(a,e.assetName);return await t.access(i),{apkPath:i,manifest:e}}catch(e){throw new n("UNSUPPORTED_OPERATION","Android touch gestures require the bundled Android touch helper artifact, but it was not found or could not be read",{manifestPath:o,error:i(e).message},e)}}async function en(e){let{adb:t,artifact:r}=e,a=r.manifest.packageName,i=r.manifest.versionCode,o=`${e.deviceKey}\0${a}\0${i}`;if(eo.has(o))return{packageName:a,versionCode:i,installed:!1,reason:"current"};let s=await es(t,a);if(void 0!==s&&s>=i)return eo.add(o),{packageName:a,versionCode:i,installedVersionCode:s,installed:!1,reason:"current"};await ed(r);let d=await A(r.apkPath,{provider:e.adbProvider,replace:!0,allowTestPackages:!0,allowFailure:!0,timeoutMs:3e4});if(0!==d.exitCode)throw new n("COMMAND_FAILED","Failed to install Android multi-touch helper",{packageName:a,versionCode:i,stdout:d.stdout,stderr:d.stderr,exitCode:d.exitCode});return eo.add(o),{packageName:a,versionCode:i,installedVersionCode:s,installed:!0,reason:void 0===s?"missing":"outdated"}}let eo=new Set;async function es(e,t){let r=await e(["shell","cmd","package","list","packages","--show-versioncode",t],{allowFailure:!0,timeoutMs:5e3});if(0!==r.exitCode)return;let a=RegExp(`package:${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(?:\\s|$).*versionCode:(\\d+)`).exec(`${r.stdout}
3
- ${r.stderr}`);return a?Number(a[1]):void 0}async function ed(e){let t=await el(e.apkPath);if(t!==e.manifest.sha256)throw new n("COMMAND_FAILED","Android multi-touch helper APK checksum mismatch",{apkPath:e.apkPath,expectedSha256:e.manifest.sha256,actualSha256:t})}async function el(r){let a=e.createHash("sha256");return a.update(await t.readFile(r)),a.digest("hex")}function eu(e,t){if("string"!=typeof e||0===e.length)throw new n("INVALID_ARGS",`Android multi-touch helper manifest ${t} is required.`);return e}function ec(e,t,r){return O(e,t,r,"multi-touch helper")}let ep=["window_animation_scale","transition_animation_scale","animator_duration_scale"];async function em(e,t,r,a,i){switch(t.toLowerCase()){case"wifi":{let t=G(r);await c(e,["shell","svc","wifi",t?"enable":"disable"]);return}case"airplane":{let t=G(r);await c(e,["shell","settings","put","global","airplane_mode_on",t?"1":"0"]),await c(e,["shell","am","broadcast","-a","android.intent.action.AIRPLANE_MODE","--ez","state",t?"true":"false"]);return}case"location":{if("set"===r.toLowerCase()){if("emulator"!==e.kind)throw new n("UNSUPPORTED_OPERATION","Android precise location coordinates are supported only on emulators.",{deviceId:e.id,hint:"Use an Android emulator for adb emu geo fix, or configure location through device/provider tooling."});let{latitude:t,longitude:r}=P(i);return await c(e,["emu","geo","fix",String(r),String(t)]),{latitude:t,longitude:r}}let t=G(r);await c(e,["shell","settings","put","secure","location_mode",t?"3":"0"]);return}case"animations":{let t=G(r)?"1":"0";for(let r of ep)await c(e,["shell","settings","put","global",r,t]);return{scale:t,keys:[...ep]}}case"appearance":{let t=await ef(e,r);await c(e,["shell","cmd","uimode","night","dark"===t?"yes":"no"]);return}case"clear-app-state":{if("clear"!==r.toLowerCase())throw new n("INVALID_ARGS","settings clear-app-state only supports clear.");if(!a)throw new n("INVALID_ARGS","settings clear-app-state requires an app id or an active app session.");let t=await p(e,a);if("intent"===t.type)throw new n("INVALID_ARGS","settings clear-app-state requires a package name, not an intent.");await c(e,["shell","am","force-stop",t.value],{allowFailure:!0});let i=await c(e,["shell","pm","clear",t.value],{allowFailure:!0});if(0!==i.exitCode||!/\bSuccess\b/i.test(i.stdout))throw new n("COMMAND_FAILED",`Failed to clear Android app data for ${t.value}`,{package:t.value,stdout:i.stdout,stderr:i.stderr,exitCode:i.exitCode});return{package:t.value,cleared:!0}}case"fingerprint":{let t=function(e){let t=e.trim().toLowerCase();if("match"===t)return"match";if("nonmatch"===t)return"nonmatch";throw new n("INVALID_ARGS",`Invalid fingerprint state: ${e}. Use match|nonmatch.`)}(r);await eh(e,t);return}case"permission":{if(!a)throw new n("INVALID_ARGS","permission setting requires an active app in session");let t=V(r),o=function(e,t){let r=K(e);if(t?.trim())throw new n("INVALID_ARGS",`Permission mode is only supported for photos. Received: ${t}.`);if("camera"===r)return{kind:"pm",value:"android.permission.CAMERA",type:"camera"};if("microphone"===r)return{kind:"pm",value:"android.permission.RECORD_AUDIO",type:"microphone"};if("photos"===r)return{kind:"pm",value:"android.permission.READ_MEDIA_IMAGES",type:"photos"};if("contacts"===r)return{kind:"pm",value:"android.permission.READ_CONTACTS",type:"contacts"};if("notifications"===r)return{kind:"notifications",appOps:"POST_NOTIFICATION",permission:"android.permission.POST_NOTIFICATIONS"};throw new n("INVALID_ARGS",`Unsupported permission target on Android: ${e}. Use camera|microphone|photos|contacts|notifications.`)}(i?.permissionTarget,i?.permissionMode);if("notifications"===o.kind)return void await e_(e,a,t,o);let s="grant"===t?"grant":"revoke";if("photos"===o.type)return void await ew(e,a,s);await c(e,["shell","pm",s,a,o.value]);return}default:throw new n("INVALID_ARGS",`Unsupported setting: ${t}`)}}async function eh(e,t){var r;let a,i,o=(r=e,i=[["shell","cmd","fingerprint","touch",a="match"===t?"1":"9999"],["shell","cmd","fingerprint","finger",a]],"emulator"===r.kind&&i.push(["emu","finger","touch",a]),i),s=[];for(let t of o){let r=await c(e,t,{allowFailure:!0});if(0===r.exitCode)return;s.push({args:t,stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode})}let d=$(s);if(s.length>0&&s.every(e=>{var t,r;let a;return t=e.stdout,r=e.stderr,(a=`${t}
4
- ${r}`.toLowerCase()).includes("unknown command")||a.includes("can't find service: fingerprint")||a.includes("service fingerprint was not found")||a.includes("fingerprint cmd unavailable")||a.includes("emu command is not supported")||a.includes("emulator console is not running")||a.includes("fingerprint")&&a.includes("not found")}))throw new n("UNSUPPORTED_OPERATION","Android fingerprint simulation is not supported on this target/runtime.",{deviceId:e.id,action:t,hint:"Use an Android emulator with biometric support, or a device/runtime that exposes cmd fingerprint.",attempts:d});throw new n("COMMAND_FAILED","Failed to simulate Android fingerprint.",{deviceId:e.id,action:t,attempts:d})}async function ef(e,t){let r=U(t);if("toggle"!==r)return r;let a=await c(e,["shell","cmd","uimode","night"],{allowFailure:!0});if(0!==a.exitCode)throw new n("COMMAND_FAILED","Failed to read current Android appearance",{stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});let i=function(e,t){let r=/night mode:\s*(yes|no|auto)\b/i.exec(`${e}
5
- ${t}`);if(!r)return null;let a=r[1]?.toLowerCase();return"yes"===a?"dark":"no"===a?"light":"auto"===a?"auto":null}(a.stdout,a.stderr);if(!i)throw new n("COMMAND_FAILED","Unable to determine current Android appearance for toggle",{stdout:a.stdout,stderr:a.stderr});return"auto"===i?"dark":"dark"===i?"light":"dark"}async function ew(e,t,r){let a=await eA(e),i=[];for(let n of null!==a&&a>=33?["android.permission.READ_MEDIA_IMAGES","android.permission.READ_EXTERNAL_STORAGE"]:["android.permission.READ_EXTERNAL_STORAGE","android.permission.READ_MEDIA_IMAGES"]){let a=await c(e,["shell","pm",r,t,n],{allowFailure:!0});if(0===a.exitCode)return;i.push({permission:n,stderr:a.stderr,exitCode:a.exitCode})}throw new n("COMMAND_FAILED",`Failed to ${r} Android photos permission`,{appPackage:t,sdkInt:a,attempts:i})}async function e_(e,t,r,a){"grant"===r?await c(e,["shell","pm","grant",t,a.permission],{allowFailure:!0}):(await c(e,["shell","pm","revoke",t,a.permission],{allowFailure:!0}),"reset"===r&&(await c(e,["shell","pm","clear-permission-flags",t,a.permission,"user-set"],{allowFailure:!0}),await c(e,["shell","pm","clear-permission-flags",t,a.permission,"user-fixed"],{allowFailure:!0}))),await c(e,["shell","appops","set",t,a.appOps,"grant"===r?"allow":"deny"===r?"deny":"default"])}async function eA(e){let t=await c(e,["shell","getprop","ro.build.version.sdk"],{allowFailure:!0});if(0!==t.exitCode)return null;let r=Number.parseInt(t.stdout.trim(),10);return!Number.isFinite(r)||r<=0?null:r}let eg=Buffer.from([137,80,78,71,13,10,26,10]);async function ey(e,t,r={}){if(!1===r.stabilize)return void await eI(e,t);await eN(e);try{await q(1e3),await eI(e,t)}finally{await ev(e).catch(()=>{})}}async function eN(e){let t=t=>c(e,["shell",t],{allowFailure:!0});await t("settings put global sysui_demo_allowed 1");let r=e=>t(`am broadcast -a com.android.systemui.demo -e command ${e}`);await r("clock -e hhmm 0941"),await r("notifications -e visible false")}async function ev(e){await c(e,["shell","am broadcast -a com.android.systemui.demo -e command exit"],{allowFailure:!0})}async function eI(e,t){let r=await c(e,["exec-out","screencap","-p"],{binaryStdout:!0});if(!r.stdoutBuffer)throw new n("COMMAND_FAILED","Failed to capture screenshot");let i=r.stdoutBuffer.indexOf(eg);if(i<0)throw new n("COMMAND_FAILED","Screenshot data does not contain a valid PNG header");let o=function(e,t){let r=t+eg.length;for(;r+8<=e.length;){let t=e.readUInt32BE(r),a=r+4,i=e.toString("ascii",a,a+4),n=r+12+t;if(n>e.length)break;if("IEND"===i)return n;r=n}return null}(r.stdoutBuffer,i);if(!o)throw new n("COMMAND_FAILED","Screenshot data does not contain a complete PNG payload");await a.writeFile(t,r.stdoutBuffer.subarray(i,o))}function eM(e){return{open:(t,r)=>f(e,t,{activity:r?.activity,appBundleId:r?.appBundleId,launchArgs:r?.launchArgs,url:r?.url}),openDevice:()=>u(e),close:t=>h(e,t),tap:(t,r)=>C(e,t,r),doubleTap:async(t,r)=>{await C(e,t,r),await C(e,t,r)},swipe:(t,r,a,i,n)=>X(e,{x1:t,y1:r,x2:a,y2:i,durationMs:n}),pan:(t,r,a,i,n)=>X(e,{x1:t,y1:r,x2:a,y2:i,durationMs:n}),fling:(t,r,a,i,n)=>X(e,{x1:t,y1:r,x2:a,y2:i,durationMs:n}),longPress:(t,r,a)=>v(e,t,r,a),focus:(t,r)=>k(e,t,r),type:(t,r)=>I(e,t,r),fill:(t,r,a,i)=>D(e,t,r,a,i),scroll:(t,r)=>N(e,t,r),pinch:(t,r,a)=>Q(e,{scale:t,x:r,y:a}),screenshot:(t,r)=>ey(e,t,r),snapshot:async t=>{let r=await o("snapshot_capture",async()=>await L(e,{appBundleId:t?.appBundleId,interactiveOnly:t?.interactiveOnly,depth:t?.depth,scope:t?.scope,raw:t?.raw,helperSessionScope:t?.appBundleId?"daemon-session":"command"}),{backend:"android"});return{nodes:r.nodes??[],truncated:r.truncated??!1,backend:"android",...T(r)}},back:t=>E(e),home:()=>y(e),rotate:t=>S(e,t),rotateGesture:(t,r,a,i)=>W(e,{degrees:t,x:r,y:a,velocity:i}),transformGesture:t=>Y(e,t),appSwitcher:()=>g(e),readClipboard:()=>B(e),writeClipboard:t=>H(e,t),setSetting:(t,r,a,i)=>em(e,t,r,a,i)}}export{eM as createAndroidInteractor};
1
+ import{B as e,F as t}from"./sdk-contracts.js";import{F as n,c as r,d as i,j as a,l as o,n as s,o as c,p as l}from"./sdk-android-adb.js";import{Tt as u,hn as d}from"./tv-remote.js";import{n as ee,t as te}from"./version.js";import{t as ne}from"./timeouts.js";import{c as f,d as p,l as re,n as m}from"./app-lifecycle.js";import{p as h}from"./android-input-ownership.js";import{a as g,s as _}from"./device-input-state.js";import{d as v,f as y,l as ie,u as ae}from"./snapshot-helper.js";import{Dt as oe}from"./internal/daemon.js";import{a as se,c as ce,d as le,f as ue,i as de,l as b,m as x,n as S,o as C,p as w,r as T,t as E,u as D}from"./input-actions.js";import{a as O,i as k,n as A,r as j,t as M}from"./setting-state.js";import N from"node:crypto";import{promises as P}from"node:fs";import F from"node:path";import I from"node:fs/promises";const L=`android-multitouch-helper-v1`,R=`ANDROID_MULTITOUCH_HELPER_NO_FINAL_RESULT`,z=`ANDROID_MULTITOUCH_HELPER_REPORTED_FAILURE`;async function B(t,n){let r=await G(t,{kind:`swipe`,...n});if(r)return r;try{return await K(t,{kind:`swipe`,...n})}catch(r){return a({level:`warn`,phase:`android_swipe_helper_fallback`,data:{error:e(r).message}}),await w(t,n.x1,n.y1,n.x2,n.y2,n.durationMs),{backend:`adb-input-swipe-fallback`}}}async function V(e,n){if(!Number.isFinite(n.scale)||n.scale<=0)throw new t(`INVALID_ARGS`,`gesture pinch requires scale > 0`);let r=await U(e,n.x,n.y);return await W(e,{kind:`pinch`,x:r.x,y:r.y,scale:n.scale,durationMs:n.durationMs})}async function H(e,n){if(!Number.isFinite(n.degrees))throw new t(`INVALID_ARGS`,`gesture rotate requires finite degrees`);if(n.velocity!==void 0&&(!Number.isFinite(n.velocity)||n.velocity===0))throw new t(`INVALID_ARGS`,`gesture rotate velocity must be a non-zero number`);let r=await U(e,n.x,n.y),i=n.degrees;return await W(e,{kind:`rotate`,x:r.x,y:r.y,degrees:i,durationMs:n.durationMs})}async function fe(e,n){if(!Number.isFinite(n.scale)||n.scale<=0)throw new t(`INVALID_ARGS`,`gesture transform requires scale > 0`);if(!Number.isFinite(n.degrees))throw new t(`INVALID_ARGS`,`gesture transform requires finite degrees`);if(![n.x,n.y,n.dx,n.dy].every(Number.isFinite))throw new t(`INVALID_ARGS`,`gesture transform requires finite x y dx dy`);return await W(e,{kind:`transform`,x:n.x,y:n.y,dx:n.dx,dy:n.dy,scale:n.scale,degrees:n.degrees,durationMs:n.durationMs})}async function U(e,t,n){if(t!==void 0&&n!==void 0)return{x:t,y:n};let r=await se(e);return{x:Math.round(r.width/2),y:Math.round(r.height/2)}}async function W(e,t){return await G(e,t)||await K(e,t)}async function G(e,t){let n=i(e);if(n)return{backend:`provider-native-touch`,...await n(t)??{}}}async function K(e,t){let i=r(e),s=await _e(),c=o(e),l=await n(`android_multitouch_helper_install`,async()=>await ye({adb:i,adbProvider:c,artifact:s,deviceKey:Ce(e)}),{packageName:s.manifest.packageName,versionCode:s.manifest.versionCode});a({phase:`android_multitouch_helper_install_decision`,data:l});let u=await n(`android_multitouch_helper_gesture`,async()=>await me({adb:i,request:q(t),packageName:s.manifest.packageName,instrumentationRunner:s.manifest.instrumentationRunner}),{packageName:s.manifest.packageName,version:s.manifest.version});return{backend:`android-multitouch-helper`,helperVersion:s.manifest.version,installReason:l.reason,...u}}function q(e){let t=Math.round(pe(e));switch(e.kind){case`swipe`:return{kind:`swipe`,x1:Math.round(e.x1),y1:Math.round(e.y1),x2:Math.round(e.x2),y2:Math.round(e.y2),durationMs:t};case`pinch`:return{kind:`pinch`,x:Math.round(e.x),y:Math.round(e.y),scale:e.scale,radius:160,durationMs:t};case`rotate`:return{kind:`rotate`,x:Math.round(e.x),y:Math.round(e.y),degrees:e.degrees,radius:160,durationMs:t};case`transform`:return{kind:`transform`,x:Math.round(e.x),y:Math.round(e.y),dx:Math.round(e.dx),dy:Math.round(e.dy),scale:e.scale,degrees:e.degrees,durationMs:t}}}function pe(e){if(e.durationMs!==void 0)return e.durationMs;if(e.kind===`swipe`||e.kind===`pinch`)return 300;let t=Math.ceil(Math.abs(e.degrees)/3)*16;return Math.min(Math.max(300,t),2400)}async function me(e){let n=Buffer.from(JSON.stringify({protocol:L,...e.request})).toString(`base64`),r=await e.adb([`shell`,`am`,`instrument`,`-w`,`-e`,`payloadBase64`,n,e.instrumentationRunner],{allowFailure:!0,timeoutMs:45e3}),i;try{i=he(`${r.stdout}\n${r.stderr}`)}catch(e){if(e instanceof t){if(e.code===z)throw new t(`COMMAND_FAILED`,e.message,e.details,e);if(e.code!==R)throw e}throw new t(`COMMAND_FAILED`,r.exitCode===0?`Android multi-touch helper output could not be parsed`:`Android multi-touch helper failed before returning parseable output`,{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode},e)}if(r.exitCode!==0)throw new t(`COMMAND_FAILED`,`Android multi-touch helper failed`,l(r,{helper:i}));return i}function he(e){let n=ie(e).results.find(e=>e.agentDeviceProtocol===L);if(!n)throw new t(R,`Android multi-touch helper did not return a final result`);if(n.ok!==`true`)throw new t(z,ge(n),{errorType:n.errorType,helper:n});return{kind:n.kind,helperApiVersion:n.helperApiVersion,injectedEvents:y(n.injectedEvents),elapsedMs:y(n.elapsedMs)}}function ge(e){return e.message&&e.message!==`null`?e.message:e.errorType||`Android multi-touch helper returned an error`}async function _e(){let n=ee(),r=F.join(te(),`android-multitouch-helper`,`dist`),i=F.join(r,`agent-device-android-multitouch-helper-${n}.manifest.json`);try{let e=ve(JSON.parse(await I.readFile(i,`utf8`))),t=F.join(r,e.assetName);return await I.access(t),{apkPath:t,manifest:e}}catch(n){throw new t(`UNSUPPORTED_OPERATION`,`Android touch gestures require the bundled Android touch helper artifact, but it was not found or could not be read`,{manifestPath:i,error:e(n).message},n)}}function ve(e){if(!e||typeof e!=`object`||Array.isArray(e))throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest must be an object.`);let n=e;return{name:X(n.name,`name`,`android-multitouch-helper`),version:Y(n.version,`version`),assetName:Y(n.assetName,`assetName`),sha256:Te(n.sha256),packageName:X(n.packageName,`packageName`,`com.callstack.agentdevice.multitouchhelper`),versionCode:we(n.versionCode,`versionCode`),instrumentationRunner:X(n.instrumentationRunner,`instrumentationRunner`,`com.callstack.agentdevice.multitouchhelper/.MultiTouchInstrumentation`),statusProtocol:X(n.statusProtocol,`statusProtocol`,L)}}async function ye(e){let{adb:t,artifact:n}=e,r=n.manifest.packageName,i=n.manifest.versionCode,a=`${e.deviceKey}\0${r}\0${i}`;if(J.has(a))return{packageName:r,versionCode:i,installed:!1,reason:`current`};let o=await be(t,r);if(o!==void 0&&o>=i)return J.add(a),{packageName:r,versionCode:i,installedVersionCode:o,installed:!1,reason:`current`};await xe(n);let l=await c(n.apkPath,{provider:e.adbProvider,replace:!0,allowTestPackages:!0,allowFailure:!0,timeoutMs:3e4});if(l.exitCode!==0)throw s(`Failed to install Android multi-touch helper`,l,{packageName:r,versionCode:i});return J.add(a),{packageName:r,versionCode:i,installedVersionCode:o,installed:!0,reason:o===void 0?`missing`:`outdated`}}const J=new Set;async function be(e,t){let n=await e([`shell`,`cmd`,`package`,`list`,`packages`,`--show-versioncode`,t],{allowFailure:!0,timeoutMs:5e3});if(n.exitCode!==0)return;let r=RegExp(`package:${Ee(t)}(?:\\s|$).*versionCode:(\\d+)`).exec(`${n.stdout}\n${n.stderr}`);return r?Number(r[1]):void 0}async function xe(e){let n=await Se(e.apkPath);if(n!==e.manifest.sha256)throw new t(`COMMAND_FAILED`,`Android multi-touch helper APK checksum mismatch`,{apkPath:e.apkPath,expectedSha256:e.manifest.sha256,actualSha256:n})}async function Se(e){let t=N.createHash(`sha256`);return t.update(await I.readFile(e)),t.digest(`hex`)}function Ce(e){return`${e.platform}:${e.id}`}function Y(e,n){if(typeof e!=`string`||e.length===0)throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest ${n} is required.`);return e}function we(e,t){return ae(e,t,`multi-touch helper`)}function X(e,t,n){return v(e,t,n,`multi-touch helper`)}function Te(e){let n=Y(e,`sha256`).trim().toLowerCase();if(n.length!==64||!/^[0-9a-f]+$/.test(n))throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest sha256 must be a 64-character hex string.`);return n}function Ee(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const Z=[`window_animation_scale`,`transition_animation_scale`,`animator_duration_scale`];async function De(e,n,r,i,a){switch(n.toLowerCase()){case`wifi`:await h(e,[`shell`,`svc`,`wifi`,M(r)?`enable`:`disable`]);return;case`airplane`:{let t=M(r),n=t?`1`:`0`,i=t?`true`:`false`;await h(e,[`shell`,`settings`,`put`,`global`,`airplane_mode_on`,n]),await h(e,[`shell`,`am`,`broadcast`,`-a`,`android.intent.action.AIRPLANE_MODE`,`--ez`,`state`,i]);return}case`location`:if(r.toLowerCase()===`set`){if(e.kind!==`emulator`)throw new t(`UNSUPPORTED_OPERATION`,`Android precise location coordinates are supported only on emulators.`,{deviceId:e.id,hint:`Use an Android emulator for adb emu geo fix, or configure location through device/provider tooling.`});let{latitude:n,longitude:r}=u(a);return await h(e,[`emu`,`geo`,`fix`,String(r),String(n)]),{latitude:n,longitude:r}}await h(e,[`shell`,`settings`,`put`,`secure`,`location_mode`,M(r)?`3`:`0`]);return;case`animations`:{let t=M(r)?`1`:`0`;for(let n of Z)await h(e,[`shell`,`settings`,`put`,`global`,n,t]);return{scale:t,keys:[...Z]}}case`appearance`:await h(e,[`shell`,`cmd`,`uimode`,`night`,await Me(e,r)===`dark`?`yes`:`no`]);return;case`clear-app-state`:{if(r.toLowerCase()!==`clear`)throw new t(`INVALID_ARGS`,`settings clear-app-state only supports clear.`);if(!i)throw new t(`INVALID_ARGS`,`settings clear-app-state requires an app id or an active app session.`);let n=await p(e,i);if(n.type===`intent`)throw new t(`INVALID_ARGS`,`settings clear-app-state requires a package name, not an intent.`);await h(e,[`shell`,`am`,`force-stop`,n.value],{allowFailure:!0});let a=await h(e,[`shell`,`pm`,`clear`,n.value],{allowFailure:!0});if(a.exitCode!==0||!/\bSuccess\b/i.test(a.stdout))throw new t(`COMMAND_FAILED`,`Failed to clear Android app data for ${n.value}`,{package:n.value,stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});return{package:n.value,cleared:!0}}case`fingerprint`:await ke(e,Oe(r));return;case`permission`:{if(!i)throw new t(`INVALID_ARGS`,`permission setting requires an active app in session`);let n=j(r),o=Pe(a?.permissionTarget,a?.permissionMode);if(o.kind===`notifications`){await Ie(e,i,n,o);return}let s=n===`grant`?`grant`:`revoke`;if(o.type===`photos`){await Fe(e,i,s);return}await h(e,[`shell`,`pm`,s,i,o.value]);return}default:throw new t(`INVALID_ARGS`,`Unsupported setting: ${n}`)}}function Oe(e){let n=e.trim().toLowerCase();if(n===`match`)return`match`;if(n===`nonmatch`)return`nonmatch`;throw new t(`INVALID_ARGS`,`Invalid fingerprint state: ${e}. Use match|nonmatch.`)}async function ke(e,n){let r=Ae(e,n),i=[];for(let t of r){let n=await h(e,t,{allowFailure:!0});if(n.exitCode===0)return;i.push({args:t,stdout:n.stdout,stderr:n.stderr,exitCode:n.exitCode})}let a=O(i);throw i.length>0&&i.every(e=>je(e.stdout,e.stderr))?new t(`UNSUPPORTED_OPERATION`,`Android fingerprint simulation is not supported on this target/runtime.`,{deviceId:e.id,action:n,hint:`Use an Android emulator with biometric support, or a device/runtime that exposes cmd fingerprint.`,attempts:a}):new t(`COMMAND_FAILED`,`Failed to simulate Android fingerprint.`,{deviceId:e.id,action:n,attempts:a})}function Ae(e,t){let n=t===`match`?`1`:`9999`,r=[[`shell`,`cmd`,`fingerprint`,`touch`,n],[`shell`,`cmd`,`fingerprint`,`finger`,n]];return e.kind===`emulator`&&r.push([`emu`,`finger`,`touch`,n]),r}function je(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`unknown command`)||n.includes(`can't find service: fingerprint`)||n.includes(`service fingerprint was not found`)||n.includes(`fingerprint cmd unavailable`)||n.includes(`emu command is not supported`)||n.includes(`emulator console is not running`)||n.includes(`fingerprint`)&&n.includes(`not found`)}async function Me(e,n){let r=A(n);if(r!==`toggle`)return r;let i=await h(e,[`shell`,`cmd`,`uimode`,`night`],{allowFailure:!0});if(i.exitCode!==0)throw s(`Failed to read current Android appearance`,i);let a=Ne(i.stdout,i.stderr);if(!a)throw new t(`COMMAND_FAILED`,`Unable to determine current Android appearance for toggle`,{stdout:i.stdout,stderr:i.stderr});return a===`auto`?`dark`:a===`dark`?`light`:`dark`}function Ne(e,t){let n=/night mode:\s*(yes|no|auto)\b/i.exec(`${e}\n${t}`);if(!n)return null;let r=n[1]?.toLowerCase();return r===`yes`?`dark`:r===`no`?`light`:r===`auto`?`auto`:null}function Pe(e,n){let r=k(e);if(n?.trim())throw new t(`INVALID_ARGS`,`Permission mode is only supported for photos. Received: ${n}.`);if(r===`camera`)return{kind:`pm`,value:`android.permission.CAMERA`,type:`camera`};if(r===`microphone`)return{kind:`pm`,value:`android.permission.RECORD_AUDIO`,type:`microphone`};if(r===`photos`)return{kind:`pm`,value:`android.permission.READ_MEDIA_IMAGES`,type:`photos`};if(r===`contacts`)return{kind:`pm`,value:`android.permission.READ_CONTACTS`,type:`contacts`};if(r===`notifications`)return{kind:`notifications`,appOps:`POST_NOTIFICATION`,permission:`android.permission.POST_NOTIFICATIONS`};throw new t(`INVALID_ARGS`,`Unsupported permission target on Android: ${e}. Use camera|microphone|photos|contacts|notifications.`)}async function Fe(e,n,r){let i=await Le(e),a=i!==null&&i>=33?[`android.permission.READ_MEDIA_IMAGES`,`android.permission.READ_EXTERNAL_STORAGE`]:[`android.permission.READ_EXTERNAL_STORAGE`,`android.permission.READ_MEDIA_IMAGES`],o=[];for(let t of a){let i=await h(e,[`shell`,`pm`,r,n,t],{allowFailure:!0});if(i.exitCode===0)return;o.push({permission:t,stderr:i.stderr,exitCode:i.exitCode})}throw new t(`COMMAND_FAILED`,`Failed to ${r} Android photos permission`,{appPackage:n,sdkInt:i,attempts:o})}async function Ie(e,t,n,r){let i=n===`grant`?`allow`:n===`deny`?`deny`:`default`;n===`grant`?await h(e,[`shell`,`pm`,`grant`,t,r.permission],{allowFailure:!0}):(await h(e,[`shell`,`pm`,`revoke`,t,r.permission],{allowFailure:!0}),n===`reset`&&(await h(e,[`shell`,`pm`,`clear-permission-flags`,t,r.permission,`user-set`],{allowFailure:!0}),await h(e,[`shell`,`pm`,`clear-permission-flags`,t,r.permission,`user-fixed`],{allowFailure:!0}))),await h(e,[`shell`,`appops`,`set`,t,r.appOps,i])}async function Le(e){let t=await h(e,[`shell`,`getprop`,`ro.build.version.sdk`],{allowFailure:!0});if(t.exitCode!==0)return null;let n=Number.parseInt(t.stdout.trim(),10);return!Number.isFinite(n)||n<=0?null:n}const Q=Buffer.from([137,80,78,71,13,10,26,10]);async function Re(e,t,n={}){if(n.stabilize===!1){await $(e,t);return}await ze(e);try{await ne(1e3),await $(e,t)}finally{await Be(e).catch(()=>{})}}async function ze(e){let t=t=>h(e,[`shell`,t],{allowFailure:!0});await t(`settings put global sysui_demo_allowed 1`);let n=e=>t(`am broadcast -a com.android.systemui.demo -e command ${e}`);await n(`clock -e hhmm 0941`),await n(`notifications -e visible false`)}async function Be(e){await h(e,[`shell`,`am broadcast -a com.android.systemui.demo -e command exit`],{allowFailure:!0})}async function $(e,n){let r=await h(e,[`exec-out`,`screencap`,`-p`],{binaryStdout:!0});if(!r.stdoutBuffer)throw new t(`COMMAND_FAILED`,`Failed to capture screenshot`);let i=r.stdoutBuffer.indexOf(Q);if(i<0)throw new t(`COMMAND_FAILED`,`Screenshot data does not contain a valid PNG header`);let a=Ve(r.stdoutBuffer,i);if(!a)throw new t(`COMMAND_FAILED`,`Screenshot data does not contain a complete PNG payload`);await P.writeFile(n,r.stdoutBuffer.subarray(i,a))}function Ve(e,t){let n=t+Q.length;for(;n+8<=e.length;){let t=e.readUInt32BE(n),r=n+4,i=e.toString(`ascii`,r,r+4),a=n+12+t;if(a>e.length)return null;if(i===`IEND`)return a;n=a}return null}function He(e){return{open:(t,n)=>f(e,t,{activity:n?.activity,appBundleId:n?.appBundleId,launchArgs:n?.launchArgs,url:n?.url}),openDevice:()=>re(e),close:t=>m(e,t),tap:(t,n)=>b(e,t,n),doubleTap:async(t,n)=>{await b(e,t,n),await b(e,t,n)},swipe:(t,n,r,i,a)=>B(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),pan:(t,n,r,i,a)=>B(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),fling:(t,n,r,i,a)=>B(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),longPress:(t,n,r)=>ce(e,t,n,r),focus:(t,n)=>de(e,t,n),type:(t,n)=>x(e,t,n),fill:(t,n,r,i)=>T(e,t,n,r,i),scroll:(t,n)=>ue(e,t,n),pinch:(t,n,r)=>V(e,{scale:t,x:n,y:r}),screenshot:(t,n)=>Re(e,t,n),snapshot:async t=>{let r=await n(`snapshot_capture`,async()=>await oe(e,{appBundleId:t?.appBundleId,interactiveOnly:t?.interactiveOnly,depth:t?.depth,scope:t?.scope,raw:t?.raw,helperSessionScope:t?.appBundleId?`daemon-session`:`command`}),{backend:`android`});return{nodes:r.nodes??[],truncated:r.truncated??!1,backend:`android`,...d(r)}},back:t=>S(e),home:()=>C(e),rotate:t=>le(e,t),rotateGesture:(t,n,r,i)=>H(e,{degrees:t,x:n,y:r,velocity:i}),transformGesture:t=>fe(e,t),appSwitcher:()=>E(e),tvRemote:(t,n)=>D(e,t,n),readClipboard:()=>g(e),writeClipboard:t=>_(e,t),setSetting:(t,n,r,i)=>De(e,t,n,r,i)}}export{He as createAndroidInteractor};
@@ -0,0 +1,7 @@
1
+ import{t as e}from"./rolldown-runtime.js";import{F as t}from"./sdk-contracts.js";import{_ as n,a as r,l as i,n as a,o,v as s,w as c}from"./sdk-android-adb.js";import{t as l}from"./timeouts.js";import{D as u,E as ee,h as d,p as f,u as p}from"./android-input-ownership.js";import{promises as m}from"node:fs";import te from"node:os";import h from"node:path";const ne=[`mCurrentFocus=Window{`,`mFocusedApp=AppWindowToken{`,`mResumedActivity:`,`ResumedActivity:`],re=/\bApplication Not Responding:\s*([A-Za-z0-9_.]+)/i,ie=/([^{}]*\bis(?:n't| not)\s+responding[^{}]*)/i,ae=/\b([A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z0-9_]+)+)\b/;function g(e){let t=new Set;for(let n of e.split(`
2
+ `)){let e=n.trim();if(!e)continue;let r=e.split(/\s+/)[0]??``;if(!r.includes(`/`))continue;let i=r.split(`/`)[0]??``;i.includes(`.`)&&i&&t.add(i)}return Array.from(t)}function _(e){return e.split(`
3
+ `).map(e=>{let t=e.trim();return t.startsWith(`package:`)?t.slice(8):t}).filter(Boolean)}function v(e){return y(e,e=>ce(e))}function oe(e){return y(e,(e,t)=>se(e,t))}function y(e,t){let n=e.split(`
4
+ `);for(let e of ne)for(let r of n){let n=r.indexOf(e);if(n===-1)continue;let i=r.trim(),a=t(r.slice(n+e.length),i);if(a)return a}return null}function se(e,t){let n=e.split(`}`)[0]?.trim()??e.trim(),r=re.exec(n);if(r){let e=r[1];return{package:e,focusedWindow:`Application Not Responding: ${e}`,raw:t}}let i=ie.exec(n);if(!i)return null;let a=i[1];if(a===void 0)return null;let o=a.trim().replace(/\s+/g,` `),s=ae.exec(o)?.[1];return{...s?{package:s}:{},focusedWindow:o,raw:t}}function ce(e){for(let t of e.trim().split(/\s+/)){let e=t.indexOf(`/`);if(e<=0)continue;let n=b(t.slice(0,e),!1),r=b(t.slice(e+1),!0);if(n&&r&&n.length===e)return{package:n,activity:r}}return null}function b(e,t){let n=0;for(;n<e.length&&le(e[n],t);)n+=1;return e.slice(0,n)}function le(e,t){if(!e)return!1;let n=e.charCodeAt(0);return n>=48&&n<=57||n>=65&&n<=90||n>=97&&n<=122||e===`_`||e===`.`||t&&e===`$`}const ue=/\.(?:apk|aab)$/i,de=/^[A-Za-z_][\w]*(\.[A-Za-z_][\w]*)+$/;function x(e){let t=e.trim();return t.length===0?`other`:ue.test(t)?t.includes(`/`)||t.includes(`\\`)||t.startsWith(`.`)||t.startsWith(`~`)||!S(t)?`binary`:`package`:S(t)?`package`:`other`}function S(e){return de.test(e)}function fe(e){return`Android runtime hints require an installed package name, not "${e}". Install or reinstall the app first, then relaunch by package.`}var pe=e({closeAndroidApp:()=>G,getAndroidAppState:()=>N,getAndroidBlockingDialogFocus:()=>P,inferAndroidAppName:()=>M,installAndroidApp:()=>He,installAndroidInstallablePath:()=>Q,installAndroidInstallablePathAndResolvePackageName:()=>$,isAmStartError:()=>H,listAndroidApps:()=>A,openAndroidApp:()=>L,openAndroidDevice:()=>W,parseAndroidLaunchComponent:()=>U,reinstallAndroidApp:()=>Ue,resolveAndroidApp:()=>k});const me={settings:{type:`intent`,value:`android.settings.SETTINGS`}},C=`android.intent.category.LAUNCHER`,w=`android.intent.category.LEANBACK_LAUNCHER`,T=`android.intent.category.DEFAULT`,E=`Run agent-device apps --platform android to discover the installed package name, then retry open with that exact package.`,he=new Set([`localhost`,`127.0.0.1`,`::1`,`[::1]`]),D=ee();function O(e){return{platform:`android`,deviceId:e.id,variant:e.target??``}}async function k(e,n){let r=n.trim();if(x(r)===`package`)return{type:`package`,value:r};let i=me[r.toLowerCase()];if(i)return i;let a=O(e),o=D.get(a,r);if(o)return o;let s=(await f(e,[`shell`,`pm`,`list`,`packages`])).stdout.split(`
5
+ `).map(e=>e.replace(`package:`,``).trim()).filter(Boolean).filter(e=>e.toLowerCase().includes(r.toLowerCase())),c=s[0];if(c!==void 0&&s.length===1)return D.set(a,r,{type:`package`,value:c});throw s.length>1?new t(`INVALID_ARGS`,`Multiple packages matched "${n}"`,{matches:s,hint:`Run agent-device apps --platform android to see the exact installed package names before retrying open.`}):new t(`APP_NOT_INSTALLED`,`No package found matching "${n}"`,{hint:E})}async function A(e,t){let n=await ge(e);return(t===`user-installed`?(await ve(e)).filter(e=>n.has(e)):Array.from(n)).sort((e,t)=>e.localeCompare(t)).map(e=>({package:e,name:M(e)}))}async function ge(e){let t=new Set;for(let n of j(e,{includeFallbackWhenUnknown:!0})){let r=await f(e,[`shell`,`cmd`,`package`,`query-activities`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,n],{allowFailure:!0});if(!(r.exitCode!==0||r.stdout.trim().length===0))for(let e of g(r.stdout))t.add(e)}return t}function _e(e){return j(e)[0]??C}function j(e,t={}){return e.target===`tv`?[w]:e.target===`mobile`?[C]:t.includeFallbackWhenUnknown?[C,w]:[C]}async function ve(e){return _((await f(e,[`shell`,`pm`,`list`,`packages`,`-3`])).stdout)}function M(e){let t=new Set([`com`,`android`,`google`,`app`,`apps`,`service`,`services`,`mobile`,`client`]),n=e.split(`.`).flatMap(e=>e.split(/[_-]+/)).map(e=>e.trim().toLowerCase()).filter(e=>e.length>0),r=n[n.length-1]??e;for(let e=n.length-1;e>=0;--e){let i=n[e];if(i&&!t.has(i)){r=i;break}}return r.split(/[^a-z0-9]+/i).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}async function N(e){return await F(e,[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`]])||await F(e,[[`shell`,`dumpsys`,`activity`,`activities`],[`shell`,`dumpsys`,`activity`]])||{}}async function P(e){return await ye(e,[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`]])}async function F(e,t){for(let n of t){let t=v((await f(e,n,{allowFailure:!0})).stdout??``);if(t)return t}return null}async function ye(e,t){for(let n of t){let t=oe((await f(e,n,{allowFailure:!0})).stdout??``);if(t)return t}return null}function be(e){let t;try{t=new URL(e)}catch{return null}let n=t.hostname.toLowerCase();if(!he.has(n)||!t.port)return null;let r=Number(t.port);return Number.isInteger(r)?`tcp:${r}`:null}async function xe(e,n){let a=be(n);if(!a)return;let o=r(i(e));try{await o.ensure({local:a,remote:a})}catch(e){let n={localPort:a.replace(`tcp:`,``),operation:`adb reverse ${a} ${a}`};throw e instanceof t&&Object.assign(n,{hint:e.details?.hint,diagnosticId:e.details?.diagnosticId,logPath:e.details?.logPath}),new t(`COMMAND_FAILED`,`Failed to ensure Android port reverse ${a} before opening localhost URL`,n,e)}}function Se(e){return/^[A-Za-z0-9_@%+=:,./-]+$/.test(e)?e:`'${e.replace(/'/g,`'\\''`)}'`}function I(e){return(e.launchArgs??[]).map(Se)}async function L(e,t,n){e.booted||await d(e.id);let r=ke(n),i=r.activity,a=t.trim();if(u(a)){await Ce(e,a,r);return}if(r.url!==void 0){await we(e,t,r);return}let o=await k(e,t),s=_e(e);if(o.type===`intent`){await Te(e,o.value,r);return}if(i){await Ee(e,o.value,i,s,r);return}await De(e,o.value,s,r)}async function Ce(e,n,r){if(r.activity)throw new t(`INVALID_ARGS`,`Activity override is not supported when opening a deep link URL`);await xe(e,n),await f(e,[`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.VIEW`,`-d`,n,...Ae(r.appBundleId),...I(r)])}async function we(e,n,r){if(r.activity)throw new t(`INVALID_ARGS`,`Activity override is not supported when opening an app-bound deep link URL`);let i=r.url?.trim()??``;if(!u(i))throw new t(`INVALID_ARGS`,`Android app-bound open requires a valid URL target`);await f(e,[`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.VIEW`,`-d`,i,`-p`,await Oe(e,n,`app-bound open`),...I(r)])}async function Te(e,n,r){if(r.activity)throw new t(`INVALID_ARGS`,`Activity override requires a package name, not an intent`);await f(e,[`shell`,`am`,`start`,`-W`,`-a`,n,...I(r)])}async function Ee(e,t,n,r,i){let a=n.includes(`/`)?n:`${t}/${n.startsWith(`.`)?n:`.${n}`}`;try{await f(e,R(a,r,i))}catch(n){throw await je(e,t,n),n}}async function De(e,t,n,r){let i=await f(e,[`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,T,`-c`,n,`-p`,t,...I(r)],{allowFailure:!0});if(i.exitCode===0&&!H(i.stdout,i.stderr))return;let o=await Me(e,t);if(!o)throw await B(e,t)?a(`Failed to launch ${t}`,i):z(t);await f(e,R(o,n,r))}function R(e,t,n){return[`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,T,`-c`,t,`-n`,e,...I(n)]}async function Oe(e,n,r){let i=await k(e,n);if(i.type===`intent`)throw new t(`INVALID_ARGS`,`Android ${r} requires a package name, not an intent`);return i.value}function ke(e){return typeof e==`string`?{activity:e}:e??{}}function Ae(e){let t=e?.trim();return t?[`-p`,t]:[]}function z(e){return new t(`APP_NOT_INSTALLED`,`No package found matching "${e}"`,{package:e,hint:E})}async function B(e,t){let n=await f(e,[`shell`,`pm`,`path`,t],{allowFailure:!0}),r=`${n.stdout}\n${n.stderr}`;return n.exitCode===0&&/\bpackage:/i.test(r)?!0:(V(r),!1)}async function je(e,n,r){if(V(r instanceof t?`${String(r.details?.stdout??``)}\n${String(r.details?.stderr??``)}`:``)||!await B(e,n))throw z(n)}function V(e){return/\bunknown package\b/i.test(e)||/\bpackage .* (?:was|is) not found\b/i.test(e)||/\bpackage .* does not exist\b/i.test(e)||/\bcould not find package\b/i.test(e)}async function Me(e,t){let n=Array.from(new Set(j(e,{includeFallbackWhenUnknown:!0})));for(let r of n){let n=await f(e,[`shell`,`cmd`,`package`,`resolve-activity`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,r,t],{allowFailure:!0});if(n.exitCode!==0)continue;let i=U(n.stdout);if(i)return i}return null}function H(e,t){let n=`${e}\n${t}`;return/Error:.*(?:Activity not started|unable to resolve Intent)/i.test(n)}function U(e){let t=e.split(`
6
+ `).map(e=>e.trim()).filter(Boolean);for(let e=t.length-1;e>=0;--e){let n=t[e];if(n===void 0||!n.includes(`/`))continue;let r=n.split(/\s+/)[0];if(r!==void 0)return r}return null}async function W(e){e.booted||await d(e.id)}async function G(e,n){if(n.trim().toLowerCase()===`settings`){await f(e,[`shell`,`am`,`force-stop`,`com.android.settings`]),await K(e,`com.android.settings`);return}let r=await k(e,n);if(r.type===`intent`)throw new t(`INVALID_ARGS`,`Close requires a package name, not an intent`);await f(e,[`shell`,`am`,`force-stop`,r.value]),await K(e,r.value)}async function K(e,t){await Ne(e,t),await Fe(e,t)}async function Ne(e,t){let n=Date.now()+2e3;for(;Date.now()<n;){if((await Pe(e))?.package!==t)return;await l(50)}}async function Pe(e){for(let t of[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`],[`shell`,`dumpsys`,`activity`,`activities`],[`shell`,`dumpsys`,`activity`]]){let n=v((await f(e,t,{allowFailure:!0})).stdout??``);if(n)return n}return null}async function Fe(e,t){let n=Date.now()+2e3;for(;Date.now()<n;){if(!await q(e,t)&&(await l(150),!await q(e,t)))return;await l(50)}}async function q(e,t){return((await f(e,[`shell`,`pidof`,t],{allowFailure:!0})).stdout??``).trim().length>0}async function Ie(e,n){let r=await k(e,n);if(r.type===`intent`)throw new t(`INVALID_ARGS`,`App uninstall requires a package name, not an intent`);let i=await f(e,[`uninstall`,r.value],{allowFailure:!0});if(i.exitCode!==0){let e=`${i.stdout}\n${i.stderr}`.toLowerCase();if(!e.includes(`unknown package`)&&!e.includes(`not installed`))throw a(`adb uninstall failed for ${r.value}`,i)}return{package:r.value}}let J=null;function Le(){return`${process.env.PATH??``}::${process.env.AGENT_DEVICE_BUNDLETOOL_JAR??``}`}async function Re(){let e=Le();if(J?.key===e)return J.invocation;if(await c(`bundletool`)){let t={cmd:`bundletool`,prefixArgs:[]};return J={key:e,invocation:t},t}let r=await n(process.env.AGENT_DEVICE_BUNDLETOOL_JAR,`AGENT_DEVICE_BUNDLETOOL_JAR`);if(!r)throw new t(`TOOL_MISSING`,`bundletool not found in PATH. Install bundletool or set AGENT_DEVICE_BUNDLETOOL_JAR to a bundletool-all.jar path.`);let i={cmd:`java`,prefixArgs:[`-jar`,r]};return J={key:e,invocation:i},i}async function Y(e){let t=await Re();await s(t.cmd,[...t.prefixArgs,...e])}function ze(e){return h.extname(e).toLowerCase()===`.aab`}async function Be(e,t){let n=i(e),r=`universal`;if(n.installBundle){await n.installBundle(t,{mode:r});return}let a=await m.mkdtemp(h.join(te.tmpdir(),`agent-device-aab-`)),o=h.join(a,`bundle.apks`);try{await Y([`build-apks`,`--bundle`,t,`--output`,o,`--mode`,r]),await Y([`install-apks`,`--apks`,o,`--device-id`,e.id])}finally{await m.rm(a,{recursive:!0,force:!0})}}async function X(e,t){if(ze(t)){await Be(e,t);return}await o(t,{device:e,replace:!0})}async function Z(e){let t=await f(e,[`shell`,`pm`,`list`,`packages`]);return new Set(t.stdout.split(`
7
+ `).map(e=>e.replace(`package:`,``).trim()).filter(Boolean))}async function Ve(e,t){let n=await Z(e),r=Array.from(n).filter(e=>!t.has(e));if(r.length===1)return r[0]}async function Q(e,t){await D.invalidateWhile(O(e),async()=>{e.booted||await d(e.id),await X(e,t)})}async function $(e,t,n){let r=n?void 0:await Z(e);return await Q(e,t),n??(r?await Ve(e,r):void 0)}async function He(e,t){e.booted||await d(e.id);let n=await p({kind:`path`,path:t});try{let t=await $(e,n.installablePath,n.packageName),r=t?M(t):void 0;return{archivePath:n.archivePath,installablePath:n.installablePath,packageName:t,appName:r,launchTarget:t}}finally{await n.cleanup()}}async function Ue(e,t,n){return await D.invalidateWhile(O(e),async()=>{e.booted||await d(e.id);let{package:r}=await Ie(e,t),i=await p({kind:`path`,path:n},{resolveIdentity:!1});try{await Q(e,i.installablePath)}finally{await i.cleanup()}return{package:r}})}export{M as a,L as c,k as d,x as f,_ as g,g as h,P as i,W as l,v as m,G as n,H as o,fe as p,N as r,A as s,pe as t,U as u};