@xenon-device-management/xenon 2.13.2 → 2.15.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.
- package/README.md +17 -8
- package/lib/package.json +5 -5
- package/lib/public/assets/{AnnotationOverlay-BXruClDk.js → AnnotationOverlay-Btq3dZRI.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-qTS4LPLC.js → ApiKeyGate-C9OoAgg_.js} +1 -1
- package/lib/public/assets/DeviceMosaicView-DBC_xczB.js +2 -0
- package/lib/public/assets/{EmptyState-Cr17PdDv.js → EmptyState-DqWU2-vM.js} +1 -1
- package/lib/public/assets/{FieldGroup-Bvn08svv.js → FieldGroup-CveQ3xrA.js} +1 -1
- package/lib/public/assets/{FilterMenu-DDSO8aDf.js → FilterMenu-BfKDSfHz.js} +1 -1
- package/lib/public/assets/{Menu-B-Qn2X3T.js → Menu-CtdCybzA.js} +1 -1
- package/lib/public/assets/{Modal-Cg9V5JYU.js → Modal-Bsvj1tm2.js} +1 -1
- package/lib/public/assets/{RecordingPage-BTUs9VTW.js → RecordingPage-CRofOiyx.js} +1 -1
- package/lib/public/assets/{RecordingsPage-DYcfxITK.js → RecordingsPage-jNQHb2IR.js} +2 -2
- package/lib/public/assets/{SegmentedControl-Zy7suhT5.js → SegmentedControl-C8HtTha-.js} +1 -1
- package/lib/public/assets/{SettingCard-CWYmycoe.js → SettingCard-D8K1LZNo.js} +1 -1
- package/lib/public/assets/{Table-io9fwa98.js → Table-zNTX92Pn.js} +1 -1
- package/lib/public/assets/{activity-Bdf_uIWl.js → activity-CJ8LwZq9.js} +1 -1
- package/lib/public/assets/ai-settings-CgxQZqrc.js +16 -0
- package/lib/public/assets/{api-keys-CPqtVpm3.js → api-keys-ECpo-zTo.js} +1 -1
- package/lib/public/assets/{apps-BgZan0H9.js → apps-BjGymyf3.js} +2 -2
- package/lib/public/assets/{arrow-left-B27ckB31.js → arrow-left-BWfCbeHP.js} +1 -1
- package/lib/public/assets/{arrow-right-DOF8ITCz.js → arrow-right-DC7UGf_n.js} +1 -1
- package/lib/public/assets/{arrow-up-right-eFG5KTFS.js → arrow-up-right-BOjwybDk.js} +1 -1
- package/lib/public/assets/{auth-shell-DlC3_27j.js → auth-shell-CxMTo9aH.js} +2 -2
- package/lib/public/assets/{builds-page-BuXpPfKo.js → builds-page-DOCm4HkA.js} +1 -1
- package/lib/public/assets/{button-fIW8owMI.js → button-Cg08-EMT.js} +1 -1
- package/lib/public/assets/{calendar-WNjmrF-a.js → calendar-CA1ikLJs.js} +1 -1
- package/lib/public/assets/{check-BdTh1Sq7.js → check-BMTaH72W.js} +1 -1
- package/lib/public/assets/{chevron-right-B4BKlsqk.js → chevron-right-B2QOv9Nx.js} +1 -1
- package/lib/public/assets/{circle-check-zJU80_EF.js → circle-check-CYXBoUqT.js} +1 -1
- package/lib/public/assets/{circle-x-5Uwquo0h.js → circle-x-DTwnSAnl.js} +1 -1
- package/lib/public/assets/{clock-BtUprDsZ.js → clock-C8_HViQP.js} +1 -1
- package/lib/public/assets/{copy-DGSbZI3u.js → copy-Be7LNMXV.js} +1 -1
- package/lib/public/assets/derive-UnLRp4r3.js +1 -0
- package/lib/public/assets/device-explorer-BOqtfGXz.js +282 -0
- package/lib/public/assets/{download-WG6l7BgD.js → download-B56lwxWP.js} +1 -1
- package/lib/public/assets/forgot-password-CCU6bzLn.js +1 -0
- package/lib/public/assets/h264Stream-C-OtmIuk.js +26 -0
- package/lib/public/assets/index-B9KQ6LsY.js +15 -0
- package/lib/public/assets/{index-DI1fGIyj.js → index-BOxe43p1.js} +5 -5
- package/lib/public/assets/{index-HDQVe1gi.js → index-CfxsUslp.js} +1 -1
- package/lib/public/assets/{input-BjDRKCea.js → input-CZHFjbSg.js} +1 -1
- package/lib/public/assets/{line-chart-C0zdgd0v.js → line-chart-Gl_Dz3X_.js} +1 -1
- package/lib/public/assets/{list-checks-BdW5CjM8.js → list-checks-CCgjeyGY.js} +1 -1
- package/lib/public/assets/{lock-Dy3L4I8E.js → lock-CkOwgDcx.js} +1 -1
- package/lib/public/assets/{login-BHbY9CLG.js → login-D5cc4w7M.js} +1 -1
- package/lib/public/assets/maintenance-settings-t7qr3fvX.js +6 -0
- package/lib/public/assets/{monitor-8xjkeHHi.js → monitor-DEJixKQO.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-CXdKMXQ8.js → mouse-pointer-2-CtOrk4M8.js} +1 -1
- package/lib/public/assets/{network-Cj8JOKON.js → network-Dii9qdvk.js} +1 -1
- package/lib/public/assets/{overview-CTg7F0im.js → overview-rolAACrO.js} +1 -1
- package/lib/public/assets/{page-header--aTyJXq6.js → page-header-COQZxJfR.js} +1 -1
- package/lib/public/assets/{play-B-FodgHV.js → play-DrN1Se_R.js} +1 -1
- package/lib/public/assets/{plus-CsIk10Cq.js → plus-5aq-9uRx.js} +1 -1
- package/lib/public/assets/{profile-page-DkFKkMfl.js → profile-page-6i5kY6kF.js} +1 -1
- package/lib/public/assets/{reset-password-I2-zqLlI.js → reset-password-sp2IBrc7.js} +1 -1
- package/lib/public/assets/runbook-page-DJDjeopU.js +253 -0
- package/lib/public/assets/{select-DvCSv25z.js → select-Bpyn7rWO.js} +1 -1
- package/lib/public/assets/{selector-detail-redirect-BLVfMJs_.js → selector-detail-redirect-BH74vmKv.js} +1 -1
- package/lib/public/assets/{selector-health-page-oqadRokn.js → selector-health-page-Cw_g218H.js} +2 -2
- package/lib/public/assets/session-detail-page-AZJE6XXE.js +4 -0
- package/lib/public/assets/settings-C3uyGhyc.js +6 -0
- package/lib/public/assets/{settings-Dep_3kbA.css → settings-K8UHWMdI.css} +1 -1
- package/lib/public/assets/{stat-tile-DV9Ix-4O.js → stat-tile-xVqAKIlI.js} +1 -1
- package/lib/public/assets/{tablet-6b_SEN2X.js → tablet-BxUScAeO.js} +1 -1
- package/lib/public/assets/{teams-BjZyfuHh.js → teams-B5ljGkY2.js} +1 -1
- package/lib/public/assets/{trash-2-CTZpPSdd.js → trash-2-B701i8sy.js} +1 -1
- package/lib/public/assets/{upload-P3XzwQUb.js → upload-C2gr44RS.js} +1 -1
- package/lib/public/assets/{use-builds-data-1cOCMf-z.js → use-builds-data-NeQOFEoy.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-DgF9w088.js → use-password-reset-mode-DEzJq5ef.js} +1 -1
- package/lib/public/assets/{users-DMF9Mk5H.js → users-D_vv_yz4.js} +1 -1
- package/lib/public/assets/{users-CiNTKUiz.js → users-MY6eNe6s.js} +1 -1
- package/lib/public/assets/{video-off-CHp1KU_T.js → video-off-OK1lNZQn.js} +1 -1
- package/lib/public/assets/webhook-settings-BU-C9lBf.js +1 -0
- package/lib/public/assets/{zap-DRz0tDq5.js → zap-DO9ry1iz.js} +1 -1
- package/lib/public/index.html +1 -1
- package/lib/schema.json +23 -25
- package/lib/src/XenonCapabilityManager.js +22 -5
- package/lib/src/app/dashboardPluginLink.js +117 -0
- package/lib/src/app/index.js +13 -34
- package/lib/src/app/openapi/control.yaml +73 -20
- package/lib/src/app/openapi/grid.yaml +31 -7
- package/lib/src/app/openapi/identity.yaml +103 -31
- package/lib/src/app/openapi/platform.yaml +165 -39
- package/lib/src/app/openapi/sessions.yaml +10 -3
- package/lib/src/app/routers/apikeys.js +14 -2
- package/lib/src/app/routers/auth.js +29 -5
- package/lib/src/app/routers/control.js +20 -2
- package/lib/src/app/routers/dashboard.js +69 -26
- package/lib/src/app/routers/grid.js +37 -13
- package/lib/src/app/routers/profile.js +21 -0
- package/lib/src/app/routers/streamType.js +7 -2
- package/lib/src/app/routers/users.js +10 -5
- package/lib/src/app/routers/webhook.js +8 -4
- package/lib/src/app/swagger.js +2 -0
- package/lib/src/app/ws/h264StreamWs.js +10 -1
- package/lib/src/config.js +51 -5
- package/lib/src/dashboard/commands.js +185 -141
- package/lib/src/dashboard/event-manager.js +88 -67
- package/lib/src/dashboard/services/failure-analysis-service.js +136 -45
- package/lib/src/dashboard/sessionCommandSummary.js +39 -0
- package/lib/src/data-service/appleFamily.js +43 -0
- package/lib/src/data-service/db.js +11 -0
- package/lib/src/data-service/device-service.js +6 -1
- package/lib/src/data-service/device-store.js +76 -3
- package/lib/src/data-service/deviceClaims.js +37 -0
- package/lib/src/data-service/deviceFieldOwners.js +25 -11
- package/lib/src/data-service/deviceSettings.js +79 -0
- package/lib/src/data-service/prisma-store.js +169 -15
- package/lib/src/data-service/web-config-service.js +50 -92
- package/lib/src/device-managers/AndroidDeviceManager.js +9 -31
- package/lib/src/device-managers/android/AndroidH264StreamService.js +69 -20
- package/lib/src/device-managers/android/H264Multiplexer.js +38 -4
- package/lib/src/device-managers/ios/WDAClient.js +44 -43
- package/lib/src/device-managers/shellCommands.js +96 -0
- package/lib/src/device-utils.js +114 -40
- package/lib/src/gateway/defaultGateway.js +1 -1
- package/lib/src/gateway/forwardToNode.js +2 -0
- package/lib/src/gateway/healReport.js +87 -0
- package/lib/src/gateway/sessionGateway.js +12 -2
- package/lib/src/generated/client/edge.js +18 -3
- package/lib/src/generated/client/index-browser.js +15 -0
- package/lib/src/generated/client/index.d.ts +2086 -379
- package/lib/src/generated/client/index.js +18 -3
- package/lib/src/generated/client/package.json +1 -1
- package/lib/src/generated/client/schema.prisma +38 -7
- package/lib/src/generated/client/wasm.js +15 -0
- package/lib/src/interceptors/CommandInterceptor.js +271 -188
- package/lib/src/interceptors/xenonScripts.js +55 -0
- package/lib/src/interfaces/IPluginArgs.js +1 -2
- package/lib/src/middleware/authMiddleware.js +15 -3
- package/lib/src/middleware/commandCaller.js +3 -3
- package/lib/src/middleware/verifyCredential.js +58 -1
- package/lib/src/plugin.js +23 -4
- package/lib/src/prisma.js +1 -1
- package/lib/src/scripts/database-check.js +124 -0
- package/lib/src/services/AICommandService.js +9 -2
- package/lib/src/services/AIService.js +281 -32
- package/lib/src/services/CleanupService.js +6 -1
- package/lib/src/services/EmailService.js +29 -0
- package/lib/src/services/InterceptorService.js +70 -13
- package/lib/src/services/NetworkConditioningService.js +85 -88
- package/lib/src/services/NotificationService.js +36 -29
- package/lib/src/services/OrphanSweeper.js +7 -0
- package/lib/src/services/PortAllocator.js +5 -0
- package/lib/src/services/SelectorVerificationJob.js +15 -4
- package/lib/src/services/ServerManager.js +105 -9
- package/lib/src/services/SessionLifecycleService.js +133 -75
- package/lib/src/services/ShutdownCoordinator.js +33 -0
- package/lib/src/services/SocketServer.js +19 -3
- package/lib/src/services/TeamService.js +20 -3
- package/lib/src/services/bug-report/video-slice.js +4 -1
- package/lib/src/services/device-access/leaseOverride.js +7 -5
- package/lib/src/services/healing/FuzzyXmlHealingProvider.js +51 -0
- package/lib/src/services/healing/HealEtalonService.js +3 -2
- package/lib/src/services/healing/HealingOrchestrator.js +85 -71
- package/lib/src/services/healing/OcrHealingProvider.js +41 -64
- package/lib/src/services/healing/ResilioTreeHealingProvider.js +41 -50
- package/lib/src/services/healing/VisualAiHealingProvider.js +1 -1
- package/lib/src/services/healing/fingerprintIdentity.js +126 -0
- package/lib/src/services/healing/resilioPath.js +274 -0
- package/lib/src/services/interceptor/AndroidProxyAdapter.js +18 -0
- package/lib/src/services/interceptor/interceptorOptions.js +44 -0
- package/lib/src/services/logcat/SessionDeviceLogs.js +271 -0
- package/lib/src/services/logcat/deviceLogBook.js +163 -0
- package/lib/src/services/network/AndroidRadios.js +71 -0
- package/lib/src/services/network/PhoneNetworkLedger.js +184 -0
- package/lib/src/services/network/PhoneNetworkRestore.js +330 -0
- package/lib/src/services/network/adbForPhone.js +60 -0
- package/lib/src/services/network/phoneNetworkLock.js +20 -0
- package/lib/src/services/network/xenonProxy.js +107 -0
- package/lib/src/services/ocr/ocrData.js +118 -0
- package/lib/src/services/ocr/vendor/LICENSE-tessdata.txt +202 -0
- package/lib/src/services/ocr/vendor/eng.traineddata.gz +0 -0
- package/lib/src/services/omni-vision/OmniVisionService.js +132 -95
- package/lib/src/services/omni-vision/VisionAssertionService.js +28 -25
- package/lib/src/services/omni-vision/ocrTextMatch.js +105 -0
- package/lib/src/services/omni-vision/screenScale.js +68 -0
- package/lib/src/services/passwordResetLink.js +50 -9
- package/lib/src/services/passwordResetMode.js +3 -1
- package/lib/src/services/publicUrl.js +45 -0
- package/lib/src/services/recording/concurrency-gate.js +12 -3
- package/lib/src/services/session/sessionIdentity.js +5 -3
- package/lib/src/services/session/shutdownReason.js +26 -0
- package/lib/src/services/settings/SelfHealingSwitch.js +70 -0
- package/lib/src/services/settings/aiEngineSettings.js +219 -0
- package/lib/src/services/settings/labSettings.js +149 -0
- package/lib/src/services/token/mintedLifetime.js +51 -0
- package/lib/src/services/webhookEvents.js +173 -0
- package/lib/src/sessions/SessionManager.js +8 -2
- package/lib/src/sessions/sessionMemory.js +25 -0
- package/lib/test/helpers/fake-adb.js +58 -0
- package/lib/test/helpers/fake-ai-provider.js +188 -0
- package/lib/test/helpers/fake-device-table.js +63 -24
- package/lib/test/helpers/loki-stores.js +33 -0
- package/lib/test/helpers/page-sources.js +164 -0
- package/lib/test/helpers/scratch-database.js +58 -13
- package/lib/test/integration/audit-ingest.spec.js +10 -10
- package/lib/test/integration/auth-flow.spec.js +9 -14
- package/lib/test/integration/auth-rate-limit.spec.js +6 -3
- package/lib/test/integration/bug-report-route.spec.js +22 -2
- package/lib/test/integration/forgot-password-rate-limit.spec.js +6 -3
- package/lib/test/integration/forgot-password.spec.js +17 -18
- package/lib/test/integration/legacy-deprecation-headers.spec.js +5 -2
- package/lib/test/integration/migration.spec.js +6 -1
- package/lib/test/integration/node-pair-auth-rest.spec.js +5 -9
- package/lib/test/integration/ports-router.spec.js +4 -4
- package/lib/test/integration/prisma-device-store-add-devices.spec.js +2 -3
- package/lib/test/integration/prisma-device-store-team-filter.spec.js +2 -5
- package/lib/test/integration/profile-tokens.spec.js +10 -16
- package/lib/test/integration/recordings-router.spec.js +16 -16
- package/lib/test/integration/reset-revokes-sessions.spec.js +4 -9
- package/lib/test/integration/role-matrix.spec.js +4 -9
- package/lib/test/integration/sdk-leases-router.spec.js +9 -9
- package/lib/test/integration/sdk-version.spec.js +2 -2
- package/lib/test/integration/selector-health-endpoint.spec.js +5 -5
- package/lib/test/integration/team-membership-migration.spec.js +73 -12
- package/lib/test/integration/team-visibility-apps.spec.js +15 -22
- package/lib/test/integration/team-visibility-control.spec.js +96 -69
- package/lib/test/integration/team-visibility-dashboard.spec.js +7 -23
- package/lib/test/integration/team-visibility-grid.spec.js +16 -15
- package/lib/test/integration/team-visibility-leases.spec.js +8 -11
- package/lib/test/integration/team-visibility-recordings.spec.js +6 -19
- package/lib/test/integration/team-visibility-sessions.spec.js +6 -19
- package/lib/test/integration/users-flow.spec.js +7 -12
- package/lib/test/integration/users-lockout.spec.js +5 -3
- package/lib/test/unit/EmailService.test.js +66 -0
- package/lib/test/unit/ResilioTreeHealingProvider.test.js +46 -19
- package/lib/test/unit/TeamService.test.js +3 -0
- package/lib/test/unit/XenonCapabilityManager.spec.js +4 -2
- package/lib/test/unit/ai-call-time-limit.spec.js +130 -0
- package/lib/test/unit/ai-engine-settings.spec.js +338 -0
- package/lib/test/unit/ai-provider-switch.spec.js +130 -0
- package/lib/test/unit/ai-rate-limit.spec.js +126 -0
- package/lib/test/unit/android-h264-viewers.spec.js +272 -0
- package/lib/test/unit/android-preview-hold.spec.js +3 -2
- package/lib/test/unit/apikeys-router.spec.js +84 -0
- package/lib/test/unit/apple-family.spec.js +36 -0
- package/lib/test/unit/authMiddleware.bearer.spec.js +9 -0
- package/lib/test/unit/authMiddleware.credentialLifetime.spec.js +149 -0
- package/lib/test/unit/bug-report/video-slice.spec.js +19 -0
- package/lib/test/unit/cleanup-settings-db.spec.js +103 -0
- package/lib/test/unit/cleanup-settings.spec.js +244 -0
- package/lib/test/unit/dashboard-api-paths.spec.js +101 -0
- package/lib/test/unit/dashboard-commands.spec.js +249 -0
- package/lib/test/unit/dashboard-events-device-logs.spec.js +207 -0
- package/lib/test/unit/dashboard-events-team-scope.spec.js +9 -6
- package/lib/test/unit/dashboard-plugin-link.spec.js +213 -0
- package/lib/test/unit/database-check.spec.js +108 -0
- package/lib/test/unit/default-plugin-args.spec.js +33 -0
- package/lib/test/unit/device-log-book.spec.js +180 -0
- package/lib/test/unit/device-offline-webhook.spec.js +81 -0
- package/lib/test/unit/device-setting-columns.spec.js +66 -0
- package/lib/test/unit/device-settings-loki.spec.js +87 -0
- package/lib/test/unit/device-settings-survive.spec.js +555 -0
- package/lib/test/unit/device-sync-lost-update.spec.js +5 -0
- package/lib/test/unit/emulator-boot.spec.js +91 -0
- package/lib/test/unit/enable-dashboard-option.spec.js +57 -0
- package/lib/test/unit/event-log-session-data.spec.js +244 -0
- package/lib/test/unit/failure-analysis.spec.js +182 -0
- package/lib/test/unit/failure-categories.spec.js +128 -0
- package/lib/test/unit/fingerprint-identity.spec.js +433 -0
- package/lib/test/unit/h264-multiplexer.spec.js +46 -0
- package/lib/test/unit/heal-report.spec.js +71 -0
- package/lib/test/unit/healing-instrumentation.spec.js +6 -1
- package/lib/test/unit/healing-tier-gate.spec.js +73 -29
- package/lib/test/unit/healing-tiers-every-session.spec.js +219 -0
- package/lib/test/unit/hub-node-gateway-appium-server.spec.js +219 -0
- package/lib/test/unit/hub-node-same-host.spec.js +5 -0
- package/lib/test/unit/interceptor-visual-elements.spec.js +192 -0
- package/lib/test/unit/iphone-ipad-only.spec.js +132 -0
- package/lib/test/unit/json-logging-option.spec.js +74 -0
- package/lib/test/unit/lab-settings.spec.js +218 -0
- package/lib/test/unit/lease/lease-override.spec.js +23 -6
- package/lib/test/unit/lease/session-lease-proof.spec.js +16 -4
- package/lib/test/unit/max-sessions.spec.js +240 -0
- package/lib/test/unit/ocr-healing-tier-real-ocr.spec.js +113 -0
- package/lib/test/unit/ocr-healing-tier.spec.js +122 -0
- package/lib/test/unit/ocr-offline.spec.js +213 -0
- package/lib/test/unit/ocr-text-match.spec.js +44 -0
- package/lib/test/unit/omni-virtual-element-store.spec.js +301 -0
- package/lib/test/unit/omni-vision-analyze-screen.spec.js +72 -0
- package/lib/test/unit/omni-vision-ios-points.spec.js +159 -0
- package/lib/test/unit/omni-vision-tap-text.spec.js +129 -0
- package/lib/test/unit/omni-vision.spec.js +6 -8
- package/lib/test/unit/package-engines.spec.js +27 -0
- package/lib/test/unit/partial-config-file.spec.js +14 -0
- package/lib/test/unit/passwordResetMode.test.js +10 -2
- package/lib/test/unit/phone-network/boot-clean-up.spec.js +64 -0
- package/lib/test/unit/phone-network/interceptor-options.spec.js +77 -0
- package/lib/test/unit/phone-network/interceptor-proxy-restore.spec.js +145 -0
- package/lib/test/unit/phone-network/network-conditioning.spec.js +145 -0
- package/lib/test/unit/phone-network/phone-network-restore.spec.js +246 -0
- package/lib/test/unit/phone-network/session-end-paths.spec.js +182 -0
- package/lib/test/unit/phone-network/session-start.spec.js +110 -0
- package/lib/test/unit/phone-network/xenon-proxy.spec.js +69 -0
- package/lib/test/unit/plugin.spec.js +3 -3
- package/lib/test/unit/profile-router.test.js +122 -1
- package/lib/test/unit/queue-requester.spec.js +1 -1
- package/lib/test/unit/recording-options.spec.js +181 -0
- package/lib/test/unit/reset-link-address.spec.js +266 -0
- package/lib/test/unit/reset-link.test.js +10 -4
- package/lib/test/unit/resilio-path-healing.spec.js +270 -0
- package/lib/test/unit/selector-learning-every-session.spec.js +179 -0
- package/lib/test/unit/selector-verification-clean-build.spec.js +130 -0
- package/lib/test/unit/selector-verification-job.spec.js +13 -13
- package/lib/test/unit/self-healing-boot.spec.js +80 -0
- package/lib/test/unit/self-healing-setting.spec.js +273 -0
- package/lib/test/unit/session-app-resolution.spec.js +5 -1
- package/lib/test/unit/session-attribution-wiring.spec.js +41 -13
- package/lib/test/unit/session-create-credential-owner.spec.js +242 -0
- package/lib/test/unit/session-credential-strip.spec.js +1 -1
- package/lib/test/unit/session-device-logs.spec.js +347 -0
- package/lib/test/unit/session-end-failure-analysis.spec.js +133 -0
- package/lib/test/unit/session-failed-webhook.spec.js +170 -0
- package/lib/test/unit/session-log-indexes.spec.js +64 -3
- package/lib/test/unit/shell-commands.spec.js +133 -0
- package/lib/test/unit/socketServer.scopedEmit.spec.js +2 -0
- package/lib/test/unit/socketServer.sessionCookie.spec.js +2 -1
- package/lib/test/unit/stream-start-pipelines.spec.js +264 -0
- package/lib/test/unit/stream-status-type.spec.js +4 -0
- package/lib/test/unit/token-mint-limits.spec.js +165 -0
- package/lib/test/unit/unexpected-shutdown-reason.spec.js +163 -0
- package/lib/test/unit/verify-credential.spec.js +33 -0
- package/lib/test/unit/vision-assertion.spec.js +119 -0
- package/lib/test/unit/wda-client-shell.spec.js +133 -0
- package/lib/test/unit/web-config-service.spec.js +98 -0
- package/lib/test/unit/webhook-delivery.spec.js +24 -0
- package/lib/test/unit/webhook-payloads.spec.js +273 -0
- package/lib/test/unit/xenon-execute-scripts.spec.js +138 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/web/src/components/webhook-settings/webhookEventVariables.json +30 -0
- package/package.json +5 -5
- package/prisma/migrations/20261004140000_keep_device_settings/migration.sql +19 -0
- package/prisma/migrations/20261004174413_locator_etalon_path/migration.sql +2 -0
- package/prisma/schema.prisma +38 -7
- package/schema.json +23 -25
- package/scripts/generate-types-from-schema.js +1 -2
- package/scripts/lib/always-present-plugin-args.js +4 -1
- package/lib/public/assets/BugReportButton-BoGhlI7F.js +0 -15
- package/lib/public/assets/DeviceMosaicView-8J0oYqMM.js +0 -2
- package/lib/public/assets/ai-settings-Bce5SJob.js +0 -21
- package/lib/public/assets/device-explorer-C-KnhMHI.js +0 -279
- package/lib/public/assets/forgot-password-DnudqFFo.js +0 -1
- package/lib/public/assets/maintenance-settings-zDVlOMVK.js +0 -6
- package/lib/public/assets/recording-group-store-WVM3hoXn.js +0 -26
- package/lib/public/assets/runbook-page-DJTVCJX7.js +0 -124
- package/lib/public/assets/session-detail-page-CGn55Pvl.js +0 -4
- package/lib/public/assets/settings-MsVU2ukE.js +0 -6
- package/lib/public/assets/webhook-settings-DiS8geIm.js +0 -1
- /package/lib/public/assets/{BugReportButton-CPBjfiv4.css → index-CPBjfiv4.css} +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
9
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
10
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
11
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
12
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
13
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
14
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
18
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.PhoneNetworkLedger = void 0;
|
|
22
|
+
const typedi_1 = require("typedi");
|
|
23
|
+
const async_lock_1 = __importDefault(require("async-lock"));
|
|
24
|
+
const prisma_1 = require("../../prisma");
|
|
25
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
26
|
+
const PREFIX = 'phone-network:';
|
|
27
|
+
/**
|
|
28
|
+
* Xenon's record of the network changes it made to its phones, kept until
|
|
29
|
+
* each is put back. In memory a session's changes are undone when it ends; a
|
|
30
|
+
* crash or a kill -9 forgets them, and the phone stays offline or pointed at
|
|
31
|
+
* a proxy nobody serves. At its next start the server reads this record and
|
|
32
|
+
* puts those phones back (PhoneNetworkRestore).
|
|
33
|
+
*
|
|
34
|
+
* Kept in this server's own database, which is what makes it this server's:
|
|
35
|
+
* a hub and a node on one machine see the same Android phones, but each has
|
|
36
|
+
* its own database. It has no table of its own: WebConfig is the database's
|
|
37
|
+
* key-value table (MetricsService keeps its counters there), and a new table
|
|
38
|
+
* would mean a migration and a regenerated client for a handful of rows. One
|
|
39
|
+
* row per session, `phone-network:<sessionId>`, holding the JSON above.
|
|
40
|
+
*
|
|
41
|
+
* A failed write is logged and never fails a session: the change is still
|
|
42
|
+
* made, and undone when the session ends; only a crash before then would
|
|
43
|
+
* leave it.
|
|
44
|
+
*/
|
|
45
|
+
let PhoneNetworkLedger = class PhoneNetworkLedger {
|
|
46
|
+
constructor() {
|
|
47
|
+
this.logger = logger_1.default.scope('PhoneNetwork');
|
|
48
|
+
this.lock = new async_lock_1.default();
|
|
49
|
+
}
|
|
50
|
+
/** Adds `part` to the session's record, creating the record if needed. */
|
|
51
|
+
note(sessionId, udid, part) {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
yield this.guard(`record a network change for ${udid} (session ${sessionId})`, () => this.lock.acquire(sessionId, () => __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
const current = yield this.read(sessionId);
|
|
55
|
+
const next = Object.assign(Object.assign({}, (current !== null && current !== void 0 ? current : { sessionId, udid, at: Date.now() })), part);
|
|
56
|
+
yield this.write(next);
|
|
57
|
+
})));
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/** The part was put back: drops it, and the record once nothing is left. */
|
|
61
|
+
settle(sessionId, part) {
|
|
62
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
+
yield this.guard(`settle the ${part} change of session ${sessionId}`, () => this.lock.acquire(sessionId, () => __awaiter(this, void 0, void 0, function* () {
|
|
64
|
+
const current = yield this.read(sessionId);
|
|
65
|
+
if (!current)
|
|
66
|
+
return;
|
|
67
|
+
const next = Object.assign({}, current);
|
|
68
|
+
delete next[part];
|
|
69
|
+
if (next.offline || next.proxy)
|
|
70
|
+
yield this.write(next);
|
|
71
|
+
else
|
|
72
|
+
yield this.remove(sessionId);
|
|
73
|
+
})));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/** Drops the session's record, whatever it holds. */
|
|
77
|
+
forget(sessionId) {
|
|
78
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
79
|
+
yield this.guard(`forget the network changes of session ${sessionId}`, () => this.lock.acquire(sessionId, () => this.remove(sessionId)));
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/** The session's record, or null. */
|
|
83
|
+
get(sessionId) {
|
|
84
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
85
|
+
var _a;
|
|
86
|
+
try {
|
|
87
|
+
return yield this.read(sessionId);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
this.logger.warn(`Could not read the phone network record: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** Every record, oldest first. A row that isn't a record is dropped. */
|
|
96
|
+
list() {
|
|
97
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
var _a;
|
|
99
|
+
let rows = [];
|
|
100
|
+
try {
|
|
101
|
+
rows = yield prisma_1.prisma.webConfig.findMany({
|
|
102
|
+
where: { name: { startsWith: PREFIX } },
|
|
103
|
+
select: { name: true, value: true },
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
this.logger.warn(`Could not read the phone network record: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
const out = [];
|
|
111
|
+
for (const row of rows) {
|
|
112
|
+
const change = parseChange(row.value);
|
|
113
|
+
if (change)
|
|
114
|
+
out.push(change);
|
|
115
|
+
else
|
|
116
|
+
yield this.remove(row.name.slice(PREFIX.length)).catch(() => undefined);
|
|
117
|
+
}
|
|
118
|
+
return out.sort((a, b) => a.at - b.at);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
read(sessionId) {
|
|
122
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
123
|
+
const row = yield prisma_1.prisma.webConfig.findUnique({ where: { name: PREFIX + sessionId } });
|
|
124
|
+
return row ? parseChange(row.value) : null;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
write(change) {
|
|
128
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
129
|
+
const name = PREFIX + change.sessionId;
|
|
130
|
+
const value = JSON.stringify(change);
|
|
131
|
+
yield prisma_1.prisma.webConfig.upsert({
|
|
132
|
+
where: { name },
|
|
133
|
+
update: { value },
|
|
134
|
+
create: { id: name, name, value },
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
remove(sessionId) {
|
|
139
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
140
|
+
yield prisma_1.prisma.webConfig.deleteMany({ where: { name: PREFIX + sessionId } });
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
guard(what, fn) {
|
|
144
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
145
|
+
var _a;
|
|
146
|
+
try {
|
|
147
|
+
yield fn();
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
this.logger.warn(`Could not ${what}: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
exports.PhoneNetworkLedger = PhoneNetworkLedger;
|
|
156
|
+
exports.PhoneNetworkLedger = PhoneNetworkLedger = __decorate([
|
|
157
|
+
(0, typedi_1.Service)()
|
|
158
|
+
], PhoneNetworkLedger);
|
|
159
|
+
function parseChange(value) {
|
|
160
|
+
try {
|
|
161
|
+
const v = JSON.parse(value);
|
|
162
|
+
if (!v || typeof v.sessionId !== 'string' || typeof v.udid !== 'string')
|
|
163
|
+
return null;
|
|
164
|
+
if (typeof v.at !== 'number')
|
|
165
|
+
return null;
|
|
166
|
+
const change = { sessionId: v.sessionId, udid: v.udid, at: v.at };
|
|
167
|
+
if (v.offline && typeof v.offline === 'object') {
|
|
168
|
+
change.offline = {
|
|
169
|
+
wifiOn: boolOrNull(v.offline.wifiOn),
|
|
170
|
+
dataOn: boolOrNull(v.offline.dataOn),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (v.proxy && typeof v.proxy.set === 'string') {
|
|
174
|
+
change.proxy = Object.assign({ set: v.proxy.set, previous: typeof v.proxy.previous === 'string' ? v.proxy.previous : null }, (typeof v.proxy.reversePort === 'number' ? { reversePort: v.proxy.reversePort } : {}));
|
|
175
|
+
}
|
|
176
|
+
return change.offline || change.proxy ? change : null;
|
|
177
|
+
}
|
|
178
|
+
catch (_a) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function boolOrNull(v) {
|
|
183
|
+
return typeof v === 'boolean' ? v : null;
|
|
184
|
+
}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
42
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
43
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
44
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
45
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
46
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
47
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
51
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
52
|
+
};
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.PhoneNetworkRestore = exports.LEFTOVER_MAX_AGE_MS = void 0;
|
|
55
|
+
const typedi_1 = require("typedi");
|
|
56
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
57
|
+
const PortAllocator_1 = require("../PortAllocator");
|
|
58
|
+
const AndroidProxyAdapter_1 = require("../interceptor/AndroidProxyAdapter");
|
|
59
|
+
const adbForPhone_1 = require("./adbForPhone");
|
|
60
|
+
const AndroidRadios_1 = require("./AndroidRadios");
|
|
61
|
+
const PhoneNetworkLedger_1 = require("./PhoneNetworkLedger");
|
|
62
|
+
const phoneNetworkLock_1 = require("./phoneNetworkLock");
|
|
63
|
+
const xenonProxy_1 = require("./xenonProxy");
|
|
64
|
+
/** A record that still can't be undone after this long is dropped: the phone is gone. */
|
|
65
|
+
exports.LEFTOVER_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
66
|
+
/**
|
|
67
|
+
* Puts a phone's network back after a session that changed it: a network
|
|
68
|
+
* profile (`Offline` turns Wi-Fi and mobile data off) or the network
|
|
69
|
+
* interceptor (the phone's global HTTP proxy). Both change the whole phone,
|
|
70
|
+
* not one app, so a phone left that way has no network for the next test.
|
|
71
|
+
*
|
|
72
|
+
* Every way a session ends on this server calls restoreSession, before the
|
|
73
|
+
* phone is released so the next session can't have its own changes undone:
|
|
74
|
+
* the test's delete (SessionLifecycleService.deleteSession), Appium's own end
|
|
75
|
+
* of a session (its new-command timeout, a crashed driver: the plugin's
|
|
76
|
+
* onUnexpectedShutdown), Xenon's idle release (releaseBlockedDevices), a
|
|
77
|
+
* stale heartbeat (OrphanSweeper) and a graceful shutdown. It is keyed by the
|
|
78
|
+
* session id alone, so it works for sessions Xenon keeps no other record of
|
|
79
|
+
* (SESSION_MANAGER holds a local session only with the dashboard or video on).
|
|
80
|
+
*
|
|
81
|
+
* Once per session: a second ending finds the first one's work in progress
|
|
82
|
+
* and waits for it, and a later one finds nothing to do.
|
|
83
|
+
*
|
|
84
|
+
* What a crash forgets, PhoneNetworkLedger remembers. restoreLeftovers undoes
|
|
85
|
+
* it at this server's next start (cleanUpAtBoot) and before the phone's next
|
|
86
|
+
* session here (a phone that was unplugged when its session ended).
|
|
87
|
+
*/
|
|
88
|
+
let PhoneNetworkRestore = class PhoneNetworkRestore {
|
|
89
|
+
constructor() {
|
|
90
|
+
this.logger = logger_1.default.scope('PhoneNetwork');
|
|
91
|
+
this.inflight = new Map();
|
|
92
|
+
this.radios = new AndroidRadios_1.AndroidRadios(adbForPhone_1.adbForPhone);
|
|
93
|
+
this.proxies = new AndroidProxyAdapter_1.AndroidProxyAdapter(adbForPhone_1.adbForPhone);
|
|
94
|
+
}
|
|
95
|
+
/** Undoes what the session changed on its phone's network, once. Never throws. */
|
|
96
|
+
restoreSession(sessionId, why) {
|
|
97
|
+
if (!sessionId)
|
|
98
|
+
return Promise.resolve();
|
|
99
|
+
const running = this.inflight.get(sessionId);
|
|
100
|
+
if (running)
|
|
101
|
+
return running;
|
|
102
|
+
const work = this.undoSession(sessionId, why).finally(() => this.inflight.delete(sessionId));
|
|
103
|
+
this.inflight.set(sessionId, work);
|
|
104
|
+
return work;
|
|
105
|
+
}
|
|
106
|
+
/** Every session this server still has network changes for (shutdown). Never throws. */
|
|
107
|
+
restoreAll(why) {
|
|
108
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
109
|
+
const { net, icp } = yield this.services();
|
|
110
|
+
const ids = new Set([...net.sessionIds(), ...icp.sessionIds(), ...this.inflight.keys()]);
|
|
111
|
+
yield Promise.all([...ids].map((id) => this.restoreSession(id, why)));
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Undoes what the ledger says ended sessions left on their phones: one
|
|
116
|
+
* phone's (`udid`) or every phone's. Skips sessions this server still runs.
|
|
117
|
+
* A record that can't be undone now (the phone isn't connected) is kept for
|
|
118
|
+
* next time, for a week. Returns how many records were fully undone.
|
|
119
|
+
* Never throws.
|
|
120
|
+
*/
|
|
121
|
+
restoreLeftovers() {
|
|
122
|
+
return __awaiter(this, arguments, void 0, function* (opts = {}) {
|
|
123
|
+
var _a;
|
|
124
|
+
let done = 0;
|
|
125
|
+
try {
|
|
126
|
+
const ledger = typedi_1.Container.get(PhoneNetworkLedger_1.PhoneNetworkLedger);
|
|
127
|
+
const entries = (yield ledger.list()).filter((e) => !opts.udid || e.udid === opts.udid);
|
|
128
|
+
for (const entry of entries) {
|
|
129
|
+
if (yield this.isLive(entry.sessionId))
|
|
130
|
+
continue;
|
|
131
|
+
if (yield this.undoLeftover(entry.sessionId, entry.udid))
|
|
132
|
+
done++;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
this.logger.warn(`Could not put back what earlier sessions left: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
|
|
137
|
+
}
|
|
138
|
+
return done;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* At this server's start: first what the ledger holds, then each of this
|
|
143
|
+
* server's Android phones (`udids`) whose proxy is a capture an earlier run
|
|
144
|
+
* left (this machine, one of this server's capture ports, nothing answering
|
|
145
|
+
* there; such a phone has no network). That second check also catches a
|
|
146
|
+
* crash from before the ledger existed. A proxy that points anywhere else,
|
|
147
|
+
* or at a capture that is running, is left alone. Never throws.
|
|
148
|
+
*/
|
|
149
|
+
cleanUpAtBoot(udids) {
|
|
150
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
var _a;
|
|
152
|
+
yield this.restoreLeftovers({});
|
|
153
|
+
let range;
|
|
154
|
+
try {
|
|
155
|
+
range = typedi_1.Container.get(PortAllocator_1.PortAllocator).rangeOf('proxy');
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
this.logger.warn(`Could not check the phones' proxies: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
for (const udid of udids)
|
|
162
|
+
yield this.clearDeadCaptureProxy(udid, range);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
undoSession(sessionId, why) {
|
|
166
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
167
|
+
var _a, _b;
|
|
168
|
+
let services;
|
|
169
|
+
try {
|
|
170
|
+
services = yield this.services();
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
this.logger.warn(`Could not put back the network of session ${sessionId}: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const { net, icp } = services;
|
|
177
|
+
const profile = net.has(sessionId);
|
|
178
|
+
const capture = icp.isActive(sessionId);
|
|
179
|
+
if (!profile && !capture)
|
|
180
|
+
return;
|
|
181
|
+
this.logger.info(`Putting back the phone network of session ${sessionId} (${why})`);
|
|
182
|
+
if (profile) {
|
|
183
|
+
try {
|
|
184
|
+
yield net.reset(sessionId);
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
this.logger.warn(`Network profile reset failed for ${sessionId}: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (capture) {
|
|
191
|
+
try {
|
|
192
|
+
yield icp.stop(sessionId);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
this.logger.warn(`Interceptor stop failed for ${sessionId}: ${(_b = err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : err}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
isLive(sessionId) {
|
|
201
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
202
|
+
if (this.inflight.has(sessionId))
|
|
203
|
+
return true;
|
|
204
|
+
const { net, icp } = yield this.services();
|
|
205
|
+
return net.has(sessionId) || icp.isActive(sessionId);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/** One record, under the phone's lock, read again there: another clean-up may have done it. */
|
|
209
|
+
undoLeftover(sessionId, udid) {
|
|
210
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
211
|
+
const ledger = typedi_1.Container.get(PhoneNetworkLedger_1.PhoneNetworkLedger);
|
|
212
|
+
return yield (0, phoneNetworkLock_1.withPhoneNetworkLock)(udid, () => __awaiter(this, void 0, void 0, function* () {
|
|
213
|
+
const change = yield ledger.get(sessionId);
|
|
214
|
+
if (!change)
|
|
215
|
+
return false;
|
|
216
|
+
let undone = true;
|
|
217
|
+
if (change.offline && !(yield this.undoOffline(change, change.offline)))
|
|
218
|
+
undone = false;
|
|
219
|
+
if (change.proxy && !(yield this.undoProxy(change, change.proxy)))
|
|
220
|
+
undone = false;
|
|
221
|
+
if (!undone && Date.now() - change.at > exports.LEFTOVER_MAX_AGE_MS) {
|
|
222
|
+
yield ledger.forget(sessionId);
|
|
223
|
+
this.logger.warn(`Gave up putting back the network of ${udid} (session ${sessionId}): it has not ` +
|
|
224
|
+
'been reachable for a week.');
|
|
225
|
+
}
|
|
226
|
+
return undone;
|
|
227
|
+
}));
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
undoOffline(change, prior) {
|
|
231
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
232
|
+
var _a;
|
|
233
|
+
const { udid, sessionId } = change;
|
|
234
|
+
const which = AndroidRadios_1.AndroidRadios.toRestore(prior);
|
|
235
|
+
try {
|
|
236
|
+
yield this.radios.turnOn(udid, which);
|
|
237
|
+
yield typedi_1.Container.get(PhoneNetworkLedger_1.PhoneNetworkLedger).settle(sessionId, 'offline');
|
|
238
|
+
const what = [which.wifi && 'Wi-Fi', which.data && 'mobile data']
|
|
239
|
+
.filter(Boolean)
|
|
240
|
+
.join(' and ');
|
|
241
|
+
if (what) {
|
|
242
|
+
this.logger.warn(`Turned ${what} back on on ${udid}: session ${sessionId} took the phone offline and ` +
|
|
243
|
+
'ended without putting it back.');
|
|
244
|
+
}
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
this.logger.warn(`Could not turn the network back on on ${udid} (session ${sessionId}): ` +
|
|
249
|
+
`${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}. Trying again at its next session and at the next start.`);
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
undoProxy(change, proxy) {
|
|
255
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
256
|
+
var _a, _b;
|
|
257
|
+
const { udid, sessionId } = change;
|
|
258
|
+
try {
|
|
259
|
+
const now = (yield this.proxies.readProxy(udid)).trim();
|
|
260
|
+
if (now === proxy.set) {
|
|
261
|
+
yield this.proxies.restoreProxy(udid, proxy.previous);
|
|
262
|
+
if (proxy.reversePort)
|
|
263
|
+
yield this.removeReverse(udid, proxy.reversePort);
|
|
264
|
+
this.logger.warn(`Put back the proxy of ${udid} (${(_a = proxy.previous) !== null && _a !== void 0 ? _a : 'none'}): session ${sessionId} ` +
|
|
265
|
+
`pointed it at ${proxy.set} and ended without putting it back.`);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
this.logger.info(`Left the proxy of ${udid} as it is (${now || 'none'}): it changed after session ` +
|
|
269
|
+
`${sessionId} set ${proxy.set}.`);
|
|
270
|
+
}
|
|
271
|
+
yield typedi_1.Container.get(PhoneNetworkLedger_1.PhoneNetworkLedger).settle(sessionId, 'proxy');
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
this.logger.warn(`Could not put back the proxy of ${udid} (session ${sessionId}): ${(_b = err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : err}. ` +
|
|
276
|
+
'Trying again at its next session and at the next start.');
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
clearDeadCaptureProxy(udid, range) {
|
|
282
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
283
|
+
var _a;
|
|
284
|
+
try {
|
|
285
|
+
yield (0, phoneNetworkLock_1.withPhoneNetworkLock)(udid, () => __awaiter(this, void 0, void 0, function* () {
|
|
286
|
+
const now = yield this.proxies.readProxy(udid);
|
|
287
|
+
const proxy = (0, xenonProxy_1.parseProxySetting)(now);
|
|
288
|
+
if (!proxy || !(yield (0, xenonProxy_1.isDeadCaptureProxy)(now, range)))
|
|
289
|
+
return;
|
|
290
|
+
yield this.proxies.clearProxy(udid);
|
|
291
|
+
if (proxy.host === '127.0.0.1' || proxy.host.toLowerCase() === 'localhost') {
|
|
292
|
+
yield this.removeReverse(udid, proxy.port);
|
|
293
|
+
}
|
|
294
|
+
this.logger.warn(`Cleared the proxy of ${udid} (${now.trim()}): it pointed at this machine's network ` +
|
|
295
|
+
`capture port ${proxy.port}, where nothing answers, so the phone had no network.`);
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
this.logger.debug(`Could not check the proxy of ${udid}: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
removeReverse(udid, port) {
|
|
304
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
305
|
+
try {
|
|
306
|
+
yield this.proxies.removeReverse(udid, port);
|
|
307
|
+
}
|
|
308
|
+
catch (_a) {
|
|
309
|
+
// Gone with the adb server or the connection: nothing to remove.
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
/** Imported lazily: InterceptorService pulls in the socket server. */
|
|
314
|
+
services() {
|
|
315
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
316
|
+
const [{ NetworkConditioningService }, { InterceptorService }] = yield Promise.all([
|
|
317
|
+
Promise.resolve().then(() => __importStar(require('../NetworkConditioningService'))),
|
|
318
|
+
Promise.resolve().then(() => __importStar(require('../InterceptorService'))),
|
|
319
|
+
]);
|
|
320
|
+
return {
|
|
321
|
+
net: typedi_1.Container.get(NetworkConditioningService),
|
|
322
|
+
icp: typedi_1.Container.get(InterceptorService),
|
|
323
|
+
};
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
exports.PhoneNetworkRestore = PhoneNetworkRestore;
|
|
328
|
+
exports.PhoneNetworkRestore = PhoneNetworkRestore = __decorate([
|
|
329
|
+
(0, typedi_1.Service)()
|
|
330
|
+
], PhoneNetworkRestore);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.adbForPhone = void 0;
|
|
46
|
+
const typedi_1 = require("typedi");
|
|
47
|
+
/**
|
|
48
|
+
* A phone's adb as the rest of Xenon reaches it: appium-adb's, which finds the
|
|
49
|
+
* binary through ANDROID_HOME / ANDROID_SDK_ROOT and runs it without a shell,
|
|
50
|
+
* and which talks to a remote adb server for a phone that has one. Never a
|
|
51
|
+
* bare `adb`: a server started without the SDK on its PATH (Xenon Control, a
|
|
52
|
+
* service) has none.
|
|
53
|
+
*
|
|
54
|
+
* Imported lazily: AndroidDeviceManager pulls in most of the device layer.
|
|
55
|
+
*/
|
|
56
|
+
const adbForPhone = (udid) => __awaiter(void 0, void 0, void 0, function* () {
|
|
57
|
+
const { default: AndroidDeviceManager } = yield Promise.resolve().then(() => __importStar(require('../../device-managers/AndroidDeviceManager')));
|
|
58
|
+
return yield typedi_1.Container.get(AndroidDeviceManager).getAdbForDevice(udid);
|
|
59
|
+
});
|
|
60
|
+
exports.adbForPhone = adbForPhone;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.withPhoneNetworkLock = withPhoneNetworkLock;
|
|
7
|
+
const async_lock_1 = __importDefault(require("async-lock"));
|
|
8
|
+
const lock = new async_lock_1.default({ maxPending: 10000 });
|
|
9
|
+
/**
|
|
10
|
+
* One change at a time to a phone's network settings (Wi-Fi, mobile data, the
|
|
11
|
+
* global proxy): a session's start reading and changing them, its end putting
|
|
12
|
+
* them back, and a clean-up of what an earlier run left. Without it, a
|
|
13
|
+
* clean-up that read a dead proxy could clear the one a new session had just
|
|
14
|
+
* set, or a session could read another's half-undone state as the phone's own.
|
|
15
|
+
*
|
|
16
|
+
* Not reentrant: never call it from inside itself for the same phone.
|
|
17
|
+
*/
|
|
18
|
+
function withPhoneNetworkLock(udid, fn) {
|
|
19
|
+
return lock.acquire(`phone-network:${udid}`, fn);
|
|
20
|
+
}
|