@xenon-device-management/xenon 2.15.0 → 2.17.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 (206) hide show
  1. package/README.md +38 -26
  2. package/lib/package.json +2 -1
  3. package/lib/public/assets/{AnnotationOverlay-Btq3dZRI.js → AnnotationOverlay-CjmMCS0S.js} +1 -1
  4. package/lib/public/assets/{ApiKeyGate-C9OoAgg_.js → ApiKeyGate-B4Ob0v3C.js} +1 -1
  5. package/lib/public/assets/{DeviceMosaicView-DBC_xczB.js → DeviceMosaicView-DTVOB_NO.js} +1 -1
  6. package/lib/public/assets/{EmptyState-DqWU2-vM.js → EmptyState-DcEG-oAi.js} +1 -1
  7. package/lib/public/assets/{FieldGroup-CveQ3xrA.js → FieldGroup-CtnCDshE.js} +1 -1
  8. package/lib/public/assets/{FilterMenu-BfKDSfHz.js → FilterMenu-BK0dE9cc.js} +1 -1
  9. package/lib/public/assets/{Menu-CtdCybzA.js → Menu-eKsmLpKS.js} +1 -1
  10. package/lib/public/assets/{Modal-Bsvj1tm2.js → Modal-DPwop3Lz.js} +1 -1
  11. package/lib/public/assets/{RecordingPage-CRofOiyx.js → RecordingPage-CfgSkZkB.js} +1 -1
  12. package/lib/public/assets/{RecordingsPage-jNQHb2IR.js → RecordingsPage-Bzl9pTUY.js} +1 -1
  13. package/lib/public/assets/{SegmentedControl-C8HtTha-.js → SegmentedControl-B_TZTrPm.js} +1 -1
  14. package/lib/public/assets/{SettingCard-D8K1LZNo.js → SettingCard-DlmvZJPM.js} +1 -1
  15. package/lib/public/assets/{Table-zNTX92Pn.js → Table-BigPWKXi.js} +1 -1
  16. package/lib/public/assets/{activity-CJ8LwZq9.js → activity-CvuZdQYu.js} +1 -1
  17. package/lib/public/assets/{ai-settings-CgxQZqrc.js → ai-settings-lI2UJ2n7.js} +1 -1
  18. package/lib/public/assets/{api-keys-ECpo-zTo.js → api-keys-CPgf9XJM.js} +1 -1
  19. package/lib/public/assets/{apps-BjGymyf3.js → apps-DIzOvtUp.js} +1 -1
  20. package/lib/public/assets/{arrow-left-BWfCbeHP.js → arrow-left-DiP5ywBT.js} +1 -1
  21. package/lib/public/assets/{arrow-right-DC7UGf_n.js → arrow-right-DtW_8hm5.js} +1 -1
  22. package/lib/public/assets/{arrow-up-right-BOjwybDk.js → arrow-up-right-C89FbNU0.js} +1 -1
  23. package/lib/public/assets/{auth-shell-CxMTo9aH.js → auth-shell-B7sUDb--.js} +2 -2
  24. package/lib/public/assets/{builds-page-DOCm4HkA.js → builds-page-BBhvrMfA.js} +1 -1
  25. package/lib/public/assets/{button-Cg08-EMT.js → button-DugsdUw1.js} +1 -1
  26. package/lib/public/assets/{calendar-CA1ikLJs.js → calendar-grKd08ZH.js} +1 -1
  27. package/lib/public/assets/{check-BMTaH72W.js → check-ORjyNfnX.js} +1 -1
  28. package/lib/public/assets/{chevron-right-B2QOv9Nx.js → chevron-right-Ei2FxUXq.js} +1 -1
  29. package/lib/public/assets/{circle-check-CYXBoUqT.js → circle-check-Day0HjW4.js} +1 -1
  30. package/lib/public/assets/{circle-x-DTwnSAnl.js → circle-x-CHxSRLHp.js} +1 -1
  31. package/lib/public/assets/{clock-C8_HViQP.js → clock-BDHewWTx.js} +1 -1
  32. package/lib/public/assets/{copy-Be7LNMXV.js → copy-CTwT4I3D.js} +1 -1
  33. package/lib/public/assets/{device-explorer-BOqtfGXz.js → device-explorer-DlhrKJ-g.js} +1 -1
  34. package/lib/public/assets/{download-B56lwxWP.js → download-D3bjz_vr.js} +1 -1
  35. package/lib/public/assets/{forgot-password-CCU6bzLn.js → forgot-password-BNBLi023.js} +1 -1
  36. package/lib/public/assets/{h264Stream-C-OtmIuk.js → h264Stream-C51Au2i_.js} +1 -1
  37. package/lib/public/assets/index-Dk5J6HB1.js +219 -0
  38. package/lib/public/assets/{index-B9KQ6LsY.js → index-DncSz5LG.js} +1 -1
  39. package/lib/public/assets/{index-CfxsUslp.js → index-Z7_BY3pJ.js} +1 -1
  40. package/lib/public/assets/{input-CZHFjbSg.js → input-bTXM4l09.js} +1 -1
  41. package/lib/public/assets/{line-chart-Gl_Dz3X_.js → line-chart-B3YmVYyr.js} +1 -1
  42. package/lib/public/assets/{list-checks-CCgjeyGY.js → list-checks-BClKEj1-.js} +1 -1
  43. package/lib/public/assets/{lock-CkOwgDcx.js → lock-CZsenX3D.js} +1 -1
  44. package/lib/public/assets/{login-D5cc4w7M.js → login-B6A6tOp9.js} +1 -1
  45. package/lib/public/assets/{maintenance-settings-t7qr3fvX.js → maintenance-settings-CmCJGy7O.js} +1 -1
  46. package/lib/public/assets/{monitor-DEJixKQO.js → monitor-BdTNEglU.js} +1 -1
  47. package/lib/public/assets/{mouse-pointer-2-CtOrk4M8.js → mouse-pointer-2-BM8CaLtt.js} +1 -1
  48. package/lib/public/assets/{network-Dii9qdvk.js → network-BS6vUc1x.js} +1 -1
  49. package/lib/public/assets/{overview-rolAACrO.js → overview-Yfk_Y7g3.js} +1 -1
  50. package/lib/public/assets/{page-header-COQZxJfR.js → page-header-DhXCwpml.js} +1 -1
  51. package/lib/public/assets/{play-DrN1Se_R.js → play-D08gP1td.js} +1 -1
  52. package/lib/public/assets/{plus-5aq-9uRx.js → plus-BKboorfa.js} +1 -1
  53. package/lib/public/assets/{profile-page-6i5kY6kF.js → profile-page-Cd9NodrA.js} +1 -1
  54. package/lib/public/assets/{reset-password-sp2IBrc7.js → reset-password-acLDyyDW.js} +1 -1
  55. package/lib/public/assets/{runbook-page-DJDjeopU.js → runbook-page-CWLkC1Fd.js} +153 -102
  56. package/lib/public/assets/{select-Bpyn7rWO.js → select-DZoRKNti.js} +1 -1
  57. package/lib/public/assets/{selector-detail-redirect-BH74vmKv.js → selector-detail-redirect-DS4qrt84.js} +1 -1
  58. package/lib/public/assets/{selector-health-page-Cw_g218H.js → selector-health-page-TpmN61G3.js} +1 -1
  59. package/lib/public/assets/session-detail-page-C0wYaXqy.js +4 -0
  60. package/lib/public/assets/{settings-C3uyGhyc.js → settings-CxPGEY2j.js} +1 -1
  61. package/lib/public/assets/{stat-tile-xVqAKIlI.js → stat-tile-DUpyYgl-.js} +1 -1
  62. package/lib/public/assets/{tablet-BxUScAeO.js → tablet-SA02BJNQ.js} +1 -1
  63. package/lib/public/assets/{teams-B5ljGkY2.js → teams-jamD0Kxr.js} +1 -1
  64. package/lib/public/assets/{trash-2-B701i8sy.js → trash-2-DO8fv6w4.js} +1 -1
  65. package/lib/public/assets/{upload-C2gr44RS.js → upload-CaiGCStb.js} +1 -1
  66. package/lib/public/assets/{use-builds-data-NeQOFEoy.js → use-builds-data-jS-fcvVf.js} +1 -1
  67. package/lib/public/assets/{use-password-reset-mode-DEzJq5ef.js → use-password-reset-mode-BzoenegM.js} +1 -1
  68. package/lib/public/assets/{users-MY6eNe6s.js → users-Bnw7Vs_B.js} +1 -1
  69. package/lib/public/assets/{users-D_vv_yz4.js → users-DHvDrriG.js} +1 -1
  70. package/lib/public/assets/{video-off-OK1lNZQn.js → video-off-FEvy0w5U.js} +1 -1
  71. package/lib/public/assets/{webhook-settings-BU-C9lBf.js → webhook-settings-CBVB5lxi.js} +1 -1
  72. package/lib/public/assets/{zap-DO9ry1iz.js → zap-BMrTJO6M.js} +1 -1
  73. package/lib/public/index.html +1 -1
  74. package/lib/schema.json +11 -7
  75. package/lib/src/InternalHttpClient.js +27 -5
  76. package/lib/src/XenonCapabilityManager.js +10 -3
  77. package/lib/src/app/index.js +2 -0
  78. package/lib/src/app/openapi/grid.yaml +102 -0
  79. package/lib/src/app/openapi/sessions.yaml +7 -5
  80. package/lib/src/app/routers/auth.js +2 -1
  81. package/lib/src/app/routers/users.js +2 -1
  82. package/lib/src/app/ws/nodeSocketRelay.js +77 -11
  83. package/lib/src/dashboard/commandLogFields.js +42 -0
  84. package/lib/src/dashboard/event-manager.js +23 -154
  85. package/lib/src/dashboard/services/failure-analysis-service.js +8 -86
  86. package/lib/src/dashboard/services/failureCategories.js +264 -0
  87. package/lib/src/device-managers/AndroidDeviceManager.js +61 -23
  88. package/lib/src/device-managers/IOSDeviceManager.js +10 -0
  89. package/lib/src/device-utils.js +25 -8
  90. package/lib/src/gateway/forwardToNode.js +4 -11
  91. package/lib/src/gateway/hubSessionToken.js +27 -4
  92. package/lib/src/gateway/nodeAsk.js +57 -0
  93. package/lib/src/gateway/nodeSessionDeviceLogs.js +35 -0
  94. package/lib/src/gateway/nodeWebDriverUrl.js +3 -5
  95. package/lib/src/helpers/outboundProxy.js +219 -0
  96. package/lib/src/index.js +21 -0
  97. package/lib/src/interceptors/CommandInterceptor.js +13 -15
  98. package/lib/src/interceptors/xenonScripts.js +28 -7
  99. package/lib/src/logger.js +3 -1
  100. package/lib/src/plugin.js +12 -4
  101. package/lib/src/scripts/database-check.js +3 -3
  102. package/lib/src/scripts/initialize-database.js +29 -33
  103. package/lib/src/scripts/run-migrations.js +320 -25
  104. package/lib/src/services/ApiKeyService.js +16 -5
  105. package/lib/src/services/InterceptorService.js +6 -2
  106. package/lib/src/services/OrphanSweeper.js +6 -0
  107. package/lib/src/services/SelectorStateService.js +11 -7
  108. package/lib/src/services/SelectorVerificationJob.js +6 -2
  109. package/lib/src/services/SessionLifecycleService.js +52 -23
  110. package/lib/src/services/SocketClient.js +11 -0
  111. package/lib/src/services/SocketServer.js +361 -42
  112. package/lib/src/services/TeamService.js +5 -1
  113. package/lib/src/services/TracingService.js +171 -60
  114. package/lib/src/services/UserService.js +14 -3
  115. package/lib/src/services/UserSessionService.js +16 -4
  116. package/lib/src/services/healing/HealingOrchestrator.js +3 -1
  117. package/lib/src/services/identity/identityChanges.js +41 -0
  118. package/lib/src/services/interceptor/CertManager.js +50 -16
  119. package/lib/src/services/logcat/NodeDeviceLogStore.js +99 -0
  120. package/lib/src/services/logcat/NodeDeviceLogsCollector.js +164 -0
  121. package/lib/src/services/logcat/SessionDeviceLogs.js +279 -37
  122. package/lib/src/services/logcat/deviceLogBook.js +5 -4
  123. package/lib/src/services/logcat/iosDriverLog.js +178 -0
  124. package/lib/src/services/logcat/nodeDeviceLogs.js +91 -0
  125. package/lib/src/services/metrics/nodeMetrics.js +20 -49
  126. package/lib/src/services/selector-health/SelectorVisibilityResolver.js +121 -0
  127. package/lib/src/services/token/JwtKeyService.js +8 -2
  128. package/lib/src/services/token/mcpTokenTtl.js +27 -0
  129. package/lib/src/sessions/LocalSession.js +29 -1
  130. package/lib/src/sessions/RemoteSession.js +23 -4
  131. package/lib/src/sessions/SessionManager.js +33 -9
  132. package/lib/test/helpers/driver-log.js +38 -0
  133. package/lib/test/helpers/fake-http-proxy.js +106 -0
  134. package/lib/test/helpers/fake-logcat.js +80 -0
  135. package/lib/test/helpers/hub-node-pair.js +227 -0
  136. package/lib/test/helpers/otlp-probe.js +107 -0
  137. package/lib/test/integration/androidDevices.spec.js +1 -1
  138. package/lib/test/integration/ios/01iOSSimulator.spec.js +1 -1
  139. package/lib/test/integration/ios/02iOSDevices.spec.js +1 -1
  140. package/lib/test/integration/selector-state-flow.spec.js +5 -5
  141. package/lib/test/unit/XenonCapabilityManager.spec.js +23 -0
  142. package/lib/test/unit/android-adb-retry.spec.js +112 -0
  143. package/lib/test/unit/command-log-network-capture.spec.js +191 -0
  144. package/lib/test/unit/dashboard-events-device-logs.spec.js +74 -11
  145. package/lib/test/unit/dashboard-events-team-scope.spec.js +6 -3
  146. package/lib/test/unit/database-check.spec.js +1 -0
  147. package/lib/test/unit/device-utils.spec.js +13 -4
  148. package/lib/test/unit/event-log-session-data.spec.js +11 -4
  149. package/lib/test/unit/failure-analysis.spec.js +22 -8
  150. package/lib/test/unit/failure-categories.spec.js +24 -22
  151. package/lib/test/unit/failure-categorization.spec.js +393 -0
  152. package/lib/test/unit/healing-tier-gate.spec.js +3 -2
  153. package/lib/test/unit/hub-node-gateway-appium-server.spec.js +72 -1
  154. package/lib/test/unit/hub-node-iphone-readiness.spec.js +134 -0
  155. package/lib/test/unit/hub-node-session-device-logs.spec.js +194 -0
  156. package/lib/test/unit/hub-node-session-metrics.spec.js +24 -192
  157. package/lib/test/unit/hub-remote-session-heartbeat.spec.js +8 -1
  158. package/lib/test/unit/interceptor/CertManager.spec.js +89 -0
  159. package/lib/test/unit/ios-driver-log.spec.js +168 -0
  160. package/lib/test/unit/ios-simulator-session-readiness.spec.js +187 -0
  161. package/lib/test/unit/ios-simulator-session-teardown.spec.js +158 -0
  162. package/lib/test/unit/logcat-ws.spec.js +78 -37
  163. package/lib/test/unit/logger-json-levels.spec.js +44 -0
  164. package/lib/test/unit/mcp-token-ttl.spec.js +38 -0
  165. package/lib/test/unit/node-device-log-store.spec.js +102 -0
  166. package/lib/test/unit/node-device-logs-collector.spec.js +343 -0
  167. package/lib/test/unit/node-gateway-create-appium-server.spec.js +8 -1
  168. package/lib/test/unit/node-hub-session-owner.spec.js +8 -1
  169. package/lib/test/unit/node-session-device-logs-route.spec.js +103 -0
  170. package/lib/test/unit/observability-example-loki.spec.js +25 -0
  171. package/lib/test/unit/openapi-for-site.spec.js +80 -0
  172. package/lib/test/unit/outbound-proxy.spec.js +320 -0
  173. package/lib/test/unit/port-exhaustion.spec.js +2 -1
  174. package/lib/test/unit/proxy-option.spec.js +358 -0
  175. package/lib/test/unit/run-migrations-database.spec.js +440 -0
  176. package/lib/test/unit/run-migrations-fail-fast.test.js +41 -27
  177. package/lib/test/unit/run-migrations.test.js +264 -0
  178. package/lib/test/unit/selector-health-actions.spec.js +3 -1
  179. package/lib/test/unit/selector-state-service.spec.js +72 -23
  180. package/lib/test/unit/selector-verification-clean-build.spec.js +1 -1
  181. package/lib/test/unit/selector-verification-job.spec.js +15 -6
  182. package/lib/test/unit/selector-verification-missed-regression.spec.js +2 -2
  183. package/lib/test/unit/selector-visibility-resolver.spec.js +209 -0
  184. package/lib/test/unit/session-device-logs-hub-node.spec.js +340 -0
  185. package/lib/test/unit/session-device-logs-ios.spec.js +180 -0
  186. package/lib/test/unit/session-device-logs-lifecycle.spec.js +137 -0
  187. package/lib/test/unit/session-device-logs.spec.js +43 -96
  188. package/lib/test/unit/session-end-failure-analysis.spec.js +5 -5
  189. package/lib/test/unit/session-log-indexes.spec.js +16 -1
  190. package/lib/test/unit/socket-team-scope.integration.spec.js +749 -0
  191. package/lib/test/unit/socketServer.scopedEmit.spec.js +237 -5
  192. package/lib/test/unit/tracing-export-routing.spec.js +137 -0
  193. package/lib/test/unit/tracing-session-spans.spec.js +337 -0
  194. package/lib/test/unit/unexpected-shutdown-reason.spec.js +2 -2
  195. package/lib/tsconfig.tsbuildinfo +1 -1
  196. package/package.json +2 -1
  197. package/schema.json +11 -7
  198. package/scripts/dev/readme-screenshots.js +276 -23
  199. package/scripts/dev/screenshot-fixtures/logcat.js +157 -0
  200. package/scripts/dev/screenshot-fixtures/phone-screen.html +469 -0
  201. package/scripts/dev/screenshot-fixtures/session.js +242 -0
  202. package/scripts/export-openapi.js +32 -0
  203. package/scripts/lib/openapi-for-site.js +79 -0
  204. package/lib/public/assets/index-BOxe43p1.js +0 -219
  205. package/lib/public/assets/session-detail-page-AZJE6XXE.js +0 -4
  206. package/lib/src/scripts/prepare-prisma.js +0 -89
@@ -39,6 +39,10 @@ const log = logger_1.default.scope('SelectorVerification');
39
39
  * selector that was never found again was verified as fixed.
40
40
  */
41
41
  const CLEAN_BUILDS_TO_RESOLVE = 3;
42
+ /** The selector a row is about, as the socket server's scoped emit names it. */
43
+ function keyOfRow(row) {
44
+ return { strategy: row.original_strategy, selector: row.original_selector };
45
+ }
42
46
  /**
43
47
  * Periodically scans Pending SelectorState rows and promotes them to Resolved
44
48
  * once enough clean CI builds have been observed (no heals on the selector
@@ -180,7 +184,7 @@ let SelectorVerificationJob = class SelectorVerificationJob {
180
184
  return promoted;
181
185
  }));
182
186
  log.info(`[${row.id}] promoted to resolved (${row.original_strategy}=${row.original_selector})`);
183
- this.socket.emitToDashboard(SocketEvents_1.SocketEvents.SELECTOR_RESOLVED, this.serialize(updated));
187
+ void this.socket.emitToDashboardForSelector(SocketEvents_1.SocketEvents.SELECTOR_RESOLVED, this.serialize(updated), keyOfRow(row));
184
188
  });
185
189
  }
186
190
  updateProgress(row, cleanBuildCount) {
@@ -189,7 +193,7 @@ let SelectorVerificationJob = class SelectorVerificationJob {
189
193
  where: { id: row.id },
190
194
  data: { clean_builds_count: cleanBuildCount, last_event_at: new Date() },
191
195
  });
192
- this.socket.emitToDashboard(SocketEvents_1.SocketEvents.SELECTOR_PROGRESS, this.serialize(updated));
196
+ void this.socket.emitToDashboardForSelector(SocketEvents_1.SocketEvents.SELECTOR_PROGRESS, this.serialize(updated), keyOfRow(row));
193
197
  });
194
198
  }
195
199
  serialize(row) {
@@ -56,8 +56,6 @@ const typedi_1 = require("typedi");
56
56
  const uuid_1 = require("uuid");
57
57
  const lodash_1 = __importDefault(require("lodash"));
58
58
  const axios_1 = __importDefault(require("axios"));
59
- const https_proxy_agent_1 = require("https-proxy-agent");
60
- const http_proxy_agent_1 = require("http-proxy-agent");
61
59
  const logger_1 = __importDefault(require("../logger"));
62
60
  const helpers_1 = require("../helpers");
63
61
  const InternalHttpClient_1 = require("../InternalHttpClient");
@@ -81,6 +79,7 @@ const appVisibility_1 = require("./device-access/appVisibility");
81
79
  const LiveSessionOwners_1 = require("./device-access/LiveSessionOwners");
82
80
  const sessionMemory_1 = require("../sessions/sessionMemory");
83
81
  const SessionMetricsService_1 = require("./metrics/SessionMetricsService");
82
+ const SessionDeviceLogs_1 = require("./logcat/SessionDeviceLogs");
84
83
  const appCapability_1 = require("./session/appCapability");
85
84
  const CircuitBreaker_1 = require("../data-service/CircuitBreaker");
86
85
  const nodeWebDriverUrl_1 = require("../gateway/nodeWebDriverUrl");
@@ -848,7 +847,7 @@ let SessionLifecycleService = class SessionLifecycleService {
848
847
  }
849
848
  finalizeSession(session_1, device_1, caps_1, driver_1, isRemote_1) {
850
849
  return __awaiter(this, arguments, void 0, function* (session, device, caps, driver, isRemote, apiKeyId = null, userId = null, allocatedAt) {
851
- var _a;
850
+ var _a, _b;
852
851
  const sessionId = session.value[0];
853
852
  const sessionResponse = session.value[1];
854
853
  const requestedCaps = Object.assign({}, ((_a = caps.firstMatch) === null || _a === void 0 ? void 0 : _a[0]) || {}, caps.alwaysMatch || {});
@@ -865,6 +864,23 @@ let SessionLifecycleService = class SessionLifecycleService {
865
864
  'xenon.udid': device.udid,
866
865
  });
867
866
  }
867
+ try {
868
+ yield this.registerSession(sessionId, sessionResponse, device, xenonCapabilities, driver, isRemote, apiKeyId, userId, allocatedAt);
869
+ }
870
+ catch (err) {
871
+ // The create fails, so no ending of the session will come to end it.
872
+ tracingService.endSessionSpan(sessionId, {
873
+ failed: true,
874
+ reason: `Xenon could not finish creating the session: ${(_b = err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : err}`,
875
+ });
876
+ throw err;
877
+ }
878
+ });
879
+ }
880
+ /** The rest of finalizeSession, once the session's span has started. */
881
+ registerSession(sessionId, sessionResponse, device, xenonCapabilities, driver, isRemote, apiKeyId, userId, allocatedAt) {
882
+ return __awaiter(this, void 0, void 0, function* () {
883
+ const context = typedi_1.Container.get(PluginContext_1.PluginContext);
868
884
  // The session takes over the pending claim its phone was allocated with.
869
885
  yield (0, device_service_1.claimDeviceForSession)(device, sessionId, {
870
886
  session_id: sessionId,
@@ -889,15 +905,26 @@ let SessionLifecycleService = class SessionLifecycleService {
889
905
  if (sessionInstance instanceof LocalSession_1.LocalSession) {
890
906
  typedi_1.Container.get(LiveSessionOwners_1.LiveSessionOwners).record(sessionId, userId);
891
907
  }
892
- // A node samples its own phones for its hub, whatever its dashboard
893
- // setting (the hub collects the figures). A hub and a standalone server
894
- // start sampling in EventManager.onSessionStarted, once the row exists.
908
+ // A node samples its own phones for its hub, and records their device
909
+ // log unless the session turned it off, whatever its dashboard setting
910
+ // (the hub collects both). A hub and a standalone server start them in
911
+ // EventManager.onSessionStarted, once the row exists.
895
912
  if (!this.isHub(context.pluginArgs) && sessionInstance instanceof LocalSession_1.LocalSession) {
896
913
  typedi_1.Container.get(SessionMetricsService_1.SessionMetricsService).start({
897
914
  sessionId,
898
915
  device: freshDevice,
899
916
  capabilities: sessionResponse,
900
917
  });
918
+ if (xenonCapabilities[XenonCapabilityManager_1.XENON_CAPABILITIES.SAVE_DEVICE_LOGS] !== false) {
919
+ void typedi_1.Container.get(SessionDeviceLogs_1.SessionDeviceLogs).start({
920
+ sessionId,
921
+ device: freshDevice,
922
+ since: allocatedAt,
923
+ // An iPhone's or simulator's: the log its driver captures.
924
+ driverLog: sessionInstance.deviceLog(),
925
+ appUnderTest: sessionInstance.appUnderTest(),
926
+ });
927
+ }
901
928
  }
902
929
  yield this.applyPostSessionLogic(sessionInstance, xenonCapabilities, freshDevice);
903
930
  });
@@ -1063,19 +1090,8 @@ let SessionLifecycleService = class SessionLifecycleService {
1063
1090
  timeout: exports.REMOTE_CREATE_TIMEOUT_MS,
1064
1091
  retry: false,
1065
1092
  };
1066
- if (context.pluginArgs.proxy) {
1067
- this.logger.info(`Added proxy to axios config: ${JSON.stringify(context.pluginArgs.proxy)}`);
1068
- const proxyUrl = typeof context.pluginArgs.proxy === 'string'
1069
- ? context.pluginArgs.proxy
1070
- : `http://${context.pluginArgs.proxy.host}:${context.pluginArgs.proxy.port}`;
1071
- config.httpsAgent = new https_proxy_agent_1.HttpsProxyAgent(proxyUrl, {
1072
- rejectUnauthorized: context.pluginArgs.tlsRejectUnauthorized,
1073
- });
1074
- config.httpAgent = new http_proxy_agent_1.HttpProxyAgent(proxyUrl, {
1075
- rejectUnauthorized: context.pluginArgs.tlsRejectUnauthorized,
1076
- });
1077
- config.proxy = false;
1078
- }
1093
+ // Its proxy is InternalHttpClient's, by Xenon's one rule: the `proxy`
1094
+ // option, else the environment's (helpers/outboundProxy.ts).
1079
1095
  const createdSession = yield this.invokeSessionRequest(config, context.pluginArgs.tlsRejectUnauthorized);
1080
1096
  if (createdSession instanceof Error) {
1081
1097
  return createdSession;
@@ -1207,10 +1223,18 @@ let SessionLifecycleService = class SessionLifecycleService {
1207
1223
  if (sessionId) {
1208
1224
  typedi_1.Container.get(LiveSessionOwners_1.LiveSessionOwners).forget(sessionId);
1209
1225
  (0, sessionMemory_1.forgetSessionMemory)(sessionId);
1226
+ // Whether or not SESSION_MANAGER holds the session (a local one with
1227
+ // the dashboard off), and once: a second delete finds no span.
1228
+ typedi_1.Container.get(TracingService_1.TracingService).endSessionSpan(sessionId, {
1229
+ failed: status === SessionStatus_1.SessionStatus.FAILED,
1230
+ reason,
1231
+ });
1210
1232
  // Before the lock's "still in memory?" check, which a dashboard-off
1211
- // node fails. On a node this ends the figures it holds for its hub;
1212
- // on a hub it collects a node session's last ones. Idempotent.
1233
+ // node fails. On a node this ends the figures and device log it
1234
+ // holds for its hub; on a hub it collects a node session's last ones,
1235
+ // before onSessionStopped's failure analysis reads the log. Idempotent.
1213
1236
  yield typedi_1.Container.get(SessionMetricsService_1.SessionMetricsService).stop(sessionId);
1237
+ yield typedi_1.Container.get(SessionDeviceLogs_1.SessionDeviceLogs).stop(sessionId);
1214
1238
  yield sessionCleanupLock.acquire(sessionId, () => __awaiter(this, void 0, void 0, function* () {
1215
1239
  const session = SessionManager_1.SESSION_MANAGER.getSession(sessionId);
1216
1240
  if (!session) {
@@ -1240,8 +1264,11 @@ let SessionLifecycleService = class SessionLifecycleService {
1240
1264
  logger_1.default.warn(`Port release failed: ${err.message}`);
1241
1265
  }
1242
1266
  }
1243
- // 1. iOS Profiling Archival
1244
- if (device && ((_a = device.platform) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'ios') {
1267
+ // 1. iOS Profiling Archival. Only on an iPhone, the one place
1268
+ // EventManager.onSessionStarted starts it. A simulator's driver refuses
1269
+ // the stop without relaxed security, and the refused stop counted as a
1270
+ // failed command of the session: every simulator session ended failed.
1271
+ if (device && ((_a = device.platform) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'ios' && device.realDevice === true) {
1245
1272
  this.logger.info(`[${sessionId}] Stopping iOS profiling for asset archival`);
1246
1273
  try {
1247
1274
  const traceBase64 = yield session.stopPerformanceRecording();
@@ -1323,7 +1350,9 @@ let SessionLifecycleService = class SessionLifecycleService {
1323
1350
  SessionManager_1.SESSION_MANAGER.removeSession(sessionId);
1324
1351
  typedi_1.Container.get(LiveSessionOwners_1.LiveSessionOwners).forget(sessionId);
1325
1352
  (0, sessionMemory_1.forgetSessionMemory)(sessionId);
1353
+ typedi_1.Container.get(TracingService_1.TracingService).endSessionSpan(sessionId, { failed: true, reason });
1326
1354
  yield typedi_1.Container.get(SessionMetricsService_1.SessionMetricsService).stop(sessionId);
1355
+ yield typedi_1.Container.get(SessionDeviceLogs_1.SessionDeviceLogs).stop(sessionId);
1327
1356
  }));
1328
1357
  });
1329
1358
  }
@@ -15,6 +15,7 @@ const typedi_1 = require("typedi");
15
15
  const logger_1 = __importDefault(require("../logger"));
16
16
  const config_1 = require("../config");
17
17
  const SocketEvents_1 = require("../enums/SocketEvents");
18
+ const outboundProxy_1 = require("../helpers/outboundProxy");
18
19
  let SocketClient = class SocketClient {
19
20
  constructor() {
20
21
  this.socket = null;
@@ -46,6 +47,16 @@ let SocketClient = class SocketClient {
46
47
  reconnectionDelay: 1000,
47
48
  reconnectionDelayMax: 5000,
48
49
  auth: socketAuth,
50
+ // The proxy the node's other calls to its hub take (its phone reports,
51
+ // its JWKS fetch); it used to go straight to the hub whatever the
52
+ // environment said. Polling goes as plain requests, as axios sends
53
+ // them; only the WebSocket upgrade needs a CONNECT tunnel, and when the
54
+ // proxy refuses one (a stock Squid allows port 443 only) the
55
+ // connection stays on polling.
56
+ transportOptions: {
57
+ polling: { agent: (0, outboundProxy_1.proxyAgentFor)(normalizedHubUrl) },
58
+ websocket: { agent: (0, outboundProxy_1.socketProxyAgentFor)(normalizedHubUrl) },
59
+ },
49
60
  });
50
61
  this.socket.on('connect', () => {
51
62
  var _a, _b, _c;