@xenon-device-management/xenon 1.22.0 → 1.22.2

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 (82) hide show
  1. package/lib/package.json +1 -1
  2. package/lib/public/assets/{ApiKeyGate-B_1qvA7M.js → ApiKeyGate-Snh2iZH5.js} +1 -1
  3. package/lib/public/assets/{BugReportButton-0m4q010L.js → BugReportButton-Sk0KGufH.js} +1 -1
  4. package/lib/public/assets/DeviceMosaicView-O292tZkA.js +2 -0
  5. package/lib/public/assets/{EmptyState-CTmGgoSm.js → EmptyState-CQun3BJE.js} +1 -1
  6. package/lib/public/assets/{FieldGroup-oWAJBBAu.js → FieldGroup-DmYZP2j7.js} +1 -1
  7. package/lib/public/assets/{Modal-LXKxgyV7.js → Modal-C5W8lMJ6.js} +1 -1
  8. package/lib/public/assets/{SegmentedControl-BSWMM3qE.js → SegmentedControl-BIzlKyUd.js} +1 -1
  9. package/lib/public/assets/{SettingCard-CeHOIGW9.js → SettingCard-DAFqQw7S.js} +1 -1
  10. package/lib/public/assets/{Table-EHWJCCzN.js → Table-DRkkM4OR.js} +1 -1
  11. package/lib/public/assets/{activity-CHfTPuBn.js → activity-B6x69oOy.js} +1 -1
  12. package/lib/public/assets/{ai-settings-B_dT4ctk.js → ai-settings-CSZQis7F.js} +1 -1
  13. package/lib/public/assets/{api-keys-BIKaKigW.js → api-keys-4ddjSngL.js} +1 -1
  14. package/lib/public/assets/{apps-Dp4crlsn.js → apps-CePIfnu9.js} +1 -1
  15. package/lib/public/assets/{arrow-left-QDqb-yc1.js → arrow-left-CaOe18yC.js} +1 -1
  16. package/lib/public/assets/{arrow-right-D_onnhWU.js → arrow-right-FEQsqDvH.js} +1 -1
  17. package/lib/public/assets/{arrow-up-right-DOwr8XxB.js → arrow-up-right-Dnszb73S.js} +1 -1
  18. package/lib/public/assets/{auth-shell-NbHUThYR.js → auth-shell-Vq1rYIVD.js} +2 -2
  19. package/lib/public/assets/{builds-page-CQseApdL.js → builds-page-FfsUegKZ.js} +1 -1
  20. package/lib/public/assets/{button-Do8mBvkU.js → button-DrqhMLZ0.js} +1 -1
  21. package/lib/public/assets/{calendar-DrH0mjSD.js → calendar-BuRuUa7q.js} +1 -1
  22. package/lib/public/assets/{check-C8Un1cwf.js → check-DYccNk73.js} +1 -1
  23. package/lib/public/assets/{chevron-right-DHjIgZvn.js → chevron-right-C6ny35i3.js} +1 -1
  24. package/lib/public/assets/{circle-check-Dkg-Ka4_.js → circle-check-Cokzy9a2.js} +1 -1
  25. package/lib/public/assets/{circle-x-BPKWtaZx.js → circle-x-BhUzyxKR.js} +1 -1
  26. package/lib/public/assets/{clock-CQLUBaRx.js → clock-BCG0drfS.js} +1 -1
  27. package/lib/public/assets/{copy-CWx0Ec1c.js → copy-BKOm9hko.js} +1 -1
  28. package/lib/public/assets/{device-explorer-BjrTj6Sm.js → device-explorer-CIjs3BXP.js} +1 -1
  29. package/lib/public/assets/{download-DJ_pjhGv.js → download-R2k7Nd4P.js} +1 -1
  30. package/lib/public/assets/{forgot-password-MmPdS0S2.js → forgot-password-CiZcr-gB.js} +1 -1
  31. package/lib/public/assets/{funnel-DR8YKoFW.js → funnel-SD4F5IdR.js} +1 -1
  32. package/lib/public/assets/{index-DbzQAv86.js → index-BHDlQcd6.js} +2 -2
  33. package/lib/public/assets/{input-CKKtQEU5.js → input-Ilib6feD.js} +1 -1
  34. package/lib/public/assets/{layers-DprXM0fz.js → layers-CLuKN5Oy.js} +1 -1
  35. package/lib/public/assets/{lock-BXGh7bJZ.js → lock-s9ZhGDFF.js} +1 -1
  36. package/lib/public/assets/{login-DOYeWP9H.js → login-EkaxK7K5.js} +1 -1
  37. package/lib/public/assets/{maintenance-settings-w8U3BRZk.js → maintenance-settings-yYhZaU7n.js} +1 -1
  38. package/lib/public/assets/{mouse-pointer-2-BD4cWXDU.js → mouse-pointer-2-C6yy1DTx.js} +1 -1
  39. package/lib/public/assets/{network-C5bJeATc.js → network-B8fPwmW9.js} +1 -1
  40. package/lib/public/assets/{overview-DzAXi4vj.js → overview-DrTe5eJi.js} +1 -1
  41. package/lib/public/assets/{page-header-CqaoOYPH.js → page-header-DhD-Kua9.js} +1 -1
  42. package/lib/public/assets/{plus-CJfVcjJv.js → plus-B75ovH3O.js} +1 -1
  43. package/lib/public/assets/{profile-page-Mk1yk1c4.js → profile-page-C2p5rB3x.js} +1 -1
  44. package/lib/public/assets/{reset-password-D1gtSgzr.js → reset-password-tCemYrqV.js} +1 -1
  45. package/lib/public/assets/{runbook-page-B7EjVqsY.js → runbook-page-Dq3wdSII.js} +1 -1
  46. package/lib/public/assets/{select-DVYczzD1.js → select-Cfv-njLE.js} +1 -1
  47. package/lib/public/assets/{selector-detail-page-C6b-Z-ep.js → selector-detail-page-8xf4CxH5.js} +1 -1
  48. package/lib/public/assets/{selector-health-DhzDZLlF.js → selector-health-BP45B44s.js} +1 -1
  49. package/lib/public/assets/{selector-health-page-w5zIE-r8.js → selector-health-page-D-8SdN_l.js} +1 -1
  50. package/lib/public/assets/{session-detail-page-CiPVLY-1.js → session-detail-page-Dm4tnoTT.js} +1 -1
  51. package/lib/public/assets/{settings-Bm-LdOxO.js → settings-ChfaAd8F.js} +1 -1
  52. package/lib/public/assets/{status-pill-outline-BgQ_Ur6B.js → status-pill-outline-CLa0C1Sg.js} +1 -1
  53. package/lib/public/assets/{tablet-1GdHxaSx.js → tablet-0sIFwy2Z.js} +1 -1
  54. package/lib/public/assets/{teams-D8rZexDl.js → teams-BFhWbK5i.js} +1 -1
  55. package/lib/public/assets/{trash-2-DJgpvIoq.js → trash-2-BcQxmJP-.js} +1 -1
  56. package/lib/public/assets/{upload-BJ0V1QbY.js → upload-BucqEWub.js} +1 -1
  57. package/lib/public/assets/{use-password-reset-mode-BVFQr1mo.js → use-password-reset-mode-XoxXVCzm.js} +1 -1
  58. package/lib/public/assets/{users-Bxnci4rK.js → users-B7uveXL_.js} +1 -1
  59. package/lib/public/assets/{users-DUb_rmxk.js → users-DyB6E3FF.js} +1 -1
  60. package/lib/public/assets/{webhook-settings-DL54Ijod.js → webhook-settings-qoHAETzC.js} +1 -1
  61. package/lib/public/assets/{zap-BLSqrVs9.js → zap-CqGOjAjn.js} +1 -1
  62. package/lib/public/index.html +1 -1
  63. package/lib/src/app/routers/control.js +18 -15
  64. package/lib/src/app/routers/recordings.js +5 -3
  65. package/lib/src/services/VideoPipelineService.js +44 -14
  66. package/lib/src/services/fillMissingScreenSize.js +50 -0
  67. package/lib/src/services/recording/RecordingOrchestrator.js +103 -2
  68. package/lib/src/services/recording/activeRecordings.js +8 -2
  69. package/lib/src/services/recording/annotation-render.js +189 -47
  70. package/lib/src/services/recording/probeDuration.js +21 -0
  71. package/lib/src/services/recording/proof-bundle.js +38 -15
  72. package/lib/src/services/recording/recordingTiming.js +86 -0
  73. package/lib/test/unit/active-recordings.spec.js +8 -0
  74. package/lib/test/unit/annotation-render.spec.js +226 -0
  75. package/lib/test/unit/composite-geometry.spec.js +80 -0
  76. package/lib/test/unit/fill-missing-screen-size.spec.js +65 -0
  77. package/lib/test/unit/proof-bundle.spec.js +65 -0
  78. package/lib/test/unit/recording-orchestrator.spec.js +95 -0
  79. package/lib/test/unit/recording-timing.spec.js +88 -0
  80. package/lib/tsconfig.tsbuildinfo +1 -1
  81. package/package.json +1 -1
  82. package/lib/public/assets/DeviceMosaicView-D4uKl1Gv.js +0 -2
@@ -0,0 +1,88 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const chai_1 = require("chai");
37
+ const fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const recordingTiming_1 = require("../../src/services/recording/recordingTiming");
41
+ describe('recording timing', () => {
42
+ let dir;
43
+ beforeEach(() => {
44
+ dir = fs.mkdtempSync(path.join(os.tmpdir(), 'rec-timing-'));
45
+ });
46
+ afterEach(() => fs.rmSync(dir, { recursive: true, force: true }));
47
+ const video = () => path.join(dir, 'rec-1', 'video', 'rec-1.mp4');
48
+ it('lives in the recording directory, so cleanup removes it with the video', () => {
49
+ (0, chai_1.expect)((0, recordingTiming_1.recordingTimingPath)('/r/rec-1/video/rec-1.mp4')).to.equal('/r/rec-1/timing.json');
50
+ });
51
+ it('round-trips, and reads nothing when the file is missing or corrupt', () => {
52
+ (0, chai_1.expect)((0, recordingTiming_1.readRecordingTiming)(video())).to.equal(undefined);
53
+ (0, recordingTiming_1.writeRecordingTiming)(video(), { spawnedAtMs: 1000, groupT0Ms: 2200 });
54
+ (0, chai_1.expect)((0, recordingTiming_1.readRecordingTiming)(video())).to.deep.equal({
55
+ version: 1,
56
+ spawnedAtMs: 1000,
57
+ groupT0Ms: 2200,
58
+ });
59
+ fs.writeFileSync((0, recordingTiming_1.recordingTimingPath)(video()), '{"version":1,"spawnedAtMs":"x"}');
60
+ (0, chai_1.expect)((0, recordingTiming_1.readRecordingTiming)(video())).to.equal(undefined);
61
+ });
62
+ // The dashboard stamps t=0 when start() returns; a device's video starts
63
+ // when its ffmpeg spawns. A mark at timecode T is at T + (t0 - spawn) in it.
64
+ it('shifts by how long before t=0 the video started', () => {
65
+ (0, chai_1.expect)((0, recordingTiming_1.markShiftMs)({ version: 1, spawnedAtMs: 1000, groupT0Ms: 2200 })).to.equal(1200);
66
+ });
67
+ it('shifts backwards for a device added after t=0', () => {
68
+ (0, chai_1.expect)((0, recordingTiming_1.markShiftMs)({ version: 1, spawnedAtMs: 9000, groupT0Ms: 2200 })).to.equal(-6800);
69
+ });
70
+ it('does not shift without timing, or with a clearly corrupt one', () => {
71
+ (0, chai_1.expect)((0, recordingTiming_1.markShiftMs)(undefined)).to.equal(0);
72
+ (0, chai_1.expect)((0, recordingTiming_1.markShiftMs)({ version: 1, spawnedAtMs: 0, groupT0Ms: 10 * 60 * 1000 })).to.equal(0);
73
+ });
74
+ it('moves start and end, and keeps an open mark open', () => {
75
+ const out = (0, recordingTiming_1.shiftAnnotations)([
76
+ { shape: 'RECT', geometry: '{}', timecode_ms: 1000, end_timecode_ms: 3000 },
77
+ { shape: 'RECT', geometry: '{}', timecode_ms: 500, end_timecode_ms: null },
78
+ ], 1200);
79
+ (0, chai_1.expect)(out.map((a) => [a.timecode_ms, a.end_timecode_ms])).to.deep.equal([
80
+ [2200, 4200],
81
+ [1700, null],
82
+ ]);
83
+ });
84
+ it('returns the same marks when there is nothing to shift', () => {
85
+ const anns = [{ shape: 'RECT', geometry: '{}', timecode_ms: 1000 }];
86
+ (0, chai_1.expect)((0, recordingTiming_1.shiftAnnotations)(anns, 0)).to.equal(anns);
87
+ });
88
+ });
@@ -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/androidH264Config.ts","../src/app/routers/apikeys.ts","../src/app/routers/apps.ts","../src/app/routers/audit.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/iosStreamPort.ts","../src/app/routers/ports.ts","../src/app/routers/processes.ts","../src/app/routers/profile.ts","../src/app/routers/profileIdentity.ts","../src/app/routers/projects.ts","../src/app/routers/recordingRequests.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/streamStartConflict.ts","../src/app/routers/streamType.ts","../src/app/routers/teams.ts","../src/app/routers/users.ts","../src/app/routers/webhook.ts","../src/app/ws/h264StreamWs.ts","../src/app/ws/logcatWs.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/AndroidH264StreamService.ts","../src/device-managers/android/AndroidStreamService.ts","../src/device-managers/android/DeviceLockManager.ts","../src/device-managers/android/H264Multiplexer.ts","../src/device-managers/android/LogcatMultiplexer.ts","../src/device-managers/android/LogcatStreamService.ts","../src/device-managers/android/ScrcpyServerSession.ts","../src/device-managers/android/androidStreamReuse.ts","../src/device-managers/android/displayState.ts","../src/device-managers/android/h264NalParser.ts","../src/device-managers/android/scrcpyVersion.ts","../src/device-managers/cloud/CapabilityManager.ts","../src/device-managers/cloud/Devices.ts","../src/device-managers/ios/IOSDiscoveryService.ts","../src/device-managers/ios/IOSLogStreamService.ts","../src/device-managers/ios/IOSStreamService.ts","../src/device-managers/ios/WDAClient.ts","../src/device-managers/ios/iosAppIds.ts","../src/device-managers/ios/iosBattery.ts","../src/device-managers/ios/iosStreamDiagnostics.ts","../src/device-managers/ios/tunnelProcess.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/ffmpegPath.ts","../src/helpers/index.ts","../src/helpers/networkAddresses.ts","../src/helpers/singleFlight.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/deviceAccessGuard.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/goIosVersion.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/DisplayStateService.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/passwordResetLink.ts","../src/services/passwordResetMode.ts","../src/services/sessionTokenGate.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/device-access/SessionOwnerResolver.ts","../src/services/device-access/actor.ts","../src/services/device-access/deviceAccessPolicy.ts","../src/services/device-access/ticketActorAccess.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/inspector/verifyLocator.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/logcat/PackageResolver.ts","../src/services/logcat/logSource.ts","../src/services/logcat/logcatParse.ts","../src/services/logcat/ostraceParse.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/activeRecordings.ts","../src/services/recording/annotation-render.ts","../src/services/recording/annotationImage.ts","../src/services/recording/busy-precheck.ts","../src/services/recording/concurrency-gate.ts","../src/services/recording/ensureMjpegForRecording.ts","../src/services/recording/manualLock.ts","../src/services/recording/probeDuration.ts","../src/services/recording/proof-bundle.ts","../src/services/recording/recording-store.ts","../src/services/recording/recordingCleanup.ts","../src/services/session/sessionIdentity.ts","../src/services/telemetry/attributes.ts","../src/services/token/JwtKeyService.ts","../src/services/token/StreamTicketService.ts","../src/services/token/mcpScopes.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/artifact-store.ts","../test/helpers/seedUser.ts","../test/helpers/test-container.ts","../test/integration/androidDevices.spec.ts","../test/integration/audit-ingest.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/healing-events-sessionfilter.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-health-endpoint.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/McpScopes.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/SessionLifecycleCaps.spec.ts","../test/unit/SessionTokenGate.spec.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/active-recordings.spec.ts","../test/unit/aggregate-hotspots-tuple-key.spec.ts","../test/unit/android-h264-config.spec.ts","../test/unit/android-h264-service.spec.ts","../test/unit/android-stream-adb-path.spec.ts","../test/unit/android-stream-encoder.spec.ts","../test/unit/android-stream-fast-start.spec.ts","../test/unit/android-stream-release.spec.ts","../test/unit/android-stream-reuse-service.spec.ts","../test/unit/android-stream-reuse.spec.ts","../test/unit/android-stream-stall-eof.spec.ts","../test/unit/annotation-image.spec.ts","../test/unit/annotation-render.spec.ts","../test/unit/apps-download.spec.ts","../test/unit/auth-disabled-resolution.spec.ts","../test/unit/auth-me-auth-disabled.test.ts","../test/unit/authMiddleware.bearer.spec.ts","../test/unit/authMiddleware.mcpAudience.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-access-guard.spec.ts","../test/unit/device-access-policy.spec.ts","../test/unit/device-service.spec.ts","../test/unit/device-utils.spec.ts","../test/unit/display-state.spec.ts","../test/unit/enrich-devices-team-names.test.ts","../test/unit/event-manager-regression-hook.spec.ts","../test/unit/extract-access-key-token-pair.test.ts","../test/unit/ffmpeg-path.spec.ts","../test/unit/findAndLockDevice-leasemerge.spec.ts","../test/unit/go-ios-version.spec.ts","../test/unit/h264-multiplexer.spec.ts","../test/unit/h264-nal-parser.spec.ts","../test/unit/h264-ws-frame.spec.ts","../test/unit/healing-instrumentation.spec.ts","../test/unit/healing-state-endpoints.spec.ts","../test/unit/healing-tier-gate.spec.ts","../test/unit/helpers.spec.ts","../test/unit/inspector-hierarchy.spec.ts","../test/unit/ios-stream-port.spec.ts","../test/unit/ios-tunnel-process.spec.ts","../test/unit/iosBattery.spec.ts","../test/unit/iosStreamDiagnostics.spec.ts","../test/unit/logcat-multiplexer.spec.ts","../test/unit/logcat-parse.spec.ts","../test/unit/logcat-stream-service.spec.ts","../test/unit/logcat-ws.spec.ts","../test/unit/loginRateLimiter.test.ts","../test/unit/manualLock.test.ts","../test/unit/mjpeg-proxy-cache.spec.ts","../test/unit/network-addresses.test.ts","../test/unit/normalize-eol.spec.js","../test/unit/omni-vision.spec.ts","../test/unit/ostrace-parse.spec.ts","../test/unit/package-resolver.spec.ts","../test/unit/passwordResetMode.test.ts","../test/unit/plugin.spec.ts","../test/unit/port-exhaustion.spec.ts","../test/unit/prisma.spec.ts","../test/unit/probe-duration.spec.ts","../test/unit/profile-router.test.ts","../test/unit/profileIdentity.spec.ts","../test/unit/proof-bundle.spec.ts","../test/unit/rateLimitMiddleware.test.ts","../test/unit/recording-cleanup.spec.ts","../test/unit/recording-instrumentation.spec.ts","../test/unit/recording-orchestrator.spec.ts","../test/unit/recording-requests.spec.ts","../test/unit/recording-source-ended.spec.ts","../test/unit/recording-store.spec.ts","../test/unit/remux-to-standard-mp4.spec.ts","../test/unit/reset-link.test.ts","../test/unit/roleGuard.test.ts","../test/unit/run-migrations-fail-fast.test.ts","../test/unit/run-migrations.test.ts","../test/unit/schema-required-args.spec.ts","../test/unit/scrcpy-server-session.spec.ts","../test/unit/scrcpy-version.spec.ts","../test/unit/selector-state-service.spec.ts","../test/unit/selector-verification-job.spec.ts","../test/unit/session-attribution-wiring.spec.ts","../test/unit/session-identity.spec.ts","../test/unit/session-owner-resolver.spec.ts","../test/unit/shutdown-sync-nets.spec.ts","../test/unit/single-flight.spec.ts","../test/unit/socketServer.bearer.spec.ts","../test/unit/stream-start-conflict.spec.ts","../test/unit/stream-status-type.spec.ts","../test/unit/stream-stop-ownership.spec.ts","../test/unit/stream-ticket-identity.spec.ts","../test/unit/sync-dir.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/verify-locator.spec.ts","../test/unit/video-pipeline-args.spec.ts","../test/unit/video-pipeline-exit.spec.ts","../test/unit/video-pipeline-outputpath.spec.ts","../test/unit/wda-cap-injection.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/lease/nodePairAuth.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/androidH264Config.ts","../src/app/routers/apikeys.ts","../src/app/routers/apps.ts","../src/app/routers/audit.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/iosStreamPort.ts","../src/app/routers/ports.ts","../src/app/routers/processes.ts","../src/app/routers/profile.ts","../src/app/routers/profileIdentity.ts","../src/app/routers/projects.ts","../src/app/routers/recordingRequests.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/streamStartConflict.ts","../src/app/routers/streamType.ts","../src/app/routers/teams.ts","../src/app/routers/users.ts","../src/app/routers/webhook.ts","../src/app/ws/h264StreamWs.ts","../src/app/ws/logcatWs.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/AndroidH264StreamService.ts","../src/device-managers/android/AndroidStreamService.ts","../src/device-managers/android/DeviceLockManager.ts","../src/device-managers/android/H264Multiplexer.ts","../src/device-managers/android/LogcatMultiplexer.ts","../src/device-managers/android/LogcatStreamService.ts","../src/device-managers/android/ScrcpyServerSession.ts","../src/device-managers/android/androidStreamReuse.ts","../src/device-managers/android/displayState.ts","../src/device-managers/android/h264NalParser.ts","../src/device-managers/android/scrcpyVersion.ts","../src/device-managers/cloud/CapabilityManager.ts","../src/device-managers/cloud/Devices.ts","../src/device-managers/ios/IOSDiscoveryService.ts","../src/device-managers/ios/IOSLogStreamService.ts","../src/device-managers/ios/IOSStreamService.ts","../src/device-managers/ios/WDAClient.ts","../src/device-managers/ios/iosAppIds.ts","../src/device-managers/ios/iosBattery.ts","../src/device-managers/ios/iosStreamDiagnostics.ts","../src/device-managers/ios/tunnelProcess.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/ffmpegPath.ts","../src/helpers/index.ts","../src/helpers/networkAddresses.ts","../src/helpers/singleFlight.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/deviceAccessGuard.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/goIosVersion.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/DisplayStateService.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/fillMissingScreenSize.ts","../src/services/passwordResetLink.ts","../src/services/passwordResetMode.ts","../src/services/sessionTokenGate.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/device-access/SessionOwnerResolver.ts","../src/services/device-access/actor.ts","../src/services/device-access/deviceAccessPolicy.ts","../src/services/device-access/ticketActorAccess.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/inspector/verifyLocator.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/logcat/PackageResolver.ts","../src/services/logcat/logSource.ts","../src/services/logcat/logcatParse.ts","../src/services/logcat/ostraceParse.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/activeRecordings.ts","../src/services/recording/annotation-render.ts","../src/services/recording/annotationImage.ts","../src/services/recording/busy-precheck.ts","../src/services/recording/concurrency-gate.ts","../src/services/recording/ensureMjpegForRecording.ts","../src/services/recording/manualLock.ts","../src/services/recording/probeDuration.ts","../src/services/recording/proof-bundle.ts","../src/services/recording/recording-store.ts","../src/services/recording/recordingCleanup.ts","../src/services/recording/recordingTiming.ts","../src/services/session/sessionIdentity.ts","../src/services/telemetry/attributes.ts","../src/services/token/JwtKeyService.ts","../src/services/token/StreamTicketService.ts","../src/services/token/mcpScopes.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/artifact-store.ts","../test/helpers/seedUser.ts","../test/helpers/test-container.ts","../test/integration/androidDevices.spec.ts","../test/integration/audit-ingest.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/healing-events-sessionfilter.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-health-endpoint.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/McpScopes.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/SessionLifecycleCaps.spec.ts","../test/unit/SessionTokenGate.spec.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/active-recordings.spec.ts","../test/unit/aggregate-hotspots-tuple-key.spec.ts","../test/unit/android-h264-config.spec.ts","../test/unit/android-h264-service.spec.ts","../test/unit/android-stream-adb-path.spec.ts","../test/unit/android-stream-encoder.spec.ts","../test/unit/android-stream-fast-start.spec.ts","../test/unit/android-stream-release.spec.ts","../test/unit/android-stream-reuse-service.spec.ts","../test/unit/android-stream-reuse.spec.ts","../test/unit/android-stream-stall-eof.spec.ts","../test/unit/annotation-image.spec.ts","../test/unit/annotation-render.spec.ts","../test/unit/apps-download.spec.ts","../test/unit/auth-disabled-resolution.spec.ts","../test/unit/auth-me-auth-disabled.test.ts","../test/unit/authMiddleware.bearer.spec.ts","../test/unit/authMiddleware.mcpAudience.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/composite-geometry.spec.ts","../test/unit/concurrency-gate.spec.ts","../test/unit/config.spec.js","../test/unit/device-access-guard.spec.ts","../test/unit/device-access-policy.spec.ts","../test/unit/device-service.spec.ts","../test/unit/device-utils.spec.ts","../test/unit/display-state.spec.ts","../test/unit/enrich-devices-team-names.test.ts","../test/unit/event-manager-regression-hook.spec.ts","../test/unit/extract-access-key-token-pair.test.ts","../test/unit/ffmpeg-path.spec.ts","../test/unit/fill-missing-screen-size.spec.ts","../test/unit/findAndLockDevice-leasemerge.spec.ts","../test/unit/go-ios-version.spec.ts","../test/unit/h264-multiplexer.spec.ts","../test/unit/h264-nal-parser.spec.ts","../test/unit/h264-ws-frame.spec.ts","../test/unit/healing-instrumentation.spec.ts","../test/unit/healing-state-endpoints.spec.ts","../test/unit/healing-tier-gate.spec.ts","../test/unit/helpers.spec.ts","../test/unit/inspector-hierarchy.spec.ts","../test/unit/ios-stream-port.spec.ts","../test/unit/ios-tunnel-process.spec.ts","../test/unit/iosBattery.spec.ts","../test/unit/iosStreamDiagnostics.spec.ts","../test/unit/logcat-multiplexer.spec.ts","../test/unit/logcat-parse.spec.ts","../test/unit/logcat-stream-service.spec.ts","../test/unit/logcat-ws.spec.ts","../test/unit/loginRateLimiter.test.ts","../test/unit/manualLock.test.ts","../test/unit/mjpeg-proxy-cache.spec.ts","../test/unit/network-addresses.test.ts","../test/unit/normalize-eol.spec.js","../test/unit/omni-vision.spec.ts","../test/unit/ostrace-parse.spec.ts","../test/unit/package-resolver.spec.ts","../test/unit/passwordResetMode.test.ts","../test/unit/plugin.spec.ts","../test/unit/port-exhaustion.spec.ts","../test/unit/prisma.spec.ts","../test/unit/probe-duration.spec.ts","../test/unit/profile-router.test.ts","../test/unit/profileIdentity.spec.ts","../test/unit/proof-bundle.spec.ts","../test/unit/rateLimitMiddleware.test.ts","../test/unit/recording-cleanup.spec.ts","../test/unit/recording-instrumentation.spec.ts","../test/unit/recording-orchestrator.spec.ts","../test/unit/recording-requests.spec.ts","../test/unit/recording-source-ended.spec.ts","../test/unit/recording-store.spec.ts","../test/unit/recording-timing.spec.ts","../test/unit/remux-to-standard-mp4.spec.ts","../test/unit/reset-link.test.ts","../test/unit/roleGuard.test.ts","../test/unit/run-migrations-fail-fast.test.ts","../test/unit/run-migrations.test.ts","../test/unit/schema-required-args.spec.ts","../test/unit/scrcpy-server-session.spec.ts","../test/unit/scrcpy-version.spec.ts","../test/unit/selector-state-service.spec.ts","../test/unit/selector-verification-job.spec.ts","../test/unit/session-attribution-wiring.spec.ts","../test/unit/session-identity.spec.ts","../test/unit/session-owner-resolver.spec.ts","../test/unit/shutdown-sync-nets.spec.ts","../test/unit/single-flight.spec.ts","../test/unit/socketServer.bearer.spec.ts","../test/unit/stream-start-conflict.spec.ts","../test/unit/stream-status-type.spec.ts","../test/unit/stream-stop-ownership.spec.ts","../test/unit/stream-ticket-identity.spec.ts","../test/unit/sync-dir.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/verify-locator.spec.ts","../test/unit/video-pipeline-args.spec.ts","../test/unit/video-pipeline-exit.spec.ts","../test/unit/video-pipeline-outputpath.spec.ts","../test/unit/wda-cap-injection.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/lease/nodePairAuth.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.22.0",
3
+ "version": "1.22.2",
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 l,j as n,X as P,T as Ae,Y as Ie}from"./index-DbzQAv86.js";import{p as De}from"./labels-C21SA4JG.js";import{A as q,I as Me}from"./keycodes-bIEJfQzr.js";import{a as ke}from"./page-header-CqaoOYPH.js";function _e(e,t){return e!=="auto"?e:t<=1?"1":t===2?"2x1":t<=4?"2x2":"3x2"}const Oe={selected:new Set,layout:"2x2",tiles:[],groupId:null,compositeEnabled:!1,downloadableVideoCount:0,recordingPhase:"idle",recording:!1,startedAt:null,annotateMode:!1,shape:"RECT",color:"#ff3333",overlayAnnotations:{},errorBanner:null,banner:null};function $e(e,t){var r;switch(t.type){case"SET_LAYOUT":return{...e,layout:t.layout};case"TOGGLE_SELECTED":{const o=new Set(e.selected);return o.has(t.udid)?o.delete(t.udid):o.add(t.udid),{...e,selected:o}}case"CLEAR_SELECTED":return{...e,selected:new Set};case"SET_TILES":return{...e,tiles:t.tiles};case"ADD_TILE":return e.tiles.some(o=>o.udid===t.tile.udid)?e:{...e,tiles:[...e.tiles,t.tile]};case"PATCH_TILE_DIMS":{let o=!1;const s=e.tiles.map(p=>p.udid!==t.udid||p.screenWidth&&p.screenHeight?p:(o=!0,{...p,screenWidth:t.screenWidth,screenHeight:t.screenHeight,aspect:t.aspect??p.aspect}));return o?{...e,tiles:s}:e}case"REMOVE_TILE":return e.recordingPhase!=="idle"?e:{...e,tiles:e.tiles.filter(o=>o.udid!==t.udid)};case"BIND_RECORDING_IDS":{const o=e.tiles.map(s=>({...s,recordingId:t.map[s.udid]??s.recordingId}));return{...e,tiles:o}}case"SET_RECORDING_PHASE":{const o=t.phase;return{...e,recordingPhase:o,recording:o==="recording"||o==="stopping"}}case"START_RECORDING":{const o=e.tiles.map(s=>({...s,recordingId:t.tileIds[s.udid]??s.recordingId}));return{...e,groupId:t.groupId,compositeEnabled:t.compositeEnabled===!0,downloadableVideoCount:0,recordingPhase:"recording",recording:!0,annotateMode:!0,startedAt:t.startedAt,overlayAnnotations:{},tiles:o}}case"REHYDRATE_RECORDING":{const o=e.tiles.map(s=>({...s,recordingId:t.tileIds[s.udid]??s.recordingId}));return{...e,groupId:t.groupId,compositeEnabled:t.compositeEnabled,downloadableVideoCount:0,recordingPhase:"recording",recording:!0,annotateMode:!1,startedAt:t.startedAt,overlayAnnotations:t.overlayAnnotations,tiles:o}}case"STOP_RECORDING":{const o=e.tiles.map(s=>({...s,recordingId:void 0}));return{...e,recording:!1,recordingPhase:"idle",annotateMode:!1,overlayAnnotations:{},downloadableVideoCount:t.downloadableVideoCount??e.downloadableVideoCount,compositeEnabled:t.compositeEnabled!==void 0?t.compositeEnabled:e.compositeEnabled,tiles:o}}case"SET_ANNOTATE_MODE":return{...e,annotateMode:t.enabled};case"SET_SHAPE":return{...e,shape:t.shape};case"SET_COLOR":return{...e,color:t.color};case"SET_OVERLAY_ANNOTATIONS":return{...e,overlayAnnotations:{...e.overlayAnnotations,[t.recordingId]:t.annotations}};case"CLEAR_OVERLAY_ANNOTATIONS":return{...e,overlayAnnotations:{}};case"SET_ERROR_BANNER":return{...e,errorBanner:t.message,banner:t.message?{tone:"error",message:t.message}:null};case"SET_BANNER":return{...e,banner:t.banner,errorBanner:((r=t.banner)==null?void 0:r.tone)==="error"?t.banner.message:null};default:return e}}const pe=l.createContext(null);function Pe(){const e=l.useContext(pe);if(!e)throw new Error("useMosaic must be used inside <MosaicContext.Provider>");return e}function Le(){return l.useReducer($e,Oe)}function Be(e){const t=Math.max(0,Math.floor(e/1e3)),r=Math.floor(t/3600),o=Math.floor(t%3600/60),s=t%60;return r>0?`${r}:${String(o).padStart(2,"0")}:${String(s).padStart(2,"0")}`:`${o}:${String(s).padStart(2,"0")}`}function Ye(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 ie=[{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 Ue(e){return De(e)}function Fe({devices:e,inMosaic:t,onToggle:r}){const[o,s]=l.useState(""),[p,d]=l.useState({}),N=l.useMemo(()=>{const c=o.trim().toLowerCase();return c?e.filter(y=>y.udid.toLowerCase().includes(c)||(y.name??"").toLowerCase().includes(c)||(y.platform??"").toLowerCase().includes(c)):e},[e,o]),j=l.useMemo(()=>{const c=ie.map(h=>({id:h.id,label:h.label,rows:[]})),y=h=>c.find((v,b)=>ie[b].match(h))??c[c.length-1];for(const h of N)y((h.platform??"").toLowerCase()).rows.push(h);return c.filter(h=>h.rows.length>0)},[N]);return e.length===0?n.jsx("div",{className:"text-xs text-[var(--text-dim)] p-3",children:"No devices online."}):n.jsxs("div",{className:"flex flex-col gap-2",children:[n.jsxs("div",{className:"relative",children:[n.jsx("span",{"aria-hidden":!0,className:"absolute left-2 top-1/2 -translate-y-1/2 text-[var(--text-dim)] text-xs",children:"⌕"}),n.jsx("input",{type:"text",value:o,placeholder:"Filter devices...",onChange:c=>s(c.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(--color-info)]"})]}),j.map(c=>{const y=p[c.id];return n.jsxs("section",{className:"flex flex-col gap-1",children:[n.jsxs("button",{type:"button",onClick:()=>d(h=>({...h,[c.id]:!h[c.id]})),className:"flex items-center justify-between w-full text-[11px] text-[var(--text-dim)] hover:text-[rgb(var(--rgb-fg))] px-1 py-0.5",children:[n.jsxs("span",{className:"flex items-center gap-1.5",children:[n.jsx("span",{"aria-hidden":!0,className:`inline-block transition-transform ${y?"-rotate-90":"rotate-0"}`,children:"▾"}),c.label]}),n.jsx("span",{children:c.rows.length})]}),!y&&n.jsx("ul",{className:"flex flex-col gap-1 text-sm",children:c.rows.map(h=>{const v=h.busyReason==="manual_self",b=t.has(h.udid),f=!!h.busy&&!v&&!b||!!h.offline,g=h.offline?"Offline":Ye(h.busyReason),I=!f;return n.jsx("li",{children:n.jsxs("button",{type:"button",disabled:f,draggable:!f,onDragStart:D=>{D.dataTransfer.effectAllowed="copy",D.dataTransfer.setData("application/x-xenon-udid",h.udid),D.dataTransfer.setData("text/plain",h.udid)},onClick:()=>r(h.udid),className:`w-full flex items-center gap-2 px-2 py-1.5 rounded text-left border transition-colors ${f?"opacity-60 cursor-not-allowed border-transparent":b?"border-[rgb(var(--rgb-accent)/0.4)] bg-[rgb(var(--rgb-accent)/0.05)] hover:bg-[rgb(var(--rgb-accent)/0.1)]":"border-transparent hover:border-[var(--border)] hover:bg-[var(--surface-2)]"}`,title:f?`${g} — release first`:b?"Click to remove from mosaic (or drag to a cell)":"Click to add to mosaic (or drag to a cell)",children:[n.jsx("span",{"aria-hidden":!0,className:`inline-flex items-center justify-center w-4 h-4 text-xs ${b?"text-[var(--color-accent-soft)]":"text-[var(--text-dim)]"}`,children:b?"●":"○"}),n.jsx("span",{"aria-hidden":!0,className:`inline-block w-1.5 h-1.5 rounded-full ${h.offline?"bg-zinc-600":I?"bg-[var(--color-success)]":"bg-yellow-500"}`}),n.jsx("span",{className:"font-medium flex-1 truncate text-[13px]",children:h.name??h.udid}),h.platform&&n.jsx("span",{className:"text-[11px] px-1.5 py-0.5 rounded bg-[var(--surface-2)] text-[var(--text-dim)]",children:Ue(h.platform)})]})},h.udid)})})]},c.id)}),o&&j.length===0&&n.jsxs("div",{className:"text-xs text-[var(--text-dim)] px-1 py-2",children:['No devices match "',o,'".']}),n.jsxs("div",{className:"text-[10px] text-[var(--text-dim)] px-1 pt-2 flex items-center gap-3",children:[n.jsxs("span",{className:"inline-flex items-center gap-1",children:[n.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-[var(--color-success)] inline-block"})," available"]}),n.jsxs("span",{className:"inline-flex items-center gap-1",children:[n.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-yellow-500 inline-block"})," in use"]}),n.jsxs("span",{className:"inline-flex items-center gap-1",children:[n.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-zinc-600 inline-block"})," offline"]})]})]})}const Xe=[{id:"1",label:"1",icon:n.jsx(Ge,{})},{id:"2x1",label:"2×1",icon:n.jsx(We,{})},{id:"2x2",label:"2×2",icon:n.jsx(ze,{})},{id:"3x2",label:"3×2",icon:n.jsx(He,{})},{id:"auto",label:"Auto",icon:n.jsx(Ke,{})}];function Ve({value:e,onChange:t}){return n.jsx("div",{role:"tablist",className:"inline-flex border border-[var(--border)] rounded-md overflow-hidden text-xs bg-[rgb(var(--rgb-white)/0.02)]",children:Xe.map(r=>{const o=e===r.id;return n.jsxs("button",{role:"tab","aria-selected":o,onClick:()=>t(r.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 ${o?"bg-[var(--surface-2)] text-[rgb(var(--rgb-fg))]":"text-[var(--text-dim)] hover:bg-[var(--surface-2)] hover:text-[rgb(var(--rgb-fg))]"}`,title:`Layout: ${r.label}`,children:[n.jsx("span",{className:"w-3.5 h-3.5 inline-flex items-center justify-center",children:r.icon}),n.jsx("span",{children:r.label})]},r.id)})})}function U(e){return n.jsx("rect",{rx:1.2,ry:1.2,fill:"currentColor",...e})}function Ge(){return n.jsx("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:U({x:2,y:2,width:10,height:10})})}function We(){return n.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:[U({x:1.5,y:3,width:5,height:8}),U({x:7.5,y:3,width:5,height:8})]})}function ze(){return n.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:[U({x:1.5,y:1.5,width:5,height:5}),U({x:7.5,y:1.5,width:5,height:5}),U({x:1.5,y:7.5,width:5,height:5}),U({x:7.5,y:7.5,width:5,height:5})]})}function He(){return n.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:[U({x:1,y:1.5,width:3.3,height:5}),U({x:5.35,y:1.5,width:3.3,height:5}),U({x:9.7,y:1.5,width:3.3,height:5}),U({x:1,y:7.5,width:3.3,height:5}),U({x:5.35,y:7.5,width:3.3,height:5}),U({x:9.7,y:7.5,width:3.3,height:5})]})}function Ke(){return n.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",fill:"none",stroke:"currentColor",strokeWidth:"1.4",children:[n.jsx("rect",{x:"2",y:"2",width:"10",height:"10",rx:"1.5"}),n.jsx("circle",{cx:"7",cy:"7",r:"1.5",fill:"currentColor",stroke:"none"})]})}const Je=1600;function qe(e,t){const r=Je/Math.max(e,t);return{w:Math.round(e*r),h:Math.round(t*r)}}function Ze(e,t,r,o=document){if(t<=0||r<=0)return null;const{w:s,h:p}=qe(t,r),d=o.createElement("canvas");d.width=s,d.height=p;const N=d.getContext("2d");if(!N)return null;N.scale(s/t,p/r),re(N,t,r,e);try{return d.toDataURL("image/png")}catch{return null}}function Qe(e,t,r){const o={left:0,top:0,width:e,height:t};if(!r||!(r>0)||e<=0||t<=0)return o;const s=e/t;if(Math.abs(s-r)<1e-6)return o;if(r>s){const d=e/r;return{left:0,top:(t-d)/2,width:e,height:d}}const p=t*r;return{left:(e-p)/2,top:0,width:p,height:t}}const et=(e,t)=>e===t||!!e&&!!t&&e.left===t.left&&e.top===t.top&&e.width===t.width&&e.height===t.height,tt=2,rt=2e3;function ae(e,t,r){if(e.length>=rt)return e;const o=e[e.length-1];return o&&Math.hypot(t-o[0],r-o[1])<tt?e:[...e,[t,r]]}function ce(e,t){const r=(e||"#ff3333").trim();if(/^#[0-9a-fA-F]{6}$/.test(r)){const o=parseInt(r.slice(1,3),16),s=parseInt(r.slice(3,5),16),p=parseInt(r.slice(5,7),16);return`rgba(${o},${s},${p},${t})`}return r}function re(e,t,r,o){var h;const s=o.geometry,p=o.color||"#ff3333",d=Math.max(4,Math.round(Math.min(t,r)*.008));if(e.lineCap="round",e.lineJoin="round",o.shape==="CIRCLE"){const v=(s.x??0)*t,b=(s.y??0)*r,f=Math.max(2,(s.w??0)*t),g=Math.max(2,(s.h??0)*r);e.beginPath(),e.ellipse(v,b,f,g,0,0,Math.PI*2),e.fillStyle=ce(p,.22),e.fill(),e.lineWidth=d+2,e.strokeStyle="rgba(0,0,0,0.55)",e.stroke(),e.beginPath(),e.ellipse(v,b,f,g,0,0,Math.PI*2),e.lineWidth=d,e.strokeStyle=p,e.stroke();return}if(o.shape==="ARROW"){const v=(s.x??0)*t,b=(s.y??0)*r,f=v+(s.w??0)*t,g=b+(s.h??0)*r;e.lineWidth=d+2,e.strokeStyle="rgba(0,0,0,0.55)",e.beginPath(),e.moveTo(v,b),e.lineTo(f,g),e.stroke(),e.lineWidth=d,e.strokeStyle=p,e.beginPath(),e.moveTo(v,b),e.lineTo(f,g),e.stroke();const I=Math.atan2(g-b,f-v),D=Math.max(14,d*3);e.fillStyle=p,e.beginPath(),e.moveTo(f,g),e.lineTo(f-D*Math.cos(I-.4),g-D*Math.sin(I-.4)),e.lineTo(f-D*Math.cos(I+.4),g-D*Math.sin(I+.4)),e.closePath(),e.fill();return}if(o.shape==="TEXT"&&o.text){const v=Math.max(16,Math.round(Math.min(t,r)*.035));e.font=`bold ${v}px sans-serif`,e.lineWidth=3,e.strokeStyle="rgba(0,0,0,0.65)",e.strokeText(o.text,(s.x??0)*t,(s.y??0)*r),e.fillStyle=p,e.fillText(o.text,(s.x??0)*t,(s.y??0)*r);return}if(o.shape==="FREEHAND"&&(((h=s.points)==null?void 0:h.length)??0)>=2){const v=s.points,b=()=>{e.beginPath(),e.moveTo(v[0][0]*t,v[0][1]*r);for(let f=1;f<v.length;f++)e.lineTo(v[f][0]*t,v[f][1]*r)};e.lineWidth=d+2,e.strokeStyle="rgba(0,0,0,0.55)",b(),e.stroke(),e.lineWidth=d,e.strokeStyle=p,b(),e.stroke();return}const N=(s.x??0)*t,j=(s.y??0)*r,c=Math.max(2,(s.w??0)*t),y=Math.max(2,(s.h??0)*r);e.fillStyle=ce(p,.22),e.fillRect(N,j,c,y),e.lineWidth=d+2,e.strokeStyle="rgba(0,0,0,0.55)",e.strokeRect(N,j,c,y),e.lineWidth=d,e.strokeStyle=p,e.strokeRect(N,j,c,y)}function le(e,t,r,o,s){if(o<=0||s<=0)return null;if(e==="CIRCLE"){const c=Math.hypot(r.curX-r.startX,r.curY-r.startY);return c<=4?null:{shape:e,color:t,geometry:{x:r.startX/o,y:r.startY/s,w:c/o,h:c/s}}}if(e==="ARROW"){const c=r.curX-r.startX,y=r.curY-r.startY;return Math.hypot(c,y)<=6?null:{shape:e,color:t,geometry:{x:r.startX/o,y:r.startY/s,w:c/o,h:y/s}}}if(e==="FREEHAND"){if(r.points.length<2)return null;const c=r.points.map(b=>b[0]),y=r.points.map(b=>b[1]),h=Math.min(...c),v=Math.min(...y);return{shape:e,color:t,geometry:{x:h/o,y:v/s,w:Math.max(.005,(Math.max(...c)-h)/o),h:Math.max(.005,(Math.max(...y)-v)/s),points:r.points.map(([b,f])=>[b/o,f/s])}}}const p=Math.min(r.startX,r.curX)/o,d=Math.min(r.startY,r.curY)/s,N=Math.abs(r.curX-r.startX)/o,j=Math.abs(r.curY-r.startY)/s;return N<=.005||j<=.005?null:{shape:e,color:t,geometry:{x:p,y:d,w:N,h:j}}}function nt({enabled:e,shape:t,color:r,onCommit:o,committed:s,onCommittedChange:p,mediaAspect:d}){const N=l.useRef(null),[j,c]=l.useState(null),[y,h]=l.useState([]),[v,b]=l.useState(null),f=s??y,g=l.useRef(f),I=l.useRef(j),D=l.useRef(t),k=l.useRef(r),i=l.useRef(e);g.current=f,I.current=j,D.current=t,k.current=r,i.current=e;const m=l.useCallback(u=>{const E=u(g.current);g.current=E,p?p(E):h(E)},[p]),w=l.useCallback(()=>{const u=N.current;if(!u)return;const E=u.getContext("2d");if(!E)return;E.clearRect(0,0,u.width,u.height);for(const T of g.current)re(E,u.width,u.height,T);const C=I.current;if(C){const T=le(D.current,k.current,C,u.width,u.height);T&&re(E,u.width,u.height,T)}},[]);l.useEffect(()=>{const u=N.current;if(!u)return;const E=()=>{const T=Math.max(1,Math.round(u.clientWidth)),L=Math.max(1,Math.round(u.clientHeight));(u.width!==T||u.height!==L)&&(u.width=T,u.height=L),w()};E();const C=new ResizeObserver(E);return C.observe(u),()=>C.disconnect()},[w]),l.useEffect(()=>{w()},[j,f,w]),l.useEffect(()=>{var T;const u=(T=N.current)==null?void 0:T.parentElement;if(!u)return;const E=()=>{const L=d?Qe(u.clientWidth,u.clientHeight,d):null;b(X=>et(X,L)?X:L)};E();const C=new ResizeObserver(E);return C.observe(u),()=>C.disconnect()},[d]),l.useEffect(()=>{var E;if(e)return;c(null);const u=N.current;if(u&&u.hasPointerCapture)try{for(let C=0;C<8;C++)(E=u.hasPointerCapture)!=null&&E.call(u,C)&&u.releasePointerCapture(C)}catch{}},[e]);const A=u=>{const C=N.current.getBoundingClientRect();return{px:u.clientX-C.left,py:u.clientY-C.top,w:C.width,h:C.height}},x=u=>{const E=I.current;if(!E||!i.current){c(null);return}const C=N.current;if(!C||C.width<=0){c(null);return}const T=A(u),L=le(D.current,k.current,{...E,curX:T.px,curY:T.py,points:ae(E.points,T.px,T.py)},T.w,T.h);L&&(m(X=>[...X,L]),o(L,Ze(L,T.w,T.h))),c(null)},S=u=>{if(!e||u.button!==0)return;u.preventDefault(),u.currentTarget.setPointerCapture(u.pointerId);const E=A(u);c({startX:E.px,startY:E.py,curX:E.px,curY:E.py,points:[[E.px,E.py]]})},_=u=>{if(!I.current)return;const E=A(u);c(C=>C&&{...C,curX:E.px,curY:E.py,points:ae(C.points,E.px,E.py)})},M=u=>{try{u.currentTarget.releasePointerCapture(u.pointerId)}catch{}x(u)};return n.jsx("canvas",{ref:N,style:{position:"absolute",...v?{left:`${v.left}px`,top:`${v.top}px`,width:`${v.width}px`,height:`${v.height}px`}:{inset:0,width:"100%",height:"100%"},pointerEvents:e?"auto":"none",cursor:e?"crosshair":"default",zIndex:e?40:25},onPointerDown:S,onPointerMove:_,onPointerUp:M,onPointerCancel:M})}const ot=3,st=45e3,de=[2e3,5e3,1e4];function it(e){const t=Math.max(0,Math.min(e,de.length-1));return de[t]}function at(e){return e<ot}function ue(e){const t=(e.lastError||"").toLowerCase();return/did not find test app|not installed|webdriveragentrunner|applicationverification/.test(t)?"WebDriverAgent is not installed on this device. Install and trust WDA, then retry.":/unsupported ios version|manualpairingtunnelstart/.test(t)?"This device’s iOS version is not supported for live streaming on this host.":/econnrefused|econnreset|connection reset|tunnel/.test(t)?"Lost the connection to the device tunnel. Reconnect the device, then retry.":e.lastError&&e.lastError.trim()?e.lastError.trim():"We couldn’t start the live stream for this device."}function ct(e){for(let t=0;t+4<e.length;t++){const r=e[t]===0&&e[t+1]===0&&e[t+2]===0&&e[t+3]===1,o=e[t]===0&&e[t+1]===0&&e[t+2]===1;if(!r&&!o)continue;const s=t+(r?4:3);if((e[s]&31)===7){const p=d=>d.toString(16).padStart(2,"0");return`avc1.${p(e[s+1])}${p(e[s+2])}${p(e[s+3])}`}}return"avc1.42e01e"}const lt=({wsUrl:e,onFatal:t,onReady:r,onFrameSize:o,className:s})=>{const p=l.useRef(null),d=l.useRef(t);d.current=t;const N=l.useRef(r);N.current=r;const j=l.useRef(o);return j.current=o,l.useEffect(()=>{var k;const c=window,y=p.current;if(typeof c.VideoDecoder>"u"||!y){(k=d.current)==null||k.call(d);return}const h=y.getContext("2d");let v=!1,b=!1,f=!0,g=0;const I=new c.VideoDecoder({output:i=>{var m,w;if(v){i.close();return}(y.width!==i.displayWidth||y.height!==i.displayHeight)&&(y.width=i.displayWidth,y.height=i.displayHeight,(m=j.current)==null||m.call(j,i.displayWidth,i.displayHeight)),h==null||h.drawImage(i,0,0),i.close(),f&&(f=!1,(w=N.current)==null||w.call(N))},error:()=>{var i;return(i=d.current)==null?void 0:i.call(d)}}),D=new WebSocket(e);return D.binaryType="arraybuffer",D.onerror=()=>{var i;return(i=d.current)==null?void 0:i.call(d)},D.onclose=()=>{var i;v||(i=d.current)==null||i.call(d)},D.onmessage=i=>{var x;const m=i.data,w=new DataView(m).getUint8(0),A=new Uint8Array(m,1);if(w===0){if(b)return;try{I.configure({codec:ct(A),optimizeForLatency:!0}),b=!0}catch{(x=d.current)==null||x.call(d)}return}if(!(!b||I.state!=="configured"))try{I.decode(new c.EncodedVideoChunk({type:w===1?"key":"delta",timestamp:g,data:A})),g+=11111}catch{}},()=>{v=!0;try{D.close()}catch{}try{I.state!=="closed"&&I.close()}catch{}}},[e]),n.jsx("canvas",{ref:p,className:s,style:{width:"100%",height:"100%",objectFit:"contain",display:"block"}})};function dt(e,t,r){return(e==="android"||e==="androidtv"||e==="android-tv")&&t==="h264"&&r?"h264":"mjpeg"}const ut=10,ht=500;function ft({udid:e,name:t,recordingId:r,annotateMode:o,shape:s,color:p,aspect:d="9 / 16",screenWidth:N,screenHeight:j,platform:c,onAnnotation:y,overlayAnnotations:h,onOverlayAnnotationsChange:v,onRemove:b}){const[f,g]=l.useState("connecting"),[I,D]=l.useState(),k=l.useCallback((a,R)=>{if(!(a>0)||!(R>0))return;const O=a/R;D(B=>B!==void 0&&Math.abs(B-O)<.001?B:O)},[]),[i,m]=l.useState(0),[w,A]=l.useState(null),x=l.useRef(0),S=l.useRef(null),[_,M]=l.useState(""),[u,E]=l.useState([]),[C,T]=l.useState(!1),L=l.useRef(null),X=l.useRef(null),G=c==="android"||c==="androidtv"||c==="android-tv",xe=c==="ios"||c==="tvos";l.useEffect(()=>{let a=!1;if(r){A(null),g("connecting"),m(Date.now()),P.startStream(e).catch(()=>{});return}const R=typeof window.VideoDecoder<"u";return(async()=>{try{const O=encodeURIComponent(e),B=await fetch(`/xenon/api/control/${O}/stream/status`);if(!B.ok)return;const $=await B.json();if(dt(c||"",$==null?void 0:$.type,R)!=="h264"||!($!=null&&$.h264Path))return;const F=await fetch(`/xenon/api/control/${O}/stream/ticket`,{method:"POST"});if(!F.ok)return;const{ticket:Y}=await F.json();if(a||!Y)return;const K=`${window.location.protocol==="https:"?"wss":"ws"}://${window.location.host}${$.h264Path}?ticket=${encodeURIComponent(Y)}`;A(K)}catch{}})(),()=>{a=!0}},[e,c,r]);const H=!o&&!!N&&!!j,ne=(a,R,O)=>{const B=N,$=j,F=Math.min(1,Math.max(0,(a-O.left)/O.width)),Y=Math.min(1,Math.max(0,(R-O.top)/O.height));return{x:Math.round(F*B),y:Math.round(Y*$)}},be=(a,R,O)=>{const B=Date.now()+Math.random(),$=a-O.left,F=R-O.top;E(Y=>[...Y,{id:B,x:$,y:F}]),window.setTimeout(()=>{E(Y=>Y.filter(z=>z.id!==B))},500)},ye=a=>{if(!H||f==="unavailable"||a.button!==0)return;const R=a.currentTarget;R.setPointerCapture(a.pointerId),X.current={startX:a.clientX,startY:a.clientY,startTime:Date.now(),rect:R.getBoundingClientRect()},be(a.clientX,a.clientY,X.current.rect),R.focus()},ve=async a=>{var K,se;const R=X.current;if(X.current=null,!R||!H)return;try{a.currentTarget.releasePointerCapture(a.pointerId)}catch{}const O=a.clientX-R.startX,B=a.clientY-R.startY,$=Math.hypot(O,B),F=Date.now()-R.startTime,Y=ne(R.startX,R.startY,R.rect),z=$<ut;try{if(z&&F<ht)await P.tap(e,Y.x,Y.y);else if(z)await((se=(K=P).touchAndHold)==null?void 0:se.call(K,e,Y.x,Y.y,F));else{const J=ne(a.clientX,a.clientY,R.rect);await P.swipe(e,Y.x,Y.y,J.x,J.y)}}catch(J){console.error(`[DeviceTile] gesture failed for ${e}`,J)}},we=()=>{X.current=null},Ee=async a=>{if(!H||f==="unavailable"||a.metaKey||a.ctrlKey||a.altKey)return;const R=a.key;try{if(R.length===1){a.preventDefault(),await P.typeText(e,R);return}if(R==="Backspace"){a.preventDefault(),G?await P.pressKey(e,67):await P.typeText(e,"\b");return}if(R==="Enter"){a.preventDefault(),G?await P.pressKey(e,66):await P.typeText(e,`
2
- `);return}if(R==="Tab"){a.preventDefault(),await P.typeText(e," ");return}R==="Escape"&&G&&(a.preventDefault(),await P.pressKey(e,q.BACK))}catch(O){console.error(`[DeviceTile] keystroke "${R}" failed for ${e}`,O)}},Ne=()=>P.pressKey(e,G?q.HOME:Me.HOME).catch(a=>console.error(`[DeviceTile] home failed for ${e}`,a)),Re=()=>P.pressKey(e,q.BACK).catch(a=>console.error(`[DeviceTile] back failed for ${e}`,a)),Se=()=>P.pressKey(e,q.APP_SWITCH).catch(a=>console.error(`[DeviceTile] app-switch failed for ${e}`,a)),Te=async()=>{try{const a=await P.getScreenshot(e),R=(a==null?void 0:a.screenshot)??(a==null?void 0:a.value)??a;if(!R)return;const O=await fetch(`data:image/png;base64,${R}`).then(F=>F.blob()),B=URL.createObjectURL(O),$=document.createElement("a");$.href=B,$.download=`${t||e}-${Date.now()}.png`,document.body.appendChild($),$.click(),$.remove(),setTimeout(()=>URL.revokeObjectURL(B),1e3)}catch(a){console.error(`[DeviceTile] screenshot failed for ${e}`,a)}};l.useEffect(()=>{console.log(`[DeviceTile] Rendering ${e} in state: ${f}`)},[e,f]);const je=`/xenon/api/control/${encodeURIComponent(e)}/stream?t=${i}`,oe=l.useCallback(async()=>{try{const a=await fetch(`/xenon/api/control/${encodeURIComponent(e)}/stream/status`);if(!a.ok)throw new Error(String(a.status));const R=await a.json();M(ue({lastError:R==null?void 0:R.lastError,status:R==null?void 0:R.status}))}catch{M(ue({}))}},[e]),Q=l.useCallback(()=>{const a=x.current;at(a)?(x.current=a+1,S.current&&clearTimeout(S.current),S.current=setTimeout(()=>{g("connecting"),m(Date.now())},it(a))):(g("unavailable"),oe())},[oe]);l.useEffect(()=>{if(f!=="connecting")return;const a=setTimeout(Q,st);return()=>clearTimeout(a)},[f,i,Q]),l.useEffect(()=>()=>{S.current&&clearTimeout(S.current)},[]);const Ce=()=>{S.current&&clearTimeout(S.current),x.current=0,M(""),g("connecting"),m(Date.now())},W=!!r,ee=t||e.slice(0,16)+"…";return n.jsxs("div",{className:"flex items-stretch justify-center w-full h-full min-h-0 min-w-0 gap-2",children:[n.jsxs("div",{className:"relative bg-[var(--black)] rounded-lg overflow-hidden border border-[var(--border)] group max-h-full max-w-full self-center",style:{aspectRatio:d,height:"100%"},children:[f==="connecting"&&n.jsxs("div",{className:"flex flex-col items-center justify-center bg-neutral-900/80 text-neutral-400 absolute inset-0 z-20 pointer-events-none",children:[n.jsx("div",{className:"relative flex items-center justify-center",children:n.jsx("div",{className:"w-12 h-12 border-4 border-neutral-800 border-t-neutral-400 rounded-full animate-spin"})}),n.jsx("div",{className:"mt-4 font-bold text-neutral-200",children:"Starting Stream…"}),n.jsx("div",{className:"text-[10px] mt-1 text-neutral-500 opacity-60 font-mono",children:e}),n.jsx("div",{className:"text-[10px] mt-4 text-neutral-400 bg-white/5 px-2 py-1 rounded",children:"Waiting for device connection..."})]}),f==="unavailable"&&n.jsx("div",{className:"flex items-center justify-center bg-black text-neutral-400 absolute inset-0 z-20",children:n.jsxs("div",{className:"text-center p-6",children:[n.jsx("div",{className:"text-5xl mb-4 opacity-20",children:"📵"}),n.jsx("div",{className:"font-bold text-white text-lg",children:"Connection Failed"}),n.jsx("p",{className:"text-xs mt-2 text-neutral-500 leading-relaxed max-w-[240px] mx-auto",children:_||"We couldn’t start the live stream for this device."}),n.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 active:scale-95",onClick:Ce,children:"Retry connection"})]})}),w?n.jsx(lt,{wsUrl:w,className:"absolute inset-0 w-full h-full object-contain bg-black select-none pointer-events-none",onReady:()=>g("live"),onFrameSize:k,onFatal:()=>{console.warn(`[DeviceTile] H.264 fatal for ${e}; falling back to MJPEG`),A(null),g("connecting"),m(Date.now())}}):n.jsx("img",{src:je,alt:ee,className:"absolute inset-0 w-full h-full object-contain bg-black select-none pointer-events-none",draggable:!1,style:{opacity:f==="live"?1:0,transition:"opacity 0.3s ease-in-out"},onLoad:a=>{console.log(`[DeviceTile] Image LOADED for ${e}`),k(a.currentTarget.naturalWidth,a.currentTarget.naturalHeight),x.current=0,S.current&&clearTimeout(S.current),g("live")},onError:a=>{console.error(`[DeviceTile] Image ERROR for ${e}`,a),Q()}},i),H&&f!=="unavailable"&&n.jsx("div",{ref:L,className:`absolute inset-0 z-[35] touch-none outline-none ${C?"ring-2 ring-[rgb(var(--rgb-accent)/0.6)] ring-inset rounded-lg":""}`,tabIndex:0,onPointerDown:ye,onPointerUp:ve,onPointerCancel:we,onKeyDown:Ee,onFocus:()=>T(!0),onBlur:()=>T(!1)}),n.jsx("div",{className:"absolute inset-0 z-20 pointer-events-none overflow-hidden",children:u.map(a=>n.jsx("span",{className:"absolute block w-10 h-10 rounded-full border-2 border-[var(--color-accent-soft)] animate-ping",style:{left:a.x-20,top:a.y-20}},a.id))}),W&&n.jsx(nt,{enabled:o,shape:s,color:p,committed:h,mediaAspect:I,onCommittedChange:v,onCommit:(a,R)=>y(r,a,R)}),W&&o&&f==="live"&&n.jsx("div",{className:"absolute top-3 left-14 right-3 z-30 flex justify-end pointer-events-none",children:n.jsx("span",{className:"px-2 py-0.5 rounded-sm bg-[var(--color-info)] text-white text-[10px] font-semibold shadow-lg whitespace-nowrap overflow-hidden text-ellipsis",children:"Annotating · drag to mark"})}),W&&n.jsx("div",{className:"absolute top-3 left-3 z-30 pointer-events-none",children:n.jsx("span",{className:"px-2 py-0.5 rounded-sm bg-red-600 text-white text-[9px] font-black shadow-lg",children:"REC"})}),n.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:[f==="live"&&n.jsx("span",{className:"px-2 py-0.5 rounded-sm bg-[var(--color-success)] text-[var(--color-on-success)] text-[11px] font-semibold shadow-lg w-fit",children:"Live"}),n.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:ee})]})]}),n.jsx(pt,{streamState:f,recording:W,isIOS:xe,isAndroid:G,platform:c,onClose:!W&&b?()=>b(e):void 0,sendHome:Ne,sendBack:Re,sendAppSwitch:Se,captureScreenshot:Te,displayName:ee})]})}function pt({streamState:e,recording:t,isIOS:r,isAndroid:o,platform:s,onClose:p,sendHome:d,sendBack:N,sendAppSwitch:j,captureScreenshot:c,displayName:y}){const h=e==="live";return!p&&!h?null:n.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:[n.jsx("div",{className:"flex flex-col items-center gap-1",children:p&&n.jsx("button",{type:"button","aria-label":`Remove ${y} from mosaic`,title:"Remove from mosaic",onClick:p,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-red-600/70 transition-colors text-base leading-none",children:"×"})}),h&&n.jsxs("div",{className:"flex flex-col items-center gap-1.5 my-auto",children:[(o||r)&&n.jsx("button",{type:"button",title:`Home${s?` (${s})`:""}`,onClick:d,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/15 transition-colors text-sm",children:"⌂"}),o&&n.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:"‹"}),o&&n.jsx("button",{type:"button",title:"Recent apps",onClick:j,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/15 transition-colors text-sm",children:"▢"})]}),h&&n.jsx("div",{className:"flex flex-col items-center gap-1",children:n.jsx("button",{type:"button",title:"Screenshot",onClick:c,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/15 transition-colors text-sm",children:"⎙"})})]})}const mt={1:"1fr","2x1":"1fr 1fr","2x2":"1fr 1fr","3x2":"1fr 1fr 1fr"},gt={1:"1fr","2x1":"1fr","2x2":"1fr 1fr","3x2":"1fr 1fr"},xt={1:1,"2x1":2,"2x2":4,"3x2":6};function bt({layout:e,tiles:t,annotateMode:r,shape:o,color:s,onAnnotation:p,overlayAnnotationsByRecording:d,onOverlayAnnotationsChange:N,onRemove:j,onDropDevice:c}){const y=_e(e,t.length),h=xt[y];t.length;const v=t.length,b=e==="auto"?Math.max(v,1):h,f=Math.max(0,b-v);return n.jsxs("div",{style:{display:"grid",gap:8,gridTemplateColumns:mt[y],gridTemplateRows:gt[y]},className:"h-full",children:[t.map(g=>n.jsx(ft,{udid:g.udid,name:g.name,mjpegPort:g.mjpegPort,recordingId:g.recordingId,aspect:g.aspect,screenWidth:g.screenWidth,screenHeight:g.screenHeight,platform:g.platform,annotateMode:r,shape:o,color:s,overlayAnnotations:g.recordingId?d==null?void 0:d[g.recordingId]:void 0,onOverlayAnnotationsChange:g.recordingId&&N?I=>N(g.recordingId,I):void 0,onAnnotation:p,onRemove:j},g.udid)),Array.from({length:f},(g,I)=>n.jsx(yt,{onDropDevice:c},`empty-${I}`))]})}function yt({onDropDevice:e}){const[t,r]=l.useState(!1);return n.jsxs("div",{onDragOver:o=>{e&&(o.preventDefault(),o.dataTransfer.dropEffect="copy",t||r(!0))},onDragLeave:()=>r(!1),onDrop:o=>{if(r(!1),!e)return;o.preventDefault();const s=o.dataTransfer.getData("application/x-xenon-udid")||o.dataTransfer.getData("text/plain");s&&e(s)},className:`flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed transition-colors ${t?"border-[rgb(var(--rgb-accent)/0.6)] bg-[rgb(var(--rgb-accent)/0.05)]":"border-[var(--border)] bg-[rgb(var(--rgb-white)/0.02)]"} text-[var(--text-dim)] min-h-[120px]`,children:[n.jsx("div",{"aria-hidden":"true",className:`w-8 h-8 rounded-full flex items-center justify-center text-xl ${t?"bg-[rgb(var(--rgb-accent)/0.15)] text-[var(--color-accent-soft)]":"bg-[rgb(var(--rgb-fg)/0.05)] text-[rgb(var(--rgb-fg)/0.6)]"}`,children:"+"}),n.jsx("div",{className:"text-xs font-medium",children:"Drag device here"}),n.jsx("div",{className:"text-[10px] opacity-70",children:"or click one in the panel"})]})}const V="/xenon/api/recordings";class me extends Error{constructor(t){super(t.message??t.error),this.body=t}}async function Z(e,t){const r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:t?JSON.stringify(t):void 0});if(r.status===409)throw new me(await r.json());if(!r.ok)throw new Error(`HTTP ${r.status}`);return r.json()}function vt(e,t){return Z(V,{udids:e,...t})}function wt(e){return Z(`${V}/${encodeURIComponent(e)}/stop`)}function he(e,t){return Z(`${V}/${encodeURIComponent(e)}/annotation`,t)}function Et(e,t){return Z(`${V}/${encodeURIComponent(e)}/annotations/clear`,{timecodeMs:t})}async function Nt(){const e=await fetch(`${V}/active`);if(!e.ok)throw new Error(`HTTP ${e.status}`);return e.json()}function Rt(e){return`${V}/${encodeURIComponent(e)}/videos.zip`}function St(e,t){return`${V}/${encodeURIComponent(e)}/video.mp4`}function Tt(e){return`${V}/${encodeURIComponent(e)}/composite.mp4`}function jt(e,t){return t==="starting"?"Starting…":e<=0?"Record":e===1?"● Record":`● Record ${e} devices`}function Ct({selectedUdids:e,onClearMarks:t}){const{state:r,dispatch:o}=Pe(),[s,p]=l.useState(()=>Date.now());l.useEffect(()=>{if(r.recordingPhase!=="recording"&&r.recordingPhase!=="stopping")return;p(Date.now());const x=window.setInterval(()=>p(Date.now()),250);return()=>window.clearInterval(x)},[r.recordingPhase]);const d=r.startedAt&&(r.recordingPhase==="recording"||r.recordingPhase==="stopping")?s-r.startedAt:0,N=r.recordingPhase==="starting"||r.recordingPhase==="stopping",j=r.recordingPhase==="idle"&&e.length>0&&!N,c=r.recordingPhase==="recording"||r.recording&&r.tiles.some(x=>!!x.recordingId),y=c&&!N,h=y,v=h&&r.annotateMode,b=!!r.groupId&&r.recordingPhase==="idle",f=e.length>1||r.downloadableVideoCount>1,g=b&&r.compositeEnabled,I=b&&!r.compositeEnabled&&(r.downloadableVideoCount===1||r.downloadableVideoCount===0&&e.length<=1),D=async()=>{var x;if(j){o({type:"SET_RECORDING_PHASE",phase:"starting"}),o({type:"SET_BANNER",banner:null});try{const S=await vt(e);if(!((x=S.recordings)!=null&&x.length)){o({type:"SET_RECORDING_PHASE",phase:"idle"}),o({type:"SET_BANNER",banner:{tone:"error",message:"Recording did not start. Check that live preview is working, then try again."}});return}const _={};for(const u of S.recordings)_[u.udid]=u.id;const M=e.length-S.recordings.length;o({type:"START_RECORDING",groupId:S.groupId,startedAt:Date.now(),tileIds:_,compositeEnabled:S.compositeEnabled===!0}),M>0?o({type:"SET_BANNER",banner:{tone:"error",message:`Recording started on ${S.recordings.length} of ${e.length} devices (${M} failed).`}}):o({type:"SET_BANNER",banner:null})}catch(S){if(o({type:"SET_RECORDING_PHASE",phase:"idle"}),S instanceof me)if(S.body.error==="device_busy"){const _=(S.body.busyDevices??[]).map(M=>`${M.udid} (${M.reason})`).join(", ");o({type:"SET_BANNER",banner:{tone:"error",message:`Cannot start — these devices are busy: ${_}`}})}else o({type:"SET_BANNER",banner:{tone:"error",message:`Server-wide recording cap reached (${S.body.active}/${S.body.limit}).`}});else o({type:"SET_BANNER",banner:{tone:"error",message:`Recording failed: ${S.message}`}})}}},k=async()=>{if(!(!r.groupId||r.recordingPhase!=="recording")){o({type:"SET_RECORDING_PHASE",phase:"stopping"});try{const x=await wt(r.groupId),S=(x.recordings??[]).filter(M=>M.status==="STOPPED"&&(M.sizeBytes??0)>=1024),_=(x.recordings??[]).filter(M=>M.status!=="STOPPED");o({type:"STOP_RECORDING",downloadableVideoCount:S.length,compositeEnabled:r.compositeEnabled}),S.length===0?o({type:"SET_BANNER",banner:{tone:"error",message:_.length>0?`Recording failed on ${_.length} device(s). Nothing to download.`:"Recording stopped but no playable video was produced."}}):_.length>0?o({type:"SET_BANNER",banner:{tone:"info",message:`${S.length} video(s) ready · ${_.length} failed. Use Download below.`}}):o({type:"SET_BANNER",banner:{tone:"info",message:S.length===1?"Video ready — download below.":`${S.length} videos ready — download below.`}})}catch(x){o({type:"STOP_RECORDING"}),o({type:"SET_BANNER",banner:{tone:"error",message:`Stop failed: ${x.message}`}})}}},i=()=>{h&&o({type:"SET_ANNOTATE_MODE",enabled:!r.annotateMode})},m=()=>{h&&t()},w=Object.values(r.overlayAnnotations).some(x=>((x==null?void 0:x.length)??0)>0),A=[{id:"RECT",label:"Rect"},{id:"CIRCLE",label:"Circle"},{id:"ARROW",label:"Arrow"},{id:"FREEHAND",label:"Draw"}];return n.jsxs("div",{className:"flex items-center gap-2 flex-wrap justify-end",children:[(r.recordingPhase==="recording"||r.recordingPhase==="stopping")&&n.jsxs("span",{className:"inline-flex items-center gap-1.5 px-2 py-1 rounded text-xs font-mono tabular-nums bg-[rgb(var(--rgb-red-600)/0.2)] text-[var(--red-200)] border border-[rgb(var(--rgb-red)/0.4)]",title:"Recording elapsed time",children:[n.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-[var(--red)] animate-pulse"}),"REC ",Be(d),r.recordingPhase==="stopping"?" · Stopping…":""]}),n.jsx("button",{type:"button",onClick:D,disabled:!j,title:e.length===0?"Add a device to the mosaic first":"Record every device currently in the mosaic",className:"px-3 py-1.5 text-sm rounded bg-[var(--red-600)] text-white disabled:opacity-40 disabled:cursor-not-allowed light:disabled:bg-transparent light:disabled:text-[var(--text)] light:disabled:ring-1 light:disabled:ring-inset light:disabled:ring-[color:var(--border)]",children:jt(e.length,r.recordingPhase)}),n.jsx("button",{type:"button",onClick:k,disabled:!y,className:"px-3 py-1.5 text-sm rounded border border-[var(--border)] disabled:opacity-40 disabled:cursor-not-allowed",children:r.recordingPhase==="stopping"?"Stopping…":"⏹ Stop"}),n.jsx("button",{type:"button",onClick:i,disabled:!h,title:h?r.annotateMode?"Exit annotate mode (tap/swipe resumes)":"Draw on the live screen — saved with the recording":"Start recording to annotate",className:`px-3 py-1.5 text-sm rounded border ${r.annotateMode?"bg-[var(--color-info)] text-white border-transparent":"border-[var(--border)]"} disabled:opacity-40 disabled:cursor-not-allowed`,children:"✎ Annotate"}),c&&n.jsxs("div",{className:"flex items-center gap-1 ml-1 pl-2 border-l border-[var(--border)]",children:[A.map(x=>n.jsx("button",{type:"button",disabled:!v,"aria-pressed":r.shape===x.id,onClick:()=>o({type:"SET_SHAPE",shape:x.id}),className:`px-2 py-1 text-xs rounded border disabled:opacity-40 disabled:cursor-not-allowed ${r.shape===x.id?"bg-[var(--surface-2)] border-[var(--color-info)] text-[rgb(var(--rgb-fg))]":"border-[var(--border)] opacity-80 hover:opacity-100"}`,children:x.label},x.id)),n.jsx("input",{type:"color",value:r.color,disabled:!v,onChange:x=>o({type:"SET_COLOR",color:x.target.value}),title:"Annotation color","aria-label":"Annotation color",className:"w-7 h-7 rounded border border-[var(--border)] bg-transparent cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed"}),n.jsx("button",{type:"button",onClick:m,disabled:!h||!w,title:"Clear marks from the preview and the recording",className:"px-2 py-1 text-xs rounded border border-[var(--border)] hover:bg-[var(--surface-2)] disabled:opacity-40 disabled:cursor-not-allowed",children:"Clear marks"})]}),b&&I&&n.jsx("a",{href:St(r.groupId),className:"ml-1 px-3 py-1.5 text-sm rounded border border-[var(--border)] hover:bg-[var(--surface-2)]",download:!0,children:"⤓ Download video"}),b&&!I&&n.jsxs("a",{href:Rt(r.groupId),className:"ml-1 px-3 py-1.5 text-sm rounded border border-[var(--border)] hover:bg-[var(--surface-2)]",download:`videos-${r.groupId}.zip`,title:"ZIP of mp4 files only (no JSON extras)",children:["⤓ Download ",f?"videos":"video"]}),g&&n.jsx("a",{href:Tt(r.groupId),className:"px-3 py-1.5 text-sm rounded border border-[var(--border)] hover:bg-[var(--surface-2)]",download:`side-by-side-${r.groupId}.mp4`,title:"All devices in one side-by-side video",children:"⤓ Side-by-side"})]})}function At({remainingSec:e,onContinue:t,onReleaseNow:r}){return n.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:n.jsxs("div",{className:"bg-[var(--bg)] border border-[var(--border)] rounded-lg w-full max-w-md p-6",children:[n.jsx("h3",{id:"idle-warning-title",className:"text-base font-semibold mb-2",children:"Are you still there?"}),n.jsxs("p",{className:"text-sm text-[var(--text-dim)] mb-4",children:["Your devices will be released in"," ",n.jsxs("span",{className:"text-[var(--text)] font-mono font-semibold",children:[e,"s"]})," due to inactivity. Click ",n.jsx("strong",{children:"Continue Session"})," to keep them locked."]}),n.jsxs("div",{className:"flex justify-end gap-2",children:[n.jsx("button",{type:"button",onClick:r,className:"h-9 px-3 rounded-md border border-[var(--border)] text-sm text-[var(--text-dim)] hover:text-[var(--text)]",children:"Release now"}),n.jsx("button",{type:"button",autoFocus:!0,onClick:t,className:"h-9 px-4 rounded-md bg-[var(--color-accent)] text-[var(--color-on-accent)] text-sm font-medium",children:"Continue session"})]})]})})}function ge(e,t,r){return!r||!e?!1:e===`manual_${r}_${t}`}function It(e,t){return e.busy?ge(e.session_id,e.udid,t):!1}function Dt(){let e=Promise.resolve();return{enqueue(t){const r=e.then(t,t);return e=r.then(()=>{},()=>{}),r}}}const fe=["mousemove","mousedown","keydown","touchstart","wheel"];function Mt({idleAfterMs:e,warningSec:t,onWarning:r,onTimeout:o,enabled:s}){const[p,d]=l.useState(!1),[N,j]=l.useState(null),c=l.useRef(null),y=l.useRef(null),h=l.useRef(!1),v=l.useRef(r),b=l.useRef(o);v.current=r,b.current=o;function f(){c.current&&(clearTimeout(c.current),c.current=null),y.current&&(clearInterval(y.current),y.current=null)}function g(){h.current=!0,d(!0),j(t),v.current(t);let k=t;y.current=setInterval(()=>{k-=1,k<=0?(f(),h.current=!1,d(!1),j(null),b.current()):j(k)},1e3)}function I(){f(),h.current=!1,d(!1),j(null);const k=e-t*1e3;c.current=setTimeout(g,Math.max(0,k))}function D(){s&&I()}return l.useEffect(()=>{if(!s){f(),h.current=!1,d(!1),j(null);return}function k(){h.current||I()}return fe.forEach(i=>window.addEventListener(i,k,{passive:!0})),I(),()=>{fe.forEach(i=>window.removeEventListener(i,k)),f()}},[s,e,t]),{warning:p,remainingSec:N,reset:D}}const kt=5*60*1e3,_t=30;function te(e){if(!e)return"9 / 16";const t=Number(e.screenWidth),r=Number(e.screenHeight);if(Number.isFinite(t)&&Number.isFinite(r)&&t>0&&r>0)return`${t} / ${r}`;const o=(e.platform||"").toLowerCase();return o==="tvos"||o==="androidtv"||o==="android-tv"?"16 / 9":"9 / 16"}function Ot(e,t){if(e.busy)return e.session_id?e.session_id.startsWith("manual_")?ge(e.session_id,e.udid,t)?"manual_self":"manual_other":"automation":"unknown"}function $t(e,t){return{udid:e.udid,name:e.name,platform:e.platform,busy:e.busy,busyReason:Ot(e,t),mjpegServerPort:e.mjpegServerPort,offline:!!e.offline}}function Ut(){const[e,t]=Le(),[r,o]=l.useState([]),[s,p]=l.useState(0),[d,N]=l.useState(null),j=l.useRef(!1),c=l.useRef(Dt());l.useEffect(()=>{let i=!1;return fetch("/xenon/api/auth/me",{credentials:"include"}).then(m=>m.ok?m.json():null).then(m=>{!i&&(m!=null&&m.userId)&&N(m.userId)}).catch(()=>{}),()=>{i=!0}},[]),l.useEffect(()=>{let i=!1;const m=async()=>{try{const A=await P.getDevices();i||o(Array.isArray(A)?A:[])}catch{}};m();const w=window.setInterval(m,5e3);return()=>{i=!0,window.clearInterval(w)}},[s]),l.useEffect(()=>{let i=!1;if(!d||j.current)return;j.current=!0;const m=w=>{const A=Number(w.screenWidth),x=Number(w.screenHeight);return{udid:w.udid,name:w.name,mjpegPort:0,aspect:te(w),screenWidth:Number.isFinite(A)&&A>0?A:void 0,screenHeight:Number.isFinite(x)&&x>0?x:void 0,platform:w.platform}};return(async()=>{var x;let w=[],A=[];try{const S=await P.getDevices();w=Array.isArray(S)?S:[];const _=w.filter(u=>It(u,d));if(A=(await Promise.all(_.map(async u=>{try{const E=await fetch(`/xenon/api/control/${encodeURIComponent(u.udid)}/stream/status`);if(!E.ok)return null;const C=await E.json();return(C==null?void 0:C.status)==="running"?u:null}catch{return null}}))).filter(u=>u!==null).map(m),i)return;A.length>0&&t({type:"SET_TILES",tiles:A})}catch{}try{const{serverNow:S,groups:_}=await Nt(),M=_[0];if(i||!M)return;const u=new Set(A.map(T=>T.udid)),E=M.recordings.filter(T=>!u.has(T.udid)).map(T=>w.find(L=>L.udid===T.udid)).filter(T=>!!T).map(m);E.length>0&&t({type:"SET_TILES",tiles:[...A,...E]});const C={};for(const T of M.annotations){let L;try{L=JSON.parse(T.geometry)}catch{continue}(C[x=T.recordingId]??(C[x]=[])).push({shape:T.shape,color:T.color,geometry:L,text:T.text??void 0})}t({type:"REHYDRATE_RECORDING",groupId:M.groupId,startedAt:Date.now()-(S-Date.parse(M.startedAt)),tileIds:Object.fromEntries(M.recordings.map(T=>[T.udid,T.id])),compositeEnabled:M.compositeEnabled,overlayAnnotations:C})}catch{}})(),()=>{i=!0}},[d,t]),l.useEffect(()=>{for(const i of e.tiles){if(i.screenWidth&&i.screenHeight)continue;const m=r.find(x=>x.udid===i.udid);if(!m)continue;const w=Number(m.screenWidth),A=Number(m.screenHeight);Number.isFinite(w)&&w>0&&Number.isFinite(A)&&A>0&&t({type:"PATCH_TILE_DIMS",udid:i.udid,screenWidth:w,screenHeight:A,aspect:te(m)})}},[r,e.tiles,t]);const y=l.useMemo(()=>new Set(e.tiles.map(i=>i.udid)),[e.tiles]),h=l.useMemo(()=>r.map(i=>$t(i,d)),[r,d]),v=i=>{const m=Number(i.screenWidth),w=Number(i.screenHeight);return{udid:i.udid,name:i.name,mjpegPort:0,aspect:te(i),screenWidth:Number.isFinite(m)&&m>0?m:void 0,screenHeight:Number.isFinite(w)&&w>0?w:void 0,platform:i.platform}},b=async i=>{if(e.tiles.some(x=>x.udid===i)){await f(i);return}if(e.recording){t({type:"SET_ERROR_BANNER",message:"Stop recording before changing the mosaic layout."});return}const w=r.find(x=>x.udid===i);if(!w)return;t({type:"ADD_TILE",tile:v(w)});const A=await P.startStream(i).catch(()=>null);Ie(A)&&t({type:"REMOVE_TILE",udid:i})},f=async i=>{t({type:"REMOVE_TILE",udid:i});try{await fetch(`/xenon/api/control/${encodeURIComponent(i)}/stream/stop`,{method:"POST"})}catch{}p(m=>m+1)},g=(i,m,w)=>{if(!e.groupId)return;const A=e.groupId,x=e.startedAt?Date.now()-e.startedAt:0,S={recordingId:i,timecodeMs:x,shape:m.shape,geometry:JSON.stringify(m.geometry),color:m.color,text:m.text};c.current.enqueue(async()=>{try{return await he(A,w?{...S,image:w}:S)}catch(_){if(!w)throw _;return he(A,S)}}).catch(_=>t({type:"SET_ERROR_BANNER",message:`Annotation failed: ${_.message}`}))},I=()=>{if(!e.groupId)return;const i=e.groupId,m=e.startedAt?Date.now()-e.startedAt:0;t({type:"CLEAR_OVERLAY_ANNOTATIONS"}),c.current.enqueue(()=>Et(i,m)).catch(()=>t({type:"SET_ERROR_BANNER",message:"Couldn't clear marks from the recording. They will still appear in the video."}))},D=()=>{for(const i of e.tiles){const m=`/xenon/api/control/${encodeURIComponent(i.udid)}/stream/stop`;try{navigator.sendBeacon?navigator.sendBeacon(m):fetch(m,{method:"POST",keepalive:!0}).catch(()=>{})}catch{}}e.tiles.forEach(i=>t({type:"REMOVE_TILE",udid:i.udid}))},k=Mt({idleAfterMs:kt,warningSec:_t,enabled:e.tiles.length>0,onWarning:()=>{},onTimeout:()=>{D()}});return n.jsxs(pe.Provider,{value:{state:e,dispatch:t},children:[n.jsxs("div",{className:"flex flex-col h-full p-4 gap-3",children:[n.jsxs("header",{className:"flex items-center justify-between gap-3",children:[n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx(ke,{icon:Ae,children:"Live devices"}),n.jsx(Ve,{value:e.layout,onChange:i=>t({type:"SET_LAYOUT",layout:i})})]}),n.jsx(Ct,{selectedUdids:e.tiles.map(i=>i.udid),onClearMarks:I})]}),e.banner&&n.jsxs("div",{role:"alert",className:`text-sm rounded border px-3 py-2 flex items-center gap-2 ${e.banner.tone==="error"?"border-[rgb(var(--rgb-red)/0.4)] bg-[rgb(var(--rgb-red)/0.1)] text-[var(--red-100)]":"border-[rgb(var(--rgb-success)/0.4)] bg-[rgb(var(--rgb-success)/0.1)] text-[var(--text)]"}`,children:[n.jsx("span",{className:"flex-1",children:e.banner.message}),n.jsx("button",{type:"button",className:"text-xs opacity-80 hover:opacity-100",onClick:()=>t({type:"SET_BANNER",banner:null}),children:"Dismiss"})]}),n.jsxs("div",{className:"grid grid-cols-[260px_1fr] gap-3 flex-1 min-h-0",children:[n.jsxs("aside",{className:"flex flex-col gap-2 border border-[var(--border)] rounded p-3 overflow-y-auto min-h-0",children:[n.jsxs("div",{className:"flex items-center justify-between",children:[n.jsx("span",{className:"text-xs text-[var(--text-dim)]",children:"Devices"}),n.jsx("button",{className:"text-xs underline opacity-70 hover:opacity-100",onClick:()=>p(i=>i+1),children:"Refresh"})]}),n.jsx(Fe,{devices:h,inMosaic:y,onToggle:b}),n.jsx("p",{className:"mt-2 text-[11px] text-[var(--text-dim)] leading-relaxed",children:"Click a device to add it to the mosaic. Record captures every device in the mosaic. After Stop, download the video (mp4) — not a proof bundle."})]}),n.jsx("main",{className:"border border-[var(--border)] rounded p-2 overflow-y-auto h-full min-h-0",children:n.jsx(bt,{layout:e.layout,tiles:e.tiles,annotateMode:e.annotateMode,shape:e.shape,color:e.color,overlayAnnotationsByRecording:e.overlayAnnotations,onOverlayAnnotationsChange:(i,m)=>t({type:"SET_OVERLAY_ANNOTATIONS",recordingId:i,annotations:m}),onAnnotation:g,onRemove:f,onDropDevice:b})})]})]}),k.warning&&k.remainingSec!==null&&n.jsx(At,{remainingSec:k.remainingSec,onContinue:k.reset,onReleaseNow:D})]})}export{Ut as default};