@xenon-device-management/xenon 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +446 -0
- package/lib/package.json +207 -0
- package/lib/public/assets/Layouts-7IT8aFLI.js +11 -0
- package/lib/public/assets/Layouts-DPMls9vh.css +1 -0
- package/lib/public/assets/ai-settings-BbnfgdEx.js +11 -0
- package/lib/public/assets/apps-CRMrI4_p.js +16 -0
- package/lib/public/assets/apps-CcM77dgg.css +1 -0
- package/lib/public/assets/badge-B1nKs8zj.css +1 -0
- package/lib/public/assets/badge-CSvl5xIU.js +11 -0
- package/lib/public/assets/button-CJlKn4PZ.css +1 -0
- package/lib/public/assets/button-CvLaGFYj.js +26 -0
- package/lib/public/assets/calendar-6w-D6Oaw.js +6 -0
- package/lib/public/assets/clock-DcdeWBPr.js +6 -0
- package/lib/public/assets/cpu-DiSoXT9n.js +6 -0
- package/lib/public/assets/device-explorer-CajM63OJ.js +193 -0
- package/lib/public/assets/device-explorer-CxdUAoTL.css +1 -0
- package/lib/public/assets/index-ByQwMN5T.js +174 -0
- package/lib/public/assets/index-C1DBaoSh.js +1 -0
- package/lib/public/assets/index-qzCez_kk.css +1 -0
- package/lib/public/assets/lock-B23ibZmo.js +6 -0
- package/lib/public/assets/maintenance-settings-CirzA6yG.js +6 -0
- package/lib/public/assets/mouse-pointer-2-Cz76SHFb.js +6 -0
- package/lib/public/assets/plus-BBwlIevt.js +6 -0
- package/lib/public/assets/session-dashboard-C2k7FFv_.css +1 -0
- package/lib/public/assets/session-dashboard-HPDtwPOZ.js +62 -0
- package/lib/public/assets/settings-DrZsZwdc.js +1 -0
- package/lib/public/assets/trash-2-DQpvzJec.js +6 -0
- package/lib/public/assets/useSocket-Dxsqae2a.js +16 -0
- package/lib/public/assets/webhook-settings-CDPgsgkb.css +1 -0
- package/lib/public/assets/webhook-settings-Cp-B4Nrw.js +1 -0
- package/lib/public/assets/zap-DovP6iow.js +6 -0
- package/lib/public/favicon.ico +0 -0
- package/lib/public/favicon.png +0 -0
- package/lib/public/favicon.svg +9 -0
- package/lib/public/index.html +46 -0
- package/lib/public/logo.svg +17 -0
- package/lib/public/logo192.png +0 -0
- package/lib/public/logo512.png +0 -0
- package/lib/public/manifest.json +25 -0
- package/lib/public/robots.txt +3 -0
- package/lib/schema.json +348 -0
- package/lib/src/InternalHttpClient.js +212 -0
- package/lib/src/PluginContext.js +29 -0
- package/lib/src/XenonCapabilityManager.js +199 -0
- package/lib/src/app/index.js +167 -0
- package/lib/src/app/routers/apps.js +79 -0
- package/lib/src/app/routers/config.js +131 -0
- package/lib/src/app/routers/control.js +835 -0
- package/lib/src/app/routers/dashboard.js +301 -0
- package/lib/src/app/routers/grid.js +352 -0
- package/lib/src/app/routers/reservation.js +190 -0
- package/lib/src/app/routers/webhook.js +83 -0
- package/lib/src/app/swagger-docs.js +203 -0
- package/lib/src/app/swagger.js +366 -0
- package/lib/src/chromeUtils.js +148 -0
- package/lib/src/commands/handle.js +19 -0
- package/lib/src/commands/index.js +8 -0
- package/lib/src/config.js +73 -0
- package/lib/src/dashboard/asset-manager.js +84 -0
- package/lib/src/dashboard/commands.js +284 -0
- package/lib/src/dashboard/event-manager.js +699 -0
- package/lib/src/dashboard/services/app-service.js +134 -0
- package/lib/src/dashboard/services/failure-analysis-service.js +173 -0
- package/lib/src/dashboard/services/session-service.js +113 -0
- package/lib/src/data-service/CircuitBreaker.js +83 -0
- package/lib/src/data-service/config-service.js +155 -0
- package/lib/src/data-service/db.js +122 -0
- package/lib/src/data-service/device-service.js +320 -0
- package/lib/src/data-service/device-store.interface.js +2 -0
- package/lib/src/data-service/device-store.js +345 -0
- package/lib/src/data-service/pending-sessions-service.js +25 -0
- package/lib/src/data-service/pluginArgs.js +25 -0
- package/lib/src/data-service/prisma-service.js +31 -0
- package/lib/src/data-service/prisma-store.js +385 -0
- package/lib/src/data-service/queue-service.js +150 -0
- package/lib/src/data-service/web-config-service.js +130 -0
- package/lib/src/device-managers/AndroidDeviceManager.js +1155 -0
- package/lib/src/device-managers/ChromeDriverManager.js +68 -0
- package/lib/src/device-managers/HealthMonitorService.js +325 -0
- package/lib/src/device-managers/IOSDeviceManager.js +351 -0
- package/lib/src/device-managers/NodeDevices.js +82 -0
- package/lib/src/device-managers/android/AndroidStreamService.js +370 -0
- package/lib/src/device-managers/android/DeviceLockManager.js +45 -0
- package/lib/src/device-managers/cloud/CapabilityManager.js +26 -0
- package/lib/src/device-managers/cloud/Devices.js +86 -0
- package/lib/src/device-managers/iOSTracker.js +44 -0
- package/lib/src/device-managers/index.js +89 -0
- package/lib/src/device-managers/ios/IOSDiscoveryService.js +268 -0
- package/lib/src/device-managers/ios/IOSStreamService.js +893 -0
- package/lib/src/device-managers/ios/WDAClient.js +866 -0
- package/lib/src/device-utils.js +663 -0
- package/lib/src/enums/Capabilities.js +8 -0
- package/lib/src/enums/Cloud.js +11 -0
- package/lib/src/enums/Platform.js +9 -0
- package/lib/src/enums/SessionType.js +9 -0
- package/lib/src/enums/SocketEvents.js +15 -0
- package/lib/src/helpers/UniversalMjpegProxy.js +273 -0
- package/lib/src/helpers/index.js +229 -0
- package/lib/src/index.js +95 -0
- package/lib/src/interceptors/CommandInterceptor.js +524 -0
- package/lib/src/interfaces/ICloudManager.js +2 -0
- package/lib/src/interfaces/IDevice.js +2 -0
- package/lib/src/interfaces/IDeviceFilterOptions.js +2 -0
- package/lib/src/interfaces/IDeviceManager.js +2 -0
- package/lib/src/interfaces/IOptions.js +2 -0
- package/lib/src/interfaces/IPluginArgs.js +55 -0
- package/lib/src/interfaces/ISessionCapability.js +2 -0
- package/lib/src/logger.js +225 -0
- package/lib/src/plugin.js +244 -0
- package/lib/src/prisma.js +12 -0
- package/lib/src/profiling/AndroidAppProfiler.js +213 -0
- package/lib/src/proxy/wd-command-proxy.js +221 -0
- package/lib/src/scripts/generate-database-migration.js +59 -0
- package/lib/src/scripts/initialize-database.js +55 -0
- package/lib/src/scripts/install-go-ios.js +66 -0
- package/lib/src/scripts/prepare-prisma.js +89 -0
- package/lib/src/services/AICommandService.js +143 -0
- package/lib/src/services/AIService.js +466 -0
- package/lib/src/services/CleanupService.js +141 -0
- package/lib/src/services/EventBus.js +74 -0
- package/lib/src/services/InspectorService.js +395 -0
- package/lib/src/services/MetricsService.js +134 -0
- package/lib/src/services/NetworkConditioningService.js +173 -0
- package/lib/src/services/NotificationService.js +163 -0
- package/lib/src/services/RequestLogService.js +252 -0
- package/lib/src/services/ResourceIsolationService.js +122 -0
- package/lib/src/services/SecurityService.js +120 -0
- package/lib/src/services/ServerManager.js +284 -0
- package/lib/src/services/SessionHeartbeatService.js +158 -0
- package/lib/src/services/SessionLifecycleService.js +572 -0
- package/lib/src/services/SocketClient.js +71 -0
- package/lib/src/services/SocketServer.js +87 -0
- package/lib/src/services/TracingService.js +132 -0
- package/lib/src/services/VideoPipelineService.js +220 -0
- package/lib/src/services/healing/FuzzyXmlHealingProvider.js +333 -0
- package/lib/src/services/healing/HealEtalonService.js +98 -0
- package/lib/src/services/healing/HealedLocatorGenerator.js +132 -0
- package/lib/src/services/healing/HealingOrchestrator.js +165 -0
- package/lib/src/services/healing/LlmHealingProvider.js +77 -0
- package/lib/src/services/healing/OcrHealingProvider.js +119 -0
- package/lib/src/services/healing/ResilioTreeHealingProvider.js +100 -0
- package/lib/src/services/healing/VisualAiHealingProvider.js +90 -0
- package/lib/src/services/healing/types.js +12 -0
- package/lib/src/services/omni-vision/OmniVisionService.js +718 -0
- package/lib/src/services/omni-vision/VisionAssertionService.js +68 -0
- package/lib/src/sessions/CloudSession.js +42 -0
- package/lib/src/sessions/LocalSession.js +313 -0
- package/lib/src/sessions/RemoteSession.js +287 -0
- package/lib/src/sessions/SessionManager.js +238 -0
- package/lib/src/sessions/XenonSession.js +44 -0
- package/lib/src/types/CLIArgs.js +2 -0
- package/lib/src/types/CloudArgs.js +2 -0
- package/lib/src/types/CloudSchema.js +131 -0
- package/lib/src/types/DeviceType.js +2 -0
- package/lib/src/types/DeviceUpdate.js +2 -0
- package/lib/src/types/IOSDevice.js +2 -0
- package/lib/src/types/Platform.js +2 -0
- package/lib/src/types/SessionStatus.js +11 -0
- package/lib/src/validators/CapabilityValidator.js +93 -0
- package/lib/test/e2e/android/conf.spec.js +43 -0
- package/lib/test/e2e/android/conf2.spec.js +44 -0
- package/lib/test/e2e/android/conf3.spec.js +44 -0
- package/lib/test/e2e/e2ehelper.js +113 -0
- package/lib/test/e2e/hubnode/forward-request.spec.js +224 -0
- package/lib/test/e2e/hubnode/hubnode.spec.js +214 -0
- package/lib/test/e2e/ios/conf1.spec.js +39 -0
- package/lib/test/e2e/ios/conf2.spec.js +39 -0
- package/lib/test/e2e/plugin-harness.js +236 -0
- package/lib/test/e2e/plugin.spec.js +97 -0
- package/lib/test/e2e/telemetry_verification.spec.js +83 -0
- package/lib/test/e2e/video-recording-test.spec.js +63 -0
- package/lib/test/helpers/test-container.js +112 -0
- package/lib/test/integration/androidDevices.spec.js +137 -0
- package/lib/test/integration/cliArgs.js +73 -0
- package/lib/test/integration/ios/01iOSSimulator.spec.js +291 -0
- package/lib/test/integration/ios/02iOSDevices.spec.js +75 -0
- package/lib/test/integration/testHelpers.js +74 -0
- package/lib/test/unit/AndroidDeviceManager.spec.js +178 -0
- package/lib/test/unit/ChromeDriverManager.spec.js +26 -0
- package/lib/test/unit/CleanupService.spec.js +21 -0
- package/lib/test/unit/DeviceModel.spec.js +157 -0
- package/lib/test/unit/FuzzyXmlHealingProvider.test.js +294 -0
- package/lib/test/unit/GetAdbOriginal.js +42 -0
- package/lib/test/unit/HealingCascade.test.js +128 -0
- package/lib/test/unit/IOSDeviceManager.spec.js +261 -0
- package/lib/test/unit/RemoteIOs.spec.js +78 -0
- package/lib/test/unit/ResilioTreeHealingProvider.test.js +96 -0
- package/lib/test/unit/commands.spec.js +27 -0
- package/lib/test/unit/config.spec.js +27 -0
- package/lib/test/unit/device-service.spec.js +307 -0
- package/lib/test/unit/device-utils.spec.js +313 -0
- package/lib/test/unit/fixtures/device.config.js +4 -0
- package/lib/test/unit/fixtures/devices.js +89 -0
- package/lib/test/unit/helpers.spec.js +62 -0
- package/lib/test/unit/omni-vision.spec.js +100 -0
- package/lib/test/unit/plugin.spec.js +133 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +207 -0
- package/prisma/data.db +0 -0
- package/prisma/dev.db +0 -0
- package/prisma/dev.db-journal +0 -0
- package/prisma/migrations/20231011074725_initial_tables/migration.sql +47 -0
- package/prisma/migrations/20231226115334_update_session_log/migration.sql +2 -0
- package/prisma/migrations/20251204113710_add_video_recording_enabled/migration.sql +29 -0
- package/prisma/migrations/20251204132449_add_log_table/migration.sql +11 -0
- package/prisma/migrations/20251205050111_add_profiling_support/migration.sql +47 -0
- package/prisma/migrations/20251205050947_add_is_error_field/migration.sql +24 -0
- package/prisma/migrations/20260126201337_add_app_model/migration.sql +18 -0
- package/prisma/migrations/20260130115722_add_performance_trace_and_xenon_sync/migration.sql +2 -0
- package/prisma/migrations/20260130135114_add_device_models/migration.sql +57 -0
- package/prisma/migrations/20260130140655_make_systemport_optional/migration.sql +45 -0
- package/prisma/migrations/20260130140932_make_device_fields_optional/migration.sql +45 -0
- package/prisma/migrations/20260130141040_final_schema_fix/migration.sql +45 -0
- package/prisma/migrations/20260130143234_add_device_health_fields/migration.sql +4 -0
- package/prisma/migrations/20260130144921_add_failure_category/migration.sql +2 -0
- package/prisma/migrations/20260131151456_add_webhook_config/migration.sql +10 -0
- package/prisma/migrations/20260201094507_add_device_tags/migration.sql +11 -0
- package/prisma/migrations/20260201103410_add_managed_process/migration.sql +15 -0
- package/prisma/migrations/20260201140637_add_web_config/migration.sql +22 -0
- package/prisma/migrations/20260201162232_add_session_progress/migration.sql +2 -0
- package/prisma/migrations/20260201174231_add_total_healed_count/migration.sql +2 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +210 -0
- package/schema.json +348 -0
- package/scripts/build-xenon.sh +32 -0
- package/scripts/dev/debug-gemini.ts +44 -0
- package/scripts/generate-types-from-schema.js +86 -0
- package/scripts/install-compatible-driver.js +39 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const express_1 = __importDefault(require("express"));
|
|
16
|
+
const device_service_1 = require("../../data-service/device-service");
|
|
17
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
18
|
+
const router = express_1.default.Router();
|
|
19
|
+
// Duration options in milliseconds
|
|
20
|
+
const DURATION_OPTIONS = {
|
|
21
|
+
'1h': 60 * 60 * 1000,
|
|
22
|
+
'2h': 2 * 60 * 60 * 1000,
|
|
23
|
+
'4h': 4 * 60 * 60 * 1000,
|
|
24
|
+
'8h': 8 * 60 * 60 * 1000,
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* GET /api/reservation
|
|
28
|
+
* List all currently reserved devices
|
|
29
|
+
*/
|
|
30
|
+
router.get('/', (_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
31
|
+
try {
|
|
32
|
+
const reservedDevices = yield (0, device_service_1.getReservedDevices)();
|
|
33
|
+
res.json({
|
|
34
|
+
success: true,
|
|
35
|
+
reservations: reservedDevices.map((device) => ({
|
|
36
|
+
udid: device.udid,
|
|
37
|
+
host: device.host,
|
|
38
|
+
name: device.name,
|
|
39
|
+
platform: device.platform,
|
|
40
|
+
reservedBy: device.reservedBy,
|
|
41
|
+
reservedUntil: device.reservedUntil,
|
|
42
|
+
reservationReason: device.reservationReason,
|
|
43
|
+
remainingMs: device.reservedUntil ? device.reservedUntil - Date.now() : 0,
|
|
44
|
+
})),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
logger_1.default.error(`Failed to get reservations: ${error}`);
|
|
49
|
+
res.status(500).json({ success: false, error: 'Failed to get reservations' });
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
/**
|
|
53
|
+
* POST /api/reservation
|
|
54
|
+
* Reserve a device for exclusive use
|
|
55
|
+
* Body: { udid, host, reservedBy, duration, reason? }
|
|
56
|
+
*/
|
|
57
|
+
router.post('/', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
58
|
+
try {
|
|
59
|
+
const { udid, host, reservedBy, duration, reason } = req.body;
|
|
60
|
+
if (!udid || !host || !reservedBy || !duration) {
|
|
61
|
+
return res.status(400).json({
|
|
62
|
+
success: false,
|
|
63
|
+
error: 'Missing required fields: udid, host, reservedBy, duration',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// Parse duration
|
|
67
|
+
let durationMs;
|
|
68
|
+
if (typeof duration === 'string' && DURATION_OPTIONS[duration]) {
|
|
69
|
+
durationMs = DURATION_OPTIONS[duration];
|
|
70
|
+
}
|
|
71
|
+
else if (typeof duration === 'number') {
|
|
72
|
+
durationMs = duration;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
return res.status(400).json({
|
|
76
|
+
success: false,
|
|
77
|
+
error: `Invalid duration. Use: ${Object.keys(DURATION_OPTIONS).join(', ')} or a number in milliseconds`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// Check if device exists
|
|
81
|
+
const device = yield (0, device_service_1.getDevice)({ udid: [udid] });
|
|
82
|
+
if (!device) {
|
|
83
|
+
return res.status(404).json({ success: false, error: 'Device not found' });
|
|
84
|
+
}
|
|
85
|
+
// Check if device is already reserved by someone else
|
|
86
|
+
if ((0, device_service_1.isDeviceReserved)(device) && device.reservedBy !== reservedBy) {
|
|
87
|
+
return res.status(409).json({
|
|
88
|
+
success: false,
|
|
89
|
+
error: `Device is already reserved by ${device.reservedBy} until ${new Date(device.reservedUntil).toISOString()}`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// Check if device is busy with an Appium session
|
|
93
|
+
if (device.busy && device.session_id) {
|
|
94
|
+
return res.status(409).json({
|
|
95
|
+
success: false,
|
|
96
|
+
error: 'Device is currently busy with an Appium session. Cannot reserve.',
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
yield (0, device_service_1.reserveDevice)(udid, host, reservedBy, durationMs, reason);
|
|
100
|
+
const reservedUntil = Date.now() + durationMs;
|
|
101
|
+
res.json({
|
|
102
|
+
success: true,
|
|
103
|
+
message: `Device ${udid} reserved for ${reservedBy}`,
|
|
104
|
+
reservation: {
|
|
105
|
+
udid,
|
|
106
|
+
host,
|
|
107
|
+
reservedBy,
|
|
108
|
+
reservedUntil,
|
|
109
|
+
reservationReason: reason,
|
|
110
|
+
expiresAt: new Date(reservedUntil).toISOString(),
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
logger_1.default.error(`Failed to reserve device: ${error}`);
|
|
116
|
+
res.status(500).json({ success: false, error: 'Failed to reserve device' });
|
|
117
|
+
}
|
|
118
|
+
}));
|
|
119
|
+
/**
|
|
120
|
+
* DELETE /api/reservation/:udid/:host
|
|
121
|
+
* Release a device reservation
|
|
122
|
+
*/
|
|
123
|
+
router.delete('/:udid/:host', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
124
|
+
try {
|
|
125
|
+
const { udid } = req.params;
|
|
126
|
+
const host = decodeURIComponent(req.params.host);
|
|
127
|
+
const device = yield (0, device_service_1.getDevice)({ udid: [udid] });
|
|
128
|
+
if (!device) {
|
|
129
|
+
return res.status(404).json({ success: false, error: 'Device not found' });
|
|
130
|
+
}
|
|
131
|
+
if (!(0, device_service_1.isDeviceReserved)(device)) {
|
|
132
|
+
return res.status(400).json({ success: false, error: 'Device is not reserved' });
|
|
133
|
+
}
|
|
134
|
+
yield (0, device_service_1.releaseReservation)(udid, host);
|
|
135
|
+
res.json({
|
|
136
|
+
success: true,
|
|
137
|
+
message: `Reservation released for device ${udid}`,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
logger_1.default.error(`Failed to release reservation: ${error}`);
|
|
142
|
+
res.status(500).json({ success: false, error: 'Failed to release reservation' });
|
|
143
|
+
}
|
|
144
|
+
}));
|
|
145
|
+
/**
|
|
146
|
+
* POST /api/reservation/:udid/:host/extend
|
|
147
|
+
* Extend an existing reservation
|
|
148
|
+
* Body: { duration }
|
|
149
|
+
*/
|
|
150
|
+
router.post('/:udid/:host/extend', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
151
|
+
try {
|
|
152
|
+
const { udid } = req.params;
|
|
153
|
+
const host = decodeURIComponent(req.params.host);
|
|
154
|
+
const { duration } = req.body;
|
|
155
|
+
const device = yield (0, device_service_1.getDevice)({ udid: [udid] });
|
|
156
|
+
if (!device) {
|
|
157
|
+
return res.status(404).json({ success: false, error: 'Device not found' });
|
|
158
|
+
}
|
|
159
|
+
if (!(0, device_service_1.isDeviceReserved)(device)) {
|
|
160
|
+
return res.status(400).json({ success: false, error: 'Device is not reserved' });
|
|
161
|
+
}
|
|
162
|
+
// Parse duration
|
|
163
|
+
let extensionMs;
|
|
164
|
+
if (typeof duration === 'string' && DURATION_OPTIONS[duration]) {
|
|
165
|
+
extensionMs = DURATION_OPTIONS[duration];
|
|
166
|
+
}
|
|
167
|
+
else if (typeof duration === 'number') {
|
|
168
|
+
extensionMs = duration;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
return res.status(400).json({
|
|
172
|
+
success: false,
|
|
173
|
+
error: `Invalid duration. Use: ${Object.keys(DURATION_OPTIONS).join(', ')} or a number in milliseconds`,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
// Extend from current expiry time
|
|
177
|
+
const newExpiry = (device.reservedUntil || Date.now()) + extensionMs;
|
|
178
|
+
yield (0, device_service_1.reserveDevice)(udid, host, device.reservedBy, newExpiry - Date.now(), device.reservationReason);
|
|
179
|
+
res.json({
|
|
180
|
+
success: true,
|
|
181
|
+
message: `Reservation extended for device ${udid}`,
|
|
182
|
+
newExpiresAt: new Date(newExpiry).toISOString(),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
logger_1.default.error(`Failed to extend reservation: ${error}`);
|
|
187
|
+
res.status(500).json({ success: false, error: 'Failed to extend reservation' });
|
|
188
|
+
}
|
|
189
|
+
}));
|
|
190
|
+
exports.default = router;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const NotificationService_1 = require("../../services/NotificationService");
|
|
16
|
+
const typedi_1 = require("typedi");
|
|
17
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
18
|
+
function getConfigs(req, res) {
|
|
19
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
+
try {
|
|
21
|
+
const configs = yield typedi_1.Container.get(NotificationService_1.NotificationService).getConfigs();
|
|
22
|
+
res.json(configs);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
logger_1.default.error(`Failed to get webhook configs: ${err}`);
|
|
26
|
+
res.status(500).json({ error: 'Failed to fetch configurations' });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function addConfig(req, res) {
|
|
31
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
32
|
+
const { url, events, type, payloadTemplate } = req.body;
|
|
33
|
+
if (!url || !events || !Array.isArray(events)) {
|
|
34
|
+
return res.status(400).json({ error: 'Invalid parameters: url and events array required' });
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const config = yield typedi_1.Container.get(NotificationService_1.NotificationService).saveConfig(url, events, type || 'slack', payloadTemplate);
|
|
38
|
+
res.json(config);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
logger_1.default.error(`Failed to save webhook config: ${err}`);
|
|
42
|
+
res.status(500).json({ error: 'Failed to save configuration' });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function deleteConfig(req, res) {
|
|
47
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
48
|
+
const { id } = req.params;
|
|
49
|
+
try {
|
|
50
|
+
yield typedi_1.Container.get(NotificationService_1.NotificationService).deleteConfig(id);
|
|
51
|
+
res.json({ success: true });
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
logger_1.default.error(`Failed to delete webhook config: ${err}`);
|
|
55
|
+
res.status(500).json({ error: 'Failed to delete configuration' });
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function testWebhook(req, res) {
|
|
60
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
+
const { url, type } = req.body;
|
|
62
|
+
try {
|
|
63
|
+
yield typedi_1.Container.get(NotificationService_1.NotificationService).sendSlackMessage(url, 'device_new', {
|
|
64
|
+
udid: 'test-device-udid',
|
|
65
|
+
name: 'Test Device',
|
|
66
|
+
host: '127.0.0.1',
|
|
67
|
+
});
|
|
68
|
+
res.json({ success: true });
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
res.status(500).json({ error: `Test failed: ${err}` });
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function register(router) {
|
|
76
|
+
router.get('/webhook', getConfigs);
|
|
77
|
+
router.post('/webhook', addConfig);
|
|
78
|
+
router.delete('/webhook/:id', deleteConfig);
|
|
79
|
+
router.post('/webhook/test', testWebhook);
|
|
80
|
+
}
|
|
81
|
+
exports.default = {
|
|
82
|
+
register,
|
|
83
|
+
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @swagger
|
|
4
|
+
* /api/devices:
|
|
5
|
+
* get:
|
|
6
|
+
* summary: Get all connected devices
|
|
7
|
+
* description: Retrieve a list of all connected devices with their current status
|
|
8
|
+
* tags: [Devices]
|
|
9
|
+
* responses:
|
|
10
|
+
* 200:
|
|
11
|
+
* description: List of devices
|
|
12
|
+
* content:
|
|
13
|
+
* application/json:
|
|
14
|
+
* schema:
|
|
15
|
+
* type: array
|
|
16
|
+
* items:
|
|
17
|
+
* $ref: '#/components/schemas/Device'
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
/**
|
|
21
|
+
* @swagger
|
|
22
|
+
* /api/config:
|
|
23
|
+
* get:
|
|
24
|
+
* summary: Get current configuration
|
|
25
|
+
* description: Retrieve the current runtime configuration of the Xenon plugin
|
|
26
|
+
* tags: [Configuration]
|
|
27
|
+
* responses:
|
|
28
|
+
* 200:
|
|
29
|
+
* description: Current configuration object
|
|
30
|
+
* content:
|
|
31
|
+
* application/json:
|
|
32
|
+
* schema:
|
|
33
|
+
* type: object
|
|
34
|
+
* put:
|
|
35
|
+
* summary: Update configuration
|
|
36
|
+
* description: Update plugin configuration properties. Changes are persisted. Some changes may require a server restart.
|
|
37
|
+
* tags: [Configuration]
|
|
38
|
+
* requestBody:
|
|
39
|
+
* required: true
|
|
40
|
+
* content:
|
|
41
|
+
* application/json:
|
|
42
|
+
* schema:
|
|
43
|
+
* type: object
|
|
44
|
+
* description: Partial configuration object
|
|
45
|
+
* example:
|
|
46
|
+
* deviceAvailabilityTimeoutMs: 60000
|
|
47
|
+
* maxSessions: 5
|
|
48
|
+
* responses:
|
|
49
|
+
* 200:
|
|
50
|
+
* description: Configuration updated
|
|
51
|
+
* content:
|
|
52
|
+
* application/json:
|
|
53
|
+
* schema:
|
|
54
|
+
* type: object
|
|
55
|
+
* properties:
|
|
56
|
+
* success:
|
|
57
|
+
* type: boolean
|
|
58
|
+
* restartRequired:
|
|
59
|
+
* type: boolean
|
|
60
|
+
* description: True if the updated properties require a server restart
|
|
61
|
+
* message:
|
|
62
|
+
* type: string
|
|
63
|
+
*/
|
|
64
|
+
/**
|
|
65
|
+
* @swagger
|
|
66
|
+
* /api/sessions/active:
|
|
67
|
+
* get:
|
|
68
|
+
* summary: Get active sessions
|
|
69
|
+
* description: Get statistics and list of currently active sessions in memory
|
|
70
|
+
* tags: [Sessions]
|
|
71
|
+
* responses:
|
|
72
|
+
* 200:
|
|
73
|
+
* description: Active session information
|
|
74
|
+
* content:
|
|
75
|
+
* application/json:
|
|
76
|
+
* schema:
|
|
77
|
+
* type: object
|
|
78
|
+
* properties:
|
|
79
|
+
* stats:
|
|
80
|
+
* type: object
|
|
81
|
+
* properties:
|
|
82
|
+
* total:
|
|
83
|
+
* type: number
|
|
84
|
+
* description: Total number of active sessions
|
|
85
|
+
* byType:
|
|
86
|
+
* type: object
|
|
87
|
+
* properties:
|
|
88
|
+
* local:
|
|
89
|
+
* type: number
|
|
90
|
+
* remote:
|
|
91
|
+
* type: number
|
|
92
|
+
* cloud:
|
|
93
|
+
* type: number
|
|
94
|
+
* sessions:
|
|
95
|
+
* type: array
|
|
96
|
+
* items:
|
|
97
|
+
* type: object
|
|
98
|
+
* properties:
|
|
99
|
+
* id:
|
|
100
|
+
* type: string
|
|
101
|
+
* type:
|
|
102
|
+
* type: string
|
|
103
|
+
* enum: [local, remote, cloud]
|
|
104
|
+
* deviceUdid:
|
|
105
|
+
* type: string
|
|
106
|
+
* deviceName:
|
|
107
|
+
* type: string
|
|
108
|
+
* platform:
|
|
109
|
+
* type: string
|
|
110
|
+
*/
|
|
111
|
+
/**
|
|
112
|
+
* @swagger
|
|
113
|
+
* /api/logs/requests:
|
|
114
|
+
* get:
|
|
115
|
+
* summary: Get HTTP request logs
|
|
116
|
+
* description: Retrieve recent HTTP request logs for debugging hub-node communication
|
|
117
|
+
* tags: [Observability]
|
|
118
|
+
* parameters:
|
|
119
|
+
* - in: query
|
|
120
|
+
* name: limit
|
|
121
|
+
* schema:
|
|
122
|
+
* type: integer
|
|
123
|
+
* default: 50
|
|
124
|
+
* description: Maximum number of logs to return
|
|
125
|
+
* - in: query
|
|
126
|
+
* name: method
|
|
127
|
+
* schema:
|
|
128
|
+
* type: string
|
|
129
|
+
* enum: [GET, POST, PUT, DELETE]
|
|
130
|
+
* description: Filter by HTTP method
|
|
131
|
+
* - in: query
|
|
132
|
+
* name: url
|
|
133
|
+
* schema:
|
|
134
|
+
* type: string
|
|
135
|
+
* description: Filter by URL pattern (substring match)
|
|
136
|
+
* - in: query
|
|
137
|
+
* name: hasError
|
|
138
|
+
* schema:
|
|
139
|
+
* type: boolean
|
|
140
|
+
* description: Filter by error status (true = only errors, false = only success)
|
|
141
|
+
* responses:
|
|
142
|
+
* 200:
|
|
143
|
+
* description: Request logs and statistics
|
|
144
|
+
* content:
|
|
145
|
+
* application/json:
|
|
146
|
+
* schema:
|
|
147
|
+
* type: object
|
|
148
|
+
* properties:
|
|
149
|
+
* stats:
|
|
150
|
+
* type: object
|
|
151
|
+
* properties:
|
|
152
|
+
* totalLogged:
|
|
153
|
+
* type: number
|
|
154
|
+
* description: Total requests in buffer
|
|
155
|
+
* errorCount:
|
|
156
|
+
* type: number
|
|
157
|
+
* description: Number of failed requests
|
|
158
|
+
* avgDurationMs:
|
|
159
|
+
* type: number
|
|
160
|
+
* description: Average request duration in milliseconds
|
|
161
|
+
* byMethod:
|
|
162
|
+
* type: object
|
|
163
|
+
* additionalProperties:
|
|
164
|
+
* type: number
|
|
165
|
+
* byStatusCode:
|
|
166
|
+
* type: object
|
|
167
|
+
* additionalProperties:
|
|
168
|
+
* type: number
|
|
169
|
+
* logs:
|
|
170
|
+
* type: array
|
|
171
|
+
* items:
|
|
172
|
+
* type: object
|
|
173
|
+
* properties:
|
|
174
|
+
* timestamp:
|
|
175
|
+
* type: string
|
|
176
|
+
* format: date-time
|
|
177
|
+
* direction:
|
|
178
|
+
* type: string
|
|
179
|
+
* enum: [outgoing, incoming]
|
|
180
|
+
* method:
|
|
181
|
+
* type: string
|
|
182
|
+
* url:
|
|
183
|
+
* type: string
|
|
184
|
+
* statusCode:
|
|
185
|
+
* type: number
|
|
186
|
+
* durationMs:
|
|
187
|
+
* type: number
|
|
188
|
+
* error:
|
|
189
|
+
* type: string
|
|
190
|
+
* nullable: true
|
|
191
|
+
* requestBody:
|
|
192
|
+
* type: string
|
|
193
|
+
* description: Sanitized request body (sensitive data redacted)
|
|
194
|
+
* responseBody:
|
|
195
|
+
* type: string
|
|
196
|
+
* description: Truncated response body
|
|
197
|
+
*/
|
|
198
|
+
/**
|
|
199
|
+
* @swagger
|
|
200
|
+
* tags:
|
|
201
|
+
* - name: Observability
|
|
202
|
+
* description: Debugging and monitoring endpoints
|
|
203
|
+
*/
|