@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,225 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.XenonLogger = void 0;
13
+ exports.redactSecrets = redactSecrets;
14
+ const support_1 = require("@appium/support");
15
+ /**
16
+ * Keys whose values must never appear in log output.
17
+ * Case-insensitive substring matching is used so 'myApiKey' and 'API_KEY' both match.
18
+ */
19
+ const SENSITIVE_KEY_PATTERNS = [
20
+ 'apikey',
21
+ 'api_key',
22
+ 'secret',
23
+ 'password',
24
+ 'token',
25
+ 'accesstoken',
26
+ 'refreshtoken',
27
+ 'dburl',
28
+ 'databaseurl',
29
+ 'clientsecret',
30
+ 'privatekey',
31
+ 'auth',
32
+ 'credentials',
33
+ 'secretkey',
34
+ ];
35
+ const REDACTED = '***REDACTED***';
36
+ function isSensitiveKey(key) {
37
+ const lower = key.toLowerCase().replace(/[_-]/g, '');
38
+ return SENSITIVE_KEY_PATTERNS.some((p) => lower.includes(p));
39
+ }
40
+ /**
41
+ * Deep-clone an object, replacing values of sensitive keys with ***REDACTED***.
42
+ * Safe to call before JSON.stringify for logging.
43
+ */
44
+ function redactSecrets(obj) {
45
+ if (obj === null || obj === undefined) {
46
+ return obj;
47
+ }
48
+ // Deep Intelligence: also redact keys from strings if they look like our API keys
49
+ if (typeof obj === 'string') {
50
+ let result = obj;
51
+ // Common pattern for Gemini/OpenAI/Anthropic keys in logs
52
+ const keyPatterns = [/AIza[a-zA-Z0-9-_]{35}/g, /sk-[a-zA-Z0-9]{32,}/g];
53
+ for (const pattern of keyPatterns) {
54
+ result = result.split(pattern).join(REDACTED);
55
+ }
56
+ return result;
57
+ }
58
+ if (typeof obj !== 'object') {
59
+ return obj;
60
+ }
61
+ const seen = new WeakSet();
62
+ const redact = (value) => {
63
+ if (value === null || value === undefined)
64
+ return value;
65
+ if (typeof value === 'string') {
66
+ let s = value;
67
+ const keyPatterns = [/AIza[a-zA-Z0-9-_]{35}/g, /sk-[a-zA-Z0-9]{32,}/g];
68
+ for (const pattern of keyPatterns) {
69
+ s = s.replace(pattern, REDACTED);
70
+ }
71
+ return s;
72
+ }
73
+ if (typeof value !== 'object')
74
+ return value;
75
+ if (seen.has(value)) {
76
+ return '[Circular]';
77
+ }
78
+ seen.add(value);
79
+ if (Array.isArray(value)) {
80
+ return value.map((item) => redact(item));
81
+ }
82
+ const result = {};
83
+ for (const [key, v] of Object.entries(value)) {
84
+ if (isSensitiveKey(key)) {
85
+ result[key] = REDACTED;
86
+ }
87
+ else {
88
+ result[key] = redact(v);
89
+ }
90
+ }
91
+ return result;
92
+ };
93
+ return redact(obj);
94
+ }
95
+ /**
96
+ * Enterprise-grade logger for Xenon.
97
+ * Wraps @appium/support logger with contextual capabilities.
98
+ */
99
+ class XenonLogger {
100
+ constructor(prefix = 'xenon') {
101
+ this.context = '';
102
+ this.baseLogger = support_1.logger.getLogger(prefix);
103
+ }
104
+ /**
105
+ * Configure global logger settings
106
+ */
107
+ static configure(options) {
108
+ if (options.enableJsonLogging !== undefined) {
109
+ this.isJsonLogging = options.enableJsonLogging;
110
+ }
111
+ }
112
+ /**
113
+ * Creates a sub-logger with a specific component scope.
114
+ * Example: log.scope('DeviceManager') -> [DeviceManager] Info Message
115
+ */
116
+ scope(name) {
117
+ const scoped = new XenonLogger();
118
+ scoped.baseLogger = this.baseLogger;
119
+ scoped.context = `[${name}] `;
120
+ return scoped;
121
+ }
122
+ /**
123
+ * Attaches session context to the logger.
124
+ * Example: log.withSession('abc-123') -> [abc-123] Message
125
+ */
126
+ withSession(sessionId, udid) {
127
+ const sessionLogger = new XenonLogger();
128
+ sessionLogger.baseLogger = this.baseLogger;
129
+ const udidPart = udid ? `:${udid}` : '';
130
+ sessionLogger.context = `${this.context}[${sessionId}${udidPart}] `;
131
+ return sessionLogger;
132
+ }
133
+ logMessage(level, message, ...args) {
134
+ const redactedMessage = redactSecrets(message);
135
+ const redactedArgs = args.map((arg) => redactSecrets(arg));
136
+ if (XenonLogger.isJsonLogging) {
137
+ const logEntry = {
138
+ timestamp: new Date().toISOString(),
139
+ level,
140
+ scope: this.context.trim() || 'root',
141
+ message: this.format(redactedMessage),
142
+ args: redactedArgs.length ? redactedArgs : undefined,
143
+ };
144
+ this.baseLogger.info(JSON.stringify(logEntry));
145
+ }
146
+ else {
147
+ this.baseLogger[level](`${this.context}${this.format(redactedMessage)}`, ...redactedArgs);
148
+ }
149
+ }
150
+ info(message, ...args) {
151
+ this.logMessage('info', message, ...args);
152
+ }
153
+ warn(message, ...args) {
154
+ this.logMessage('warn', message, ...args);
155
+ }
156
+ error(message, ...args) {
157
+ this.logMessage('error', message, ...args);
158
+ }
159
+ debug(message, ...args) {
160
+ this.logMessage('debug', message, ...args);
161
+ }
162
+ /**
163
+ * Enterprise Audit entry.
164
+ */
165
+ audit(action, actor = 'system', details = {}) {
166
+ const redactedDetails = redactSecrets(details);
167
+ const detailStr = Object.keys(redactedDetails).length
168
+ ? ` | Details: ${JSON.stringify(redactedDetails)}`
169
+ : '';
170
+ this.baseLogger.info(`[AUDIT] ${this.context}${action} | Actor: ${actor}${detailStr}`);
171
+ }
172
+ /**
173
+ * Performance instrumentation.
174
+ */
175
+ monitor(label, task) {
176
+ return __awaiter(this, void 0, void 0, function* () {
177
+ const start = Date.now();
178
+ try {
179
+ this.debug(`[PERF] Start: ${label}`);
180
+ const result = yield task();
181
+ const elapsed = Date.now() - start;
182
+ this.debug(`[PERF] End: ${label} | Elapsed: ${elapsed}ms`);
183
+ return result;
184
+ }
185
+ catch (err) {
186
+ const elapsed = Date.now() - start;
187
+ this.error(`[PERF] Failed: ${label} | Elapsed: ${elapsed}ms | Error: ${err.message}`);
188
+ throw err;
189
+ }
190
+ });
191
+ }
192
+ format(message) {
193
+ if (typeof message === 'object') {
194
+ try {
195
+ return JSON.stringify(message);
196
+ }
197
+ catch (e) {
198
+ return String(message);
199
+ }
200
+ }
201
+ return String(message);
202
+ }
203
+ banner(version, nodeId) {
204
+ const splash = `
205
+ \x1b[36m
206
+ __ __ ______ __ __ ______ __ __
207
+ /\\_\\_\\_\\ /\\ ___\\ /\\ "-.\\ \\ /\\ __ \\ /\\ "-.\\ \\
208
+ \\/_/\\_\\/_\\\\ \\ __\\ \\ \\ \\-. \\\\ \\ \\/\\ \\\\ \\ \\-. \\
209
+ /\\_\\_\\_\\ \\ \\_____\\\\ \\_\\\\"\\_\\\\ \\_____\\\\ \\_\\\\"\\_\\
210
+ \\/_/_/_/ \\/_____/ \\/_/ \\/_/ \\/_____/ \\/_/ \\/_/
211
+ \x1b[0m
212
+ :: \x1b[32mXenon Mobile Infrastructure\x1b[0m :: (v${version})
213
+
214
+ [Node ID: ${nodeId}]
215
+ [Status: Operational]
216
+ -------------------------------------------------------------------
217
+ `;
218
+ this.baseLogger.info(splash);
219
+ }
220
+ }
221
+ exports.XenonLogger = XenonLogger;
222
+ XenonLogger.isJsonLogging = process.env.XENON_JSON_LOGGING === 'true';
223
+ // Initial main instance
224
+ const mainLogger = new XenonLogger('xenon');
225
+ exports.default = mainLogger;
@@ -0,0 +1,244 @@
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.XenonPlugin = void 0;
49
+ /* eslint-disable no-prototype-builtins */
50
+ require("reflect-metadata");
51
+ const index_1 = __importDefault(require("./commands/index"));
52
+ const base_plugin_1 = __importDefault(require("@appium/base-plugin"));
53
+ const device_service_1 = require("./data-service/device-service");
54
+ const typedi_1 = require("typedi");
55
+ const logger_1 = __importStar(require("./logger"));
56
+ const ip_1 = __importDefault(require("ip"));
57
+ const SessionStatus_1 = require("./types/SessionStatus");
58
+ const AICommandService_1 = require("./services/AICommandService");
59
+ const TracingService_1 = require("./services/TracingService");
60
+ const CommandInterceptor_1 = require("./interceptors/CommandInterceptor");
61
+ const SessionLifecycleService_1 = require("./services/SessionLifecycleService");
62
+ const ServerManager_1 = require("./services/ServerManager");
63
+ const IPluginArgs_1 = require("./interfaces/IPluginArgs");
64
+ const NodeDevices_1 = __importDefault(require("./device-managers/NodeDevices"));
65
+ const SessionManager_1 = require("./sessions/SessionManager");
66
+ const event_manager_1 = require("./dashboard/event-manager");
67
+ const asset_manager_1 = require("./dashboard/asset-manager");
68
+ const session_service_1 = require("./dashboard/services/session-service");
69
+ const DEVICE_MANAGER_LOCK_NAME = 'DeviceManager';
70
+ class XenonPlugin extends base_plugin_1.default {
71
+ constructor(pluginName, cliArgs) {
72
+ super(pluginName, cliArgs);
73
+ this.xenonLog = logger_1.default.scope('Plugin');
74
+ this.pluginArgs = Object.assign({}, IPluginArgs_1.DefaultPluginArgs);
75
+ this.aiCommandService = typedi_1.Container.get(AICommandService_1.AICommandService);
76
+ this.xenonLog.debug(`📱 Plugin Args: ${JSON.stringify(cliArgs)}`);
77
+ this.pluginArgs = Object.assign({}, IPluginArgs_1.DefaultPluginArgs, this.cliArgs);
78
+ logger_1.XenonLogger.configure({ enableJsonLogging: this.pluginArgs.enableJsonLogging });
79
+ if (this.pluginArgs.bindHostOrIp === undefined) {
80
+ this.pluginArgs.bindHostOrIp = ip_1.default.address();
81
+ }
82
+ }
83
+ /**
84
+ * Intercepts all commands for local sessions to capture logs
85
+ */
86
+ handle(next, driver, commandName, ...args) {
87
+ return __awaiter(this, void 0, void 0, function* () {
88
+ return yield typedi_1.Container.get(CommandInterceptor_1.CommandInterceptor).handle(next, driver, commandName, args, this.pluginArgs, XenonPlugin.IS_HUB);
89
+ });
90
+ }
91
+ onUnexpectedShutdown(driver, _cause) {
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ var _a;
94
+ const sessionId = driver.sessionId;
95
+ const deviceFilter = {
96
+ session_id: sessionId ? sessionId : undefined,
97
+ udid: driver.caps && driver.caps.udid ? driver.caps.udid : undefined,
98
+ };
99
+ if (this.pluginArgs.hub !== undefined) {
100
+ yield new NodeDevices_1.default(this.pluginArgs.hub, this.pluginArgs.tlsRejectUnauthorized).unblockDevice(deviceFilter);
101
+ }
102
+ else {
103
+ yield (0, device_service_1.unblockDeviceMatchingFilter)(deviceFilter);
104
+ }
105
+ logger_1.default.info(`Unblocking device mapped with filter ${JSON.stringify(deviceFilter)} onUnexpectedShutdown from server`);
106
+ if (XenonPlugin.IS_HUB && this.pluginArgs.enableDashboard && sessionId) {
107
+ const sessionLog = this.xenonLog.withSession(sessionId, (_a = driver.caps) === null || _a === void 0 ? void 0 : _a.udid);
108
+ sessionLog.info('Unexpected shutdown for session, updating dashboard...');
109
+ const session = SessionManager_1.SESSION_MANAGER.getSession(sessionId);
110
+ if (session && session.isVideoRecordingInProgress()) {
111
+ try {
112
+ const videoData = yield session.stopVideoRecording(driver);
113
+ if (videoData) {
114
+ let videoPath = videoData;
115
+ // Principal Heuristic: If it's data (>1000 chars), save it. If it's short, it's a path.
116
+ if (videoData.length > 1000) {
117
+ videoPath = (0, asset_manager_1.saveVideoRecording)(sessionId, videoData);
118
+ }
119
+ yield (0, session_service_1.updateSessionDetails)(sessionId, { video_recording: videoPath });
120
+ }
121
+ }
122
+ catch (err) {
123
+ logger_1.default.debug(`[${sessionId}] rescue-video: Failed: ${err.message}`);
124
+ }
125
+ }
126
+ yield event_manager_1.DASHBORD_EVENT_MANAGER.onSessionStopped(sessionId, SessionStatus_1.SessionStatus.FAILED, 'Driver shut down unexpectedly');
127
+ typedi_1.Container.get(TracingService_1.TracingService).endSpan(sessionId, 'ERROR', {
128
+ 'xenon.session.stop_reason': 'Unexpected shutdown',
129
+ });
130
+ }
131
+ });
132
+ }
133
+ static updateServer(expressApp, httpServer, cliArgs) {
134
+ return __awaiter(this, void 0, void 0, function* () {
135
+ yield typedi_1.Container.get(ServerManager_1.ServerManager).updateServer(expressApp, httpServer, cliArgs);
136
+ XenonPlugin.IS_HUB = ServerManager_1.ServerManager.IS_HUB;
137
+ });
138
+ }
139
+ createSession(next, driver, jwpDesCaps, jwpReqCaps, caps) {
140
+ return __awaiter(this, void 0, void 0, function* () {
141
+ return yield typedi_1.Container.get(SessionLifecycleService_1.SessionLifecycleService).createSession(next, driver, caps);
142
+ });
143
+ }
144
+ deleteSession(next, driver, sessionId) {
145
+ return __awaiter(this, void 0, void 0, function* () {
146
+ return yield typedi_1.Container.get(SessionLifecycleService_1.SessionLifecycleService).deleteSession(next, sessionId || (driver === null || driver === void 0 ? void 0 : driver.sessionId));
147
+ });
148
+ }
149
+ analyzeScreen(driver) {
150
+ return __awaiter(this, void 0, void 0, function* () {
151
+ return yield this.aiCommandService.analyzeScreen(driver);
152
+ });
153
+ }
154
+ assertVisualState(driver, instruction) {
155
+ return __awaiter(this, void 0, void 0, function* () {
156
+ return yield this.aiCommandService.assertVisualState(driver, instruction);
157
+ });
158
+ }
159
+ omniScan(driver) {
160
+ return __awaiter(this, void 0, void 0, function* () {
161
+ return yield this.aiCommandService.omniScan(driver);
162
+ });
163
+ }
164
+ testAiLocator(driver, locator) {
165
+ return __awaiter(this, void 0, void 0, function* () {
166
+ return yield this.aiCommandService.testAiLocator(driver, locator);
167
+ });
168
+ }
169
+ /**
170
+ * Omni-Click: OCR-driven smart click by visible text.
171
+ * Accepts an object payload for compatibility with common plugin patterns.
172
+ */
173
+ omniClick(driver, payload) {
174
+ return __awaiter(this, void 0, void 0, function* () {
175
+ return yield this.aiCommandService.omniClick(driver, payload);
176
+ });
177
+ }
178
+ /**
179
+ * UI Scan Export: returns UI metadata JSON derived from screenshot OCR + lightweight heuristics.
180
+ */
181
+ uiScanExport(driver, payload) {
182
+ return __awaiter(this, void 0, void 0, function* () {
183
+ return yield this.aiCommandService.uiScanExport(driver, payload);
184
+ });
185
+ }
186
+ /**
187
+ * Smart Tap: Xenon-native, self-explanatory alias of omniClick.
188
+ */
189
+ smartTap(driver, payload) {
190
+ return __awaiter(this, void 0, void 0, function* () {
191
+ return yield this.aiCommandService.smartTap(driver, payload);
192
+ });
193
+ }
194
+ /**
195
+ * UI Inventory: Xenon-native, self-explanatory alias of uiScanExport.
196
+ */
197
+ uiInventory(driver, payload) {
198
+ return __awaiter(this, void 0, void 0, function* () {
199
+ return yield this.aiCommandService.uiInventory(driver, payload);
200
+ });
201
+ }
202
+ }
203
+ exports.XenonPlugin = XenonPlugin;
204
+ XenonPlugin.nodeBasePath = '';
205
+ XenonPlugin.newMethodMap = {
206
+ '/session/:sessionId/xenon/analyze': {
207
+ POST: { command: 'analyzeScreen' },
208
+ },
209
+ '/session/:sessionId/xenon/assert': {
210
+ POST: { command: 'assertVisualState' },
211
+ },
212
+ '/session/:sessionId/xenon/omni-scan': {
213
+ GET: { command: 'omniScan' },
214
+ },
215
+ '/session/:sessionId/xenon/test-locator': {
216
+ POST: { command: 'testAiLocator' },
217
+ },
218
+ // Xenon Omni-Interaction: Enterprise-grade AI/OCR actions
219
+ '/session/:sessionId/xenon/omni-click': {
220
+ POST: { command: 'omniClick' },
221
+ },
222
+ // Xenon Smart Interaction: clearer, primary name (alias of omniClick)
223
+ '/session/:sessionId/xenon/smart-tap': {
224
+ POST: { command: 'smartTap' },
225
+ },
226
+ // Xenon UI Scan: Rich OCR-driven UI metadata export
227
+ '/session/:sessionId/xenon/ui-scan-export': {
228
+ POST: { command: 'uiScanExport' },
229
+ },
230
+ // Xenon UI Inventory: clearer, primary name (alias of uiScanExport)
231
+ '/session/:sessionId/xenon/ui-inventory': {
232
+ POST: { command: 'uiInventory' },
233
+ },
234
+ // Compatibility aliases (Lens-style client code can keep working)
235
+ // These do NOT change Xenon's public naming; they are optional shims.
236
+ '/session/:sessionId/plugin/ai-appium-lens/aiClick': {
237
+ POST: { command: 'smartTap' },
238
+ },
239
+ '/session/:sessionId/plugin/ai-appium-lens/fetchUIElementsMetadataJson': {
240
+ POST: { command: 'uiInventory' },
241
+ },
242
+ };
243
+ XenonPlugin.IS_HUB = false;
244
+ Object.assign(XenonPlugin.prototype, index_1.default);
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prisma = void 0;
4
+ const client_1 = require("@prisma/client");
5
+ const config_1 = require("./config");
6
+ exports.prisma = new client_1.PrismaClient({
7
+ datasources: {
8
+ db: {
9
+ url: config_1.config.databaseUrl,
10
+ },
11
+ },
12
+ });