@xenon-device-management/xenon 2.9.2 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/package.json +1 -1
- package/lib/public/assets/{AnnotationOverlay-B87n4pUu.js → AnnotationOverlay-LzfXiiZJ.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-CNZJH6Cs.js → ApiKeyGate-BzFDp4mK.js} +1 -1
- package/lib/public/assets/{BugReportButton-DQ-R87ZV.js → BugReportButton-BpgWvmZo.js} +1 -1
- package/lib/public/assets/{DeviceMosaicView-dX3e7O-w.js → DeviceMosaicView-CCB-egSH.js} +2 -2
- package/lib/public/assets/{EmptyState-D3fG2GHY.js → EmptyState-DkUVmANo.js} +1 -1
- package/lib/public/assets/{FieldGroup-CUmbJZxI.js → FieldGroup-mbfnfTql.js} +1 -1
- package/lib/public/assets/{FilterMenu-Dm2c-UCk.js → FilterMenu-BH5usz4Y.js} +1 -1
- package/lib/public/assets/{Menu-Dl4uXMkH.js → Menu-Cbo9mdTk.js} +1 -1
- package/lib/public/assets/{Modal-DNmn2RDs.js → Modal-D6XfQHPr.js} +1 -1
- package/lib/public/assets/{RecordingPage-Dn3By34o.js → RecordingPage-CmFPkWqH.js} +1 -1
- package/lib/public/assets/{RecordingsPage-lCUIM4pm.js → RecordingsPage-CSlKk2qC.js} +1 -1
- package/lib/public/assets/{SegmentedControl-BdA_mHiZ.js → SegmentedControl-BFW9Dlqd.js} +1 -1
- package/lib/public/assets/{SettingCard-Dx3WhK7w.js → SettingCard-C4DioTTi.js} +1 -1
- package/lib/public/assets/{Table-D_2lhvRM.js → Table-DrKjlL00.js} +1 -1
- package/lib/public/assets/{activity-cNHHOKx5.js → activity-C9bE7Jm8.js} +1 -1
- package/lib/public/assets/{ai-settings-t6RMrDkg.js → ai-settings-DRYic4oO.js} +1 -1
- package/lib/public/assets/{api-keys-CK_qyQvW.js → api-keys-CaQU0g7c.js} +1 -1
- package/lib/public/assets/{apps-DCiI3UOb.js → apps-c1gUFmmK.js} +1 -1
- package/lib/public/assets/{arrow-left-CnYs8HVw.js → arrow-left-DMvAWA8r.js} +1 -1
- package/lib/public/assets/{arrow-right-Cc9qm6UG.js → arrow-right-BEQgxmKT.js} +1 -1
- package/lib/public/assets/{arrow-up-right-e7yBOb-c.js → arrow-up-right-Ba0e2GeU.js} +1 -1
- package/lib/public/assets/{auth-shell-DKrtcIjG.js → auth-shell-COODQnc7.js} +2 -2
- package/lib/public/assets/{builds-page-CRd5oEz6.js → builds-page-DZQgWBUs.js} +4 -4
- package/lib/public/assets/{button-C0caCjJB.js → button-WW9j43wR.js} +1 -1
- package/lib/public/assets/{calendar-BqOOkln_.js → calendar-COUs9VxF.js} +1 -1
- package/lib/public/assets/{check-DYSXRG79.js → check-D8ifHIJR.js} +1 -1
- package/lib/public/assets/{chevron-right-hCNu0CA-.js → chevron-right-BjKVUlB_.js} +1 -1
- package/lib/public/assets/{circle-check-DnQ263fa.js → circle-check-CfP79QE2.js} +1 -1
- package/lib/public/assets/{circle-x-BaRKZyz9.js → circle-x-BZbFCixZ.js} +1 -1
- package/lib/public/assets/{clock-XP3iBAhg.js → clock-BuTm35Qg.js} +1 -1
- package/lib/public/assets/{copy-D8d9UzWE.js → copy-W00zUBwN.js} +1 -1
- package/lib/public/assets/{device-explorer-pdM3L9TC.js → device-explorer-BEpS-Y-_.js} +5 -5
- package/lib/public/assets/{download-SaN9-umX.js → download-CGz-MS3y.js} +1 -1
- package/lib/public/assets/{forgot-password-DfUhEZW5.js → forgot-password-DG7SqVgU.js} +1 -1
- package/lib/public/assets/{index-C8pvxG1f.css → index-DQa193-A.css} +1 -1
- package/lib/public/assets/{index-IefGCFQQ.js → index-DWHTp1YP.js} +1 -1
- package/lib/public/assets/{index-CzaIyL9t.js → index-EkaQM1DK.js} +2 -2
- package/lib/public/assets/{input-BqFzAQT3.js → input-ChaTXeCq.js} +1 -1
- package/lib/public/assets/{keycodes-DDV2OlpQ.js → keycodes-BhbgeW_r.js} +10 -5
- package/lib/public/assets/line-chart-9P3RxScy.js +1 -0
- package/lib/public/assets/list-checks-_23IIEEL.js +11 -0
- package/lib/public/assets/{lock-Eye1CG8p.js → lock-B2JaqqRI.js} +1 -1
- package/lib/public/assets/{login-Co1IXuen.js → login-BEWIfLfC.js} +1 -1
- package/lib/public/assets/{maintenance-settings-D1fw6iqm.js → maintenance-settings-BGJIJQ-k.js} +1 -1
- package/lib/public/assets/{monitor-U5Y-PxW5.js → monitor-BrwMY4G0.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-CDAwphUm.js → mouse-pointer-2-DsWDMyrY.js} +1 -1
- package/lib/public/assets/{network-BP_U4iDZ.js → network-DfIhNVJv.js} +1 -1
- package/lib/public/assets/overview-B1mePY2_.js +6 -0
- package/lib/public/assets/{page-header-ZdzNEJp9.js → page-header-DoD3TOzZ.js} +1 -1
- package/lib/public/assets/{plus-1k0sxXyU.js → plus-BGvWvpBB.js} +1 -1
- package/lib/public/assets/{profile-page-B_GWKRy3.js → profile-page-CvV5aWvl.js} +1 -1
- package/lib/public/assets/{reset-password-DizCTkdw.js → reset-password-CGVd31LR.js} +1 -1
- package/lib/public/assets/{runbook-page-DWzRiOLZ.js → runbook-page-Bq2cvwb5.js} +1 -1
- package/lib/public/assets/{select-CiRW6v17.js → select-CTcD4jWR.js} +1 -1
- package/lib/public/assets/selector-detail-redirect-19odlD4B.js +1 -0
- package/lib/public/assets/selector-health-page-LqIsLrjW.js +11 -0
- package/lib/public/assets/session-detail-page-DQzb8_fq.js +4 -0
- package/lib/public/assets/{settings-DhwZoWv7.js → settings-C-EnGIqs.js} +1 -1
- package/lib/public/assets/stat-tile-BY0RDTRm.js +1 -0
- package/lib/public/assets/{tablet-CPJV3dBq.js → tablet-B4cFM-5B.js} +1 -1
- package/lib/public/assets/{teams-D0IBa2Ov.js → teams-BFe-RpYd.js} +1 -1
- package/lib/public/assets/time-D-gjmE1o.js +1 -0
- package/lib/public/assets/{trash-2-u8MlXUBD.js → trash-2-CJCKBOP9.js} +1 -1
- package/lib/public/assets/{upload-Cq8L3kpw.js → upload-DidWkbld.js} +1 -1
- package/lib/public/assets/use-builds-data-fnfxatOd.js +8 -0
- package/lib/public/assets/{use-password-reset-mode-Dbr2HEfw.js → use-password-reset-mode-iPen3tmC.js} +1 -1
- package/lib/public/assets/{users-CZqCE511.js → users-CrcjmRdf.js} +1 -1
- package/lib/public/assets/{users-DxLHYmFm.js → users-DBUv0CJx.js} +1 -1
- package/lib/public/assets/{video-off-mh1EEzP9.js → video-off-eh69-FKB.js} +1 -1
- package/lib/public/assets/view-state-CP9dE1UI.js +1 -0
- package/lib/public/assets/{webhook-settings-BYuOib-Q.js → webhook-settings-CQoSshBn.js} +1 -1
- package/lib/public/assets/{zap-BOpUur0M.js → zap-CICtWiLn.js} +1 -1
- package/lib/public/index.html +2 -2
- package/lib/schema.json +5 -0
- package/lib/src/app/routers/dashboard.js +86 -40
- package/lib/src/app/routers/selector-health.js +75 -0
- package/lib/src/app/swagger-docs.js +77 -8
- package/lib/src/dashboard/event-manager.js +15 -99
- package/lib/src/generated/client/edge.js +27 -3
- package/lib/src/generated/client/index-browser.js +24 -0
- package/lib/src/generated/client/index.d.ts +8207 -5283
- package/lib/src/generated/client/index.js +27 -3
- package/lib/src/generated/client/package.json +1 -1
- package/lib/src/generated/client/schema.prisma +43 -9
- package/lib/src/generated/client/wasm.js +24 -0
- package/lib/src/prisma.js +2 -0
- package/lib/src/services/CleanupService.js +1 -0
- package/lib/src/services/NotificationService.js +3 -4
- package/lib/src/services/SelectorStateService.js +151 -113
- package/lib/src/services/SelectorVerificationJob.js +14 -9
- package/lib/src/services/metrics/AndroidMetricsSampler.js +94 -0
- package/lib/src/services/metrics/IOSMetricsSampler.js +120 -0
- package/lib/src/services/metrics/SessionMetricsService.js +238 -0
- package/lib/src/services/metrics/androidMetrics.js +94 -0
- package/lib/src/services/metrics/iosMetrics.js +26 -0
- package/lib/src/services/metrics/metricsBody.js +37 -0
- package/lib/src/services/metrics/types.js +19 -0
- package/lib/src/services/selector-health/access.js +66 -0
- package/lib/src/services/selector-health/healingTrend.js +49 -0
- package/lib/src/services/selector-health/people.js +27 -0
- package/lib/src/services/selector-health/selectorDetail.js +198 -0
- package/lib/src/services/selector-health/selectorKeys.js +36 -0
- package/lib/src/services/selector-health/selectorList.js +333 -0
- package/lib/src/services/selector-health/stateView.js +67 -0
- package/lib/test/helpers/scratch-database.js +2 -0
- package/lib/test/helpers/selector-health-fixture.js +216 -0
- package/lib/test/integration/team-visibility-sessions.spec.js +1 -0
- package/lib/test/unit/CleanupService.spec.js +28 -2
- package/lib/test/unit/dashboard-events-metrics.spec.js +146 -0
- package/lib/test/unit/healing-no-cost.spec.js +134 -0
- package/lib/test/unit/healing-state-endpoints.spec.js +70 -0
- package/lib/test/unit/healing-trend.spec.js +118 -0
- package/lib/test/unit/selector-event-model.spec.js +47 -0
- package/lib/test/unit/selector-health-actions.spec.js +106 -0
- package/lib/test/unit/selector-health-detail.spec.js +131 -0
- package/lib/test/unit/selector-health-list.spec.js +185 -0
- package/lib/test/unit/selector-state-service.spec.js +74 -1
- package/lib/test/unit/selector-verification-job.spec.js +38 -0
- package/lib/test/unit/session-metrics-parse.spec.js +138 -0
- package/lib/test/unit/session-metrics-route.spec.js +140 -0
- package/lib/test/unit/session-metrics-samplers.spec.js +248 -0
- package/lib/test/unit/session-metrics-service.spec.js +215 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/prisma/migrations/20261002120000_session_metric/migration.sql +17 -0
- package/prisma/migrations/20261002130000_selector_event/migration.sql +13 -0
- package/prisma/schema.prisma +43 -9
- package/schema.json +5 -0
- package/lib/public/assets/chevron-left-BSG6mcpo.js +0 -6
- package/lib/public/assets/funnel-OefHE-54.js +0 -6
- package/lib/public/assets/layers-a3GNz2DA.js +0 -6
- package/lib/public/assets/list-checks-DMrMAFDt.js +0 -6
- package/lib/public/assets/overview-CsPyPv10.js +0 -1
- package/lib/public/assets/selector-detail-page-pA913STP.js +0 -1
- package/lib/public/assets/selector-health-BlICzZlk.css +0 -1
- package/lib/public/assets/selector-health-CHLX8KqY.js +0 -16
- package/lib/public/assets/selector-health-page-vVkg8Mii.js +0 -16
- package/lib/public/assets/session-detail-page-BCD49zDi.js +0 -4
- package/lib/public/assets/stat-tile-BaE76cnK.js +0 -8
- package/lib/public/assets/time-B8FIgwPE.js +0 -1
- package/lib/src/profiling/AndroidAppProfiler.js +0 -213
- /package/lib/public/assets/{session-detail-page-Bzzc3bMl.css → line-chart-Bzzc3bMl.css} +0 -0
|
@@ -64,8 +64,6 @@ const asset_manager_1 = require("./asset-manager");
|
|
|
64
64
|
const commands_1 = require("./commands");
|
|
65
65
|
const SessionStatus_1 = require("../types/SessionStatus");
|
|
66
66
|
const appium_ios_device_1 = require("appium-ios-device");
|
|
67
|
-
const AndroidAppProfiler_1 = require("../profiling/AndroidAppProfiler");
|
|
68
|
-
const appium_adb_1 = require("appium-adb");
|
|
69
67
|
const config_1 = require("../config");
|
|
70
68
|
const helpers_2 = require("../helpers");
|
|
71
69
|
const typedi_1 = require("typedi");
|
|
@@ -80,6 +78,7 @@ const SocketEvents_1 = require("../enums/SocketEvents");
|
|
|
80
78
|
const types_1 = require("../services/healing/types");
|
|
81
79
|
const SelectorStateService_1 = require("../services/SelectorStateService");
|
|
82
80
|
const recording_store_1 = require("../services/recording/recording-store");
|
|
81
|
+
const SessionMetricsService_1 = require("../services/metrics/SessionMetricsService");
|
|
83
82
|
const typedi_2 = require("typedi");
|
|
84
83
|
/**
|
|
85
84
|
* What a Session row keeps of the capabilities the driver returned. The row
|
|
@@ -105,8 +104,6 @@ let DashboardEventManager = class DashboardEventManager {
|
|
|
105
104
|
this.sessionToUdid = new Map();
|
|
106
105
|
// Map session ID to device info
|
|
107
106
|
this.sessionToDevice = new Map();
|
|
108
|
-
// Store app profilers for Android sessions
|
|
109
|
-
this.appProfilers = new Map();
|
|
110
107
|
// Track last log line for each session (for device logs)
|
|
111
108
|
this.lastLogLine = new Map();
|
|
112
109
|
// Track start time for each command to calculate duration
|
|
@@ -124,8 +121,10 @@ let DashboardEventManager = class DashboardEventManager {
|
|
|
124
121
|
};
|
|
125
122
|
// create directory to store screenshots, videos and log files for the session
|
|
126
123
|
(0, asset_manager_1.prepareDirectory)(session.getId());
|
|
127
|
-
//
|
|
128
|
-
|
|
124
|
+
// CPU and memory for the session page's Performance panel; sampling
|
|
125
|
+
// starts once the session's row exists, since the samples point at it.
|
|
126
|
+
const metrics = typedi_1.Container.get(SessionMetricsService_1.SessionMetricsService);
|
|
127
|
+
const sampled = metrics.appliesTo(device);
|
|
129
128
|
// If iOS real device, start performance recording (Time Profiler)
|
|
130
129
|
// Note: This only works on real devices with XCUITest driver 4.5+
|
|
131
130
|
let isIosProfilingStarted = false;
|
|
@@ -149,10 +148,17 @@ let DashboardEventManager = class DashboardEventManager {
|
|
|
149
148
|
const sessionResponse = lodash_1.default.assign({}, session.getCapabilities());
|
|
150
149
|
const tracingService = typedi_1.Container.get(TracingService_1.TracingService);
|
|
151
150
|
const traceId = tracingService.getTraceId(session.getId());
|
|
152
|
-
const createData = Object.assign(Object.assign({ id: session.getId(), build: build.id ? { connect: { id: build.id } } : undefined, name: capabilities[XenonCapabilityManager_1.XENON_CAPABILITIES.SESSION_NAME] || undefined }, storedSessionCapabilities(sessionResponse)), { node_id: device.nodeId || '', has_live_video: session.getLiveVideoUrl() !== null, video_recording_enabled: capabilities[XenonCapabilityManager_1.XENON_CAPABILITIES.VIDEO_RECORDING] === true, is_profiling_available:
|
|
151
|
+
const createData = Object.assign(Object.assign({ id: session.getId(), build: build.id ? { connect: { id: build.id } } : undefined, name: capabilities[XenonCapabilityManager_1.XENON_CAPABILITIES.SESSION_NAME] || undefined }, storedSessionCapabilities(sessionResponse)), { node_id: device.nodeId || '', has_live_video: session.getLiveVideoUrl() !== null, video_recording_enabled: capabilities[XenonCapabilityManager_1.XENON_CAPABILITIES.VIDEO_RECORDING] === true, is_profiling_available: sampled || isIosProfilingStarted, device_udid: device.udid || '', device_platform: device.platform || '', device_version: device.sdk || '', device_name: device.name, trace_id: traceId, status: 'running', api_key_id: (_a = session.apiKeyId) !== null && _a !== void 0 ? _a : null, user_id: (_b = session.userId) !== null && _b !== void 0 ? _b : null });
|
|
153
152
|
yield prisma_1.prisma.session.create({
|
|
154
153
|
data: createData,
|
|
155
154
|
});
|
|
155
|
+
if (sampled) {
|
|
156
|
+
metrics.start({
|
|
157
|
+
sessionId: session.getId(),
|
|
158
|
+
device,
|
|
159
|
+
capabilities: session.getCapabilities(),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
156
162
|
// Emit session started event
|
|
157
163
|
void typedi_1.Container.get(SocketServer_1.SocketServer).emitToDashboardForDevices(SocketEvents_1.SocketEvents.SESSION_STARTED, Object.assign(Object.assign({}, createData), { status: 'running', build_name: buildName }), { udid: device.udid });
|
|
158
164
|
// Increment Metrics
|
|
@@ -198,14 +204,14 @@ let DashboardEventManager = class DashboardEventManager {
|
|
|
198
204
|
this.stoppingSessionIds.add(sessionId);
|
|
199
205
|
try {
|
|
200
206
|
logger_1.default.info(`🟢 onSessionStopped called for session ${sessionId}`);
|
|
207
|
+
// However the session ended, in memory or not, its sampler stops here.
|
|
208
|
+
yield typedi_1.Container.get(SessionMetricsService_1.SessionMetricsService).stop(sessionId);
|
|
201
209
|
// Video recording is now handled in plugin.ts deleteSession() before the session is deleted
|
|
202
210
|
// This ensures we can call stop_recording_screen while the session is still active
|
|
203
211
|
// Here we just handle the session status update
|
|
204
212
|
const session = SessionManager_1.SESSION_MANAGER.getSession(sessionId);
|
|
205
213
|
if (session) {
|
|
206
214
|
logger_1.default.info(`Session ${sessionId} found in SESSION_MANAGER`);
|
|
207
|
-
// Save Android profiling data before cleanup
|
|
208
|
-
yield this.saveAppProfilingData(sessionId);
|
|
209
215
|
// iOS profiling is now handled in plugin.ts deleteSession() before the session is deleted
|
|
210
216
|
// This ensures we can call mobile: stopPerfRecord while the driver is still alive
|
|
211
217
|
// Clean up syslog service for real iOS devices
|
|
@@ -689,96 +695,6 @@ let DashboardEventManager = class DashboardEventManager {
|
|
|
689
695
|
}
|
|
690
696
|
});
|
|
691
697
|
}
|
|
692
|
-
startAppProfiling(sessionId, device, sessionCapabilities) {
|
|
693
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
694
|
-
// Only support Android profiling
|
|
695
|
-
if (device.platform.toLowerCase() !== 'android') {
|
|
696
|
-
return { is_profiling_available: false };
|
|
697
|
-
}
|
|
698
|
-
// Check if we have an app package
|
|
699
|
-
const appPackage = (sessionCapabilities === null || sessionCapabilities === void 0 ? void 0 : sessionCapabilities.appPackage) || (sessionCapabilities === null || sessionCapabilities === void 0 ? void 0 : sessionCapabilities['appium:appPackage']);
|
|
700
|
-
if (!appPackage) {
|
|
701
|
-
logger_1.default.info(`[Profiling] No app package found for session ${sessionId}, skipping profiling`);
|
|
702
|
-
return { is_profiling_available: false };
|
|
703
|
-
}
|
|
704
|
-
try {
|
|
705
|
-
// Principal Intelligence: Use the Properly configured ADB from the manager
|
|
706
|
-
// instead of a naked instance, to avoid 'defaultArgs not iterable' crashes.
|
|
707
|
-
const deviceManager = typedi_1.Container.get(device_managers_1.XenonManager);
|
|
708
|
-
const androidManager = (yield deviceManager.deviceInstances()).find((m) => m instanceof AndroidDeviceManager_1.default);
|
|
709
|
-
let adb;
|
|
710
|
-
if (androidManager) {
|
|
711
|
-
adb = yield androidManager.getAdbForDevice(device.udid);
|
|
712
|
-
}
|
|
713
|
-
else {
|
|
714
|
-
adb = yield appium_adb_1.ADB.createADB({});
|
|
715
|
-
}
|
|
716
|
-
// Create app profiler
|
|
717
|
-
const profiler = new AndroidAppProfiler_1.AndroidAppProfiler({
|
|
718
|
-
adb,
|
|
719
|
-
deviceUDID: device.udid,
|
|
720
|
-
appPackage,
|
|
721
|
-
});
|
|
722
|
-
// Get device info and start capture
|
|
723
|
-
const device_info = yield profiler.getDeviceInfo();
|
|
724
|
-
yield profiler.startCapture();
|
|
725
|
-
// Store profiler for this session
|
|
726
|
-
this.appProfilers.set(sessionId, profiler);
|
|
727
|
-
logger_1.default.info(`[Profiling] Started app profiling for session ${sessionId}`);
|
|
728
|
-
return {
|
|
729
|
-
is_profiling_available: true,
|
|
730
|
-
device_info,
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
catch (err) {
|
|
734
|
-
logger_1.default.error(`[Profiling] Error initializing app profiler for session ${sessionId}: ${err.message}`);
|
|
735
|
-
return { is_profiling_available: false };
|
|
736
|
-
}
|
|
737
|
-
});
|
|
738
|
-
}
|
|
739
|
-
saveAppProfilingData(sessionId) {
|
|
740
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
741
|
-
const profiler = this.appProfilers.get(sessionId);
|
|
742
|
-
if (!profiler) {
|
|
743
|
-
return;
|
|
744
|
-
}
|
|
745
|
-
try {
|
|
746
|
-
// Stop capture
|
|
747
|
-
yield profiler.stopCapture();
|
|
748
|
-
// Get logs
|
|
749
|
-
const logs = profiler.getLogs();
|
|
750
|
-
if (logs.length === 0) {
|
|
751
|
-
logger_1.default.info(`[Profiling] No profiling data to save for session ${sessionId}`);
|
|
752
|
-
return;
|
|
753
|
-
}
|
|
754
|
-
// Save to database
|
|
755
|
-
const profilingEntries = logs.map((log) => ({
|
|
756
|
-
session_id: sessionId,
|
|
757
|
-
cpu: log.cpu,
|
|
758
|
-
memory: log.memory,
|
|
759
|
-
total_cpu_used: log.total_cpu_used.toString(),
|
|
760
|
-
total_memory_used: log.total_memory_used.toString(),
|
|
761
|
-
raw_cpu_log: log.raw_cpu_log,
|
|
762
|
-
raw_memory_log: log.raw_memory_log,
|
|
763
|
-
timestamp: new Date(log.timestamp),
|
|
764
|
-
}));
|
|
765
|
-
// Batch insert profiling data
|
|
766
|
-
for (const entry of profilingEntries) {
|
|
767
|
-
yield prisma_1.prisma.profiling.create({
|
|
768
|
-
data: entry,
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
logger_1.default.info(`[Profiling] Saved ${profilingEntries.length} profiling entries for session ${sessionId}`);
|
|
772
|
-
}
|
|
773
|
-
catch (err) {
|
|
774
|
-
logger_1.default.error(`[Profiling] Error saving profiling data for session ${sessionId}: ${err.message}`);
|
|
775
|
-
}
|
|
776
|
-
finally {
|
|
777
|
-
// Clean up profiler
|
|
778
|
-
this.appProfilers.delete(sessionId);
|
|
779
|
-
}
|
|
780
|
-
});
|
|
781
|
-
}
|
|
782
698
|
// ─── Recording (free-form mosaic) events ─────────────────────────────────
|
|
783
699
|
// Emitted by RecordingOrchestrator. Distinct namespace from SESSION_*; the
|
|
784
700
|
// dashboard listens to both independently. Team-scoped: a group's started
|