@xenon-device-management/xenon 1.1.17 → 1.1.18

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenon-device-management/xenon",
3
- "version": "1.1.17",
3
+ "version": "1.1.18",
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": {
@@ -140,6 +140,7 @@ const findPublicPath = () => {
140
140
  path_1.default.resolve(__dirname, '../public'), // Alternative structure
141
141
  path_1.default.resolve(__dirname, '../../public'),
142
142
  path_1.default.resolve(__dirname, '../../../public'),
143
+ path_1.default.resolve(__dirname, '../../../../public'),
143
144
  ];
144
145
  for (const p of searchPaths) {
145
146
  if (fs_1.default.existsSync(path_1.default.join(p, 'index.html'))) {
@@ -154,9 +155,16 @@ const findPublicPath = () => {
154
155
  };
155
156
  const publicPath = findPublicPath();
156
157
  logger_1.default.info(`[Xenon] Public assets path resolved to: ${publicPath}`);
157
- staticFilesRouter.use(express_1.default.static(publicPath));
158
+ // Principal Security: Add permissive CSP and CORS for the dashboard
159
+ router.use((req, res, next) => {
160
+ res.setHeader('Content-Security-Policy', "default-src * 'unsafe-inline' 'unsafe-eval' data: blob:; frame-ancestors 'self';");
161
+ res.setHeader('Access-Control-Allow-Origin', '*');
162
+ next();
163
+ });
164
+ staticFilesRouter.use(express_1.default.static(publicPath, { index: false }));
158
165
  router.use('/api', apiRouter);
159
- router.use('/assets', express_1.default.static(config_1.config.sessionAssetsPath));
166
+ // Principal Fix: Rename collision route from /assets to /session-recordings to avoid conflict with dashboard's /assets folder
167
+ router.use('/session-recordings', express_1.default.static(config_1.config.sessionAssetsPath));
160
168
  router.use(staticFilesRouter);
161
169
  function createRouter(pluginArgs) {
162
170
  dashboard_1.default.register(apiRouter);
@@ -184,17 +192,19 @@ function createRouter(pluginArgs) {
184
192
  });
185
193
  // Fallback route for client-side routing - serve index.html for all non-API routes
186
194
  // MUST be registered after Swagger to avoid interception
187
- router.get(/^(?!\/api).*/, (req, res) => {
195
+ router.get('*', (req, res) => {
188
196
  const indexPath = path_1.default.resolve(publicPath, 'index.html');
189
197
  const url = req.originalUrl || req.url;
198
+ // Skip if it's an API call that somehow reached here
199
+ if (url.includes('/api/'))
200
+ return res.status(404).json({ error: true, message: 'Not Found' });
190
201
  logger_1.default.debug(`[Xenon] UI Fallback triggered for: ${url}. Targeting: ${indexPath}`);
191
202
  if (fs_1.default.existsSync(indexPath)) {
203
+ res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
192
204
  res.sendFile(indexPath, (err) => {
193
- if (err) {
205
+ if (err && !res.headersSent) {
194
206
  logger_1.default.error(`[Xenon] res.sendFile failed for ${indexPath}. Error: ${err.message}`);
195
- if (!res.headersSent) {
196
- res.status(404).send(`Xenon UI Asset Error: ${err.message}`);
197
- }
207
+ res.status(404).send(`Xenon UI Asset Error: ${err.message}`);
198
208
  }
199
209
  });
200
210
  }
@@ -66,6 +66,7 @@ exports.unblockCandidateDevices = unblockCandidateDevices;
66
66
  exports.releaseBlockedDevices = releaseBlockedDevices;
67
67
  exports.setupCronReleaseBlockedDevices = setupCronReleaseBlockedDevices;
68
68
  exports.setupCronUpdateDeviceList = setupCronUpdateDeviceList;
69
+ exports.setupCronLocalDiscovery = setupCronLocalDiscovery;
69
70
  exports.cleanPendingSessions = cleanPendingSessions;
70
71
  exports.setupCronCleanPendingSessions = setupCronCleanPendingSessions;
71
72
  exports.setupCronCleanExpiredReservations = setupCronCleanExpiredReservations;
@@ -578,6 +579,21 @@ function setupCronUpdateDeviceList(host, hubArgument, intervalMs, tlsRejectUnaut
578
579
  }), intervalMs);
579
580
  });
580
581
  }
582
+ /**
583
+ * Principal discovery: Periodically poll for local devices to prune stales/offlines.
584
+ * Critical for standalone Hubs where setupCronUpdateDeviceList isn't called.
585
+ */
586
+ function setupCronLocalDiscovery(host, intervalMs) {
587
+ return __awaiter(this, void 0, void 0, function* () {
588
+ if (timer) {
589
+ clearInterval(timer);
590
+ }
591
+ logger_1.default.info(`Local device discovery poll started every ${intervalMs} ms`);
592
+ timer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
593
+ yield updateDeviceList(host);
594
+ }), intervalMs);
595
+ });
596
+ }
581
597
  function cleanPendingSessions(timeoutMs) {
582
598
  return __awaiter(this, void 0, void 0, function* () {
583
599
  const pendingSessions = yield pendingStore.getAllPendingSessions();
@@ -246,6 +246,11 @@ let ServerManager = ServerManager_1 = class ServerManager {
246
246
  else {
247
247
  ServerManager_1.IS_HUB = true;
248
248
  this.logger.info(`🌐 I'm a hub and I'm listening on ${pluginArgs.bindHostOrIp}:${cliArgs.port}`);
249
+ // Principal discovery: Background poll to prune stale/offline devices on the Hub itself
250
+ (() => __awaiter(this, void 0, void 0, function* () {
251
+ const { setupCronLocalDiscovery } = yield Promise.resolve().then(() => __importStar(require('../device-utils')));
252
+ yield setupCronLocalDiscovery(pluginArgs.bindHostOrIp, pluginArgs.sendNodeDevicesToHubIntervalMs);
253
+ }))();
249
254
  const socketServer = typedi_1.Container.get(SocketServer_1.SocketServer);
250
255
  socketServer.initialize(httpServer);
251
256
  const tracingService = typedi_1.Container.get(TracingService_1.TracingService);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenon-device-management/xenon",
3
- "version": "1.1.17",
3
+ "version": "1.1.18",
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": {