@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.
Files changed (228) hide show
  1. package/README.md +446 -0
  2. package/lib/package.json +207 -0
  3. package/lib/public/assets/Layouts-7IT8aFLI.js +11 -0
  4. package/lib/public/assets/Layouts-DPMls9vh.css +1 -0
  5. package/lib/public/assets/ai-settings-BbnfgdEx.js +11 -0
  6. package/lib/public/assets/apps-CRMrI4_p.js +16 -0
  7. package/lib/public/assets/apps-CcM77dgg.css +1 -0
  8. package/lib/public/assets/badge-B1nKs8zj.css +1 -0
  9. package/lib/public/assets/badge-CSvl5xIU.js +11 -0
  10. package/lib/public/assets/button-CJlKn4PZ.css +1 -0
  11. package/lib/public/assets/button-CvLaGFYj.js +26 -0
  12. package/lib/public/assets/calendar-6w-D6Oaw.js +6 -0
  13. package/lib/public/assets/clock-DcdeWBPr.js +6 -0
  14. package/lib/public/assets/cpu-DiSoXT9n.js +6 -0
  15. package/lib/public/assets/device-explorer-CajM63OJ.js +193 -0
  16. package/lib/public/assets/device-explorer-CxdUAoTL.css +1 -0
  17. package/lib/public/assets/index-ByQwMN5T.js +174 -0
  18. package/lib/public/assets/index-C1DBaoSh.js +1 -0
  19. package/lib/public/assets/index-qzCez_kk.css +1 -0
  20. package/lib/public/assets/lock-B23ibZmo.js +6 -0
  21. package/lib/public/assets/maintenance-settings-CirzA6yG.js +6 -0
  22. package/lib/public/assets/mouse-pointer-2-Cz76SHFb.js +6 -0
  23. package/lib/public/assets/plus-BBwlIevt.js +6 -0
  24. package/lib/public/assets/session-dashboard-C2k7FFv_.css +1 -0
  25. package/lib/public/assets/session-dashboard-HPDtwPOZ.js +62 -0
  26. package/lib/public/assets/settings-DrZsZwdc.js +1 -0
  27. package/lib/public/assets/trash-2-DQpvzJec.js +6 -0
  28. package/lib/public/assets/useSocket-Dxsqae2a.js +16 -0
  29. package/lib/public/assets/webhook-settings-CDPgsgkb.css +1 -0
  30. package/lib/public/assets/webhook-settings-Cp-B4Nrw.js +1 -0
  31. package/lib/public/assets/zap-DovP6iow.js +6 -0
  32. package/lib/public/favicon.ico +0 -0
  33. package/lib/public/favicon.png +0 -0
  34. package/lib/public/favicon.svg +9 -0
  35. package/lib/public/index.html +46 -0
  36. package/lib/public/logo.svg +17 -0
  37. package/lib/public/logo192.png +0 -0
  38. package/lib/public/logo512.png +0 -0
  39. package/lib/public/manifest.json +25 -0
  40. package/lib/public/robots.txt +3 -0
  41. package/lib/schema.json +348 -0
  42. package/lib/src/InternalHttpClient.js +212 -0
  43. package/lib/src/PluginContext.js +29 -0
  44. package/lib/src/XenonCapabilityManager.js +199 -0
  45. package/lib/src/app/index.js +167 -0
  46. package/lib/src/app/routers/apps.js +79 -0
  47. package/lib/src/app/routers/config.js +131 -0
  48. package/lib/src/app/routers/control.js +835 -0
  49. package/lib/src/app/routers/dashboard.js +301 -0
  50. package/lib/src/app/routers/grid.js +352 -0
  51. package/lib/src/app/routers/reservation.js +190 -0
  52. package/lib/src/app/routers/webhook.js +83 -0
  53. package/lib/src/app/swagger-docs.js +203 -0
  54. package/lib/src/app/swagger.js +366 -0
  55. package/lib/src/chromeUtils.js +148 -0
  56. package/lib/src/commands/handle.js +19 -0
  57. package/lib/src/commands/index.js +8 -0
  58. package/lib/src/config.js +73 -0
  59. package/lib/src/dashboard/asset-manager.js +84 -0
  60. package/lib/src/dashboard/commands.js +284 -0
  61. package/lib/src/dashboard/event-manager.js +699 -0
  62. package/lib/src/dashboard/services/app-service.js +134 -0
  63. package/lib/src/dashboard/services/failure-analysis-service.js +173 -0
  64. package/lib/src/dashboard/services/session-service.js +113 -0
  65. package/lib/src/data-service/CircuitBreaker.js +83 -0
  66. package/lib/src/data-service/config-service.js +155 -0
  67. package/lib/src/data-service/db.js +122 -0
  68. package/lib/src/data-service/device-service.js +320 -0
  69. package/lib/src/data-service/device-store.interface.js +2 -0
  70. package/lib/src/data-service/device-store.js +345 -0
  71. package/lib/src/data-service/pending-sessions-service.js +25 -0
  72. package/lib/src/data-service/pluginArgs.js +25 -0
  73. package/lib/src/data-service/prisma-service.js +31 -0
  74. package/lib/src/data-service/prisma-store.js +385 -0
  75. package/lib/src/data-service/queue-service.js +150 -0
  76. package/lib/src/data-service/web-config-service.js +130 -0
  77. package/lib/src/device-managers/AndroidDeviceManager.js +1155 -0
  78. package/lib/src/device-managers/ChromeDriverManager.js +68 -0
  79. package/lib/src/device-managers/HealthMonitorService.js +325 -0
  80. package/lib/src/device-managers/IOSDeviceManager.js +351 -0
  81. package/lib/src/device-managers/NodeDevices.js +82 -0
  82. package/lib/src/device-managers/android/AndroidStreamService.js +370 -0
  83. package/lib/src/device-managers/android/DeviceLockManager.js +45 -0
  84. package/lib/src/device-managers/cloud/CapabilityManager.js +26 -0
  85. package/lib/src/device-managers/cloud/Devices.js +86 -0
  86. package/lib/src/device-managers/iOSTracker.js +44 -0
  87. package/lib/src/device-managers/index.js +89 -0
  88. package/lib/src/device-managers/ios/IOSDiscoveryService.js +268 -0
  89. package/lib/src/device-managers/ios/IOSStreamService.js +893 -0
  90. package/lib/src/device-managers/ios/WDAClient.js +866 -0
  91. package/lib/src/device-utils.js +663 -0
  92. package/lib/src/enums/Capabilities.js +8 -0
  93. package/lib/src/enums/Cloud.js +11 -0
  94. package/lib/src/enums/Platform.js +9 -0
  95. package/lib/src/enums/SessionType.js +9 -0
  96. package/lib/src/enums/SocketEvents.js +15 -0
  97. package/lib/src/helpers/UniversalMjpegProxy.js +273 -0
  98. package/lib/src/helpers/index.js +229 -0
  99. package/lib/src/index.js +95 -0
  100. package/lib/src/interceptors/CommandInterceptor.js +524 -0
  101. package/lib/src/interfaces/ICloudManager.js +2 -0
  102. package/lib/src/interfaces/IDevice.js +2 -0
  103. package/lib/src/interfaces/IDeviceFilterOptions.js +2 -0
  104. package/lib/src/interfaces/IDeviceManager.js +2 -0
  105. package/lib/src/interfaces/IOptions.js +2 -0
  106. package/lib/src/interfaces/IPluginArgs.js +55 -0
  107. package/lib/src/interfaces/ISessionCapability.js +2 -0
  108. package/lib/src/logger.js +225 -0
  109. package/lib/src/plugin.js +244 -0
  110. package/lib/src/prisma.js +12 -0
  111. package/lib/src/profiling/AndroidAppProfiler.js +213 -0
  112. package/lib/src/proxy/wd-command-proxy.js +221 -0
  113. package/lib/src/scripts/generate-database-migration.js +59 -0
  114. package/lib/src/scripts/initialize-database.js +55 -0
  115. package/lib/src/scripts/install-go-ios.js +66 -0
  116. package/lib/src/scripts/prepare-prisma.js +89 -0
  117. package/lib/src/services/AICommandService.js +143 -0
  118. package/lib/src/services/AIService.js +466 -0
  119. package/lib/src/services/CleanupService.js +141 -0
  120. package/lib/src/services/EventBus.js +74 -0
  121. package/lib/src/services/InspectorService.js +395 -0
  122. package/lib/src/services/MetricsService.js +134 -0
  123. package/lib/src/services/NetworkConditioningService.js +173 -0
  124. package/lib/src/services/NotificationService.js +163 -0
  125. package/lib/src/services/RequestLogService.js +252 -0
  126. package/lib/src/services/ResourceIsolationService.js +122 -0
  127. package/lib/src/services/SecurityService.js +120 -0
  128. package/lib/src/services/ServerManager.js +284 -0
  129. package/lib/src/services/SessionHeartbeatService.js +158 -0
  130. package/lib/src/services/SessionLifecycleService.js +572 -0
  131. package/lib/src/services/SocketClient.js +71 -0
  132. package/lib/src/services/SocketServer.js +87 -0
  133. package/lib/src/services/TracingService.js +132 -0
  134. package/lib/src/services/VideoPipelineService.js +220 -0
  135. package/lib/src/services/healing/FuzzyXmlHealingProvider.js +333 -0
  136. package/lib/src/services/healing/HealEtalonService.js +98 -0
  137. package/lib/src/services/healing/HealedLocatorGenerator.js +132 -0
  138. package/lib/src/services/healing/HealingOrchestrator.js +165 -0
  139. package/lib/src/services/healing/LlmHealingProvider.js +77 -0
  140. package/lib/src/services/healing/OcrHealingProvider.js +119 -0
  141. package/lib/src/services/healing/ResilioTreeHealingProvider.js +100 -0
  142. package/lib/src/services/healing/VisualAiHealingProvider.js +90 -0
  143. package/lib/src/services/healing/types.js +12 -0
  144. package/lib/src/services/omni-vision/OmniVisionService.js +718 -0
  145. package/lib/src/services/omni-vision/VisionAssertionService.js +68 -0
  146. package/lib/src/sessions/CloudSession.js +42 -0
  147. package/lib/src/sessions/LocalSession.js +313 -0
  148. package/lib/src/sessions/RemoteSession.js +287 -0
  149. package/lib/src/sessions/SessionManager.js +238 -0
  150. package/lib/src/sessions/XenonSession.js +44 -0
  151. package/lib/src/types/CLIArgs.js +2 -0
  152. package/lib/src/types/CloudArgs.js +2 -0
  153. package/lib/src/types/CloudSchema.js +131 -0
  154. package/lib/src/types/DeviceType.js +2 -0
  155. package/lib/src/types/DeviceUpdate.js +2 -0
  156. package/lib/src/types/IOSDevice.js +2 -0
  157. package/lib/src/types/Platform.js +2 -0
  158. package/lib/src/types/SessionStatus.js +11 -0
  159. package/lib/src/validators/CapabilityValidator.js +93 -0
  160. package/lib/test/e2e/android/conf.spec.js +43 -0
  161. package/lib/test/e2e/android/conf2.spec.js +44 -0
  162. package/lib/test/e2e/android/conf3.spec.js +44 -0
  163. package/lib/test/e2e/e2ehelper.js +113 -0
  164. package/lib/test/e2e/hubnode/forward-request.spec.js +224 -0
  165. package/lib/test/e2e/hubnode/hubnode.spec.js +214 -0
  166. package/lib/test/e2e/ios/conf1.spec.js +39 -0
  167. package/lib/test/e2e/ios/conf2.spec.js +39 -0
  168. package/lib/test/e2e/plugin-harness.js +236 -0
  169. package/lib/test/e2e/plugin.spec.js +97 -0
  170. package/lib/test/e2e/telemetry_verification.spec.js +83 -0
  171. package/lib/test/e2e/video-recording-test.spec.js +63 -0
  172. package/lib/test/helpers/test-container.js +112 -0
  173. package/lib/test/integration/androidDevices.spec.js +137 -0
  174. package/lib/test/integration/cliArgs.js +73 -0
  175. package/lib/test/integration/ios/01iOSSimulator.spec.js +291 -0
  176. package/lib/test/integration/ios/02iOSDevices.spec.js +75 -0
  177. package/lib/test/integration/testHelpers.js +74 -0
  178. package/lib/test/unit/AndroidDeviceManager.spec.js +178 -0
  179. package/lib/test/unit/ChromeDriverManager.spec.js +26 -0
  180. package/lib/test/unit/CleanupService.spec.js +21 -0
  181. package/lib/test/unit/DeviceModel.spec.js +157 -0
  182. package/lib/test/unit/FuzzyXmlHealingProvider.test.js +294 -0
  183. package/lib/test/unit/GetAdbOriginal.js +42 -0
  184. package/lib/test/unit/HealingCascade.test.js +128 -0
  185. package/lib/test/unit/IOSDeviceManager.spec.js +261 -0
  186. package/lib/test/unit/RemoteIOs.spec.js +78 -0
  187. package/lib/test/unit/ResilioTreeHealingProvider.test.js +96 -0
  188. package/lib/test/unit/commands.spec.js +27 -0
  189. package/lib/test/unit/config.spec.js +27 -0
  190. package/lib/test/unit/device-service.spec.js +307 -0
  191. package/lib/test/unit/device-utils.spec.js +313 -0
  192. package/lib/test/unit/fixtures/device.config.js +4 -0
  193. package/lib/test/unit/fixtures/devices.js +89 -0
  194. package/lib/test/unit/helpers.spec.js +62 -0
  195. package/lib/test/unit/omni-vision.spec.js +100 -0
  196. package/lib/test/unit/plugin.spec.js +133 -0
  197. package/lib/tsconfig.tsbuildinfo +1 -0
  198. package/package.json +207 -0
  199. package/prisma/data.db +0 -0
  200. package/prisma/dev.db +0 -0
  201. package/prisma/dev.db-journal +0 -0
  202. package/prisma/migrations/20231011074725_initial_tables/migration.sql +47 -0
  203. package/prisma/migrations/20231226115334_update_session_log/migration.sql +2 -0
  204. package/prisma/migrations/20251204113710_add_video_recording_enabled/migration.sql +29 -0
  205. package/prisma/migrations/20251204132449_add_log_table/migration.sql +11 -0
  206. package/prisma/migrations/20251205050111_add_profiling_support/migration.sql +47 -0
  207. package/prisma/migrations/20251205050947_add_is_error_field/migration.sql +24 -0
  208. package/prisma/migrations/20260126201337_add_app_model/migration.sql +18 -0
  209. package/prisma/migrations/20260130115722_add_performance_trace_and_xenon_sync/migration.sql +2 -0
  210. package/prisma/migrations/20260130135114_add_device_models/migration.sql +57 -0
  211. package/prisma/migrations/20260130140655_make_systemport_optional/migration.sql +45 -0
  212. package/prisma/migrations/20260130140932_make_device_fields_optional/migration.sql +45 -0
  213. package/prisma/migrations/20260130141040_final_schema_fix/migration.sql +45 -0
  214. package/prisma/migrations/20260130143234_add_device_health_fields/migration.sql +4 -0
  215. package/prisma/migrations/20260130144921_add_failure_category/migration.sql +2 -0
  216. package/prisma/migrations/20260131151456_add_webhook_config/migration.sql +10 -0
  217. package/prisma/migrations/20260201094507_add_device_tags/migration.sql +11 -0
  218. package/prisma/migrations/20260201103410_add_managed_process/migration.sql +15 -0
  219. package/prisma/migrations/20260201140637_add_web_config/migration.sql +22 -0
  220. package/prisma/migrations/20260201162232_add_session_progress/migration.sql +2 -0
  221. package/prisma/migrations/20260201174231_add_total_healed_count/migration.sql +2 -0
  222. package/prisma/migrations/migration_lock.toml +3 -0
  223. package/prisma/schema.prisma +210 -0
  224. package/schema.json +348 -0
  225. package/scripts/build-xenon.sh +32 -0
  226. package/scripts/dev/debug-gemini.ts +44 -0
  227. package/scripts/generate-types-from-schema.js +86 -0
  228. package/scripts/install-compatible-driver.js +39 -0
@@ -0,0 +1,351 @@
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 __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
12
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
13
+ return new (P || (P = Promise))(function (resolve, reject) {
14
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
15
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
16
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
17
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
18
+ });
19
+ };
20
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ const node_simctl_1 = __importDefault(require("node-simctl"));
25
+ const lodash_1 = require("lodash");
26
+ const logger_1 = __importDefault(require("../logger"));
27
+ const os_1 = __importDefault(require("os"));
28
+ const path_1 = __importDefault(require("path"));
29
+ const fs_extra_1 = __importDefault(require("fs-extra"));
30
+ const child_process_1 = require("child_process");
31
+ const util_1 = require("util");
32
+ const typedi_1 = require("typedi");
33
+ const device_store_1 = require("../data-service/device-store");
34
+ const PluginContext_1 = require("../PluginContext");
35
+ const IOSStreamService_1 = __importDefault(require("./ios/IOSStreamService"));
36
+ const IOSDiscoveryService_1 = require("./ios/IOSDiscoveryService");
37
+ const WDAClient_1 = require("./ios/WDAClient");
38
+ const execPromise = (0, util_1.promisify)(child_process_1.exec);
39
+ let IOSDeviceManager = class IOSDeviceManager {
40
+ constructor(context) {
41
+ this.context = context;
42
+ this.log = logger_1.default.scope('IOSManager');
43
+ this.wdaSoftFailures = new Map();
44
+ this.WDA_SOFT_FAIL_MAX = 3;
45
+ if (process.env.NODE_ENV !== 'test') {
46
+ typedi_1.Container.get(WDAClient_1.WDAClient)
47
+ .checkRequirements()
48
+ .catch((e) => {
49
+ this.log.error(`Failed to verify ideviceinstaller requirements: ${e.message}`);
50
+ });
51
+ }
52
+ }
53
+ get pluginArgs() {
54
+ return this.context.pluginArgs;
55
+ }
56
+ getDevices(deviceTypes, existingDeviceDetails) {
57
+ return __awaiter(this, void 0, void 0, function* () {
58
+ return typedi_1.Container.get(IOSDiscoveryService_1.IOSDiscoveryService).getDevices(deviceTypes, existingDeviceDetails);
59
+ });
60
+ }
61
+ getConnectedDevices() {
62
+ return __awaiter(this, void 0, void 0, function* () {
63
+ return typedi_1.Container.get(IOSDiscoveryService_1.IOSDiscoveryService).getConnectedDevices();
64
+ });
65
+ }
66
+ getOSVersion(udid) {
67
+ return __awaiter(this, void 0, void 0, function* () {
68
+ const device = yield typedi_1.Container.get(IOSDiscoveryService_1.IOSDiscoveryService).getDeviceInfo(udid);
69
+ return device.sdk;
70
+ });
71
+ }
72
+ getDeviceName(udid) {
73
+ return __awaiter(this, void 0, void 0, function* () {
74
+ const device = yield typedi_1.Container.get(IOSDiscoveryService_1.IOSDiscoveryService).getDeviceInfo(udid);
75
+ return device.name || 'iPhone';
76
+ });
77
+ }
78
+ // Shims for legacy unit tests
79
+ getSimulators() {
80
+ return __awaiter(this, void 0, void 0, function* () {
81
+ return typedi_1.Container.get(IOSDiscoveryService_1.IOSDiscoveryService).getSimulators();
82
+ });
83
+ }
84
+ getLocalSims() {
85
+ return __awaiter(this, void 0, void 0, function* () {
86
+ return typedi_1.Container.get(IOSDiscoveryService_1.IOSDiscoveryService).fetchLocalSimulators();
87
+ });
88
+ }
89
+ getAdditionalDeviceInfo(device) {
90
+ return __awaiter(this, void 0, void 0, function* () {
91
+ this.log.info(`Fetching additional iOS device info for ${device.udid}`);
92
+ const result = {
93
+ derivedDataPath: this.prepareDerivedDataPath(this.pluginArgs.derivedDataPath, device.udid, device.realDevice),
94
+ };
95
+ const streamStatus = typedi_1.Container.get(IOSStreamService_1.default).getStreamStatus(device.udid);
96
+ if ((streamStatus === null || streamStatus === void 0 ? void 0 : streamStatus.screenWidth) && (streamStatus === null || streamStatus === void 0 ? void 0 : streamStatus.screenHeight)) {
97
+ result.screenWidth = String(streamStatus.screenWidth);
98
+ result.screenHeight = String(streamStatus.screenHeight);
99
+ }
100
+ else {
101
+ const storeDevice = yield device_store_1.DeviceStoreFactory.getStore().findDevice({ udid: device.udid });
102
+ if (storeDevice === null || storeDevice === void 0 ? void 0 : storeDevice.screenWidth) {
103
+ result.screenWidth = storeDevice.screenWidth;
104
+ result.screenHeight = storeDevice.screenHeight;
105
+ }
106
+ }
107
+ return result;
108
+ });
109
+ }
110
+ prepareDerivedDataPath(derivedDataPath, udid, realDevice) {
111
+ const tmpPath = path_1.default.join(os_1.default.homedir(), `Library/Developer/Xcode/DerivedData/WebDriverAgent-${udid}`);
112
+ if (derivedDataPath) {
113
+ const source = realDevice ? derivedDataPath.device : derivedDataPath.simulator;
114
+ if (source)
115
+ fs_extra_1.default.copySync(source, tmpPath);
116
+ else if (!fs_extra_1.default.existsSync(tmpPath))
117
+ fs_extra_1.default.mkdirSync(tmpPath, { recursive: true });
118
+ return tmpPath;
119
+ }
120
+ return tmpPath;
121
+ }
122
+ // WDA Interactions - Delegated to WDAClient
123
+ tap(udid, x, y) {
124
+ return __awaiter(this, void 0, void 0, function* () {
125
+ return typedi_1.Container.get(WDAClient_1.WDAClient).tap(udid, x, y);
126
+ });
127
+ }
128
+ swipe(udid, x, y, endX, endY, duration) {
129
+ return __awaiter(this, void 0, void 0, function* () {
130
+ return typedi_1.Container.get(WDAClient_1.WDAClient).swipe(udid, x, y, endX, endY, duration);
131
+ });
132
+ }
133
+ typeText(udid, text) {
134
+ return __awaiter(this, void 0, void 0, function* () {
135
+ return typedi_1.Container.get(WDAClient_1.WDAClient).typeText(udid, text);
136
+ });
137
+ }
138
+ pressKey(udid, keyCode) {
139
+ return __awaiter(this, void 0, void 0, function* () {
140
+ return typedi_1.Container.get(WDAClient_1.WDAClient).pressKey(udid, keyCode);
141
+ });
142
+ }
143
+ getScreenshot(udid) {
144
+ return __awaiter(this, void 0, void 0, function* () {
145
+ return typedi_1.Container.get(WDAClient_1.WDAClient).getScreenshot(udid);
146
+ });
147
+ }
148
+ getClipboard(udid) {
149
+ return __awaiter(this, void 0, void 0, function* () {
150
+ return typedi_1.Container.get(WDAClient_1.WDAClient).getClipboard(udid);
151
+ });
152
+ }
153
+ setClipboard(udid, content) {
154
+ return __awaiter(this, void 0, void 0, function* () {
155
+ return typedi_1.Container.get(WDAClient_1.WDAClient).setClipboard(udid, content);
156
+ });
157
+ }
158
+ lock(udid) {
159
+ return __awaiter(this, void 0, void 0, function* () {
160
+ return typedi_1.Container.get(WDAClient_1.WDAClient).lock(udid);
161
+ });
162
+ }
163
+ unlock(udid) {
164
+ return __awaiter(this, void 0, void 0, function* () {
165
+ return typedi_1.Container.get(WDAClient_1.WDAClient).unlock(udid);
166
+ });
167
+ }
168
+ installApp(udid, appPath) {
169
+ return __awaiter(this, void 0, void 0, function* () {
170
+ return typedi_1.Container.get(WDAClient_1.WDAClient).installApp(udid, appPath);
171
+ });
172
+ }
173
+ uninstallApp(udid, bundleId) {
174
+ return __awaiter(this, void 0, void 0, function* () {
175
+ return typedi_1.Container.get(WDAClient_1.WDAClient).uninstallApp(udid, bundleId);
176
+ });
177
+ }
178
+ listApps(udid) {
179
+ return __awaiter(this, void 0, void 0, function* () {
180
+ return typedi_1.Container.get(WDAClient_1.WDAClient).listApps(udid);
181
+ });
182
+ }
183
+ getLogs(udid) {
184
+ return __awaiter(this, void 0, void 0, function* () {
185
+ return typedi_1.Container.get(WDAClient_1.WDAClient).getLogs(udid);
186
+ });
187
+ }
188
+ getPageSource(udid) {
189
+ return __awaiter(this, void 0, void 0, function* () {
190
+ var _a;
191
+ this.log.info(`iOS getPageSource on ${udid}`);
192
+ try {
193
+ const res = yield typedi_1.Container.get(WDAClient_1.WDAClient).sendWDACommand(udid, 'get', '/source');
194
+ return ((_a = res === null || res === void 0 ? void 0 : res.data) === null || _a === void 0 ? void 0 : _a.value) || '';
195
+ }
196
+ catch (e) {
197
+ this.log.error(`Failed to get iOS page source for ${udid}: ${e.message}`);
198
+ return '';
199
+ }
200
+ });
201
+ }
202
+ checkHealth(device) {
203
+ return __awaiter(this, void 0, void 0, function* () {
204
+ if (device.cloud)
205
+ return { healthStatus: 'Healthy' };
206
+ try {
207
+ if (device.realDevice) {
208
+ // Collect hardware stats via WDAClient (which uses go-ios if available)
209
+ // For simplicity, we'll keep some health check logic here but use the client for WDA status
210
+ const isReady = yield typedi_1.Container.get(WDAClient_1.WDAClient).verifyWDAStatus(device.udid);
211
+ if (isReady)
212
+ return { healthStatus: 'Healthy' };
213
+ // Principal Intelligence: If the device is IDLE (not busy) and WDA is down,
214
+ // check if this is expected (e.g. watchdog stopped the stream).
215
+ if (!device.busy) {
216
+ const streamService = typedi_1.Container.get(IOSStreamService_1.default);
217
+ const streamStatus = streamService.getStreamStatus(device.udid);
218
+ if (!streamStatus ||
219
+ (streamStatus.status !== 'running' && streamStatus.status !== 'starting')) {
220
+ return { healthStatus: 'Healthy', healthCheckError: 'Idle (WDA Stopped)' };
221
+ }
222
+ }
223
+ return { healthStatus: 'Unhealthy', healthCheckError: 'WDA not responding' };
224
+ }
225
+ else {
226
+ const simctl = new node_simctl_1.default();
227
+ const list = yield simctl.list();
228
+ const sim = (0, lodash_1.flatten)(Object.values(list.devices)).find((s) => s.udid === device.udid);
229
+ return sim && ['Booted', 'Shutdown'].includes(sim.state)
230
+ ? { healthStatus: 'Healthy' }
231
+ : { healthStatus: 'Unhealthy', healthCheckError: `Simulator state: ${sim === null || sim === void 0 ? void 0 : sim.state}` };
232
+ }
233
+ }
234
+ catch (err) {
235
+ return { healthStatus: 'Unhealthy', healthCheckError: err.message };
236
+ }
237
+ });
238
+ }
239
+ readyForSession(device) {
240
+ return __awaiter(this, void 0, void 0, function* () {
241
+ var _a;
242
+ if (device.cloud)
243
+ return true;
244
+ this.log.info(`🚀 [SessionStart] Verifying readiness for ${device.udid}...`);
245
+ const streamService = typedi_1.Container.get(IOSStreamService_1.default);
246
+ const streamStatus = streamService.getStreamStatus(device.udid);
247
+ if ((streamStatus === null || streamStatus === void 0 ? void 0 : streamStatus.status) === 'starting') {
248
+ const start = Date.now();
249
+ while (Date.now() - start < 30000) {
250
+ if (((_a = streamService.getStreamStatus(device.udid)) === null || _a === void 0 ? void 0 : _a.status) === 'running')
251
+ return true;
252
+ yield new Promise((r) => setTimeout(r, 2000));
253
+ }
254
+ }
255
+ let healthy = yield typedi_1.Container.get(WDAClient_1.WDAClient).verifyWDAStatus(device.udid);
256
+ if (!healthy) {
257
+ this.log.warn(`⚠️ [SessionStart] ${device.udid} is UNHEALTHY. Recovering...`);
258
+ if (yield this.recoverHealth(device)) {
259
+ const start = Date.now();
260
+ while (Date.now() - start < 15000) {
261
+ healthy = yield typedi_1.Container.get(WDAClient_1.WDAClient).verifyWDAStatus(device.udid);
262
+ if (healthy)
263
+ break;
264
+ yield new Promise((r) => setTimeout(r, 2000));
265
+ }
266
+ }
267
+ }
268
+ return healthy;
269
+ });
270
+ }
271
+ recoverHealth(device) {
272
+ return __awaiter(this, void 0, void 0, function* () {
273
+ if (device.cloud)
274
+ return true;
275
+ try {
276
+ if (device.realDevice) {
277
+ if (device.busy && device.session_id)
278
+ return false;
279
+ const streamService = typedi_1.Container.get(IOSStreamService_1.default);
280
+ const streamStatus = streamService.getStreamStatus(device.udid);
281
+ // If the stream process is alive, check whether WDA is actually responding.
282
+ // A running stream with an unresponsive WDA creates an infinite unhealthy loop
283
+ // if we blindly skip recovery.
284
+ if (streamStatus &&
285
+ (streamStatus.status === 'starting' || streamStatus.status === 'running')) {
286
+ // If still starting, give it more time
287
+ if (streamStatus.status === 'starting') {
288
+ this.log.debug(`[${device.udid}] Stream is starting, waiting for WDA to come up`);
289
+ return true;
290
+ }
291
+ // Stream is running — verify WDA health
292
+ const wdaOk = yield typedi_1.Container.get(WDAClient_1.WDAClient).verifyWDAStatus(device.udid);
293
+ if (wdaOk) {
294
+ this.log.debug(`[${device.udid}] Stream running and WDA healthy, no recovery needed`);
295
+ this.wdaSoftFailures.delete(device.udid);
296
+ return true;
297
+ }
298
+ // WDA is down despite stream running. Use soft-failure counter to avoid
299
+ // thrashing restarts on transient hiccups.
300
+ const failCount = (this.wdaSoftFailures.get(device.udid) || 0) + 1;
301
+ this.wdaSoftFailures.set(device.udid, failCount);
302
+ if (failCount < this.WDA_SOFT_FAIL_MAX) {
303
+ this.log.debug(`[${device.udid}] WDA unresponsive (soft fail ${failCount}/${this.WDA_SOFT_FAIL_MAX}), deferring restart`);
304
+ return false; // Report failure so HealthMonitor doesn't claim success
305
+ }
306
+ // Exceeded threshold — force restart
307
+ this.log.warn(`[${device.udid}] WDA unresponsive after ${failCount} checks. Force-restarting stream...`);
308
+ this.wdaSoftFailures.delete(device.udid);
309
+ try {
310
+ yield streamService.stopStream(device.udid);
311
+ }
312
+ catch (e) {
313
+ this.log.debug(`[${device.udid}] Error stopping stream: ${e.message}`);
314
+ }
315
+ yield new Promise((r) => setTimeout(r, 2000));
316
+ }
317
+ this.log.info(`🛡️ Auto-recovery for iOS device ${device.udid}...`);
318
+ yield streamService.startStream(device.udid);
319
+ return true;
320
+ }
321
+ else {
322
+ const simctl = new node_simctl_1.default();
323
+ simctl.udid = device.udid;
324
+ try {
325
+ yield simctl.shutdownDevice();
326
+ yield new Promise((r) => setTimeout(r, 2000));
327
+ }
328
+ catch (e) {
329
+ this.log.error('IOSDeviceManager: Error shutting down iOS device', e);
330
+ }
331
+ yield simctl.bootDevice();
332
+ return true;
333
+ }
334
+ }
335
+ catch (err) {
336
+ this.log.error(`Auto-recovery failed for ${device.udid}: ${err.message}`);
337
+ return false;
338
+ }
339
+ });
340
+ }
341
+ executeShell(udid, command) {
342
+ return __awaiter(this, void 0, void 0, function* () {
343
+ return typedi_1.Container.get(WDAClient_1.WDAClient).executeShell(udid, command);
344
+ });
345
+ }
346
+ };
347
+ IOSDeviceManager = __decorate([
348
+ (0, typedi_1.Service)(),
349
+ __metadata("design:paramtypes", [PluginContext_1.PluginContext])
350
+ ], IOSDeviceManager);
351
+ exports.default = IOSDeviceManager;
@@ -0,0 +1,82 @@
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 logger_1 = __importDefault(require("../logger"));
16
+ const InternalHttpClient_1 = require("../InternalHttpClient");
17
+ class NodeDevices {
18
+ constructor(host, tlsRejectUnauthorized) {
19
+ this.host = host;
20
+ this.tlsRejectUnauthorized = tlsRejectUnauthorized;
21
+ }
22
+ postDevicesToHub(devices, arg) {
23
+ return __awaiter(this, void 0, void 0, function* () {
24
+ // DeviceWithPath -> new device
25
+ // DeviceUpdate -> removed device
26
+ logger_1.default.info(`Updating remote android devices ${this.host}/xenon/api/register`);
27
+ try {
28
+ const client = InternalHttpClient_1.InternalHttpClient.getClient(this.tlsRejectUnauthorized);
29
+ yield client.post(`${this.host}/xenon/api/register`, devices, {
30
+ params: {
31
+ type: arg,
32
+ },
33
+ });
34
+ if (arg === 'add') {
35
+ logger_1.default.info(`Pushed devices to hub ${JSON.stringify(devices)}`);
36
+ }
37
+ else {
38
+ logger_1.default.info(`Removed device and pushed information to hub ${JSON.stringify(devices)}`);
39
+ }
40
+ }
41
+ catch (error) {
42
+ logger_1.default.error(`Unable to push devices update to hub. Reason: ${error}`);
43
+ }
44
+ });
45
+ }
46
+ unblockDevice(filter) {
47
+ return __awaiter(this, void 0, void 0, function* () {
48
+ logger_1.default.info(`Unblocking device ${this.host}/xenon/api/unblock`);
49
+ try {
50
+ const client = InternalHttpClient_1.InternalHttpClient.getClient(this.tlsRejectUnauthorized);
51
+ yield client.post(`${this.host}/xenon/api/unblock`, filter, {
52
+ params: {
53
+ type: 'unblock',
54
+ },
55
+ });
56
+ logger_1.default.info(`Unblocked device with filter: ${JSON.stringify(filter)}`);
57
+ }
58
+ catch (error) {
59
+ logger_1.default.error(`Unable to unblock device. Reason: ${error}`);
60
+ }
61
+ });
62
+ }
63
+ unRegisterNode(host) {
64
+ return __awaiter(this, void 0, void 0, function* () {
65
+ logger_1.default.info(`Unregistering node ${this.host}/xenon/api/register`);
66
+ try {
67
+ const client = InternalHttpClient_1.InternalHttpClient.getClient(this.tlsRejectUnauthorized);
68
+ yield client.post(`${this.host}/xenon/api/register`, [], {
69
+ params: {
70
+ type: 'unregister',
71
+ host,
72
+ },
73
+ });
74
+ logger_1.default.info(`Unregistered node ${host} from hub`);
75
+ }
76
+ catch (error) {
77
+ logger_1.default.error(`Unable to unregister node from hub. Reason: ${error}`);
78
+ }
79
+ });
80
+ }
81
+ }
82
+ exports.default = NodeDevices;