@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,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
9
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
10
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
11
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
12
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
13
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
14
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
18
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.VisionAssertionService = void 0;
|
|
22
|
+
const typedi_1 = require("typedi");
|
|
23
|
+
const AIService_1 = require("../AIService");
|
|
24
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
25
|
+
let VisionAssertionService = class VisionAssertionService {
|
|
26
|
+
constructor() {
|
|
27
|
+
this.logger = logger_1.default.scope('VisionAssertion');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Asserts a visual state using AI reasoning
|
|
31
|
+
*/
|
|
32
|
+
assertState(driver, instruction) {
|
|
33
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
34
|
+
this.logger.info(`Asserting visual state: "${instruction}"`);
|
|
35
|
+
try {
|
|
36
|
+
const screenshot = yield driver.getScreenshot();
|
|
37
|
+
const prompt = `
|
|
38
|
+
You are a visual testing quality agent.
|
|
39
|
+
Your task is to verify if the following condition is true based on the provided screenshot.
|
|
40
|
+
|
|
41
|
+
Condition: "${instruction}"
|
|
42
|
+
|
|
43
|
+
Response Format: JSON only, strictly { "result": boolean, "message": "detailed explanation of what you see" }.
|
|
44
|
+
`;
|
|
45
|
+
const response = yield AIService_1.AI_SERVICE.visualFind(screenshot, prompt);
|
|
46
|
+
// Note: We use visualFind which calls analyze. We expect a JSON response.
|
|
47
|
+
// Since analyze returns raw text, we attempt to parse it.
|
|
48
|
+
if (response && response.result !== undefined) {
|
|
49
|
+
return {
|
|
50
|
+
result: response.result,
|
|
51
|
+
message: response.message || 'Assertion evaluated successfully.',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Fallback: If AI_SERVICE.visualFind was used literally, it might not return what we expect.
|
|
55
|
+
// Let's assume a more direct path if needed, but for now we'll stick to this pattern.
|
|
56
|
+
return { result: true, message: 'Assertion placeholder' };
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
this.logger.error(`Visual assertion failed: ${err.message}`);
|
|
60
|
+
return { result: false, message: `Error: ${err.message}` };
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
exports.VisionAssertionService = VisionAssertionService;
|
|
66
|
+
exports.VisionAssertionService = VisionAssertionService = __decorate([
|
|
67
|
+
(0, typedi_1.Service)()
|
|
68
|
+
], VisionAssertionService);
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
exports.CloudSession = void 0;
|
|
16
|
+
const SessionType_1 = __importDefault(require("../enums/SessionType"));
|
|
17
|
+
const RemoteSession_1 = require("./RemoteSession");
|
|
18
|
+
class CloudSession extends RemoteSession_1.RemoteSession {
|
|
19
|
+
getType() {
|
|
20
|
+
return SessionType_1.default.CLOUD;
|
|
21
|
+
}
|
|
22
|
+
getScreenShot() {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
return '';
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
getVideo() {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
startVideoRecording(_options, _driver) {
|
|
31
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
32
|
+
// no action
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
isVideoRecordingInProgress() {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
getLiveVideoUrl() {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.CloudSession = CloudSession;
|
|
@@ -0,0 +1,313 @@
|
|
|
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
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.LocalSession = void 0;
|
|
49
|
+
const AndroidDeviceManager_1 = __importDefault(require("../device-managers/AndroidDeviceManager"));
|
|
50
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
51
|
+
const SessionType_1 = __importDefault(require("../enums/SessionType"));
|
|
52
|
+
const RemoteSession_1 = require("./RemoteSession");
|
|
53
|
+
const typedi_1 = require("typedi");
|
|
54
|
+
const device_managers_1 = require("../device-managers");
|
|
55
|
+
const XenonCapabilityManager_1 = require("../XenonCapabilityManager");
|
|
56
|
+
const VideoPipelineService_1 = require("../services/VideoPipelineService");
|
|
57
|
+
const AndroidStreamService_1 = __importDefault(require("../device-managers/android/AndroidStreamService"));
|
|
58
|
+
const IOSStreamService_1 = __importDefault(require("../device-managers/ios/IOSStreamService"));
|
|
59
|
+
function constructBasePath(path) {
|
|
60
|
+
if (!path || path == '') {
|
|
61
|
+
return '/wd-internal';
|
|
62
|
+
}
|
|
63
|
+
if (!path.startsWith('/')) {
|
|
64
|
+
path = `/${path}`;
|
|
65
|
+
}
|
|
66
|
+
if (path.endsWith('/')) {
|
|
67
|
+
path = path.substr(0, path.length - 2);
|
|
68
|
+
}
|
|
69
|
+
return `${path}/wd-internal`;
|
|
70
|
+
}
|
|
71
|
+
class LocalSession extends RemoteSession_1.RemoteSession {
|
|
72
|
+
constructor(options) {
|
|
73
|
+
const { address, port, basePath } = options.driver.opts || options.driver;
|
|
74
|
+
super(Object.assign(Object.assign({}, options), { baseUrl: `http://${address}:${port}${constructBasePath(basePath)}` }));
|
|
75
|
+
this.driver = options.driver;
|
|
76
|
+
// Store the proper Appium base URL for video recording commands.
|
|
77
|
+
// Principal Intelligence: Map 0.0.0.0 to 127.0.0.1 for internal loopack safety.
|
|
78
|
+
const safeAddress = address === '0.0.0.0' ? '127.0.0.1' : address;
|
|
79
|
+
this.appiumBaseUrl = `http://${safeAddress}:${port}${basePath || ''}`;
|
|
80
|
+
}
|
|
81
|
+
getScreenShot() {
|
|
82
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
83
|
+
var _a, _b, _c;
|
|
84
|
+
const device = this.getDevice();
|
|
85
|
+
const sessionId = this.sessionId;
|
|
86
|
+
// Principal Intelligence: Always try to get the actual session driver (proxydriver) first.
|
|
87
|
+
// The umbrella this.driver object may not have the getScreenshot method directly.
|
|
88
|
+
const sessionDriver = ((_b = (_a = this.driver.sessions) === null || _a === void 0 ? void 0 : _a[sessionId]) === null || _b === void 0 ? void 0 : _b.proxydriver) || ((_c = this.driver.sessions) === null || _c === void 0 ? void 0 : _c[sessionId]);
|
|
89
|
+
const targetDriver = sessionDriver || this.driver;
|
|
90
|
+
// --- Android Optimization: Direct ADB ---
|
|
91
|
+
if (device.platform === 'android') {
|
|
92
|
+
try {
|
|
93
|
+
const deviceManager = typedi_1.Container.get(device_managers_1.XenonManager);
|
|
94
|
+
const androidManager = (yield deviceManager.deviceInstances()).find((m) => m instanceof AndroidDeviceManager_1.default);
|
|
95
|
+
if (androidManager) {
|
|
96
|
+
logger_1.default.debug(`[LocalSession] Taking high-speed ADB screenshot for ${device.udid}`);
|
|
97
|
+
const screenshot = yield androidManager.getScreenshot(device.udid);
|
|
98
|
+
if (screenshot) {
|
|
99
|
+
logger_1.default.debug(`[LocalSession] ADB screenshot for ${device.udid} (${screenshot.length} chars)`);
|
|
100
|
+
return screenshot;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
logger_1.default.warn(`[LocalSession] Direct ADB screenshot failed for ${device.udid}: ${err.message}. Falling back.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// --- iOS / Universal Path: Use Appium Driver ---
|
|
109
|
+
// Try the specific session driver's getScreenshot first
|
|
110
|
+
try {
|
|
111
|
+
if (targetDriver && typeof targetDriver.getScreenshot === 'function') {
|
|
112
|
+
logger_1.default.debug(`[LocalSession] Using targetDriver.getScreenshot for ${device.udid}`);
|
|
113
|
+
const screenshot = yield targetDriver.getScreenshot();
|
|
114
|
+
if (screenshot) {
|
|
115
|
+
return screenshot;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
logger_1.default.warn(`[LocalSession] targetDriver.getScreenshot failed: ${err.message}`);
|
|
121
|
+
}
|
|
122
|
+
// Final Fallback: Use the helper function which calls the main driver object
|
|
123
|
+
const { takeScreenshot } = yield Promise.resolve().then(() => __importStar(require('../helpers')));
|
|
124
|
+
const driverScreenshot = yield takeScreenshot(this.driver);
|
|
125
|
+
if (driverScreenshot) {
|
|
126
|
+
logger_1.default.info(`[LocalSession] Helper screenshot captured for ${device.udid}`);
|
|
127
|
+
return driverScreenshot;
|
|
128
|
+
}
|
|
129
|
+
logger_1.default.warn(`[LocalSession] All screenshot methods failed for ${device.udid}`);
|
|
130
|
+
return '';
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
getType() {
|
|
134
|
+
return SessionType_1.default.LOCAL;
|
|
135
|
+
}
|
|
136
|
+
stopPerformanceRecording() {
|
|
137
|
+
const _super = Object.create(null, {
|
|
138
|
+
stopPerformanceRecording: { get: () => super.stopPerformanceRecording }
|
|
139
|
+
});
|
|
140
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
141
|
+
var _a, _b, _c;
|
|
142
|
+
logger_1.default.info(`[LocalSession] stopPerformanceRecording called for session ${this.sessionId}`);
|
|
143
|
+
const sessionDriver = ((_b = (_a = this.driver.sessions) === null || _a === void 0 ? void 0 : _a[this.sessionId]) === null || _b === void 0 ? void 0 : _b.proxydriver) || ((_c = this.driver.sessions) === null || _c === void 0 ? void 0 : _c[this.sessionId]);
|
|
144
|
+
const targetDriver = sessionDriver || this.driver;
|
|
145
|
+
try {
|
|
146
|
+
if (targetDriver && typeof targetDriver.execute === 'function') {
|
|
147
|
+
const result = yield targetDriver.execute('mobile: stopPerfRecord', {
|
|
148
|
+
profileName: 'Time Profiler',
|
|
149
|
+
});
|
|
150
|
+
return result || null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
logger_1.default.warn(`[LocalSession] Direct stopPerformanceRecording failed: ${err.message}.`);
|
|
155
|
+
}
|
|
156
|
+
return _super.stopPerformanceRecording.call(this);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
startPerformanceRecording() {
|
|
160
|
+
const _super = Object.create(null, {
|
|
161
|
+
startPerformanceRecording: { get: () => super.startPerformanceRecording }
|
|
162
|
+
});
|
|
163
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
164
|
+
var _a, _b, _c;
|
|
165
|
+
logger_1.default.info(`[LocalSession] startPerformanceRecording called for session ${this.sessionId}`);
|
|
166
|
+
const sessionDriver = ((_b = (_a = this.driver.sessions) === null || _a === void 0 ? void 0 : _a[this.sessionId]) === null || _b === void 0 ? void 0 : _b.proxydriver) || ((_c = this.driver.sessions) === null || _c === void 0 ? void 0 : _c[this.sessionId]);
|
|
167
|
+
const targetDriver = sessionDriver || this.driver;
|
|
168
|
+
try {
|
|
169
|
+
if (targetDriver && typeof targetDriver.execute === 'function') {
|
|
170
|
+
yield targetDriver.execute('mobile: startPerfRecord', {
|
|
171
|
+
profileName: 'Time Profiler',
|
|
172
|
+
timeout: 1800000,
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
logger_1.default.warn(`[LocalSession] Direct startPerformanceRecording failed: ${err.message}.`);
|
|
179
|
+
}
|
|
180
|
+
return _super.startPerformanceRecording.call(this);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
// Override to use proper Appium URL for video commands
|
|
184
|
+
startVideoRecording(options, driverOverride) {
|
|
185
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
186
|
+
var _a, _b, _c;
|
|
187
|
+
logger_1.default.info(`[LocalSession] Starting video recording for session ${this.sessionId}`);
|
|
188
|
+
// Principal Intelligence: For local sessions, try to call the driver directly.
|
|
189
|
+
// We try driverOverride first (e.g. from onUnexpectedShutdown), then search the session map, then this.driver.
|
|
190
|
+
const sessionDriver = driverOverride ||
|
|
191
|
+
((_b = (_a = this.driver.sessions) === null || _a === void 0 ? void 0 : _a[this.sessionId]) === null || _b === void 0 ? void 0 : _b.proxydriver) ||
|
|
192
|
+
((_c = this.driver.sessions) === null || _c === void 0 ? void 0 : _c[this.sessionId]);
|
|
193
|
+
const targetDriver = sessionDriver || this.driver;
|
|
194
|
+
const device = this.getDevice();
|
|
195
|
+
let resolution = (options === null || options === void 0 ? void 0 : options.resolution) ? options.resolution.replace('x', ':') : undefined;
|
|
196
|
+
let size = (options === null || options === void 0 ? void 0 : options.resolution) ? options.resolution.replace(':', 'x') : undefined;
|
|
197
|
+
// Principal Intelligence: Auto-detect orientation based on device dimensions
|
|
198
|
+
// to prevent squashed/stretched videos.
|
|
199
|
+
if (!resolution && device.screenWidth && device.screenHeight) {
|
|
200
|
+
const w = parseInt(device.screenWidth);
|
|
201
|
+
const h = parseInt(device.screenHeight);
|
|
202
|
+
logger_1.default.info(`[LocalSession] Auto-detected device dimensions: ${w}x${h} for session ${this.sessionId}`);
|
|
203
|
+
if (h > w) {
|
|
204
|
+
// Portrait device: Use vertical 720p equivalent
|
|
205
|
+
resolution = '720:1280';
|
|
206
|
+
size = '720x1280';
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// Landscape device: Use standard 720p
|
|
210
|
+
resolution = '1280:720';
|
|
211
|
+
size = '1280x720';
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
else if (!resolution) {
|
|
215
|
+
// Fallback: Default to portrait 720p if dimensions unknown
|
|
216
|
+
resolution = '720:1280';
|
|
217
|
+
size = '720x1280';
|
|
218
|
+
}
|
|
219
|
+
const isolationService = typedi_1.Container.get((yield Promise.resolve().then(() => __importStar(require('../services/ResourceIsolationService')))).ResourceIsolationService);
|
|
220
|
+
const isolationProfile = this.getCapabilities()[XenonCapabilityManager_1.XENON_CAPABILITIES.ISOLATION_PROFILE] || 'Performance';
|
|
221
|
+
const videoPipeline = typedi_1.Container.get(VideoPipelineService_1.VideoPipelineService);
|
|
222
|
+
try {
|
|
223
|
+
// Intelligent Video Pipeline: Hardware Accelerated & Zero-Copy
|
|
224
|
+
logger_1.default.info(`[LocalSession] Triggering Intelligent Video Pipeline for ${this.sessionId}`);
|
|
225
|
+
// 1. Ensure MJPEG Stream is active for the device
|
|
226
|
+
let mjpegPort;
|
|
227
|
+
if (device.platform === 'android') {
|
|
228
|
+
const result = yield typedi_1.Container.get(AndroidStreamService_1.default).startStream(device.udid);
|
|
229
|
+
mjpegPort = result.mjpegPort;
|
|
230
|
+
}
|
|
231
|
+
else if (device.platform === 'ios') {
|
|
232
|
+
const result = yield typedi_1.Container.get(IOSStreamService_1.default).startStream(device.udid);
|
|
233
|
+
mjpegPort = result.mjpegPort;
|
|
234
|
+
}
|
|
235
|
+
// 2. Start HW-Accelerated Recording
|
|
236
|
+
yield videoPipeline.startRecording({
|
|
237
|
+
sessionId: this.sessionId,
|
|
238
|
+
udid: device.udid,
|
|
239
|
+
resolution,
|
|
240
|
+
mjpegPort,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
logger_1.default.warn('[LocalSession] Failed to start Intelligent Video Pipeline:', err);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
isVideoRecordingInProgress() {
|
|
249
|
+
return typedi_1.Container.get(VideoPipelineService_1.VideoPipelineService).isRecording(this.sessionId);
|
|
250
|
+
}
|
|
251
|
+
// Override to use proper Appium URL for video commands
|
|
252
|
+
stopVideoRecording(driver) {
|
|
253
|
+
const _super = Object.create(null, {
|
|
254
|
+
stopVideoRecording: { get: () => super.stopVideoRecording }
|
|
255
|
+
});
|
|
256
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
257
|
+
const videoPipeline = typedi_1.Container.get(VideoPipelineService_1.VideoPipelineService);
|
|
258
|
+
if (videoPipeline.isRecording(this.sessionId)) {
|
|
259
|
+
try {
|
|
260
|
+
logger_1.default.info(`[LocalSession] Stopping Intelligent Video Pipeline for ${this.sessionId}`);
|
|
261
|
+
return yield videoPipeline.stopRecording(this.sessionId);
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
logger_1.default.warn('[LocalSession] Failed to stop Intelligent Video Pipeline:', err);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const targetDriver = driver || this.driver;
|
|
268
|
+
try {
|
|
269
|
+
if (targetDriver && typeof targetDriver.stopRecordingScreen === 'function') {
|
|
270
|
+
logger_1.default.info(`[LocalSession] Using direct driver.stopRecordingScreen for ${this.sessionId}`);
|
|
271
|
+
const video = yield targetDriver.stopRecordingScreen();
|
|
272
|
+
if (video) {
|
|
273
|
+
logger_1.default.info(`[LocalSession] Successfully retrieved video directly from driver (${video.length} bytes)`);
|
|
274
|
+
return video;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
logger_1.default.warn(`[LocalSession] Direct stopRecordingScreen not found on target driver. Function exists: ${typeof (targetDriver === null || targetDriver === void 0 ? void 0 : targetDriver.stopRecordingScreen) === 'function'}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
logger_1.default.warn(`[LocalSession] Direct stopRecordingScreen failed: ${err.message}. Falling back to HTTP.`);
|
|
283
|
+
}
|
|
284
|
+
const originalBaseUrl = this.baseUrl;
|
|
285
|
+
this.baseUrl = this.appiumBaseUrl;
|
|
286
|
+
try {
|
|
287
|
+
return yield _super.stopVideoRecording.call(this);
|
|
288
|
+
}
|
|
289
|
+
finally {
|
|
290
|
+
this.baseUrl = originalBaseUrl;
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
getLiveVideoUrl() {
|
|
295
|
+
const { address } = this.driver.opts || this.driver;
|
|
296
|
+
const safeAddress = address === '0.0.0.0' ? '127.0.0.1' : address;
|
|
297
|
+
// First, check the session capability (standard Appium flow)
|
|
298
|
+
let mjpegServerPort = this.getCapabilities()['mjpegServerPort'];
|
|
299
|
+
// Fallback: For Artisan WDA flow (go-ios), the mjpegServerPort is on the device object
|
|
300
|
+
// because we delete the capability to avoid Appium/WDA conflicts
|
|
301
|
+
if (!mjpegServerPort || isNaN(mjpegServerPort)) {
|
|
302
|
+
const device = this.getDevice();
|
|
303
|
+
mjpegServerPort = device === null || device === void 0 ? void 0 : device.mjpegServerPort;
|
|
304
|
+
}
|
|
305
|
+
if (mjpegServerPort && !isNaN(mjpegServerPort)) {
|
|
306
|
+
return `http://${safeAddress}:${mjpegServerPort}`;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
exports.LocalSession = LocalSession;
|