@xenon-device-management/xenon 2.14.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/README.md +44 -29
  2. package/lib/package.json +6 -5
  3. package/lib/public/assets/{AnnotationOverlay-B_VNtKBH.js → AnnotationOverlay-2W4JvEyZ.js} +1 -1
  4. package/lib/public/assets/{ApiKeyGate-BwuaMjkX.js → ApiKeyGate-GmckX_7r.js} +1 -1
  5. package/lib/public/assets/DeviceMosaicView-DqODzkyo.js +2 -0
  6. package/lib/public/assets/{EmptyState-BSjhk_IV.js → EmptyState-7hETKEWb.js} +1 -1
  7. package/lib/public/assets/{FieldGroup-Cb5QZsE1.js → FieldGroup-GOgssFEP.js} +1 -1
  8. package/lib/public/assets/{FilterMenu-BAaSlWbW.js → FilterMenu-Yvf6RGMd.js} +1 -1
  9. package/lib/public/assets/{Menu-JGGYrSQQ.js → Menu-KrPnfdsf.js} +1 -1
  10. package/lib/public/assets/{Modal-CFCp3n9d.js → Modal-C6WjZFeI.js} +1 -1
  11. package/lib/public/assets/{RecordingPage-CbJj_EBT.js → RecordingPage-CSRIhZEh.js} +1 -1
  12. package/lib/public/assets/{RecordingsPage-BJeYnygR.js → RecordingsPage-CB8ST3nE.js} +1 -1
  13. package/lib/public/assets/{SegmentedControl-Bg42Atjy.js → SegmentedControl-C9o90KId.js} +1 -1
  14. package/lib/public/assets/{SettingCard-Njz1pUIJ.js → SettingCard-CPm3w9l2.js} +1 -1
  15. package/lib/public/assets/{Table-BcTm5xV2.js → Table-DwIgERRt.js} +1 -1
  16. package/lib/public/assets/{activity-DOVdsfMQ.js → activity-DrLbHifn.js} +1 -1
  17. package/lib/public/assets/ai-settings-DU_Ls06P.js +16 -0
  18. package/lib/public/assets/{api-keys-BKnNSTw9.js → api-keys-CWYdEzJ_.js} +1 -1
  19. package/lib/public/assets/{apps-D0SC6uw6.js → apps-BqNVdln7.js} +1 -1
  20. package/lib/public/assets/{arrow-left-MMbTmgoE.js → arrow-left-BtB0_Jg_.js} +1 -1
  21. package/lib/public/assets/{arrow-right-BMfnGmxW.js → arrow-right-C8NhdtyN.js} +1 -1
  22. package/lib/public/assets/{arrow-up-right-DuAftMWB.js → arrow-up-right-gMg0ffvv.js} +1 -1
  23. package/lib/public/assets/{auth-shell-nZC2JNDO.js → auth-shell-CiYZ3N4M.js} +2 -2
  24. package/lib/public/assets/{builds-page-hDCgTwkS.js → builds-page-EaaqDmLN.js} +1 -1
  25. package/lib/public/assets/{button-Dda4irn_.js → button-DDau4Axv.js} +1 -1
  26. package/lib/public/assets/{calendar-BMr0Q62v.js → calendar-DQ83B1ed.js} +1 -1
  27. package/lib/public/assets/{check-CZ9iw9-e.js → check-K1ovTQam.js} +1 -1
  28. package/lib/public/assets/{chevron-right-BRYJJVC9.js → chevron-right-Biq00pMt.js} +1 -1
  29. package/lib/public/assets/{circle-check-CqAJjQ5O.js → circle-check-CBv88cH7.js} +1 -1
  30. package/lib/public/assets/{circle-x-DlDJtnVz.js → circle-x-Bmltailn.js} +1 -1
  31. package/lib/public/assets/{clock-CpuDKysU.js → clock-BPrld-3h.js} +1 -1
  32. package/lib/public/assets/{copy-CAQydNFe.js → copy-t0QB14JI.js} +1 -1
  33. package/lib/public/assets/derive-UnLRp4r3.js +1 -0
  34. package/lib/public/assets/device-explorer-BgGZ8AvV.js +282 -0
  35. package/lib/public/assets/{download-C9XG6lLv.js → download-BroHxNOh.js} +1 -1
  36. package/lib/public/assets/forgot-password-Bjmf49i6.js +1 -0
  37. package/lib/public/assets/{h264Stream-VQBphYhg.js → h264Stream-DeD__6wa.js} +6 -6
  38. package/lib/public/assets/{index-DJGc1M2f.js → index-72oF89LJ.js} +1 -1
  39. package/lib/public/assets/index-B0JeuZbg.js +15 -0
  40. package/lib/public/assets/index-B0gQkEg0.js +219 -0
  41. package/lib/public/assets/{input-Cm0tmSoc.js → input-DIc4MSC7.js} +1 -1
  42. package/lib/public/assets/{line-chart-D2DyJCml.js → line-chart-BcWm2vW8.js} +1 -1
  43. package/lib/public/assets/{list-checks-UWf1GY99.js → list-checks-B06IJJad.js} +1 -1
  44. package/lib/public/assets/{lock-CCJv9m_I.js → lock-pVLvnN30.js} +1 -1
  45. package/lib/public/assets/{login-f8vOVW-U.js → login-CN77vy--.js} +1 -1
  46. package/lib/public/assets/{maintenance-settings-C1PrkuAH.js → maintenance-settings-CSdzQSkY.js} +1 -1
  47. package/lib/public/assets/{monitor-B2jDgwm7.js → monitor-BtrN5IkI.js} +1 -1
  48. package/lib/public/assets/{mouse-pointer-2-DLI1RtzU.js → mouse-pointer-2-CNsQfo6w.js} +1 -1
  49. package/lib/public/assets/{network-ebT86qoi.js → network-BSfA8ZxG.js} +1 -1
  50. package/lib/public/assets/{overview-C0aykVrd.js → overview-BfTebCK5.js} +1 -1
  51. package/lib/public/assets/{page-header-BjzgWz-T.js → page-header-D8KUX6Qg.js} +1 -1
  52. package/lib/public/assets/{play-DLG9t8FU.js → play-Cvc8xzic.js} +1 -1
  53. package/lib/public/assets/{plus-nBFSMu_X.js → plus-C9Zj84UF.js} +1 -1
  54. package/lib/public/assets/{profile-page-CSl0UYlv.js → profile-page-Bi27STgs.js} +1 -1
  55. package/lib/public/assets/{reset-password-LeOuHqmB.js → reset-password-CPsOuwSu.js} +1 -1
  56. package/lib/public/assets/runbook-page-DEQ-DBOX.js +304 -0
  57. package/lib/public/assets/{select-88jHcl-Z.js → select-BWpnhWXj.js} +1 -1
  58. package/lib/public/assets/{selector-detail-redirect-BbXKJjZM.js → selector-detail-redirect-BefSCSpe.js} +1 -1
  59. package/lib/public/assets/{selector-health-page-B4rA1Xkv.js → selector-health-page-MSk96Zz7.js} +1 -1
  60. package/lib/public/assets/session-detail-page-DPyBNef_.js +4 -0
  61. package/lib/public/assets/{settings-Dep_3kbA.css → settings-K8UHWMdI.css} +1 -1
  62. package/lib/public/assets/{settings-GF6ZMSQ6.js → settings-V6x-cXwy.js} +1 -1
  63. package/lib/public/assets/{stat-tile-BV98e_Sv.js → stat-tile-Duex-GZD.js} +1 -1
  64. package/lib/public/assets/{tablet-BiAsnFDj.js → tablet-BBcoqBmL.js} +1 -1
  65. package/lib/public/assets/{teams-B4-jrr-1.js → teams-ts8p5rEh.js} +1 -1
  66. package/lib/public/assets/{trash-2-C5RSuX0e.js → trash-2-CZ2ew6AZ.js} +1 -1
  67. package/lib/public/assets/{upload-BG7_rMXK.js → upload-Dv99y_le.js} +1 -1
  68. package/lib/public/assets/{use-builds-data-J-riQYyC.js → use-builds-data-DrYC8JCF.js} +1 -1
  69. package/lib/public/assets/{use-password-reset-mode-C8-eH-UG.js → use-password-reset-mode-C7QkBNkP.js} +1 -1
  70. package/lib/public/assets/{users-CFba5TSy.js → users-D7tKMESH.js} +1 -1
  71. package/lib/public/assets/{users-Cp0beJm3.js → users-gJ5Y01yo.js} +1 -1
  72. package/lib/public/assets/{video-off-DDsEISjV.js → video-off-CqZVuZGs.js} +1 -1
  73. package/lib/public/assets/{webhook-settings-BGu32vSB.js → webhook-settings-DI5DSW99.js} +1 -1
  74. package/lib/public/assets/{zap-l4VaXzq8.js → zap-ClcHFxfo.js} +1 -1
  75. package/lib/public/index.html +1 -1
  76. package/lib/schema.json +16 -12
  77. package/lib/src/InternalHttpClient.js +27 -5
  78. package/lib/src/XenonCapabilityManager.js +10 -3
  79. package/lib/src/app/dashboardPluginLink.js +117 -0
  80. package/lib/src/app/index.js +13 -34
  81. package/lib/src/app/openapi/control.yaml +6 -4
  82. package/lib/src/app/openapi/grid.yaml +8 -2
  83. package/lib/src/app/openapi/identity.yaml +103 -31
  84. package/lib/src/app/openapi/platform.yaml +33 -15
  85. package/lib/src/app/openapi/sessions.yaml +12 -3
  86. package/lib/src/app/routers/apikeys.js +14 -2
  87. package/lib/src/app/routers/auth.js +31 -6
  88. package/lib/src/app/routers/control.js +13 -5
  89. package/lib/src/app/routers/dashboard.js +42 -27
  90. package/lib/src/app/routers/grid.js +5 -3
  91. package/lib/src/app/routers/profile.js +21 -0
  92. package/lib/src/app/routers/users.js +12 -6
  93. package/lib/src/app/swagger.js +2 -0
  94. package/lib/src/app/ws/h264StreamWs.js +10 -1
  95. package/lib/src/app/ws/nodeSocketRelay.js +77 -11
  96. package/lib/src/config.js +1 -0
  97. package/lib/src/dashboard/commandLogFields.js +42 -0
  98. package/lib/src/dashboard/commands.js +5 -5
  99. package/lib/src/dashboard/event-manager.js +64 -204
  100. package/lib/src/dashboard/services/failure-analysis-service.js +118 -105
  101. package/lib/src/dashboard/services/failureCategories.js +264 -0
  102. package/lib/src/dashboard/sessionCommandSummary.js +39 -0
  103. package/lib/src/data-service/device-store.js +14 -1
  104. package/lib/src/data-service/prisma-store.js +19 -7
  105. package/lib/src/data-service/web-config-service.js +7 -0
  106. package/lib/src/device-managers/AndroidDeviceManager.js +61 -23
  107. package/lib/src/device-managers/IOSDeviceManager.js +10 -0
  108. package/lib/src/device-managers/android/AndroidH264StreamService.js +69 -20
  109. package/lib/src/device-managers/android/H264Multiplexer.js +38 -4
  110. package/lib/src/device-utils.js +29 -3
  111. package/lib/src/gateway/defaultGateway.js +1 -1
  112. package/lib/src/gateway/forwardToNode.js +6 -11
  113. package/lib/src/gateway/healReport.js +87 -0
  114. package/lib/src/gateway/hubSessionToken.js +27 -4
  115. package/lib/src/gateway/nodeWebDriverUrl.js +3 -5
  116. package/lib/src/gateway/sessionGateway.js +12 -2
  117. package/lib/src/generated/client/edge.js +4 -3
  118. package/lib/src/generated/client/index-browser.js +1 -0
  119. package/lib/src/generated/client/index.d.ts +32 -0
  120. package/lib/src/generated/client/index.js +4 -3
  121. package/lib/src/generated/client/package.json +1 -1
  122. package/lib/src/generated/client/schema.prisma +3 -0
  123. package/lib/src/generated/client/wasm.js +1 -0
  124. package/lib/src/helpers/outboundProxy.js +219 -0
  125. package/lib/src/index.js +21 -0
  126. package/lib/src/interceptors/CommandInterceptor.js +256 -196
  127. package/lib/src/interceptors/xenonScripts.js +28 -7
  128. package/lib/src/logger.js +3 -1
  129. package/lib/src/middleware/authMiddleware.js +15 -3
  130. package/lib/src/middleware/commandCaller.js +3 -3
  131. package/lib/src/middleware/verifyCredential.js +58 -1
  132. package/lib/src/plugin.js +16 -6
  133. package/lib/src/scripts/database-check.js +3 -3
  134. package/lib/src/scripts/initialize-database.js +35 -33
  135. package/lib/src/scripts/run-migrations.js +1079 -27
  136. package/lib/src/services/AICommandService.js +9 -2
  137. package/lib/src/services/AIService.js +192 -42
  138. package/lib/src/services/ApiKeyService.js +16 -5
  139. package/lib/src/services/EmailService.js +29 -0
  140. package/lib/src/services/InterceptorService.js +6 -2
  141. package/lib/src/services/OrphanSweeper.js +8 -0
  142. package/lib/src/services/SelectorStateService.js +11 -7
  143. package/lib/src/services/SelectorVerificationJob.js +21 -6
  144. package/lib/src/services/ServerManager.js +11 -1
  145. package/lib/src/services/SessionLifecycleService.js +118 -44
  146. package/lib/src/services/SocketClient.js +11 -0
  147. package/lib/src/services/SocketServer.js +380 -45
  148. package/lib/src/services/TeamService.js +5 -1
  149. package/lib/src/services/TracingService.js +171 -60
  150. package/lib/src/services/UserService.js +14 -3
  151. package/lib/src/services/UserSessionService.js +16 -4
  152. package/lib/src/services/bug-report/video-slice.js +4 -1
  153. package/lib/src/services/device-access/leaseOverride.js +7 -5
  154. package/lib/src/services/healing/FuzzyXmlHealingProvider.js +51 -0
  155. package/lib/src/services/healing/HealEtalonService.js +3 -2
  156. package/lib/src/services/healing/HealingOrchestrator.js +57 -72
  157. package/lib/src/services/healing/OcrHealingProvider.js +41 -64
  158. package/lib/src/services/healing/ResilioTreeHealingProvider.js +41 -50
  159. package/lib/src/services/healing/VisualAiHealingProvider.js +1 -1
  160. package/lib/src/services/healing/fingerprintIdentity.js +126 -0
  161. package/lib/src/services/healing/resilioPath.js +274 -0
  162. package/lib/src/services/identity/identityChanges.js +41 -0
  163. package/lib/src/services/logcat/SessionDeviceLogs.js +358 -0
  164. package/lib/src/services/logcat/deviceLogBook.js +164 -0
  165. package/lib/src/services/logcat/iosDriverLog.js +178 -0
  166. package/lib/src/services/ocr/ocrData.js +118 -0
  167. package/lib/src/services/ocr/vendor/LICENSE-tessdata.txt +202 -0
  168. package/lib/src/services/ocr/vendor/eng.traineddata.gz +0 -0
  169. package/lib/src/services/omni-vision/OmniVisionService.js +83 -56
  170. package/lib/src/services/passwordResetLink.js +50 -9
  171. package/lib/src/services/passwordResetMode.js +3 -1
  172. package/lib/src/services/publicUrl.js +45 -0
  173. package/lib/src/services/selector-health/SelectorVisibilityResolver.js +121 -0
  174. package/lib/src/services/session/sessionIdentity.js +5 -3
  175. package/lib/src/services/session/shutdownReason.js +26 -0
  176. package/lib/src/services/settings/aiEngineSettings.js +219 -0
  177. package/lib/src/services/token/JwtKeyService.js +8 -2
  178. package/lib/src/services/token/mcpTokenTtl.js +27 -0
  179. package/lib/src/services/token/mintedLifetime.js +51 -0
  180. package/lib/src/sessions/LocalSession.js +29 -1
  181. package/lib/src/sessions/RemoteSession.js +12 -2
  182. package/lib/src/sessions/SessionManager.js +4 -1
  183. package/lib/src/sessions/sessionMemory.js +25 -0
  184. package/lib/test/helpers/driver-log.js +38 -0
  185. package/lib/test/helpers/fake-ai-provider.js +188 -0
  186. package/lib/test/helpers/fake-http-proxy.js +106 -0
  187. package/lib/test/helpers/loki-stores.js +33 -0
  188. package/lib/test/helpers/otlp-probe.js +107 -0
  189. package/lib/test/helpers/page-sources.js +164 -0
  190. package/lib/test/helpers/scratch-database.js +57 -13
  191. package/lib/test/integration/androidDevices.spec.js +1 -1
  192. package/lib/test/integration/audit-ingest.spec.js +10 -10
  193. package/lib/test/integration/auth-flow.spec.js +9 -14
  194. package/lib/test/integration/auth-rate-limit.spec.js +6 -3
  195. package/lib/test/integration/bug-report-route.spec.js +22 -2
  196. package/lib/test/integration/forgot-password-rate-limit.spec.js +6 -3
  197. package/lib/test/integration/forgot-password.spec.js +17 -18
  198. package/lib/test/integration/ios/01iOSSimulator.spec.js +1 -1
  199. package/lib/test/integration/ios/02iOSDevices.spec.js +1 -1
  200. package/lib/test/integration/legacy-deprecation-headers.spec.js +5 -2
  201. package/lib/test/integration/migration.spec.js +6 -1
  202. package/lib/test/integration/node-pair-auth-rest.spec.js +5 -9
  203. package/lib/test/integration/ports-router.spec.js +4 -4
  204. package/lib/test/integration/prisma-device-store-add-devices.spec.js +2 -3
  205. package/lib/test/integration/prisma-device-store-team-filter.spec.js +2 -5
  206. package/lib/test/integration/profile-tokens.spec.js +10 -16
  207. package/lib/test/integration/recordings-router.spec.js +16 -16
  208. package/lib/test/integration/reset-revokes-sessions.spec.js +4 -9
  209. package/lib/test/integration/role-matrix.spec.js +4 -9
  210. package/lib/test/integration/sdk-leases-router.spec.js +9 -9
  211. package/lib/test/integration/sdk-version.spec.js +2 -2
  212. package/lib/test/integration/selector-health-endpoint.spec.js +5 -5
  213. package/lib/test/integration/selector-state-flow.spec.js +5 -5
  214. package/lib/test/integration/team-membership-migration.spec.js +73 -12
  215. package/lib/test/integration/team-visibility-apps.spec.js +15 -22
  216. package/lib/test/integration/team-visibility-control.spec.js +96 -69
  217. package/lib/test/integration/team-visibility-dashboard.spec.js +7 -23
  218. package/lib/test/integration/team-visibility-grid.spec.js +16 -15
  219. package/lib/test/integration/team-visibility-leases.spec.js +8 -11
  220. package/lib/test/integration/team-visibility-recordings.spec.js +6 -19
  221. package/lib/test/integration/team-visibility-sessions.spec.js +6 -19
  222. package/lib/test/integration/users-flow.spec.js +7 -12
  223. package/lib/test/integration/users-lockout.spec.js +5 -3
  224. package/lib/test/unit/EmailService.test.js +66 -0
  225. package/lib/test/unit/ResilioTreeHealingProvider.test.js +46 -19
  226. package/lib/test/unit/XenonCapabilityManager.spec.js +23 -0
  227. package/lib/test/unit/ai-call-time-limit.spec.js +130 -0
  228. package/lib/test/unit/ai-engine-settings.spec.js +338 -0
  229. package/lib/test/unit/ai-provider-switch.spec.js +130 -0
  230. package/lib/test/unit/ai-rate-limit.spec.js +126 -0
  231. package/lib/test/unit/android-adb-retry.spec.js +112 -0
  232. package/lib/test/unit/android-h264-viewers.spec.js +272 -0
  233. package/lib/test/unit/android-preview-hold.spec.js +3 -2
  234. package/lib/test/unit/apikeys-router.spec.js +84 -0
  235. package/lib/test/unit/authMiddleware.bearer.spec.js +9 -0
  236. package/lib/test/unit/authMiddleware.credentialLifetime.spec.js +149 -0
  237. package/lib/test/unit/bug-report/video-slice.spec.js +19 -0
  238. package/lib/test/unit/command-log-network-capture.spec.js +191 -0
  239. package/lib/test/unit/dashboard-events-device-logs.spec.js +255 -0
  240. package/lib/test/unit/dashboard-events-team-scope.spec.js +6 -3
  241. package/lib/test/unit/dashboard-plugin-link.spec.js +213 -0
  242. package/lib/test/unit/database-check.spec.js +1 -0
  243. package/lib/test/unit/device-log-book.spec.js +180 -0
  244. package/lib/test/unit/device-utils.spec.js +13 -4
  245. package/lib/test/unit/enable-dashboard-option.spec.js +57 -0
  246. package/lib/test/unit/event-log-session-data.spec.js +251 -0
  247. package/lib/test/unit/failure-analysis.spec.js +196 -0
  248. package/lib/test/unit/failure-categories.spec.js +130 -0
  249. package/lib/test/unit/failure-categorization.spec.js +393 -0
  250. package/lib/test/unit/fingerprint-identity.spec.js +433 -0
  251. package/lib/test/unit/h264-multiplexer.spec.js +46 -0
  252. package/lib/test/unit/heal-report.spec.js +71 -0
  253. package/lib/test/unit/healing-instrumentation.spec.js +6 -1
  254. package/lib/test/unit/healing-tier-gate.spec.js +74 -29
  255. package/lib/test/unit/healing-tiers-every-session.spec.js +219 -0
  256. package/lib/test/unit/hub-node-gateway-appium-server.spec.js +243 -1
  257. package/lib/test/unit/interceptor-visual-elements.spec.js +192 -0
  258. package/lib/test/unit/ios-driver-log.spec.js +168 -0
  259. package/lib/test/unit/ios-simulator-session-readiness.spec.js +187 -0
  260. package/lib/test/unit/ios-simulator-session-teardown.spec.js +158 -0
  261. package/lib/test/unit/lease/lease-override.spec.js +23 -6
  262. package/lib/test/unit/lease/session-lease-proof.spec.js +16 -4
  263. package/lib/test/unit/logcat-ws.spec.js +78 -37
  264. package/lib/test/unit/logger-json-levels.spec.js +44 -0
  265. package/lib/test/unit/mcp-token-ttl.spec.js +38 -0
  266. package/lib/test/unit/observability-example-loki.spec.js +25 -0
  267. package/lib/test/unit/ocr-healing-tier-real-ocr.spec.js +113 -0
  268. package/lib/test/unit/ocr-healing-tier.spec.js +122 -0
  269. package/lib/test/unit/ocr-offline.spec.js +213 -0
  270. package/lib/test/unit/omni-virtual-element-store.spec.js +301 -0
  271. package/lib/test/unit/omni-vision-ios-points.spec.js +0 -2
  272. package/lib/test/unit/omni-vision.spec.js +5 -7
  273. package/lib/test/unit/openapi-for-site.spec.js +80 -0
  274. package/lib/test/unit/outbound-proxy.spec.js +320 -0
  275. package/lib/test/unit/package-engines.spec.js +27 -0
  276. package/lib/test/unit/passwordResetMode.test.js +10 -2
  277. package/lib/test/unit/port-exhaustion.spec.js +2 -1
  278. package/lib/test/unit/profile-router.test.js +122 -1
  279. package/lib/test/unit/proxy-option.spec.js +358 -0
  280. package/lib/test/unit/queue-requester.spec.js +1 -1
  281. package/lib/test/unit/reset-link-address.spec.js +266 -0
  282. package/lib/test/unit/reset-link.test.js +10 -4
  283. package/lib/test/unit/resilio-path-healing.spec.js +270 -0
  284. package/lib/test/unit/run-migrations-database.spec.js +807 -0
  285. package/lib/test/unit/run-migrations-fail-fast.test.js +41 -27
  286. package/lib/test/unit/run-migrations.test.js +864 -0
  287. package/lib/test/unit/selector-health-actions.spec.js +3 -1
  288. package/lib/test/unit/selector-learning-every-session.spec.js +179 -0
  289. package/lib/test/unit/selector-state-service.spec.js +72 -23
  290. package/lib/test/unit/selector-verification-clean-build.spec.js +130 -0
  291. package/lib/test/unit/selector-verification-job.spec.js +28 -19
  292. package/lib/test/unit/selector-verification-missed-regression.spec.js +2 -2
  293. package/lib/test/unit/selector-visibility-resolver.spec.js +209 -0
  294. package/lib/test/unit/self-healing-boot.spec.js +10 -5
  295. package/lib/test/unit/self-healing-setting.spec.js +7 -6
  296. package/lib/test/unit/session-app-resolution.spec.js +5 -1
  297. package/lib/test/unit/session-attribution-wiring.spec.js +41 -13
  298. package/lib/test/unit/session-create-credential-owner.spec.js +242 -0
  299. package/lib/test/unit/session-credential-strip.spec.js +1 -1
  300. package/lib/test/unit/session-device-logs-ios.spec.js +180 -0
  301. package/lib/test/unit/session-device-logs.spec.js +356 -0
  302. package/lib/test/unit/session-end-failure-analysis.spec.js +133 -0
  303. package/lib/test/unit/session-log-indexes.spec.js +64 -3
  304. package/lib/test/unit/socket-team-scope.integration.spec.js +749 -0
  305. package/lib/test/unit/socketServer.scopedEmit.spec.js +237 -5
  306. package/lib/test/unit/socketServer.sessionCookie.spec.js +2 -1
  307. package/lib/test/unit/stream-start-pipelines.spec.js +72 -18
  308. package/lib/test/unit/token-mint-limits.spec.js +165 -0
  309. package/lib/test/unit/tracing-export-routing.spec.js +137 -0
  310. package/lib/test/unit/tracing-session-spans.spec.js +337 -0
  311. package/lib/test/unit/unexpected-shutdown-reason.spec.js +163 -0
  312. package/lib/test/unit/verify-credential.spec.js +33 -0
  313. package/lib/test/unit/vision-assertion.spec.js +3 -2
  314. package/lib/test/unit/web-config-service.spec.js +19 -0
  315. package/lib/tsconfig.tsbuildinfo +1 -1
  316. package/package.json +6 -5
  317. package/prisma/migrations/20261004174413_locator_etalon_path/migration.sql +2 -0
  318. package/prisma/schema.prisma +3 -0
  319. package/schema.json +16 -12
  320. package/scripts/dev/readme-screenshots.js +276 -23
  321. package/scripts/dev/screenshot-fixtures/logcat.js +157 -0
  322. package/scripts/dev/screenshot-fixtures/phone-screen.html +469 -0
  323. package/scripts/dev/screenshot-fixtures/session.js +242 -0
  324. package/scripts/export-openapi.js +32 -0
  325. package/scripts/lib/openapi-for-site.js +79 -0
  326. package/lib/public/assets/BugReportButton-BAt0_pzq.js +0 -15
  327. package/lib/public/assets/DeviceMosaicView-BYaQHhTf.js +0 -2
  328. package/lib/public/assets/ai-settings-CsuJroRf.js +0 -21
  329. package/lib/public/assets/device-explorer-CdO6_nrn.js +0 -282
  330. package/lib/public/assets/forgot-password-CGnkRgE9.js +0 -1
  331. package/lib/public/assets/index-BbKEXUFH.js +0 -219
  332. package/lib/public/assets/runbook-page-CMMeQml9.js +0 -124
  333. package/lib/public/assets/session-detail-page-COL_RZxw.js +0 -4
  334. package/lib/src/scripts/prepare-prisma.js +0 -89
  335. /package/lib/public/assets/{BugReportButton-CPBjfiv4.css → index-CPBjfiv4.css} +0 -0
@@ -14,9 +14,26 @@
14
14
  * - A name Xenon doesn't have fails with `unknown command`.
15
15
  */
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.XENON_SCRIPT_NAMES = void 0;
17
+ exports.XENON_SCRIPT_NAMES = exports.NETWORK_CAPTURE_SCRIPTS = void 0;
18
+ exports.interceptorScriptName = interceptorScriptName;
19
+ exports.isNetworkCaptureScript = isNetworkCaptureScript;
18
20
  exports.xenonScriptName = xenonScriptName;
19
21
  exports.unknownXenonScriptMessage = unknownXenonScriptMessage;
22
+ /**
23
+ * The scripts CommandInterceptor answers from the session's network capture.
24
+ * Their arguments and answers are the capture (requests, HAR, mocks), which
25
+ * REST shows to admins only, so the command log keeps neither (commandLogFields).
26
+ * A new script that answers from the capture goes in this list. A hub hides
27
+ * by its own copy, so it needs the release that adds the script.
28
+ */
29
+ exports.NETWORK_CAPTURE_SCRIPTS = [
30
+ 'addMock',
31
+ 'removeMock',
32
+ 'clearMocks',
33
+ 'getMocks',
34
+ 'getRequests',
35
+ 'exportHar',
36
+ ];
20
37
  /** The names CommandInterceptor and the session-details commands answer. Keep in step with both. */
21
38
  exports.XENON_SCRIPT_NAMES = [
22
39
  'setAutowaitProperties',
@@ -29,18 +46,22 @@ exports.XENON_SCRIPT_NAMES = [
29
46
  'analyzeScreen',
30
47
  'omniScan',
31
48
  'assertVisualState',
32
- 'addMock',
33
- 'removeMock',
34
- 'clearMocks',
35
- 'getMocks',
36
- 'getRequests',
37
- 'exportHar',
49
+ ...exports.NETWORK_CAPTURE_SCRIPTS,
38
50
  'setSessionName',
39
51
  'setSessionStatus',
40
52
  'debug',
41
53
  'addTag',
42
54
  'captureEvidence',
43
55
  ];
56
+ /** The name CommandInterceptor routes a script by: its `xe:` / `xenon:` prefix, if any, taken off. */
57
+ function interceptorScriptName(script) {
58
+ return script.replace(/^(xe|xenon)\s*:\s*/, '').trim();
59
+ }
60
+ /** Whether CommandInterceptor answers `script` from the session's network capture. */
61
+ function isNetworkCaptureScript(script) {
62
+ return (typeof script === 'string' &&
63
+ exports.NETWORK_CAPTURE_SCRIPTS.includes(interceptorScriptName(script)));
64
+ }
44
65
  /** The name in a `xenon: <name>` or `xe: <name>` script, or null for any other script. */
45
66
  function xenonScriptName(script) {
46
67
  if (typeof script !== 'string')
package/lib/src/logger.js CHANGED
@@ -185,7 +185,9 @@ class XenonLogger {
185
185
  }
186
186
  if (redactedArgs.length)
187
187
  logEntry.args = redactedArgs;
188
- this.baseLogger.info(JSON.stringify(logEntry));
188
+ // At its own level, so --log-level filters it and an error reaches
189
+ // stderr, as in text mode.
190
+ this.baseLogger[level](JSON.stringify(logEntry));
189
191
  }
190
192
  else {
191
193
  const ctxPrefix = this.buildTextPrefix(ctx);
@@ -24,7 +24,13 @@ const PluginContext_1 = require("../PluginContext");
24
24
  const hubSessionToken_1 = require("../gateway/hubSessionToken");
25
25
  const localDeviceHosts_1 = require("../device-managers/localDeviceHosts");
26
26
  const SESSION_COOKIE = 'xenon_dashboard_session';
27
- const SESSION_TTL_MS = 24 * 60 * 60 * 1000;
27
+ /** An API key's expiresAt as epoch ms, or undefined for a key that never expires. */
28
+ function keyExpiresAt(expiresAt) {
29
+ if (!expiresAt)
30
+ return undefined;
31
+ const ms = new Date(expiresAt).getTime();
32
+ return Number.isNaN(ms) ? undefined : ms;
33
+ }
28
34
  // Role → scopes derivation. The same table is used in profile token creation
29
35
  // to enforce that members can never grant 'admin'.
30
36
  // Cookie-session scopes: a logged-in user gets the full set of scopes
@@ -185,6 +191,7 @@ function authMiddleware(req, res, next) {
185
191
  apiKeyId: row.id,
186
192
  rateLimit: row.rateLimit,
187
193
  teamIds,
194
+ credentialExpiresAt: keyExpiresAt(row.expiresAt),
188
195
  };
189
196
  req.apiKey = { id: row.id, scopes: row.scopes, rateLimit: row.rateLimit, teamId: (_b = row.teamId) !== null && _b !== void 0 ? _b : null };
190
197
  return next();
@@ -224,6 +231,7 @@ function authMiddleware(req, res, next) {
224
231
  teamId: (_e = payload.teamId) !== null && _e !== void 0 ? _e : null,
225
232
  rateLimit: 300,
226
233
  teamIds,
234
+ credentialExpiresAt: typeof payload.exp === 'number' ? payload.exp * 1000 : undefined,
227
235
  };
228
236
  return next();
229
237
  }
@@ -242,11 +250,14 @@ function authMiddleware(req, res, next) {
242
250
  if (!user || user.status !== 'ACTIVE')
243
251
  return res.status(401).json({ error: 'invalid session' });
244
252
  const isSecure = req.secure || req.headers['x-forwarded-proto'] === 'https';
253
+ // Renewed for as long as the session row now lasts
254
+ // (XENON_USER_SESSION_TTL_MS). A fixed 24 hours here capped any longer
255
+ // setting: the browser dropped the cookie after a day away.
245
256
  res.cookie(SESSION_COOKIE, cookie, {
246
257
  httpOnly: true,
247
258
  secure: isSecure,
248
259
  sameSite: 'strict',
249
- maxAge: SESSION_TTL_MS,
260
+ maxAge: userSessionSvc.ttlMs(),
250
261
  });
251
262
  const teamIds = yield (0, callerTeamIds_1.computeTeamIds)({
252
263
  role: user.role,
@@ -279,7 +290,7 @@ function authMiddleware(req, res, next) {
279
290
  httpOnly: true,
280
291
  secure: isSecure,
281
292
  sameSite: 'strict',
282
- maxAge: SESSION_TTL_MS,
293
+ maxAge: userSessionSvc.ttlMs(),
283
294
  });
284
295
  const teamIds = yield (0, callerTeamIds_1.computeTeamIds)({
285
296
  role: user.role,
@@ -295,6 +306,7 @@ function authMiddleware(req, res, next) {
295
306
  apiKeyId: row.id,
296
307
  rateLimit: row.rateLimit,
297
308
  teamIds,
309
+ credentialExpiresAt: keyExpiresAt(row.expiresAt),
298
310
  };
299
311
  req.apiKey = { id: row.id, scopes: row.scopes, rateLimit: row.rateLimit, teamId: (_g = row.teamId) !== null && _g !== void 0 ? _g : null };
300
312
  return next();
@@ -103,9 +103,9 @@ class CommandCallerVerifier {
103
103
  if (!out)
104
104
  return null;
105
105
  // A Bearer token carries the scopes of the credential that minted it
106
- // (/auth/token), so it is judged like a key. Judging it by role alone,
107
- // as a xenon-session token is for leases, would let an ADMIN's ordinary
108
- // key mint its way to an override.
106
+ // (/auth/token), so it is judged like a key. Judging it by role alone
107
+ // would let an ADMIN's ordinary key mint its way to an override, as a
108
+ // xenon-session token's lease override did through 2.14.
109
109
  return {
110
110
  caller: {
111
111
  userId: out.user.id,
@@ -42,9 +42,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
42
42
  });
43
43
  };
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
- exports.ACCEPTED_BEARER_AUDIENCES = void 0;
45
+ exports.SessionTokenSubjectError = exports.ACCEPTED_BEARER_AUDIENCES = void 0;
46
46
  exports.verifyKeyPairCredential = verifyKeyPairCredential;
47
47
  exports.verifyBearerCredential = verifyBearerCredential;
48
+ exports.isWrongSessionToken = isWrongSessionToken;
49
+ exports.verifySessionTokenCredential = verifySessionTokenCredential;
48
50
  const jose = __importStar(require("jose"));
49
51
  const typedi_1 = require("typedi");
50
52
  const ApiKeyService_1 = require("../services/ApiKeyService");
@@ -96,6 +98,12 @@ function verifyKeyPairCredential(accessKey, token) {
96
98
  * JWT) means the token is wrong. Any other error (typically "JWT key service
97
99
  * not initialized") means it could not be checked, and is rethrown once no
98
100
  * audience has verified.
101
+ *
102
+ * The payload's `scopes` are the token's as of now: `admin` is dropped once
103
+ * its user is a MEMBER. A token's scopes are fixed when it is minted, for up
104
+ * to a day, and REST (resolveActor, scopeGuard) and per-command auth's
105
+ * override read them, so through 2.14 an admin demoted since kept admin
106
+ * powers until the token expired.
99
107
  */
100
108
  function verifyBearerCredential(token) {
101
109
  return __awaiter(this, void 0, void 0, function* () {
@@ -119,6 +127,55 @@ function verifyBearerCredential(token) {
119
127
  const user = yield typedi_1.Container.get(UserService_1.UserService).findById(String(payload.sub));
120
128
  if (!user || user.status !== 'ACTIVE')
121
129
  return null;
130
+ return { payload: Object.assign(Object.assign({}, payload), { scopes: scopesForLiveRole(payload.scopes, user.role) }), user };
131
+ });
132
+ }
133
+ /** A token's `scopes` claim without `admin` when its user is now a MEMBER. */
134
+ function scopesForLiveRole(scopes, role) {
135
+ const list = String(scopes !== null && scopes !== void 0 ? scopes : '')
136
+ .split(',')
137
+ .map((s) => s.trim())
138
+ .filter(Boolean);
139
+ if (role === 'ADMIN' || role === 'SUPER_ADMIN')
140
+ return list.join(',');
141
+ return list.filter((s) => s !== 'admin').join(',');
142
+ }
143
+ /** The subject of a session token is no longer an ACTIVE user (deleted or Inactive). */
144
+ class SessionTokenSubjectError extends Error {
145
+ constructor() {
146
+ super('the session token names a user who is deleted or not active');
147
+ this.name = 'SessionTokenSubjectError';
148
+ }
149
+ }
150
+ exports.SessionTokenSubjectError = SessionTokenSubjectError;
151
+ /**
152
+ * Whether verifySessionTokenCredential's error means the token is wrong (a
153
+ * bad signature, audience or lifetime, not a JWT, or a departed user), as
154
+ * opposed to a check that could not run (the signing key or the database
155
+ * unavailable). A wrong token counts as none; a check that could not run
156
+ * refuses the create, as a key whose owner can't be looked up does.
157
+ */
158
+ function isWrongSessionToken(err) {
159
+ return err instanceof jose.errors.JOSEError || err instanceof SessionTokenSubjectError;
160
+ }
161
+ /**
162
+ * A `xenon-session` token (the `xe:options.sessionToken` capability), checked
163
+ * as REST checks a Bearer token: a valid signature, audience and lifetime,
164
+ * and a subject who is an ACTIVE user, looked up now. Through 2.14 session
165
+ * create checked the signature alone, so a deleted or Inactive user's token
166
+ * created sessions as them until it expired.
167
+ *
168
+ * Throws for a token that does not verify, as JwtKeyService.verify does, so
169
+ * the session-token gate and attribution treat a wrong signature and a
170
+ * departed user alike. Whether the token may create sessions at all (its
171
+ * `scopes`) is the caller's to judge.
172
+ */
173
+ function verifySessionTokenCredential(token) {
174
+ return __awaiter(this, void 0, void 0, function* () {
175
+ const payload = yield typedi_1.Container.get(JwtKeyService_1.JwtKeyService).verify(token, { audience: 'xenon-session' });
176
+ const user = yield typedi_1.Container.get(UserService_1.UserService).findById(String(payload.sub));
177
+ if (!user || user.status !== 'ACTIVE')
178
+ throw new SessionTokenSubjectError();
122
179
  return { payload, user: user };
123
180
  });
124
181
  }
package/lib/src/plugin.js CHANGED
@@ -66,6 +66,7 @@ const IPluginArgs_1 = require("./interfaces/IPluginArgs");
66
66
  const NodeDevices_1 = __importDefault(require("./device-managers/NodeDevices"));
67
67
  const appiumUmbrella_1 = require("./sessions/appiumUmbrella");
68
68
  const LiveSessionOwners_1 = require("./services/device-access/LiveSessionOwners");
69
+ const sessionMemory_1 = require("./sessions/sessionMemory");
69
70
  const SessionMetricsService_1 = require("./services/metrics/SessionMetricsService");
70
71
  const PhoneNetworkRestore_1 = require("./services/network/PhoneNetworkRestore");
71
72
  const config_1 = require("./config");
@@ -73,6 +74,7 @@ const SessionManager_1 = require("./sessions/SessionManager");
73
74
  const event_manager_1 = require("./dashboard/event-manager");
74
75
  const asset_manager_1 = require("./dashboard/asset-manager");
75
76
  const session_service_1 = require("./dashboard/services/session-service");
77
+ const shutdownReason_1 = require("./services/session/shutdownReason");
76
78
  const DEVICE_MANAGER_LOCK_NAME = 'DeviceManager';
77
79
  class XenonPlugin extends base_plugin_1.default {
78
80
  constructor(pluginName, cliArgs) {
@@ -104,11 +106,20 @@ class XenonPlugin extends base_plugin_1.default {
104
106
  return yield typedi_1.Container.get(CommandInterceptor_1.CommandInterceptor).handle(next, driver, commandName, args, this.pluginArgs, XenonPlugin.IS_HUB);
105
107
  });
106
108
  }
107
- onUnexpectedShutdown(driver, _cause) {
109
+ onUnexpectedShutdown(driver, cause) {
108
110
  return __awaiter(this, void 0, void 0, function* () {
109
111
  var _a;
110
112
  const sessionId = driver.sessionId;
111
113
  typedi_1.Container.get(LiveSessionOwners_1.LiveSessionOwners).forget(sessionId);
114
+ (0, sessionMemory_1.forgetSessionMemory)(sessionId);
115
+ // With the dashboard on or off: this was the only place a session span
116
+ // ended, and only with the dashboard on.
117
+ if (sessionId) {
118
+ typedi_1.Container.get(TracingService_1.TracingService).endSessionSpan(sessionId, {
119
+ failed: true,
120
+ reason: (0, shutdownReason_1.unexpectedShutdownReason)(cause),
121
+ });
122
+ }
112
123
  if (sessionId)
113
124
  yield typedi_1.Container.get(SessionMetricsService_1.SessionMetricsService).stop(sessionId);
114
125
  // Appium's new-command timeout ends a session here, not in deleteSession:
@@ -136,7 +147,9 @@ class XenonPlugin extends base_plugin_1.default {
136
147
  logger_1.default.info(`Unblocking device mapped with filter ${JSON.stringify(deviceFilter)} onUnexpectedShutdown from server`);
137
148
  if (XenonPlugin.IS_HUB && this.pluginArgs.enableDashboard && sessionId) {
138
149
  const sessionLog = this.xenonLog.withSession(sessionId, (_a = driver.caps) === null || _a === void 0 ? void 0 : _a.udid);
139
- sessionLog.info('Unexpected shutdown for session, updating dashboard...');
150
+ // Appium's cause, so an idle session is filed as a timeout.
151
+ const reason = (0, shutdownReason_1.unexpectedShutdownReason)(cause);
152
+ sessionLog.info(`Unexpected shutdown for session (${reason}), updating dashboard...`);
140
153
  const session = SessionManager_1.SESSION_MANAGER.getSession(sessionId);
141
154
  if (session && session.isVideoRecordingInProgress()) {
142
155
  try {
@@ -154,10 +167,7 @@ class XenonPlugin extends base_plugin_1.default {
154
167
  logger_1.default.debug(`[${sessionId}] rescue-video: Failed: ${err.message}`);
155
168
  }
156
169
  }
157
- yield event_manager_1.DASHBORD_EVENT_MANAGER.onSessionStopped(sessionId, SessionStatus_1.SessionStatus.FAILED, 'Driver shut down unexpectedly');
158
- typedi_1.Container.get(TracingService_1.TracingService).endSpan(sessionId, 'ERROR', {
159
- 'xenon.session.stop_reason': 'Unexpected shutdown',
160
- });
170
+ yield event_manager_1.DASHBORD_EVENT_MANAGER.onSessionStopped(sessionId, SessionStatus_1.SessionStatus.FAILED, reason);
161
171
  }
162
172
  });
163
173
  }
@@ -93,9 +93,9 @@ function checkDatabase(opts) {
93
93
  }
94
94
  if (configured && configured !== client) {
95
95
  return {
96
- warning: `[Database] databaseProvider is "${configured}", but this install's database client is ` +
97
- `built for ${clientLabel}, so the setting has no effect. Your data is in ${where}. ` +
98
- `Set databaseProvider to "${client}", or remove it.`,
96
+ warning: `[Database] databaseProvider is "${configured}", which has no effect: this install ` +
97
+ `stores its data in ${clientLabel}, in ${where}, and the database itself decides how ` +
98
+ `its schema is brought up to date. Remove the setting, or set it to "${client}".`,
99
99
  };
100
100
  }
101
101
  return {};
@@ -14,42 +14,44 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const node_child_process_1 = require("node:child_process");
16
16
  const config_1 = require("../config");
17
- const prepare_prisma_1 = require("./prepare-prisma");
18
17
  const logger_1 = __importDefault(require("../logger"));
19
- const env = Object.assign(Object.assign({}, process.env), { DATABASE_URL: config_1.config.databaseUrl });
20
- function executeCmd(cmd) {
21
- var _a;
22
- try {
23
- (0, node_child_process_1.execSync)(cmd, {
24
- env,
25
- stdio: 'inherit',
26
- });
27
- }
28
- catch (error) {
29
- const msg = (_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : String(error);
30
- logger_1.default.error(`[DBInit] Failed to execute command: ${cmd} | Error: ${msg}`, error);
31
- throw error;
32
- }
33
- }
18
+ const database_check_1 = require("./database-check");
19
+ const run_migrations_1 = require("./run-migrations");
20
+ /**
21
+ * `npm run db:migrate`: bring the database up to date, then regenerate the
22
+ * client.
23
+ *
24
+ * It checks the database URL and uses the rule the server uses at startup
25
+ * (database-check.ts, then run-migrations.ts): the database decides between
26
+ * `migrate deploy` and `db push`, so a database whose migration history
27
+ * matches its tables stays on `migrate deploy`. It runs whatever
28
+ * XENON_AUTO_MIGRATE says, since it is how you update the schema when that is
29
+ * off. Through 2.15.0 it always ran `db push`, and first rewrote
30
+ * prisma/schema.prisma's provider to `databaseProvider`. With `postgresql`,
31
+ * that left a PostgreSQL schema in the checkout that the SQLite file couldn't
32
+ * use.
33
+ *
34
+ * It first warns of changes to prisma/schema.prisma that no migration makes.
35
+ * `npm run db:generate` gives the developer's database a migration history, and
36
+ * from then on it is updated with `migrate deploy`, which never applies them,
37
+ * while the log said the schema was in sync.
38
+ */
34
39
  function main() {
35
40
  return __awaiter(this, void 0, void 0, function* () {
36
- logger_1.default.info(`[DBInit] Preparing database for provider: ${config_1.config.databaseProvider}`);
37
- // 1. Ensure schema matches provider
38
- yield (0, prepare_prisma_1.preparePrismaSchema)();
39
- // 2. Handle Migrations
40
- if (config_1.config.databaseProvider === 'sqlite') {
41
- logger_1.default.info('[DBInit] Syncing SQLite schema via db push...');
42
- executeCmd('npx prisma db push --accept-data-loss --skip-generate');
43
- }
44
- else {
45
- // For PostgreSQL, we might not have migrations checked in yet.
46
- // In "Cellular Architecture", we use db push to ensure the schema is synced
47
- // without requiring migration history sync across cells.
48
- logger_1.default.info('[DBInit] Syncing PostgreSQL schema via db push...');
49
- executeCmd('npx prisma db push --accept-data-loss');
50
- }
41
+ // A PostgreSQL URL would otherwise reach the history read and fail there,
42
+ // under a message about the file.
43
+ (0, database_check_1.assertSupportedDatabase)();
44
+ (0, run_migrations_1.warnOfUnmigratedSchemaChanges)();
45
+ yield (0, run_migrations_1.syncDatabaseSchema)();
51
46
  logger_1.default.info('[DBInit] Generating Prisma Client...');
52
- executeCmd('npx prisma generate');
47
+ (0, node_child_process_1.execSync)('npx prisma generate', {
48
+ env: Object.assign(Object.assign({}, process.env), { DATABASE_URL: config_1.config.databaseUrl }),
49
+ stdio: 'inherit',
50
+ });
53
51
  });
54
52
  }
55
- (() => __awaiter(void 0, void 0, void 0, function* () { return yield main(); }))();
53
+ main().catch((err) => {
54
+ var _a;
55
+ logger_1.default.error(`[DBInit] ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
56
+ process.exit(1);
57
+ });