@xenon-device-management/xenon 1.7.4 → 1.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/lib/package.json +1 -1
  2. package/lib/public/assets/{ApiKeyGate-DX4HKDBA.js → ApiKeyGate-BVaB1Eg_.js} +1 -1
  3. package/lib/public/assets/{BugReportButton-N-NBBb3l.js → BugReportButton-CioDV7QP.js} +1 -1
  4. package/lib/public/assets/DeviceMosaicView-BtRxZTXc.js +2 -0
  5. package/lib/public/assets/{EmptyState-X7DYPoPj.js → EmptyState-UcJej2vi.js} +1 -1
  6. package/lib/public/assets/{FieldGroup-a9gzUh4W.js → FieldGroup-CYlyr34Q.js} +1 -1
  7. package/lib/public/assets/{Modal-Cuva-zxU.js → Modal-CjmzIR4Y.js} +1 -1
  8. package/lib/public/assets/{SegmentedControl-Bdc4A0UR.js → SegmentedControl-wRDvkYAQ.js} +1 -1
  9. package/lib/public/assets/{SettingCard-DVZUpQJR.js → SettingCard-BzjYlAYS.js} +1 -1
  10. package/lib/public/assets/{Table-Casq-_Hh.js → Table-ta1UYEeC.js} +1 -1
  11. package/lib/public/assets/{activity-KiLkzk_U.js → activity-jltyhcrF.js} +1 -1
  12. package/lib/public/assets/{ai-settings-0dEPftlc.js → ai-settings-Dfjxb1SZ.js} +1 -1
  13. package/lib/public/assets/{api-keys-Cg1cbSUt.js → api-keys-cFOese97.js} +1 -1
  14. package/lib/public/assets/{apps-CyetRRfI.js → apps-sHtZU8T_.js} +1 -1
  15. package/lib/public/assets/{arrow-left-C1v7ukDM.js → arrow-left-DGZzN6Sl.js} +1 -1
  16. package/lib/public/assets/{arrow-up-right-B-YmrLSY.js → arrow-up-right-DtZUe0YH.js} +1 -1
  17. package/lib/public/assets/{auth-shell-BXwxuIPK.js → auth-shell-D0eF5eyS.js} +1 -1
  18. package/lib/public/assets/{builds-page-wquO6Xb2.js → builds-page-DxDwaq_t.js} +1 -1
  19. package/lib/public/assets/{button-xZrEx8cE.js → button-3RkAu6xE.js} +1 -1
  20. package/lib/public/assets/{check-CDOZKQih.js → check-D6DDcFwM.js} +1 -1
  21. package/lib/public/assets/{chevron-right-CnpP0W_l.js → chevron-right-BtA7DR8A.js} +1 -1
  22. package/lib/public/assets/{circle-check-DDqpikKW.js → circle-check-Bjgvpq5F.js} +1 -1
  23. package/lib/public/assets/{circle-x-DcBe5J67.js → circle-x-_TOawobA.js} +1 -1
  24. package/lib/public/assets/{clock-CfNmWqai.js → clock-pm4LDp97.js} +1 -1
  25. package/lib/public/assets/{copy-DkXT5Ggc.js → copy-x-UuUQkc.js} +1 -1
  26. package/lib/public/assets/{device-explorer-B3PK2XW9.js → device-explorer-BDih8ZO9.js} +1 -1
  27. package/lib/public/assets/{download-B0tbRUtY.js → download-CuDjBGgs.js} +1 -1
  28. package/lib/public/assets/{forgot-password-DvHMwK0-.js → forgot-password-lgHuL3gW.js} +1 -1
  29. package/lib/public/assets/{funnel-DfsfiM0o.js → funnel-DQuWVevA.js} +1 -1
  30. package/lib/public/assets/{index-wbxPcwMM.js → index--TKsSVZp.js} +2 -2
  31. package/lib/public/assets/{layers-D-4LYQG3.js → layers-CdE4q4fN.js} +1 -1
  32. package/lib/public/assets/{lock-Cf_J2NEl.js → lock-D2Vg6I6G.js} +1 -1
  33. package/lib/public/assets/{login-DCIOMQb8.js → login-C2dtnYcH.js} +1 -1
  34. package/lib/public/assets/{maintenance-settings-CDDl4Q8B.js → maintenance-settings-Ck0suBTt.js} +1 -1
  35. package/lib/public/assets/{mouse-pointer-2-DoCqhosb.js → mouse-pointer-2-JuM7rDhC.js} +1 -1
  36. package/lib/public/assets/{overview-DNeH4FfG.js → overview-BBzmQBEl.js} +1 -1
  37. package/lib/public/assets/{page-header-DZpQDeCw.js → page-header-D5tpZwkI.js} +1 -1
  38. package/lib/public/assets/{plus-CTjPrkU5.js → plus-tcA3_CAo.js} +1 -1
  39. package/lib/public/assets/{profile-page-DKiZOb_Y.js → profile-page-BuuLTEtS.js} +1 -1
  40. package/lib/public/assets/{reset-password-6rl6YA7S.js → reset-password-ERq0WAyA.js} +1 -1
  41. package/lib/public/assets/{runbook-page-DTTw_cb3.js → runbook-page-BubL06xm.js} +1 -1
  42. package/lib/public/assets/{selector-detail-page-BVPMp3ZK.js → selector-detail-page-DgL7bNdy.js} +1 -1
  43. package/lib/public/assets/{selector-health-BVJ8fGyw.js → selector-health-B-V7vomq.js} +1 -1
  44. package/lib/public/assets/{selector-health-page-CZ_AAjtV.js → selector-health-page-DWy7bTdU.js} +1 -1
  45. package/lib/public/assets/{session-detail-page-BApz2wZr.js → session-detail-page-COgS5oAy.js} +1 -1
  46. package/lib/public/assets/{settings-CuYjTdtb.js → settings-D0fQ4JNR.js} +1 -1
  47. package/lib/public/assets/{shield-DVYhrtS6.js → shield-DINNxWHp.js} +1 -1
  48. package/lib/public/assets/{shield-alert-BhxlmXys.js → shield-alert-DDDbE5ER.js} +1 -1
  49. package/lib/public/assets/{status-pill-outline-DjKuXDLG.js → status-pill-outline-CFz5xaoF.js} +1 -1
  50. package/lib/public/assets/{tablet-CxgLRRaU.js → tablet-DoE7LIzI.js} +1 -1
  51. package/lib/public/assets/{teams-EuX6i7H2.js → teams-DpW-IUPC.js} +1 -1
  52. package/lib/public/assets/{trash-2-8zA1h40t.js → trash-2-Dvr08Dpq.js} +1 -1
  53. package/lib/public/assets/{useSocket-qyqK9PWr.js → useSocket-COssbQIj.js} +1 -1
  54. package/lib/public/assets/{users-CPafGHcL.js → users-DKmLh2WK.js} +1 -1
  55. package/lib/public/assets/{webhook-settings-y8EAM79b.js → webhook-settings-nwtLvp2Q.js} +1 -1
  56. package/lib/public/assets/{zap-C-b2-XlO.js → zap-CM2cWetx.js} +1 -1
  57. package/lib/public/index.html +1 -1
  58. package/lib/src/device-managers/android/AndroidStreamService.js +11 -0
  59. package/lib/src/device-managers/ios/IOSStreamService.js +55 -7
  60. package/lib/src/device-managers/ios/iosStreamDiagnostics.js +20 -0
  61. package/lib/src/services/PortAllocator.js +14 -0
  62. package/lib/test/unit/PortAllocator.test.js +21 -0
  63. package/lib/test/unit/android-stream-release.spec.js +86 -0
  64. package/lib/test/unit/iosStreamDiagnostics.spec.js +21 -0
  65. package/lib/tsconfig.tsbuildinfo +1 -1
  66. package/package.json +1 -1
  67. package/lib/public/assets/DeviceMosaicView-BEDnJqk5.js +0 -2
@@ -58,4 +58,25 @@ describe('iosStreamDiagnostics', () => {
58
58
  (0, chai_1.expect)((0, iosStreamDiagnostics_1.classifyTunnelStderr)('')).to.deep.equal({ unsupported: false, udid: null });
59
59
  });
60
60
  });
61
+ describe('isOwnStreamProcess', () => {
62
+ const udid = '00008110-00084CE80E51401E';
63
+ it('claims an iproxy forward for this udid', () => {
64
+ (0, chai_1.expect)((0, iosStreamDiagnostics_1.isOwnStreamProcess)(`iproxy -u ${udid} 9101:9100`, udid)).to.equal(true);
65
+ });
66
+ it('claims a go-ios/WDA process for this udid', () => {
67
+ (0, chai_1.expect)((0, iosStreamDiagnostics_1.isOwnStreamProcess)(`/root/.cache/xenon/goIOS/ios runwda --udid ${udid}`, udid)).to.equal(true);
68
+ });
69
+ it('does NOT claim a neighbour process bound to the same port under a different udid', () => {
70
+ // The Android stream leased port 9100; an iOS device defaulting to 9100
71
+ // must not kill it during cleanup.
72
+ (0, chai_1.expect)((0, iosStreamDiagnostics_1.isOwnStreamProcess)('iproxy -u 381103b720057ece 9100:9100', udid)).to.equal(false);
73
+ });
74
+ it('does NOT claim a process with no udid in its command', () => {
75
+ (0, chai_1.expect)((0, iosStreamDiagnostics_1.isOwnStreamProcess)('node /opt/app/android-stream-capture.js', udid)).to.equal(false);
76
+ });
77
+ it('is safe on empty command or udid', () => {
78
+ (0, chai_1.expect)((0, iosStreamDiagnostics_1.isOwnStreamProcess)('', udid)).to.equal(false);
79
+ (0, chai_1.expect)((0, iosStreamDiagnostics_1.isOwnStreamProcess)(`iproxy -u ${udid}`, '')).to.equal(false);
80
+ });
81
+ });
61
82
  });
@@ -1 +1 @@
1
- {"root":["../src/InternalHttpClient.ts","../src/PluginContext.ts","../src/XenonCapabilityManager.ts","../src/chromeUtils.ts","../src/config.ts","../src/device-utils.ts","../src/index.ts","../src/logger.ts","../src/plugin.ts","../src/prisma.ts","../src/app/index.ts","../src/app/swagger-docs.ts","../src/app/swagger.ts","../src/app/routers/apikeys.ts","../src/app/routers/apps.ts","../src/app/routers/auth.ts","../src/app/routers/bug-report.ts","../src/app/routers/build-export.ts","../src/app/routers/capabilities.ts","../src/app/routers/config.ts","../src/app/routers/control.ts","../src/app/routers/dashboard.ts","../src/app/routers/grid.ts","../src/app/routers/interceptor.ts","../src/app/routers/ports.ts","../src/app/routers/processes.ts","../src/app/routers/profile.ts","../src/app/routers/projects.ts","../src/app/routers/recordings.ts","../src/app/routers/reservation.ts","../src/app/routers/sdk-leases.ts","../src/app/routers/sdk-version.ts","../src/app/routers/teams.ts","../src/app/routers/users.ts","../src/app/routers/webhook.ts","../src/commands/handle.ts","../src/commands/index.ts","../src/dashboard/asset-manager.ts","../src/dashboard/commands.ts","../src/dashboard/event-manager.ts","../src/dashboard/services/app-service.ts","../src/dashboard/services/failure-analysis-service.ts","../src/dashboard/services/session-service.ts","../src/data-service/CircuitBreaker.ts","../src/data-service/config-service.ts","../src/data-service/db.ts","../src/data-service/device-service.ts","../src/data-service/device-store.interface.ts","../src/data-service/device-store.ts","../src/data-service/pending-sessions-service.ts","../src/data-service/pluginArgs.ts","../src/data-service/prisma-service.ts","../src/data-service/prisma-store.ts","../src/data-service/queue-service.ts","../src/data-service/web-config-service.ts","../src/device-managers/AndroidDeviceManager.ts","../src/device-managers/ChromeDriverManager.ts","../src/device-managers/HealthMonitorService.ts","../src/device-managers/IOSDeviceManager.ts","../src/device-managers/NodeDevices.ts","../src/device-managers/iOSTracker.ts","../src/device-managers/index.ts","../src/device-managers/android/AndroidStreamService.ts","../src/device-managers/android/DeviceLockManager.ts","../src/device-managers/cloud/CapabilityManager.ts","../src/device-managers/cloud/Devices.ts","../src/device-managers/ios/IOSDiscoveryService.ts","../src/device-managers/ios/IOSStreamService.ts","../src/device-managers/ios/WDAClient.ts","../src/device-managers/ios/iosBattery.ts","../src/device-managers/ios/iosStreamDiagnostics.ts","../src/enums/Capabilities.ts","../src/enums/Cloud.ts","../src/enums/Platform.ts","../src/enums/SessionType.ts","../src/enums/SocketEvents.ts","../src/generated/client/default.d.ts","../src/generated/client/default.js","../src/generated/client/edge.d.ts","../src/generated/client/edge.js","../src/generated/client/index-browser.js","../src/generated/client/index.d.ts","../src/generated/client/index.js","../src/generated/client/wasm.d.ts","../src/generated/client/wasm.js","../src/generated/client/runtime/edge-esm.js","../src/generated/client/runtime/edge.js","../src/generated/client/runtime/index-browser.d.ts","../src/generated/client/runtime/index-browser.js","../src/generated/client/runtime/library.d.ts","../src/generated/client/runtime/library.js","../src/generated/client/runtime/react-native.js","../src/generated/client/runtime/wasm.js","../src/helpers/UniversalMjpegProxy.ts","../src/helpers/index.ts","../src/interceptors/CommandInterceptor.ts","../src/interfaces/ICloudManager.ts","../src/interfaces/IDevice.ts","../src/interfaces/IDeviceFilterOptions.ts","../src/interfaces/IDeviceManager.ts","../src/interfaces/IOptions.ts","../src/interfaces/IPluginArgs.ts","../src/interfaces/ISessionCapability.ts","../src/logging/sessionContext.ts","../src/middleware/authMiddleware.ts","../src/middleware/csrfMiddleware.ts","../src/middleware/leaseTokenMiddleware.ts","../src/middleware/loginRateLimiter.ts","../src/middleware/rateLimitMiddleware.ts","../src/middleware/roleGuard.ts","../src/middleware/scopeGuard.ts","../src/profiling/AndroidAppProfiler.ts","../src/proxy/wd-command-proxy.ts","../src/scripts/generate-database-migration.ts","../src/scripts/initialize-database.ts","../src/scripts/install-go-ios.ts","../src/scripts/prepare-prisma.ts","../src/scripts/run-migrations.ts","../src/services/AICommandService.ts","../src/services/AIService.ts","../src/services/ApiKeyService.ts","../src/services/CircuitBreaker.ts","../src/services/CleanupService.ts","../src/services/DeviceReconciler.ts","../src/services/EmailService.ts","../src/services/EventBus.ts","../src/services/EventLogService.ts","../src/services/InspectorService.ts","../src/services/InterceptorService.ts","../src/services/MetricsService.ts","../src/services/NetworkConditioningService.ts","../src/services/NotificationService.ts","../src/services/OrphanSweeper.ts","../src/services/PasswordResetService.ts","../src/services/PortAllocator.ts","../src/services/ProcessMetricsService.ts","../src/services/ProcessRegistry.ts","../src/services/RequestLogService.ts","../src/services/ResourceIsolationService.ts","../src/services/SecurityService.ts","../src/services/SelectorStateService.ts","../src/services/SelectorVerificationJob.ts","../src/services/ServerManager.ts","../src/services/SessionHeartbeatService.ts","../src/services/SessionLifecycleService.ts","../src/services/ShutdownCoordinator.ts","../src/services/SocketClient.ts","../src/services/SocketServer.ts","../src/services/TeamService.ts","../src/services/TracingService.ts","../src/services/UserService.ts","../src/services/UserSessionService.ts","../src/services/VideoPipelineService.ts","../src/services/artifacts/ArtifactStore.ts","../src/services/autowait/AutowaitService.ts","../src/services/autowait/waitFor.ts","../src/services/bug-report/BugReportService.ts","../src/services/bug-report/archive.ts","../src/services/bug-report/har-collector.ts","../src/services/bug-report/manifest.ts","../src/services/bug-report/readme.ts","../src/services/bug-report/types.ts","../src/services/bug-report/video-slice.ts","../src/services/bug-report/window.ts","../src/services/healing/FuzzyXmlHealingProvider.ts","../src/services/healing/HealEtalonService.ts","../src/services/healing/HealedLocatorGenerator.ts","../src/services/healing/HealingMetrics.ts","../src/services/healing/HealingOrchestrator.ts","../src/services/healing/LlmHealingProvider.ts","../src/services/healing/OcrHealingProvider.ts","../src/services/healing/ResilioTreeHealingProvider.ts","../src/services/healing/VisualAiHealingProvider.ts","../src/services/healing/types.ts","../src/services/identity/bootstrap.ts","../src/services/identity/sessionCleanupCron.ts","../src/services/identity/userAuthorization.ts","../src/services/interceptor/AndroidProxyAdapter.ts","../src/services/interceptor/CertManager.ts","../src/services/interceptor/HarBuilder.ts","../src/services/interceptor/HostFilter.ts","../src/services/interceptor/MitmProxyHost.ts","../src/services/interceptor/MockEngine.ts","../src/services/interceptor/RequestBuffer.ts","../src/services/interceptor/SessionArchive.ts","../src/services/interceptor/types.ts","../src/services/lease/LeaseOrphanSweeper.ts","../src/services/lease/LeaseService.ts","../src/services/lease/buildCapabilityBag.ts","../src/services/lease/leaseToken.ts","../src/services/omni-vision/OmniVisionService.ts","../src/services/omni-vision/VisionAssertionService.ts","../src/services/ports/PortAllocatorClient.ts","../src/services/ports/PortAllocatorService.ts","../src/services/recording/RecordingOrchestrator.ts","../src/services/recording/annotation-render.ts","../src/services/recording/busy-precheck.ts","../src/services/recording/concurrency-gate.ts","../src/services/recording/manualLock.ts","../src/services/recording/proof-bundle.ts","../src/services/recording/recording-store.ts","../src/services/telemetry/attributes.ts","../src/services/token/JwtKeyService.ts","../src/services/token/StreamTicketService.ts","../src/sessions/CloudSession.ts","../src/sessions/LocalSession.ts","../src/sessions/RemoteSession.ts","../src/sessions/SessionManager.ts","../src/sessions/XenonSession.ts","../src/types/CLIArgs.ts","../src/types/CloudArgs.ts","../src/types/CloudSchema.ts","../src/types/DeviceType.ts","../src/types/DeviceUpdate.ts","../src/types/IOSDevice.ts","../src/types/Platform.ts","../src/types/SessionStatus.ts","../src/types/express.d.ts","../src/types/healing.d.ts","../src/types/identity.ts","../src/validators/CapabilityValidator.ts","../test/e2e/e2ehelper.ts","../test/e2e/plugin-harness.ts","../test/e2e/plugin.spec.ts","../test/e2e/startup-verification.spec.ts","../test/e2e/telemetry_verification.spec.ts","../test/e2e/video-recording-test.spec.js","../test/e2e/android/conf.spec.js","../test/e2e/android/conf2.spec.js","../test/e2e/android/conf3.spec.js","../test/e2e/hubnode/forward-request.spec.ts","../test/e2e/hubnode/hubnode.spec.ts","../test/e2e/ios/conf1.spec.js","../test/e2e/ios/conf2.spec.js","../test/helpers/seedUser.ts","../test/helpers/test-container.ts","../test/integration/androidDevices.spec.ts","../test/integration/auth-flow.spec.ts","../test/integration/auth-rate-limit.spec.ts","../test/integration/bug-report-route.spec.ts","../test/integration/cliArgs.js","../test/integration/cross-workflow.spec.ts","../test/integration/forgot-password-rate-limit.spec.ts","../test/integration/forgot-password.spec.ts","../test/integration/legacy-deprecation-headers.spec.ts","../test/integration/migration.spec.ts","../test/integration/node-pair-auth-rest.spec.ts","../test/integration/ports-router.spec.ts","../test/integration/profile-tokens.spec.ts","../test/integration/recordings-router.spec.ts","../test/integration/reset-revokes-sessions.spec.ts","../test/integration/role-matrix.spec.ts","../test/integration/sdk-leases-router.spec.ts","../test/integration/sdk-version.spec.ts","../test/integration/selector-state-flow.spec.ts","../test/integration/team-membership-migration.spec.ts","../test/integration/team-visibility-dashboard.spec.ts","../test/integration/team-visibility-grid.spec.ts","../test/integration/team-visibility-recordings.spec.ts","../test/integration/users-flow.spec.ts","../test/integration/users-lockout.spec.ts","../test/integration/ios/01iOSSimulator.spec.ts","../test/integration/ios/02iOSDevices.spec.ts","../test/unit/AndroidDeviceManager.spec.ts","../test/unit/ApiKeyService.test.ts","../test/unit/ArtifactStore.spec.ts","../test/unit/AutowaitService.spec.ts","../test/unit/CapabilitiesRouter.spec.ts","../test/unit/ChromeDriverManager.spec.js","../test/unit/CleanupService.spec.ts","../test/unit/CommandInterceptor.autowait.spec.ts","../test/unit/DeviceModel.spec.ts","../test/unit/EmailService.test.ts","../test/unit/EventLogService.spec.ts","../test/unit/FuzzyXmlHealingProvider.test.ts","../test/unit/GetAdbOriginal.js","../test/unit/HealingCascade.test.ts","../test/unit/IOSDeviceManager.spec.js","../test/unit/JwtKeyService.spec.ts","../test/unit/OrphanSweeper.test.ts","../test/unit/PasswordResetService.test.ts","../test/unit/PortAllocator.test.ts","../test/unit/ProcessRegistry.test.ts","../test/unit/ProjectsRouter.spec.ts","../test/unit/RemoteIOs.spec.js","../test/unit/ResilioTreeHealingProvider.test.ts","../test/unit/SessionHeartbeatWrite.test.ts","../test/unit/StreamTicketService.spec.ts","../test/unit/TeamService.test.ts","../test/unit/TokenEndpoint.spec.ts","../test/unit/UserService.test.ts","../test/unit/UserSessionService.test.ts","../test/unit/XenonCapabilityManager.spec.ts","../test/unit/aggregate-hotspots-tuple-key.spec.ts","../test/unit/auth-me-auth-disabled.test.ts","../test/unit/authMiddleware.bearer.spec.ts","../test/unit/authMiddleware.streamTicket.spec.ts","../test/unit/authMiddleware.test.ts","../test/unit/bootstrap-identity.test.ts","../test/unit/build-export.spec.ts","../test/unit/busy-precheck.spec.ts","../test/unit/commands.spec.js","../test/unit/concurrency-gate.spec.ts","../test/unit/config.spec.js","../test/unit/device-service.spec.ts","../test/unit/device-utils.spec.ts","../test/unit/event-manager-regression-hook.spec.ts","../test/unit/extract-access-key-token-pair.test.ts","../test/unit/findAndLockDevice-leasemerge.spec.ts","../test/unit/healing-instrumentation.spec.ts","../test/unit/healing-state-endpoints.spec.ts","../test/unit/helpers.spec.ts","../test/unit/iosBattery.spec.ts","../test/unit/iosStreamDiagnostics.spec.ts","../test/unit/loginRateLimiter.test.ts","../test/unit/omni-vision.spec.ts","../test/unit/plugin.spec.ts","../test/unit/prisma.spec.ts","../test/unit/profile-router.test.ts","../test/unit/proof-bundle.spec.ts","../test/unit/rateLimitMiddleware.test.ts","../test/unit/recording-instrumentation.spec.ts","../test/unit/recording-orchestrator.spec.ts","../test/unit/recording-store.spec.ts","../test/unit/roleGuard.test.ts","../test/unit/run-migrations-fail-fast.test.ts","../test/unit/run-migrations.test.ts","../test/unit/selector-state-service.spec.ts","../test/unit/selector-verification-job.spec.ts","../test/unit/team-filter.test.ts","../test/unit/tracing-service-init.spec.ts","../test/unit/userAuthorization.test.ts","../test/unit/users-router.test.ts","../test/unit/video-pipeline-args.spec.ts","../test/unit/video-pipeline-outputpath.spec.ts","../test/unit/bug-report/archive.spec.ts","../test/unit/bug-report/har-collector.spec.ts","../test/unit/bug-report/manifest.spec.ts","../test/unit/bug-report/readme.spec.ts","../test/unit/bug-report/route.spec.ts","../test/unit/bug-report/service.spec.ts","../test/unit/bug-report/video-slice.spec.ts","../test/unit/bug-report/window.spec.ts","../test/unit/fixtures/device.config.js","../test/unit/fixtures/devices.js","../test/unit/interceptor/AndroidProxyAdapter.spec.ts","../test/unit/interceptor/CertManager.spec.ts","../test/unit/interceptor/HarBuilder.spec.ts","../test/unit/interceptor/HostFilter.spec.ts","../test/unit/interceptor/MitmProxyHost.spec.ts","../test/unit/interceptor/MockEngine.spec.ts","../test/unit/interceptor/RequestBuffer.spec.ts","../test/unit/interceptor/SessionArchive.spec.ts","../test/unit/lease/LeaseOrphanSweeper.spec.ts","../test/unit/lease/LeaseService.spec.ts","../test/unit/lease/buildCapabilityBag.spec.ts","../test/unit/lease/leaseToken.spec.ts","../test/unit/ports/PortAllocatorService.spec.ts"],"version":"5.9.3"}
1
+ {"root":["../src/InternalHttpClient.ts","../src/PluginContext.ts","../src/XenonCapabilityManager.ts","../src/chromeUtils.ts","../src/config.ts","../src/device-utils.ts","../src/index.ts","../src/logger.ts","../src/plugin.ts","../src/prisma.ts","../src/app/index.ts","../src/app/swagger-docs.ts","../src/app/swagger.ts","../src/app/routers/apikeys.ts","../src/app/routers/apps.ts","../src/app/routers/auth.ts","../src/app/routers/bug-report.ts","../src/app/routers/build-export.ts","../src/app/routers/capabilities.ts","../src/app/routers/config.ts","../src/app/routers/control.ts","../src/app/routers/dashboard.ts","../src/app/routers/grid.ts","../src/app/routers/interceptor.ts","../src/app/routers/ports.ts","../src/app/routers/processes.ts","../src/app/routers/profile.ts","../src/app/routers/projects.ts","../src/app/routers/recordings.ts","../src/app/routers/reservation.ts","../src/app/routers/sdk-leases.ts","../src/app/routers/sdk-version.ts","../src/app/routers/teams.ts","../src/app/routers/users.ts","../src/app/routers/webhook.ts","../src/commands/handle.ts","../src/commands/index.ts","../src/dashboard/asset-manager.ts","../src/dashboard/commands.ts","../src/dashboard/event-manager.ts","../src/dashboard/services/app-service.ts","../src/dashboard/services/failure-analysis-service.ts","../src/dashboard/services/session-service.ts","../src/data-service/CircuitBreaker.ts","../src/data-service/config-service.ts","../src/data-service/db.ts","../src/data-service/device-service.ts","../src/data-service/device-store.interface.ts","../src/data-service/device-store.ts","../src/data-service/pending-sessions-service.ts","../src/data-service/pluginArgs.ts","../src/data-service/prisma-service.ts","../src/data-service/prisma-store.ts","../src/data-service/queue-service.ts","../src/data-service/web-config-service.ts","../src/device-managers/AndroidDeviceManager.ts","../src/device-managers/ChromeDriverManager.ts","../src/device-managers/HealthMonitorService.ts","../src/device-managers/IOSDeviceManager.ts","../src/device-managers/NodeDevices.ts","../src/device-managers/iOSTracker.ts","../src/device-managers/index.ts","../src/device-managers/android/AndroidStreamService.ts","../src/device-managers/android/DeviceLockManager.ts","../src/device-managers/cloud/CapabilityManager.ts","../src/device-managers/cloud/Devices.ts","../src/device-managers/ios/IOSDiscoveryService.ts","../src/device-managers/ios/IOSStreamService.ts","../src/device-managers/ios/WDAClient.ts","../src/device-managers/ios/iosBattery.ts","../src/device-managers/ios/iosStreamDiagnostics.ts","../src/enums/Capabilities.ts","../src/enums/Cloud.ts","../src/enums/Platform.ts","../src/enums/SessionType.ts","../src/enums/SocketEvents.ts","../src/generated/client/default.d.ts","../src/generated/client/default.js","../src/generated/client/edge.d.ts","../src/generated/client/edge.js","../src/generated/client/index-browser.js","../src/generated/client/index.d.ts","../src/generated/client/index.js","../src/generated/client/wasm.d.ts","../src/generated/client/wasm.js","../src/generated/client/runtime/edge-esm.js","../src/generated/client/runtime/edge.js","../src/generated/client/runtime/index-browser.d.ts","../src/generated/client/runtime/index-browser.js","../src/generated/client/runtime/library.d.ts","../src/generated/client/runtime/library.js","../src/generated/client/runtime/react-native.js","../src/generated/client/runtime/wasm.js","../src/helpers/UniversalMjpegProxy.ts","../src/helpers/index.ts","../src/interceptors/CommandInterceptor.ts","../src/interfaces/ICloudManager.ts","../src/interfaces/IDevice.ts","../src/interfaces/IDeviceFilterOptions.ts","../src/interfaces/IDeviceManager.ts","../src/interfaces/IOptions.ts","../src/interfaces/IPluginArgs.ts","../src/interfaces/ISessionCapability.ts","../src/logging/sessionContext.ts","../src/middleware/authMiddleware.ts","../src/middleware/csrfMiddleware.ts","../src/middleware/leaseTokenMiddleware.ts","../src/middleware/loginRateLimiter.ts","../src/middleware/rateLimitMiddleware.ts","../src/middleware/roleGuard.ts","../src/middleware/scopeGuard.ts","../src/profiling/AndroidAppProfiler.ts","../src/proxy/wd-command-proxy.ts","../src/scripts/generate-database-migration.ts","../src/scripts/initialize-database.ts","../src/scripts/install-go-ios.ts","../src/scripts/prepare-prisma.ts","../src/scripts/run-migrations.ts","../src/services/AICommandService.ts","../src/services/AIService.ts","../src/services/ApiKeyService.ts","../src/services/CircuitBreaker.ts","../src/services/CleanupService.ts","../src/services/DeviceReconciler.ts","../src/services/EmailService.ts","../src/services/EventBus.ts","../src/services/EventLogService.ts","../src/services/InspectorService.ts","../src/services/InterceptorService.ts","../src/services/MetricsService.ts","../src/services/NetworkConditioningService.ts","../src/services/NotificationService.ts","../src/services/OrphanSweeper.ts","../src/services/PasswordResetService.ts","../src/services/PortAllocator.ts","../src/services/ProcessMetricsService.ts","../src/services/ProcessRegistry.ts","../src/services/RequestLogService.ts","../src/services/ResourceIsolationService.ts","../src/services/SecurityService.ts","../src/services/SelectorStateService.ts","../src/services/SelectorVerificationJob.ts","../src/services/ServerManager.ts","../src/services/SessionHeartbeatService.ts","../src/services/SessionLifecycleService.ts","../src/services/ShutdownCoordinator.ts","../src/services/SocketClient.ts","../src/services/SocketServer.ts","../src/services/TeamService.ts","../src/services/TracingService.ts","../src/services/UserService.ts","../src/services/UserSessionService.ts","../src/services/VideoPipelineService.ts","../src/services/artifacts/ArtifactStore.ts","../src/services/autowait/AutowaitService.ts","../src/services/autowait/waitFor.ts","../src/services/bug-report/BugReportService.ts","../src/services/bug-report/archive.ts","../src/services/bug-report/har-collector.ts","../src/services/bug-report/manifest.ts","../src/services/bug-report/readme.ts","../src/services/bug-report/types.ts","../src/services/bug-report/video-slice.ts","../src/services/bug-report/window.ts","../src/services/healing/FuzzyXmlHealingProvider.ts","../src/services/healing/HealEtalonService.ts","../src/services/healing/HealedLocatorGenerator.ts","../src/services/healing/HealingMetrics.ts","../src/services/healing/HealingOrchestrator.ts","../src/services/healing/LlmHealingProvider.ts","../src/services/healing/OcrHealingProvider.ts","../src/services/healing/ResilioTreeHealingProvider.ts","../src/services/healing/VisualAiHealingProvider.ts","../src/services/healing/types.ts","../src/services/identity/bootstrap.ts","../src/services/identity/sessionCleanupCron.ts","../src/services/identity/userAuthorization.ts","../src/services/interceptor/AndroidProxyAdapter.ts","../src/services/interceptor/CertManager.ts","../src/services/interceptor/HarBuilder.ts","../src/services/interceptor/HostFilter.ts","../src/services/interceptor/MitmProxyHost.ts","../src/services/interceptor/MockEngine.ts","../src/services/interceptor/RequestBuffer.ts","../src/services/interceptor/SessionArchive.ts","../src/services/interceptor/types.ts","../src/services/lease/LeaseOrphanSweeper.ts","../src/services/lease/LeaseService.ts","../src/services/lease/buildCapabilityBag.ts","../src/services/lease/leaseToken.ts","../src/services/omni-vision/OmniVisionService.ts","../src/services/omni-vision/VisionAssertionService.ts","../src/services/ports/PortAllocatorClient.ts","../src/services/ports/PortAllocatorService.ts","../src/services/recording/RecordingOrchestrator.ts","../src/services/recording/annotation-render.ts","../src/services/recording/busy-precheck.ts","../src/services/recording/concurrency-gate.ts","../src/services/recording/manualLock.ts","../src/services/recording/proof-bundle.ts","../src/services/recording/recording-store.ts","../src/services/telemetry/attributes.ts","../src/services/token/JwtKeyService.ts","../src/services/token/StreamTicketService.ts","../src/sessions/CloudSession.ts","../src/sessions/LocalSession.ts","../src/sessions/RemoteSession.ts","../src/sessions/SessionManager.ts","../src/sessions/XenonSession.ts","../src/types/CLIArgs.ts","../src/types/CloudArgs.ts","../src/types/CloudSchema.ts","../src/types/DeviceType.ts","../src/types/DeviceUpdate.ts","../src/types/IOSDevice.ts","../src/types/Platform.ts","../src/types/SessionStatus.ts","../src/types/express.d.ts","../src/types/healing.d.ts","../src/types/identity.ts","../src/validators/CapabilityValidator.ts","../test/e2e/e2ehelper.ts","../test/e2e/plugin-harness.ts","../test/e2e/plugin.spec.ts","../test/e2e/startup-verification.spec.ts","../test/e2e/telemetry_verification.spec.ts","../test/e2e/video-recording-test.spec.js","../test/e2e/android/conf.spec.js","../test/e2e/android/conf2.spec.js","../test/e2e/android/conf3.spec.js","../test/e2e/hubnode/forward-request.spec.ts","../test/e2e/hubnode/hubnode.spec.ts","../test/e2e/ios/conf1.spec.js","../test/e2e/ios/conf2.spec.js","../test/helpers/seedUser.ts","../test/helpers/test-container.ts","../test/integration/androidDevices.spec.ts","../test/integration/auth-flow.spec.ts","../test/integration/auth-rate-limit.spec.ts","../test/integration/bug-report-route.spec.ts","../test/integration/cliArgs.js","../test/integration/cross-workflow.spec.ts","../test/integration/forgot-password-rate-limit.spec.ts","../test/integration/forgot-password.spec.ts","../test/integration/legacy-deprecation-headers.spec.ts","../test/integration/migration.spec.ts","../test/integration/node-pair-auth-rest.spec.ts","../test/integration/ports-router.spec.ts","../test/integration/profile-tokens.spec.ts","../test/integration/recordings-router.spec.ts","../test/integration/reset-revokes-sessions.spec.ts","../test/integration/role-matrix.spec.ts","../test/integration/sdk-leases-router.spec.ts","../test/integration/sdk-version.spec.ts","../test/integration/selector-state-flow.spec.ts","../test/integration/team-membership-migration.spec.ts","../test/integration/team-visibility-dashboard.spec.ts","../test/integration/team-visibility-grid.spec.ts","../test/integration/team-visibility-recordings.spec.ts","../test/integration/users-flow.spec.ts","../test/integration/users-lockout.spec.ts","../test/integration/ios/01iOSSimulator.spec.ts","../test/integration/ios/02iOSDevices.spec.ts","../test/unit/AndroidDeviceManager.spec.ts","../test/unit/ApiKeyService.test.ts","../test/unit/ArtifactStore.spec.ts","../test/unit/AutowaitService.spec.ts","../test/unit/CapabilitiesRouter.spec.ts","../test/unit/ChromeDriverManager.spec.js","../test/unit/CleanupService.spec.ts","../test/unit/CommandInterceptor.autowait.spec.ts","../test/unit/DeviceModel.spec.ts","../test/unit/EmailService.test.ts","../test/unit/EventLogService.spec.ts","../test/unit/FuzzyXmlHealingProvider.test.ts","../test/unit/GetAdbOriginal.js","../test/unit/HealingCascade.test.ts","../test/unit/IOSDeviceManager.spec.js","../test/unit/JwtKeyService.spec.ts","../test/unit/OrphanSweeper.test.ts","../test/unit/PasswordResetService.test.ts","../test/unit/PortAllocator.test.ts","../test/unit/ProcessRegistry.test.ts","../test/unit/ProjectsRouter.spec.ts","../test/unit/RemoteIOs.spec.js","../test/unit/ResilioTreeHealingProvider.test.ts","../test/unit/SessionHeartbeatWrite.test.ts","../test/unit/StreamTicketService.spec.ts","../test/unit/TeamService.test.ts","../test/unit/TokenEndpoint.spec.ts","../test/unit/UserService.test.ts","../test/unit/UserSessionService.test.ts","../test/unit/XenonCapabilityManager.spec.ts","../test/unit/aggregate-hotspots-tuple-key.spec.ts","../test/unit/android-stream-release.spec.ts","../test/unit/auth-me-auth-disabled.test.ts","../test/unit/authMiddleware.bearer.spec.ts","../test/unit/authMiddleware.streamTicket.spec.ts","../test/unit/authMiddleware.test.ts","../test/unit/bootstrap-identity.test.ts","../test/unit/build-export.spec.ts","../test/unit/busy-precheck.spec.ts","../test/unit/commands.spec.js","../test/unit/concurrency-gate.spec.ts","../test/unit/config.spec.js","../test/unit/device-service.spec.ts","../test/unit/device-utils.spec.ts","../test/unit/event-manager-regression-hook.spec.ts","../test/unit/extract-access-key-token-pair.test.ts","../test/unit/findAndLockDevice-leasemerge.spec.ts","../test/unit/healing-instrumentation.spec.ts","../test/unit/healing-state-endpoints.spec.ts","../test/unit/helpers.spec.ts","../test/unit/iosBattery.spec.ts","../test/unit/iosStreamDiagnostics.spec.ts","../test/unit/loginRateLimiter.test.ts","../test/unit/omni-vision.spec.ts","../test/unit/plugin.spec.ts","../test/unit/prisma.spec.ts","../test/unit/profile-router.test.ts","../test/unit/proof-bundle.spec.ts","../test/unit/rateLimitMiddleware.test.ts","../test/unit/recording-instrumentation.spec.ts","../test/unit/recording-orchestrator.spec.ts","../test/unit/recording-store.spec.ts","../test/unit/roleGuard.test.ts","../test/unit/run-migrations-fail-fast.test.ts","../test/unit/run-migrations.test.ts","../test/unit/selector-state-service.spec.ts","../test/unit/selector-verification-job.spec.ts","../test/unit/team-filter.test.ts","../test/unit/tracing-service-init.spec.ts","../test/unit/userAuthorization.test.ts","../test/unit/users-router.test.ts","../test/unit/video-pipeline-args.spec.ts","../test/unit/video-pipeline-outputpath.spec.ts","../test/unit/bug-report/archive.spec.ts","../test/unit/bug-report/har-collector.spec.ts","../test/unit/bug-report/manifest.spec.ts","../test/unit/bug-report/readme.spec.ts","../test/unit/bug-report/route.spec.ts","../test/unit/bug-report/service.spec.ts","../test/unit/bug-report/video-slice.spec.ts","../test/unit/bug-report/window.spec.ts","../test/unit/fixtures/device.config.js","../test/unit/fixtures/devices.js","../test/unit/interceptor/AndroidProxyAdapter.spec.ts","../test/unit/interceptor/CertManager.spec.ts","../test/unit/interceptor/HarBuilder.spec.ts","../test/unit/interceptor/HostFilter.spec.ts","../test/unit/interceptor/MitmProxyHost.spec.ts","../test/unit/interceptor/MockEngine.spec.ts","../test/unit/interceptor/RequestBuffer.spec.ts","../test/unit/interceptor/SessionArchive.spec.ts","../test/unit/lease/LeaseOrphanSweeper.spec.ts","../test/unit/lease/LeaseService.spec.ts","../test/unit/lease/buildCapabilityBag.spec.ts","../test/unit/lease/leaseToken.spec.ts","../test/unit/ports/PortAllocatorService.spec.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenon-device-management/xenon",
3
- "version": "1.7.4",
3
+ "version": "1.7.6",
4
4
  "description": "Xenon - Intelligent Mobile Infrastructure. A self-healing device orchestration platform for Appium.",
5
5
  "main": "./lib/src/index.js",
6
6
  "exports": {
@@ -1,2 +0,0 @@
1
- import{r as m,j as t,X as T}from"./index-wbxPcwMM.js";import{A as U,I as le}from"./keycodes-bIEJfQzr.js";function de(e,r){return e!=="auto"?e:r<=1?"1":r===2?"2x1":r<=4?"2x2":"3x2"}const ue={selected:new Set,layout:"2x2",tiles:[],groupId:null,recording:!1,startedAt:null,annotateMode:!1,shape:"RECT",color:"#ff3333",errorBanner:null};function fe(e,r){switch(r.type){case"SET_LAYOUT":return{...e,layout:r.layout};case"TOGGLE_SELECTED":{const n=new Set(e.selected);return n.has(r.udid)?n.delete(r.udid):n.add(r.udid),{...e,selected:n}}case"CLEAR_SELECTED":return{...e,selected:new Set};case"SET_TILES":return{...e,tiles:r.tiles};case"ADD_TILE":return e.tiles.some(n=>n.udid===r.tile.udid)?e:{...e,tiles:[...e.tiles,r.tile]};case"REMOVE_TILE":return{...e,tiles:e.tiles.filter(n=>n.udid!==r.udid)};case"BIND_RECORDING_IDS":{const n=e.tiles.map(l=>({...l,recordingId:r.map[l.udid]??l.recordingId}));return{...e,tiles:n}}case"START_RECORDING":{const n=e.tiles.map(l=>({...l,recordingId:r.tileIds[l.udid]??l.recordingId}));return{...e,groupId:r.groupId,recording:!0,startedAt:r.startedAt,tiles:n}}case"STOP_RECORDING":{const n=e.tiles.map(l=>({...l,recordingId:void 0}));return{...e,recording:!1,tiles:n}}case"SET_ANNOTATE_MODE":return{...e,annotateMode:r.enabled};case"SET_SHAPE":return{...e,shape:r.shape};case"SET_COLOR":return{...e,color:r.color};case"SET_ERROR_BANNER":return{...e,errorBanner:r.message};default:return e}}const V=m.createContext(null);function me(){const e=m.useContext(V);if(!e)throw new Error("useMosaic must be used inside <MosaicContext.Provider>");return e}function xe(){return m.useReducer(fe,ue)}function he(e){switch(e){case"automation":return"In automation";case"manual_other":return"Manual control by another user";case"manual_self":return"In your mosaic";case"recording_other_group":return"Recording in another group";default:return e?"Busy":null}}const G=[{id:"ios",label:"iOS",match:e=>e==="ios"},{id:"android",label:"Android",match:e=>e==="android"},{id:"tvos",label:"tvOS",match:e=>e==="tvos"},{id:"androidtv",label:"Android TV",match:e=>e==="androidtv"||e==="android-tv"},{id:"other",label:"Other",match:()=>!0}];function pe(e){return e?e==="androidtv"||e==="android-tv"?"ANDROID TV":e.toUpperCase():""}function ge({devices:e,inMosaic:r,onToggle:n}){const[l,b]=m.useState(""),[f,w]=m.useState({}),N=m.useMemo(()=>{const u=l.trim().toLowerCase();return u?e.filter(g=>g.udid.toLowerCase().includes(u)||(g.name??"").toLowerCase().includes(u)||(g.platform??"").toLowerCase().includes(u)):e},[e,l]),v=m.useMemo(()=>{const u=G.map(o=>({id:o.id,label:o.label,rows:[]})),g=o=>u.find((c,s)=>G[s].match(o))??u[u.length-1];for(const o of N)g((o.platform??"").toLowerCase()).rows.push(o);return u.filter(o=>o.rows.length>0)},[N]);return e.length===0?t.jsx("div",{className:"text-xs text-[var(--text-dim)] p-3",children:"No devices online."}):t.jsxs("div",{className:"flex flex-col gap-2",children:[t.jsxs("div",{className:"relative",children:[t.jsx("span",{"aria-hidden":!0,className:"absolute left-2 top-1/2 -translate-y-1/2 text-[var(--text-dim)] text-xs",children:"⌕"}),t.jsx("input",{type:"text",value:l,placeholder:"Filter devices...",onChange:u=>b(u.target.value),className:"w-full pl-7 pr-2 py-1.5 text-xs rounded border border-[var(--border)] bg-transparent placeholder:text-[var(--text-dim)] focus:outline-none focus:border-[var(--accent,#3b82f6)]"})]}),v.map(u=>{const g=f[u.id];return t.jsxs("section",{className:"flex flex-col gap-1",children:[t.jsxs("button",{type:"button",onClick:()=>w(o=>({...o,[u.id]:!o[u.id]})),className:"flex items-center justify-between w-full text-[10px] uppercase tracking-wider text-[var(--text-dim)] hover:text-white px-1 py-0.5",children:[t.jsxs("span",{className:"flex items-center gap-1.5",children:[t.jsx("span",{"aria-hidden":!0,className:`inline-block transition-transform ${g?"-rotate-90":"rotate-0"}`,children:"▾"}),u.label]}),t.jsx("span",{children:u.rows.length})]}),!g&&t.jsx("ul",{className:"flex flex-col gap-1 text-sm",children:u.rows.map(o=>{const c=o.busyReason==="manual_self",s=r.has(o.udid),x=!!o.busy&&!c&&!s||!!o.offline,R=o.offline?"Offline":he(o.busyReason),i=!x;return t.jsx("li",{children:t.jsxs("button",{type:"button",disabled:x,draggable:!x,onDragStart:d=>{d.dataTransfer.effectAllowed="copy",d.dataTransfer.setData("application/x-xenon-udid",o.udid),d.dataTransfer.setData("text/plain",o.udid)},onClick:()=>n(o.udid),className:`w-full flex items-center gap-2 px-2 py-1.5 rounded text-left border transition-colors ${x?"opacity-60 cursor-not-allowed border-transparent":s?"border-emerald-500/40 bg-emerald-500/5 hover:bg-emerald-500/10":"border-transparent hover:border-[var(--border)] hover:bg-[var(--surface-2,rgba(255,255,255,0.04))]"}`,title:x?`${R} — release first`:s?"Click to remove from mosaic (or drag to a cell)":"Click to add to mosaic (or drag to a cell)",children:[t.jsx("span",{"aria-hidden":!0,className:`inline-flex items-center justify-center w-4 h-4 text-xs ${s?"text-emerald-400":"text-[var(--text-dim)]"}`,children:s?"●":"○"}),t.jsx("span",{"aria-hidden":!0,className:`inline-block w-1.5 h-1.5 rounded-full ${o.offline?"bg-zinc-600":i?"bg-emerald-400 shadow-[0_0_4px_rgba(52,211,153,0.7)]":"bg-yellow-500"}`}),t.jsx("span",{className:"font-medium flex-1 truncate text-[13px]",children:o.name??o.udid}),o.platform&&t.jsx("span",{className:"text-[9px] uppercase tracking-wider px-1.5 py-0.5 rounded bg-[var(--surface-2,rgba(255,255,255,0.06))] text-[var(--text-dim)]",children:pe(o.platform)})]})},o.udid)})})]},u.id)}),l&&v.length===0&&t.jsxs("div",{className:"text-xs text-[var(--text-dim)] px-1 py-2",children:['No devices match "',l,'".']}),t.jsxs("div",{className:"text-[10px] text-[var(--text-dim)] px-1 pt-2 flex items-center gap-3",children:[t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-emerald-400 inline-block"})," available"]}),t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-yellow-500 inline-block"})," in use"]}),t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-zinc-600 inline-block"})," offline"]})]})]})}const be=[{id:"1",label:"1",icon:t.jsx(ye,{})},{id:"2x1",label:"2×1",icon:t.jsx(we,{})},{id:"2x2",label:"2×2",icon:t.jsx(Ne,{})},{id:"3x2",label:"3×2",icon:t.jsx(je,{})},{id:"auto",label:"Auto",icon:t.jsx(Re,{})}];function ve({value:e,onChange:r}){return t.jsx("div",{role:"tablist",className:"inline-flex border border-[var(--border)] rounded-md overflow-hidden text-xs bg-[var(--surface-1,rgba(255,255,255,0.02))]",children:be.map(n=>{const l=e===n.id;return t.jsxs("button",{role:"tab","aria-selected":l,onClick:()=>r(n.id),className:`flex items-center gap-1.5 px-2.5 py-1.5 border-r border-[var(--border)] last:border-r-0 transition-colors ${l?"bg-[var(--surface-2,#2a2a2a)] text-white":"text-[var(--text-dim)] hover:bg-[var(--surface-2,rgba(255,255,255,0.04))] hover:text-white"}`,title:`Layout: ${n.label}`,children:[t.jsx("span",{className:"w-3.5 h-3.5 inline-flex items-center justify-center",children:n.icon}),t.jsx("span",{children:n.label})]},n.id)})})}function C(e){return t.jsx("rect",{rx:1.2,ry:1.2,fill:"currentColor",...e})}function ye(){return t.jsx("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:C({x:2,y:2,width:10,height:10})})}function we(){return t.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:[C({x:1.5,y:3,width:5,height:8}),C({x:7.5,y:3,width:5,height:8})]})}function Ne(){return t.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:[C({x:1.5,y:1.5,width:5,height:5}),C({x:7.5,y:1.5,width:5,height:5}),C({x:1.5,y:7.5,width:5,height:5}),C({x:7.5,y:7.5,width:5,height:5})]})}function je(){return t.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:[C({x:1,y:1.5,width:3.3,height:5}),C({x:5.35,y:1.5,width:3.3,height:5}),C({x:9.7,y:1.5,width:3.3,height:5}),C({x:1,y:7.5,width:3.3,height:5}),C({x:5.35,y:7.5,width:3.3,height:5}),C({x:9.7,y:7.5,width:3.3,height:5})]})}function Re(){return t.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",fill:"none",stroke:"currentColor",strokeWidth:"1.4",children:[t.jsx("rect",{x:"2",y:"2",width:"10",height:"10",rx:"1.5"}),t.jsx("circle",{cx:"7",cy:"7",r:"1.5",fill:"currentColor",stroke:"none"})]})}function Ee({enabled:e,shape:r,color:n,onCommit:l}){const b=m.useRef(null),[f,w]=m.useState(null);m.useEffect(()=>{const c=b.current;if(!c)return;const s=()=>{c.width=c.clientWidth,c.height=c.clientHeight,N()};s();const x=new ResizeObserver(s);return x.observe(c),()=>x.disconnect()},[]);const N=m.useCallback(()=>{const c=b.current;if(!c)return;const s=c.getContext("2d");if(s&&(s.clearRect(0,0,c.width,c.height),!!f)){if(s.strokeStyle=n,s.lineWidth=3,r==="RECT"||r==="ARROW"||r==="FREEHAND")s.strokeRect(Math.min(f.startX,f.curX),Math.min(f.startY,f.curY),Math.abs(f.curX-f.startX),Math.abs(f.curY-f.startY));else if(r==="CIRCLE"){const x=Math.hypot(f.curX-f.startX,f.curY-f.startY);s.beginPath(),s.arc(f.startX,f.startY,x,0,Math.PI*2),s.stroke()}}},[f,n,r]);m.useEffect(()=>{N()},[f,N]);const v=c=>{const x=b.current.getBoundingClientRect();return{px:c.clientX-x.left,py:c.clientY-x.top,w:x.width,h:x.height}},u=c=>{if(!e)return;const s=v(c);w({startX:s.px,startY:s.py,curX:s.px,curY:s.py})},g=c=>{if(!f)return;const s=v(c);w({...f,curX:s.px,curY:s.py})},o=c=>{if(!f||!e)return;const s=v(c),x=Math.min(f.startX,s.px)/s.w,R=Math.min(f.startY,s.py)/s.h,i=Math.abs(s.px-f.startX)/s.w,d=Math.abs(s.py-f.startY)/s.h;i>.005&&d>.005&&l({shape:r,color:n,geometry:{x,y:R,w:i,h:d}}),w(null)};return t.jsx("canvas",{ref:b,style:{position:"absolute",inset:0,pointerEvents:e?"auto":"none",cursor:e?"crosshair":"default"},onMouseDown:u,onMouseMove:g,onMouseUp:o,onMouseLeave:()=>w(null)})}const Te=10,Se=500;function ke({udid:e,name:r,recordingId:n,annotateMode:l,shape:b,color:f,aspect:w="9 / 16",screenWidth:N,screenHeight:v,platform:u,onAnnotation:g,onRemove:o}){const[c,s]=m.useState("connecting"),[x,R]=m.useState(0),[i,d]=m.useState([]),[p,j]=m.useState(!1),O=m.useRef(null),_=m.useRef(null),y=u==="android"||u==="androidtv"||u==="android-tv",S=u==="ios"||u==="tvos",E=!l&&!!N&&!!v,X=(a,h,k)=>{const A=N,D=v,M=Math.min(1,Math.max(0,(a-k.left)/k.width)),I=Math.min(1,Math.max(0,(h-k.top)/k.height));return{x:Math.round(M*A),y:Math.round(I*D)}},Z=(a,h,k)=>{const A=Date.now()+Math.random(),D=a-k.left,M=h-k.top;d(I=>[...I,{id:A,x:D,y:M}]),window.setTimeout(()=>{d(I=>I.filter(B=>B.id!==A))},500)},Q=a=>{if(!E||c!=="live"||a.button!==0)return;const h=a.currentTarget;h.setPointerCapture(a.pointerId),_.current={startX:a.clientX,startY:a.clientY,startTime:Date.now(),rect:h.getBoundingClientRect()},Z(a.clientX,a.clientY,_.current.rect),h.focus()},ee=async a=>{var H,K;const h=_.current;if(_.current=null,!h||!E)return;try{a.currentTarget.releasePointerCapture(a.pointerId)}catch{}const k=a.clientX-h.startX,A=a.clientY-h.startY,D=Math.hypot(k,A),M=Date.now()-h.startTime,I=X(h.startX,h.startY,h.rect),B=D<Te;try{if(B&&M<Se)await T.tap(e,I.x,I.y);else if(B)await((K=(H=T).touchAndHold)==null?void 0:K.call(H,e,I.x,I.y,M));else{const P=X(a.clientX,a.clientY,h.rect);await T.swipe(e,I.x,I.y,P.x,P.y)}}catch(P){console.error(`[DeviceTile] gesture failed for ${e}`,P)}},te=()=>{_.current=null},re=async a=>{if(!E||c!=="live"||a.metaKey||a.ctrlKey||a.altKey)return;const h=a.key;try{if(h.length===1){a.preventDefault(),await T.typeText(e,h);return}if(h==="Backspace"){a.preventDefault(),y?await T.pressKey(e,67):await T.typeText(e,"\b");return}if(h==="Enter"){a.preventDefault(),y?await T.pressKey(e,66):await T.typeText(e,`
2
- `);return}if(h==="Tab"){a.preventDefault(),await T.typeText(e," ");return}h==="Escape"&&y&&(a.preventDefault(),await T.pressKey(e,U.BACK))}catch(k){console.error(`[DeviceTile] keystroke "${h}" failed for ${e}`,k)}},ne=()=>T.pressKey(e,y?U.HOME:le.HOME).catch(a=>console.error(`[DeviceTile] home failed for ${e}`,a)),se=()=>T.pressKey(e,U.BACK).catch(a=>console.error(`[DeviceTile] back failed for ${e}`,a)),oe=()=>T.pressKey(e,U.APP_SWITCH).catch(a=>console.error(`[DeviceTile] app-switch failed for ${e}`,a)),ae=async()=>{try{const a=await T.getScreenshot(e),h=(a==null?void 0:a.screenshot)??(a==null?void 0:a.value)??a;if(!h)return;const k=await fetch(`data:image/png;base64,${h}`).then(M=>M.blob()),A=URL.createObjectURL(k),D=document.createElement("a");D.href=A,D.download=`${r||e}-${Date.now()}.png`,document.body.appendChild(D),D.click(),D.remove(),setTimeout(()=>URL.revokeObjectURL(A),1e3)}catch(a){console.error(`[DeviceTile] screenshot failed for ${e}`,a)}};m.useEffect(()=>{console.log(`[DeviceTile] Rendering ${e} in state: ${c}`)},[e,c]);const ie=`/xenon/api/control/${encodeURIComponent(e)}/stream?t=${x}`;m.useEffect(()=>{if(c!=="connecting")return;const a=setTimeout(()=>{s(h=>h==="connecting"?"unavailable":h)},9e4);return()=>clearTimeout(a)},[c,x]);const ce=()=>{s("connecting"),R(Date.now())},L=!!n,W=r||e.slice(0,16)+"…";return t.jsxs("div",{className:"flex items-stretch justify-center w-full h-full min-h-0 min-w-0 gap-2",children:[t.jsxs("div",{className:"relative bg-[#000] rounded-lg overflow-hidden border border-[var(--border)] group max-h-full max-w-full self-center",style:{aspectRatio:w,height:"100%"},children:[c==="connecting"&&t.jsxs("div",{className:"flex flex-col items-center justify-center bg-neutral-900 text-neutral-400 absolute inset-0 z-20",children:[t.jsxs("div",{className:"relative flex items-center justify-center",children:[t.jsx("div",{className:"w-12 h-12 border-4 border-neutral-800 border-t-red-500 rounded-full animate-spin"}),t.jsx("div",{className:"absolute text-[10px] text-red-500 font-bold",children:"WDA"})]}),t.jsx("div",{className:"mt-4 font-bold text-neutral-200",children:"Starting Stream…"}),t.jsx("div",{className:"text-[10px] mt-1 text-neutral-500 opacity-60 font-mono",children:e}),t.jsx("div",{className:"text-[10px] mt-4 text-neutral-400 bg-white/5 px-2 py-1 rounded",children:"Waiting for device connection..."})]}),c==="unavailable"&&t.jsx("div",{className:"flex items-center justify-center bg-black text-neutral-400 absolute inset-0 z-20",children:t.jsxs("div",{className:"text-center p-6",children:[t.jsx("div",{className:"text-5xl mb-4 opacity-20",children:"📵"}),t.jsx("div",{className:"font-bold text-white text-lg",children:"Connection Failed"}),t.jsx("p",{className:"text-xs mt-2 text-neutral-500 leading-relaxed max-w-[240px] mx-auto",children:"We couldn't connect to the WDA MJPEG stream on port 9100. If automation is running, ensure it hasn't blocked the MJPEG port."}),t.jsx("button",{className:"mt-6 text-sm font-semibold px-6 py-2.5 rounded-full bg-red-600 hover:bg-red-500 text-white transition-all shadow-[0_0_20px_rgba(220,38,38,0.3)] active:scale-95",onClick:ce,children:"Retry Connection"})]})}),t.jsx("img",{src:ie,alt:W,className:"absolute inset-0 w-full h-full object-contain bg-black select-none pointer-events-none",draggable:!1,style:{opacity:c==="live"?1:0,transition:"opacity 0.3s ease-in-out"},onLoad:()=>{console.log(`[DeviceTile] Image LOADED for ${e}`),s("live")},onError:a=>{console.error(`[DeviceTile] Image ERROR for ${e}`,a),s("unavailable")}},x),E&&c==="live"&&t.jsx("div",{ref:O,className:`absolute inset-0 z-10 touch-none outline-none ${p?"ring-2 ring-emerald-400/60 ring-inset rounded-lg":""}`,tabIndex:0,onPointerDown:Q,onPointerUp:ee,onPointerCancel:te,onKeyDown:re,onFocus:()=>j(!0),onBlur:()=>j(!1)}),t.jsx("div",{className:"absolute inset-0 z-20 pointer-events-none overflow-hidden",children:i.map(a=>t.jsx("span",{className:"absolute block w-10 h-10 rounded-full border-2 border-emerald-300/80 animate-ping",style:{left:a.x-20,top:a.y-20}},a.id))}),L&&l&&t.jsx(Ee,{enabled:!0,shape:b,color:f,onCommit:a=>g(n,a)}),L&&t.jsx("div",{className:"absolute top-3 left-3 z-30 pointer-events-none",children:t.jsx("span",{className:"px-2 py-0.5 rounded-sm bg-red-600 text-white text-[9px] font-black shadow-lg",children:"REC"})}),t.jsxs("div",{className:"absolute top-3 left-3 z-30 flex flex-col gap-1.5 pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity duration-150",children:[c==="live"&&!L&&t.jsx("span",{className:"px-2 py-0.5 rounded-sm bg-emerald-500 text-black text-[9px] font-black shadow-lg w-fit",children:"LIVE"}),t.jsx("span",{className:"px-2 py-1 rounded bg-black/80 text-white text-[11px] font-medium backdrop-blur-md border border-white/10 shadow-xl truncate max-w-[180px]",children:W})]})]}),t.jsx(Ce,{streamState:c,recording:L,isIOS:S,isAndroid:y,platform:u,onClose:!L&&o?()=>o(e):void 0,sendHome:ne,sendBack:se,sendAppSwitch:oe,captureScreenshot:ae,displayName:W})]})}function Ce({streamState:e,recording:r,isIOS:n,isAndroid:l,platform:b,onClose:f,sendHome:w,sendBack:N,sendAppSwitch:v,captureScreenshot:u,displayName:g}){const o=e==="live";return!f&&!o?null:t.jsxs("div",{className:"self-stretch w-9 flex flex-col items-center justify-between gap-1 py-2 rounded-lg bg-black/40 border border-white/10 backdrop-blur-md text-white",children:[t.jsx("div",{className:"flex flex-col items-center gap-1",children:f&&t.jsx("button",{type:"button","aria-label":`Remove ${g} from mosaic`,title:"Remove from mosaic",onClick:f,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-red-600/70 transition-colors text-base leading-none",children:"×"})}),o&&t.jsxs("div",{className:"flex flex-col items-center gap-1.5 my-auto",children:[(l||n)&&t.jsx("button",{type:"button",title:`Home${b?` (${b})`:""}`,onClick:w,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/15 transition-colors text-sm",children:"⌂"}),l&&t.jsx("button",{type:"button",title:"Back",onClick:N,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/15 transition-colors text-sm",children:"‹"}),l&&t.jsx("button",{type:"button",title:"Recent apps",onClick:v,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/15 transition-colors text-sm",children:"▢"})]}),o&&t.jsx("div",{className:"flex flex-col items-center gap-1",children:t.jsx("button",{type:"button",title:"Screenshot",onClick:u,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/15 transition-colors text-sm",children:"⎙"})})]})}const Ie={1:"1fr","2x1":"1fr 1fr","2x2":"1fr 1fr","3x2":"1fr 1fr 1fr"},De={1:"1fr","2x1":"1fr","2x2":"1fr 1fr","3x2":"1fr 1fr"},_e={1:1,"2x1":2,"2x2":4,"3x2":6};function Ae({layout:e,tiles:r,annotateMode:n,shape:l,color:b,onAnnotation:f,onRemove:w,onDropDevice:N}){const v=de(e,r.length),u=_e[v];r.length;const g=r.length,o=e==="auto"?Math.max(g,1):u,c=Math.max(0,o-g);return t.jsxs("div",{style:{display:"grid",gap:8,gridTemplateColumns:Ie[v],gridTemplateRows:De[v]},className:"h-full",children:[r.map(s=>t.jsx(ke,{udid:s.udid,name:s.name,mjpegPort:s.mjpegPort,recordingId:s.recordingId,aspect:s.aspect,screenWidth:s.screenWidth,screenHeight:s.screenHeight,platform:s.platform,annotateMode:n,shape:l,color:b,onAnnotation:f,onRemove:w},s.udid)),Array.from({length:c},(s,x)=>t.jsx(Me,{onDropDevice:N},`empty-${x}`))]})}function Me({onDropDevice:e}){const[r,n]=m.useState(!1);return t.jsxs("div",{onDragOver:l=>{e&&(l.preventDefault(),l.dataTransfer.dropEffect="copy",r||n(!0))},onDragLeave:()=>n(!1),onDrop:l=>{if(n(!1),!e)return;l.preventDefault();const b=l.dataTransfer.getData("application/x-xenon-udid")||l.dataTransfer.getData("text/plain");b&&e(b)},className:`flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed transition-colors ${r?"border-emerald-500/60 bg-emerald-500/5":"border-[var(--border)] bg-[var(--surface-1,rgba(255,255,255,0.02))]"} text-[var(--text-dim)] min-h-[120px]`,children:[t.jsx("div",{className:`w-8 h-8 rounded-full flex items-center justify-center text-xl ${r?"bg-emerald-500/15 text-emerald-300":"bg-white/5 text-white/60"}`,children:"+"}),t.jsx("div",{className:"text-xs font-medium",children:"Drag device here"}),t.jsx("div",{className:"text-[10px] opacity-70",children:"or click one in the panel"})]})}const $="/xenon/api/recordings";class J extends Error{constructor(r){super(r.message??r.error),this.body=r}}async function Y(e,r){const n=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:r?JSON.stringify(r):void 0});if(n.status===409)throw new J(await n.json());if(!n.ok)throw new Error(`HTTP ${n.status}`);return n.json()}function Oe(e,r){return Y($,{udids:e,...r})}function $e(e){return Y(`${$}/${encodeURIComponent(e)}/stop`)}function q(e,r,n,l,b){return Y(`${$}/${encodeURIComponent(e)}/bookmark`,{recordingId:r,timecodeMs:n,label:l,note:b})}function Le(e,r){return Y(`${$}/${encodeURIComponent(e)}/annotation`,r)}function Be(e){return`${$}/${encodeURIComponent(e)}/bundle.zip`}function Pe(e){return`${$}/${encodeURIComponent(e)}/composite.mp4`}function Ue({selectedUdids:e}){const{state:r,dispatch:n}=me(),l=async()=>{if(!(r.recording||e.length===0))try{const o=await Oe(e),c={};for(const s of o.recordings)c[s.udid]=s.id;n({type:"START_RECORDING",groupId:o.groupId,startedAt:Date.now(),tileIds:c}),n({type:"SET_ERROR_BANNER",message:null})}catch(o){if(o instanceof J)if(o.body.error==="device_busy"){const c=(o.body.busyDevices??[]).map(s=>`${s.udid} (${s.reason})`).join(", ");n({type:"SET_ERROR_BANNER",message:`Cannot start — these devices are busy: ${c}`})}else n({type:"SET_ERROR_BANNER",message:`Server-wide recording cap reached (${o.body.active}/${o.body.limit}).`});else n({type:"SET_ERROR_BANNER",message:`Recording failed: ${o.message}`})}},b=async()=>{if(r.groupId)try{await $e(r.groupId)}catch(o){n({type:"SET_ERROR_BANNER",message:`Stop failed: ${o.message}`})}finally{n({type:"STOP_RECORDING"})}},f=async()=>{var x;if(!r.groupId||!r.recording)return;const o=window.prompt("Bookmark label?");if(!o)return;const c=r.startedAt?Date.now()-r.startedAt:0,s=(x=r.tiles.find(R=>R.recordingId))==null?void 0:x.recordingId;if(s)try{await q(r.groupId,s,c,o)}catch(R){n({type:"SET_ERROR_BANNER",message:`Bookmark failed: ${R.message}`})}},w=()=>n({type:"SET_ANNOTATE_MODE",enabled:!r.annotateMode}),N=!r.recording&&e.length>0,v=r.recording,u=r.groupId&&!r.recording,g=u&&r.tiles.length>=2;return t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsxs("button",{onClick:l,disabled:!N,className:"px-3 py-1.5 text-sm rounded bg-red-600 text-white disabled:opacity-40 disabled:cursor-not-allowed",children:["● ",e.length>1?"Record All":"Record"]}),t.jsx("button",{onClick:b,disabled:!v,className:"px-3 py-1.5 text-sm rounded border border-[var(--border)] disabled:opacity-40 disabled:cursor-not-allowed",children:"⏹ Stop"}),t.jsx("button",{onClick:f,disabled:!v,className:"px-3 py-1.5 text-sm rounded border border-[var(--border)] disabled:opacity-40 disabled:cursor-not-allowed",children:"🔖 Bookmark"}),t.jsx("button",{onClick:w,disabled:!r.recording,className:`px-3 py-1.5 text-sm rounded border ${r.annotateMode?"bg-[var(--accent,#3b82f6)] text-white border-transparent":"border-[var(--border)]"} disabled:opacity-40 disabled:cursor-not-allowed`,children:"✎ Annotate"}),g&&t.jsx("a",{href:Pe(r.groupId),className:"ml-2 px-3 py-1.5 text-sm rounded border border-[var(--border)] hover:bg-[var(--surface-2,#1a1a1a)]",download:`mosaic-${r.groupId}.mp4`,children:"⤓ Composite mp4"}),u&&t.jsx("a",{href:Be(r.groupId),className:`${g?"ml-1":"ml-2"} px-3 py-1.5 text-sm rounded border border-[var(--border)] hover:bg-[var(--surface-2,#1a1a1a)]`,download:!0,children:"⤓ Proof bundle"})]})}function Ye({remainingSec:e,onContinue:r,onReleaseNow:n}){return t.jsx("div",{role:"dialog","aria-modal":"true","aria-labelledby":"idle-warning-title",className:"fixed inset-0 z-40 bg-black/60 flex items-center justify-center",children:t.jsxs("div",{className:"bg-[var(--bg)] border border-[var(--border)] rounded-lg w-full max-w-md p-6",children:[t.jsx("h3",{id:"idle-warning-title",className:"text-base font-semibold mb-2",children:"Are you still there?"}),t.jsxs("p",{className:"text-sm text-[var(--text-dim)] mb-4",children:["Your devices will be released in"," ",t.jsxs("span",{className:"text-[var(--text)] font-mono font-semibold",children:[e,"s"]})," due to inactivity. Click ",t.jsx("strong",{children:"Continue Session"})," to keep them locked."]}),t.jsxs("div",{className:"flex justify-end gap-2",children:[t.jsx("button",{type:"button",onClick:n,className:"h-9 px-3 rounded-md border border-[var(--border)] text-sm text-[var(--text-dim)] hover:text-[var(--text)]",children:"Release now"}),t.jsx("button",{type:"button",autoFocus:!0,onClick:r,className:"h-9 px-4 rounded-md bg-[var(--green)] text-black text-sm font-medium",children:"Continue Session"})]})]})})}const F=["mousemove","mousedown","keydown","touchstart","wheel"];function We({idleAfterMs:e,warningSec:r,onWarning:n,onTimeout:l,enabled:b}){const[f,w]=m.useState(!1),[N,v]=m.useState(null),u=m.useRef(null),g=m.useRef(null),o=m.useRef(!1),c=m.useRef(n),s=m.useRef(l);c.current=n,s.current=l;function x(){u.current&&(clearTimeout(u.current),u.current=null),g.current&&(clearInterval(g.current),g.current=null)}function R(){o.current=!0,w(!0),v(r),c.current(r);let p=r;g.current=setInterval(()=>{p-=1,p<=0?(x(),o.current=!1,w(!1),v(null),s.current()):v(p)},1e3)}function i(){x(),o.current=!1,w(!1),v(null);const p=e-r*1e3;u.current=setTimeout(R,Math.max(0,p))}function d(){b&&i()}return m.useEffect(()=>{if(!b){x(),o.current=!1,w(!1),v(null);return}function p(){o.current||i()}return F.forEach(j=>window.addEventListener(j,p,{passive:!0})),i(),()=>{F.forEach(j=>window.removeEventListener(j,p)),x()}},[b,e,r]),{warning:f,remainingSec:N,reset:d}}const Xe=5*60*1e3,He=30;function z(e){if(!e)return"9 / 16";const r=Number(e.screenWidth),n=Number(e.screenHeight);if(Number.isFinite(r)&&Number.isFinite(n)&&r>0&&n>0)return`${r} / ${n}`;const l=(e.platform||"").toLowerCase();return l==="tvos"||l==="androidtv"||l==="android-tv"?"16 / 9":"9 / 16"}function Ke(e,r,n){return n?e===`manual_${n}_${r}`:!1}function Ge(e,r){if(e.busy)return e.session_id?e.session_id.startsWith("manual_")?Ke(e.session_id,e.udid,r)?"manual_self":"manual_other":"automation":"unknown"}function Fe(e,r){return{udid:e.udid,name:e.name,platform:e.platform,busy:e.busy,busyReason:Ge(e,r),mjpegServerPort:e.mjpegServerPort,offline:!!e.offline}}function Je(){const[e,r]=xe(),[n,l]=m.useState([]),[b,f]=m.useState(0),[w,N]=m.useState(null);m.useEffect(()=>{let i=!1;return fetch("/xenon/api/auth/me",{credentials:"include"}).then(d=>d.ok?d.json():null).then(d=>{!i&&(d!=null&&d.userId)&&N(d.userId)}).catch(()=>{}),()=>{i=!0}},[]),m.useEffect(()=>{let i=!1;const d=async()=>{try{const j=await T.getDevices();i||l(Array.isArray(j)?j:[])}catch{}};d();const p=window.setInterval(d,5e3);return()=>{i=!0,window.clearInterval(p)}},[b]),m.useEffect(()=>{let i=!1;return(async()=>{try{const d=await T.getDevices(),p=(Array.isArray(d)?d:[]).filter(y=>{var S;return y.busy&&((S=y.session_id)==null?void 0:S.startsWith("manual_"))});if(p.length===0)return;const O=(await Promise.all(p.map(async y=>{try{const S=await fetch(`/xenon/api/control/${encodeURIComponent(y.udid)}/stream/status`);if(!S.ok)return null;const E=await S.json();return(E==null?void 0:E.status)==="running"?y:null}catch{return null}}))).filter(y=>y!==null);if(i||O.length===0)return;const _=O.map(y=>{const S=Number(y.screenWidth),E=Number(y.screenHeight);return{udid:y.udid,name:y.name,mjpegPort:0,aspect:z(y),screenWidth:Number.isFinite(S)&&S>0?S:void 0,screenHeight:Number.isFinite(E)&&E>0?E:void 0,platform:y.platform}});r({type:"SET_TILES",tiles:_})}catch{}})(),()=>{i=!0}},[]),m.useEffect(()=>{const i=async d=>{var y,S;if(d.key!=="b"&&d.key!=="B")return;const p=(y=d.target)==null?void 0:y.tagName;if(p==="INPUT"||p==="TEXTAREA"||p==="SELECT"||!e.recording||!e.groupId)return;const j=window.prompt("Bookmark label?");if(!j)return;const O=e.startedAt?Date.now()-e.startedAt:0,_=(S=e.tiles.find(E=>E.recordingId))==null?void 0:S.recordingId;if(_)try{await q(e.groupId,_,O,j)}catch(E){r({type:"SET_ERROR_BANNER",message:`Bookmark failed: ${E.message}`})}};return window.addEventListener("keydown",i),()=>window.removeEventListener("keydown",i)},[e.recording,e.groupId,e.startedAt,e.tiles,r]);const v=m.useMemo(()=>new Set(e.tiles.map(i=>i.udid)),[e.tiles]),u=m.useMemo(()=>n.map(i=>Fe(i,w)),[n,w]),g=i=>{const d=Number(i.screenWidth),p=Number(i.screenHeight);return{udid:i.udid,name:i.name,mjpegPort:0,aspect:z(i),screenWidth:Number.isFinite(d)&&d>0?d:void 0,screenHeight:Number.isFinite(p)&&p>0?p:void 0,platform:i.platform}},o=async i=>{if(e.tiles.some(j=>j.udid===i)){await c(i);return}if(e.recording){r({type:"SET_ERROR_BANNER",message:"Stop recording before changing the mosaic layout."});return}const p=n.find(j=>j.udid===i);p&&(r({type:"ADD_TILE",tile:g(p)}),fetch(`/xenon/api/control/${encodeURIComponent(i)}/stream/start`,{method:"POST"}).catch(()=>{}))},c=async i=>{r({type:"REMOVE_TILE",udid:i});try{await fetch(`/xenon/api/control/${encodeURIComponent(i)}/stream/stop`,{method:"POST"})}catch{}f(d=>d+1)},s=async(i,d)=>{if(e.groupId)try{await Le(e.groupId,{recordingId:i,timecodeMs:e.startedAt?Date.now()-e.startedAt:0,shape:d.shape,geometry:JSON.stringify(d.geometry),color:d.color,text:d.text})}catch(p){r({type:"SET_ERROR_BANNER",message:`Annotation failed: ${p.message}`})}},x=()=>{for(const i of e.tiles){const d=`/xenon/api/control/${encodeURIComponent(i.udid)}/stream/stop`;try{navigator.sendBeacon?navigator.sendBeacon(d):fetch(d,{method:"POST",keepalive:!0}).catch(()=>{})}catch{}}e.tiles.forEach(i=>r({type:"REMOVE_TILE",udid:i.udid}))},R=We({idleAfterMs:Xe,warningSec:He,enabled:e.tiles.length>0,onWarning:()=>{},onTimeout:()=>{x()}});return t.jsxs(V.Provider,{value:{state:e,dispatch:r},children:[t.jsxs("div",{className:"flex flex-col h-full p-4 gap-3",children:[t.jsxs("header",{className:"flex items-center justify-between gap-3",children:[t.jsxs("div",{className:"flex items-center gap-3",children:[t.jsx("h1",{className:"text-base font-semibold",children:"Live Devices"}),t.jsx(ve,{value:e.layout,onChange:i=>r({type:"SET_LAYOUT",layout:i})})]}),t.jsx(Ue,{selectedUdids:e.tiles.map(i=>i.udid)})]}),e.errorBanner&&t.jsxs("div",{role:"alert",className:"text-sm rounded border border-red-500/40 bg-red-500/10 text-red-100 px-3 py-2 flex items-center gap-2",children:[t.jsx("span",{className:"flex-1",children:e.errorBanner}),t.jsx("button",{className:"text-xs opacity-80 hover:opacity-100",onClick:()=>r({type:"SET_ERROR_BANNER",message:null}),children:"Dismiss"})]}),t.jsxs("div",{className:"grid grid-cols-[260px_1fr] gap-3 flex-1 min-h-0",children:[t.jsxs("aside",{className:"flex flex-col gap-2 border border-[var(--border)] rounded p-3 overflow-y-auto min-h-0",children:[t.jsxs("div",{className:"flex items-center justify-between",children:[t.jsx("span",{className:"text-xs uppercase tracking-wide text-[var(--text-dim)]",children:"Devices"}),t.jsx("button",{className:"text-xs underline opacity-70 hover:opacity-100",onClick:()=>f(i=>i+1),children:"Refresh"})]}),t.jsx(ge,{devices:u,inMosaic:v,onToggle:o}),t.jsx("p",{className:"mt-2 text-[11px] text-[var(--text-dim)] leading-relaxed",children:"Click a device to add it to the mosaic. Click again (or use the × on the tile) to remove it."})]}),t.jsx("main",{className:"border border-[var(--border)] rounded p-2 overflow-y-auto h-full min-h-0",children:t.jsx(Ae,{layout:e.layout,tiles:e.tiles,annotateMode:e.annotateMode,shape:e.shape,color:e.color,onAnnotation:s,onRemove:c,onDropDevice:o})})]})]}),R.warning&&R.remainingSec!==null&&t.jsx(Ye,{remainingSec:R.remainingSec,onContinue:R.reset,onReleaseNow:x})]})}export{Je as default};