@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,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Xenon's execute scripts: `driver.execute('xenon: <name>', ...)`, or `xe:`.
|
|
4
|
+
*
|
|
5
|
+
* Who answers them:
|
|
6
|
+
* - The five session-details commands (SESSION_DETAILS_COMMANDS in
|
|
7
|
+
* dashboard/commands.ts) are answered by the server that keeps the
|
|
8
|
+
* session's record: a hub, for a session on a node's phone, when its
|
|
9
|
+
* dashboard is on (its gateway's before-hook), otherwise the server that
|
|
10
|
+
* drives the phone.
|
|
11
|
+
* - Every other one is answered by the server that drives the phone
|
|
12
|
+
* (CommandInterceptor), because that is where the session's autowait
|
|
13
|
+
* settings, Omni-Vision and network capture live. A hub forwards them.
|
|
14
|
+
* - A name Xenon doesn't have fails with `unknown command`.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.XENON_SCRIPT_NAMES = void 0;
|
|
18
|
+
exports.xenonScriptName = xenonScriptName;
|
|
19
|
+
exports.unknownXenonScriptMessage = unknownXenonScriptMessage;
|
|
20
|
+
/** The names CommandInterceptor and the session-details commands answer. Keep in step with both. */
|
|
21
|
+
exports.XENON_SCRIPT_NAMES = [
|
|
22
|
+
'setAutowaitProperties',
|
|
23
|
+
'getAutowaitProperties',
|
|
24
|
+
'smartTap',
|
|
25
|
+
'omniClick',
|
|
26
|
+
'visualTap',
|
|
27
|
+
'uiInventory',
|
|
28
|
+
'uiScanExport',
|
|
29
|
+
'analyzeScreen',
|
|
30
|
+
'omniScan',
|
|
31
|
+
'assertVisualState',
|
|
32
|
+
'addMock',
|
|
33
|
+
'removeMock',
|
|
34
|
+
'clearMocks',
|
|
35
|
+
'getMocks',
|
|
36
|
+
'getRequests',
|
|
37
|
+
'exportHar',
|
|
38
|
+
'setSessionName',
|
|
39
|
+
'setSessionStatus',
|
|
40
|
+
'debug',
|
|
41
|
+
'addTag',
|
|
42
|
+
'captureEvidence',
|
|
43
|
+
];
|
|
44
|
+
/** The name in a `xenon: <name>` or `xe: <name>` script, or null for any other script. */
|
|
45
|
+
function xenonScriptName(script) {
|
|
46
|
+
if (typeof script !== 'string')
|
|
47
|
+
return null;
|
|
48
|
+
const match = /^\s*(?:xenon|xe)\s*:\s*(\S+)\s*$/.exec(script);
|
|
49
|
+
return match ? match[1] : null;
|
|
50
|
+
}
|
|
51
|
+
/** The message for a `xenon:` / `xe:` script Xenon doesn't have. */
|
|
52
|
+
function unknownXenonScriptMessage(script) {
|
|
53
|
+
return (`Unknown Xenon command "${script.trim()}". ` +
|
|
54
|
+
`Xenon's execute commands are: ${exports.XENON_SCRIPT_NAMES.join(', ')}.`);
|
|
55
|
+
}
|
|
@@ -30,7 +30,7 @@ exports.DefaultPluginArgs = {
|
|
|
30
30
|
bindHostOrIp: 'auto',
|
|
31
31
|
enableDashboard: false,
|
|
32
32
|
bootedSimulators: false,
|
|
33
|
-
healthCheckIntervalMs:
|
|
33
|
+
healthCheckIntervalMs: 300000,
|
|
34
34
|
healthCheckSchedule: undefined,
|
|
35
35
|
removeDevicesFromDatabaseBeforeRunningThePlugin: false,
|
|
36
36
|
databaseProvider: undefined,
|
|
@@ -50,7 +50,6 @@ exports.DefaultPluginArgs = {
|
|
|
50
50
|
recordingCleanupMaxCount: 100,
|
|
51
51
|
recordingFailedCleanupDays: 2,
|
|
52
52
|
sessionHeartbeatIntervalMs: 30000,
|
|
53
|
-
enableJsonLogging: false,
|
|
54
53
|
autowait: {
|
|
55
54
|
enabled: false,
|
|
56
55
|
timeoutMs: 10000,
|
|
@@ -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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
//
|
|
108
|
-
//
|
|
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,22 +66,34 @@ 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");
|
|
71
|
+
const PhoneNetworkRestore_1 = require("./services/network/PhoneNetworkRestore");
|
|
70
72
|
const config_1 = require("./config");
|
|
71
73
|
const SessionManager_1 = require("./sessions/SessionManager");
|
|
72
74
|
const event_manager_1 = require("./dashboard/event-manager");
|
|
73
75
|
const asset_manager_1 = require("./dashboard/asset-manager");
|
|
74
76
|
const session_service_1 = require("./dashboard/services/session-service");
|
|
77
|
+
const shutdownReason_1 = require("./services/session/shutdownReason");
|
|
75
78
|
const DEVICE_MANAGER_LOCK_NAME = 'DeviceManager';
|
|
76
79
|
class XenonPlugin extends base_plugin_1.default {
|
|
77
80
|
constructor(pluginName, cliArgs) {
|
|
81
|
+
var _a;
|
|
78
82
|
super(pluginName, cliArgs);
|
|
79
83
|
this.xenonLog = logger_1.default.scope('Plugin');
|
|
80
84
|
this.pluginArgs = Object.assign({}, IPluginArgs_1.DefaultPluginArgs);
|
|
81
85
|
this.aiCommandService = typedi_1.Container.get(AICommandService_1.AICommandService);
|
|
82
86
|
this.xenonLog.debug(`📱 Plugin Args: ${JSON.stringify(cliArgs)}`);
|
|
83
87
|
this.pluginArgs = Object.assign({}, IPluginArgs_1.DefaultPluginArgs, this.cliArgs);
|
|
84
|
-
|
|
88
|
+
// The option as it was given, not the merged copy above: JSON logging is on
|
|
89
|
+
// when the option says so, or when it isn't given and XENON_JSON_LOGGING does.
|
|
90
|
+
// schema.json gives the option no default, so Appium leaves it out when
|
|
91
|
+
// nobody set it; a default here (the merge above used to add `false`) would
|
|
92
|
+
// overwrite the variable on every start. `configure` ignores undefined.
|
|
93
|
+
const jsonLogging = (_a = this.cliArgs) === null || _a === void 0 ? void 0 : _a.enableJsonLogging;
|
|
94
|
+
logger_1.XenonLogger.configure({
|
|
95
|
+
enableJsonLogging: typeof jsonLogging === 'boolean' ? jsonLogging : undefined,
|
|
96
|
+
});
|
|
85
97
|
if ((0, networkAddresses_1.shouldAutoResolveBindHost)(this.pluginArgs.bindHostOrIp)) {
|
|
86
98
|
this.pluginArgs.bindHostOrIp = (0, networkAddresses_1.resolveAdvertisedBindHost)(this.pluginArgs.bindHostOrIp);
|
|
87
99
|
}
|
|
@@ -94,13 +106,18 @@ class XenonPlugin extends base_plugin_1.default {
|
|
|
94
106
|
return yield typedi_1.Container.get(CommandInterceptor_1.CommandInterceptor).handle(next, driver, commandName, args, this.pluginArgs, XenonPlugin.IS_HUB);
|
|
95
107
|
});
|
|
96
108
|
}
|
|
97
|
-
onUnexpectedShutdown(driver,
|
|
109
|
+
onUnexpectedShutdown(driver, cause) {
|
|
98
110
|
return __awaiter(this, void 0, void 0, function* () {
|
|
99
111
|
var _a;
|
|
100
112
|
const sessionId = driver.sessionId;
|
|
101
113
|
typedi_1.Container.get(LiveSessionOwners_1.LiveSessionOwners).forget(sessionId);
|
|
114
|
+
(0, sessionMemory_1.forgetSessionMemory)(sessionId);
|
|
102
115
|
if (sessionId)
|
|
103
116
|
yield typedi_1.Container.get(SessionMetricsService_1.SessionMetricsService).stop(sessionId);
|
|
117
|
+
// Appium's new-command timeout ends a session here, not in deleteSession:
|
|
118
|
+
// the phone's network (profile, interceptor proxy) and the capture are
|
|
119
|
+
// put back before the phone is released.
|
|
120
|
+
yield typedi_1.Container.get(PhoneNetworkRestore_1.PhoneNetworkRestore).restoreSession(sessionId, 'driver shut down');
|
|
104
121
|
const deviceFilter = {
|
|
105
122
|
session_id: sessionId ? sessionId : undefined,
|
|
106
123
|
udid: driver.caps && driver.caps.udid ? driver.caps.udid : undefined,
|
|
@@ -122,7 +139,9 @@ class XenonPlugin extends base_plugin_1.default {
|
|
|
122
139
|
logger_1.default.info(`Unblocking device mapped with filter ${JSON.stringify(deviceFilter)} onUnexpectedShutdown from server`);
|
|
123
140
|
if (XenonPlugin.IS_HUB && this.pluginArgs.enableDashboard && sessionId) {
|
|
124
141
|
const sessionLog = this.xenonLog.withSession(sessionId, (_a = driver.caps) === null || _a === void 0 ? void 0 : _a.udid);
|
|
125
|
-
|
|
142
|
+
// Appium's cause, so an idle session is filed as a timeout.
|
|
143
|
+
const reason = (0, shutdownReason_1.unexpectedShutdownReason)(cause);
|
|
144
|
+
sessionLog.info(`Unexpected shutdown for session (${reason}), updating dashboard...`);
|
|
126
145
|
const session = SessionManager_1.SESSION_MANAGER.getSession(sessionId);
|
|
127
146
|
if (session && session.isVideoRecordingInProgress()) {
|
|
128
147
|
try {
|
|
@@ -140,7 +159,7 @@ class XenonPlugin extends base_plugin_1.default {
|
|
|
140
159
|
logger_1.default.debug(`[${sessionId}] rescue-video: Failed: ${err.message}`);
|
|
141
160
|
}
|
|
142
161
|
}
|
|
143
|
-
yield event_manager_1.DASHBORD_EVENT_MANAGER.onSessionStopped(sessionId, SessionStatus_1.SessionStatus.FAILED,
|
|
162
|
+
yield event_manager_1.DASHBORD_EVENT_MANAGER.onSessionStopped(sessionId, SessionStatus_1.SessionStatus.FAILED, reason);
|
|
144
163
|
typedi_1.Container.get(TracingService_1.TracingService).endSpan(sessionId, 'ERROR', {
|
|
145
164
|
'xenon.session.stop_reason': 'Unexpected shutdown',
|
|
146
165
|
});
|
package/lib/src/prisma.js
CHANGED
|
@@ -33,7 +33,7 @@ exports.getPrismaClient = getPrismaClient;
|
|
|
33
33
|
* can stub individual methods (Prisma's internal Proxy blocks property assignment).
|
|
34
34
|
*/
|
|
35
35
|
const MODEL_DELEGATES = new Set([
|
|
36
|
-
'build', 'session', 'sessionLog', 'log', 'profiling', 'app', 'device',
|
|
36
|
+
'build', 'session', 'sessionLog', 'log', 'profiling', 'app', 'device', 'deviceSetting',
|
|
37
37
|
'pendingSession', 'cLIArgs', 'webhookConfig', 'webConfig', 'locatorEtalon',
|
|
38
38
|
'lease', 'portLease', 'apiKey', 'selectorState', 'user', 'userSession',
|
|
39
39
|
'passwordResetToken', 'team', 'teamMember', 'eventLog', 'project', 'recording', 'bookmark',
|
|
@@ -0,0 +1,124 @@
|
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.clientProviderFromSchema = clientProviderFromSchema;
|
|
40
|
+
exports.describeDatabaseUrl = describeDatabaseUrl;
|
|
41
|
+
exports.checkDatabase = checkDatabase;
|
|
42
|
+
exports.assertSupportedDatabase = assertSupportedDatabase;
|
|
43
|
+
const fs = __importStar(require("node:fs"));
|
|
44
|
+
const path = __importStar(require("node:path"));
|
|
45
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
46
|
+
const config_1 = require("../config");
|
|
47
|
+
const LABEL = { sqlite: 'SQLite', postgresql: 'PostgreSQL' };
|
|
48
|
+
function providerOfUrl(url) {
|
|
49
|
+
if (url.startsWith('file:'))
|
|
50
|
+
return 'sqlite';
|
|
51
|
+
if (/^postgres(ql)?:\/\//.test(url))
|
|
52
|
+
return 'postgresql';
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
/** The datasource provider in a Prisma schema's text, or null if it has none. */
|
|
56
|
+
function clientProviderFromSchema(schema) {
|
|
57
|
+
const match = /datasource\s+\w+\s*{[^}]*?provider\s*=\s*"([^"]+)"/.exec(schema);
|
|
58
|
+
return match ? match[1] : null;
|
|
59
|
+
}
|
|
60
|
+
/** A database URL fit for a log line: a SQLite file's path, or a server URL without credentials. */
|
|
61
|
+
function describeDatabaseUrl(url) {
|
|
62
|
+
if (url.startsWith('file:'))
|
|
63
|
+
return url.slice('file:'.length);
|
|
64
|
+
try {
|
|
65
|
+
const parsed = new URL(url);
|
|
66
|
+
return `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
67
|
+
}
|
|
68
|
+
catch (_a) {
|
|
69
|
+
return url.split(':')[0] + ':';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Decide whether the server may start on `url`. Throws an Error whose message
|
|
74
|
+
* says what to change; returns a warning when `configured` disagrees with the
|
|
75
|
+
* client but the URL is usable.
|
|
76
|
+
*/
|
|
77
|
+
function checkDatabase(opts) {
|
|
78
|
+
var _a;
|
|
79
|
+
const { configured, url, client } = opts;
|
|
80
|
+
const clientLabel = (_a = LABEL[client]) !== null && _a !== void 0 ? _a : client;
|
|
81
|
+
const urlProvider = providerOfUrl(url);
|
|
82
|
+
const where = describeDatabaseUrl(url);
|
|
83
|
+
if (urlProvider !== client) {
|
|
84
|
+
const kind = urlProvider
|
|
85
|
+
? `a ${LABEL[urlProvider]} database`
|
|
86
|
+
: 'not a database this install knows';
|
|
87
|
+
const fix = client === 'sqlite'
|
|
88
|
+
? 'Set DATABASE_URL (or databaseUrl) to a SQLite file, `file:/path/to/xenon.db`, or leave it ' +
|
|
89
|
+
'unset to use the default under ~/.cache/xenon. The published plugin stores its data in SQLite only.'
|
|
90
|
+
: `Set DATABASE_URL (or databaseUrl) to a ${clientLabel} URL.`;
|
|
91
|
+
throw new Error(`[Database] Cannot start: the database URL (${where}) is ${kind}, but this install's ` +
|
|
92
|
+
`database client is built for ${clientLabel}. ${fix}`);
|
|
93
|
+
}
|
|
94
|
+
if (configured && configured !== client) {
|
|
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.`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
/** The provider the generated client next to this file was built for. */
|
|
104
|
+
function generatedClientProvider() {
|
|
105
|
+
var _a;
|
|
106
|
+
const schemaPath = path.join(__dirname, '..', 'generated', 'client', 'schema.prisma');
|
|
107
|
+
try {
|
|
108
|
+
return (_a = clientProviderFromSchema(fs.readFileSync(schemaPath, 'utf8'))) !== null && _a !== void 0 ? _a : 'sqlite';
|
|
109
|
+
}
|
|
110
|
+
catch (_b) {
|
|
111
|
+
// The published plugin's client is SQLite; a missing copy means a plain install.
|
|
112
|
+
return 'sqlite';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Startup gate: log the warning, or throw so the server doesn't start on the wrong database. */
|
|
116
|
+
function assertSupportedDatabase() {
|
|
117
|
+
const { warning } = checkDatabase({
|
|
118
|
+
configured: config_1.config.databaseProvider,
|
|
119
|
+
url: config_1.config.databaseUrl,
|
|
120
|
+
client: generatedClientProvider(),
|
|
121
|
+
});
|
|
122
|
+
if (warning)
|
|
123
|
+
logger_1.default.warn(warning);
|
|
124
|
+
}
|
|
@@ -49,12 +49,19 @@ let AICommandService = class AICommandService {
|
|
|
49
49
|
return __awaiter(this, void 0, void 0, function* () {
|
|
50
50
|
this.logger.info(`Testing AI locator: ${locator.strategy}=${locator.selector}`);
|
|
51
51
|
const omniService = typedi_1.Container.get(OmniVisionService_1.OmniVisionService);
|
|
52
|
+
// A session's own route: its test may act on what it found.
|
|
53
|
+
const keep = (found) => {
|
|
54
|
+
if (driver === null || driver === void 0 ? void 0 : driver.sessionId)
|
|
55
|
+
for (const el of found)
|
|
56
|
+
omniService.remember(driver.sessionId, el);
|
|
57
|
+
return found;
|
|
58
|
+
};
|
|
52
59
|
if (locator.strategy === '-custom:ai-text') {
|
|
53
|
-
return yield omniService.findByText(driver, locator.selector);
|
|
60
|
+
return keep(yield omniService.findByText(driver, locator.selector));
|
|
54
61
|
}
|
|
55
62
|
else if (locator.strategy === '-custom:ai-icon') {
|
|
56
63
|
const result = yield omniService.findByIcon(driver, locator.selector);
|
|
57
|
-
return result ? [result] : [];
|
|
64
|
+
return keep(result ? [result] : []);
|
|
58
65
|
}
|
|
59
66
|
throw new Error(`Unsupported AI strategy: ${locator.strategy}`);
|
|
60
67
|
});
|