@seleniumbox/sbox-mcp 1.0.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 (141) hide show
  1. package/PUBLISHING.md +115 -0
  2. package/README.md +79 -0
  3. package/dist/adapters/auth.adapter.d.ts +46 -0
  4. package/dist/adapters/auth.adapter.js +54 -0
  5. package/dist/adapters/browser.adapter.d.ts +43 -0
  6. package/dist/adapters/browser.adapter.js +39 -0
  7. package/dist/adapters/device.adapter.d.ts +60 -0
  8. package/dist/adapters/device.adapter.js +40 -0
  9. package/dist/adapters/diagnostics.adapter.d.ts +73 -0
  10. package/dist/adapters/diagnostics.adapter.js +89 -0
  11. package/dist/adapters/index.d.ts +16 -0
  12. package/dist/adapters/index.js +20 -0
  13. package/dist/adapters/project.adapter.d.ts +38 -0
  14. package/dist/adapters/project.adapter.js +39 -0
  15. package/dist/adapters/sbox-api.client.d.ts +31 -0
  16. package/dist/adapters/sbox-api.client.js +166 -0
  17. package/dist/adapters/session.adapter.d.ts +77 -0
  18. package/dist/adapters/session.adapter.js +95 -0
  19. package/dist/adapters/stats.adapter.d.ts +72 -0
  20. package/dist/adapters/stats.adapter.js +108 -0
  21. package/dist/adapters/upload.adapter.d.ts +16 -0
  22. package/dist/adapters/upload.adapter.js +25 -0
  23. package/dist/adapters/user.adapter.d.ts +24 -0
  24. package/dist/adapters/user.adapter.js +25 -0
  25. package/dist/app.d.ts +2 -0
  26. package/dist/app.js +16 -0
  27. package/dist/config/env.d.ts +23 -0
  28. package/dist/config/env.js +64 -0
  29. package/dist/config/index.d.ts +1 -0
  30. package/dist/config/index.js +8 -0
  31. package/dist/controllers/analytics.controller.d.ts +10 -0
  32. package/dist/controllers/analytics.controller.js +127 -0
  33. package/dist/controllers/auth-device.controller.d.ts +7 -0
  34. package/dist/controllers/auth-device.controller.js +60 -0
  35. package/dist/controllers/auth.controller.d.ts +5 -0
  36. package/dist/controllers/auth.controller.js +20 -0
  37. package/dist/controllers/browser.controller.d.ts +5 -0
  38. package/dist/controllers/browser.controller.js +23 -0
  39. package/dist/controllers/device.controller.d.ts +5 -0
  40. package/dist/controllers/device.controller.js +23 -0
  41. package/dist/controllers/index.d.ts +6 -0
  42. package/dist/controllers/index.js +28 -0
  43. package/dist/controllers/project-stats.controller.d.ts +5 -0
  44. package/dist/controllers/project-stats.controller.js +29 -0
  45. package/dist/controllers/session.controller.d.ts +9 -0
  46. package/dist/controllers/session.controller.js +120 -0
  47. package/dist/controllers/upload.controller.d.ts +5 -0
  48. package/dist/controllers/upload.controller.js +44 -0
  49. package/dist/controllers/user.controller.d.ts +5 -0
  50. package/dist/controllers/user.controller.js +29 -0
  51. package/dist/device-flow.store.d.ts +7 -0
  52. package/dist/device-flow.store.js +23 -0
  53. package/dist/dto/auth.dto.d.ts +26 -0
  54. package/dist/dto/auth.dto.js +9 -0
  55. package/dist/dto/browser.dto.d.ts +13 -0
  56. package/dist/dto/browser.dto.js +2 -0
  57. package/dist/dto/device.dto.d.ts +15 -0
  58. package/dist/dto/device.dto.js +2 -0
  59. package/dist/dto/index.d.ts +6 -0
  60. package/dist/dto/index.js +22 -0
  61. package/dist/dto/project-stats.dto.d.ts +8 -0
  62. package/dist/dto/project-stats.dto.js +2 -0
  63. package/dist/dto/session.dto.d.ts +50 -0
  64. package/dist/dto/session.dto.js +9 -0
  65. package/dist/dto/user.dto.d.ts +6 -0
  66. package/dist/dto/user.dto.js +2 -0
  67. package/dist/index.d.ts +6 -0
  68. package/dist/index.js +35 -0
  69. package/dist/mcp/tools/auth-tools.d.ts +5 -0
  70. package/dist/mcp/tools/auth-tools.js +132 -0
  71. package/dist/mcp/tools/helpers.d.ts +26 -0
  72. package/dist/mcp/tools/helpers.js +53 -0
  73. package/dist/mcp/tools/index.d.ts +5 -0
  74. package/dist/mcp/tools/index.js +12 -0
  75. package/dist/mcp/tools/rest-tools.d.ts +5 -0
  76. package/dist/mcp/tools/rest-tools.js +545 -0
  77. package/dist/mcp-server.d.ts +6 -0
  78. package/dist/mcp-server.js +28 -0
  79. package/dist/middleware/auth.middleware.d.ts +10 -0
  80. package/dist/middleware/auth.middleware.js +23 -0
  81. package/dist/middleware/debug-log.middleware.d.ts +6 -0
  82. package/dist/middleware/debug-log.middleware.js +84 -0
  83. package/dist/middleware/error.middleware.d.ts +8 -0
  84. package/dist/middleware/error.middleware.js +24 -0
  85. package/dist/middleware/validate.middleware.d.ts +7 -0
  86. package/dist/middleware/validate.middleware.js +42 -0
  87. package/dist/routes/analytics.routes.d.ts +1 -0
  88. package/dist/routes/analytics.routes.js +15 -0
  89. package/dist/routes/auth.routes.d.ts +1 -0
  90. package/dist/routes/auth.routes.js +15 -0
  91. package/dist/routes/browser.routes.d.ts +1 -0
  92. package/dist/routes/browser.routes.js +11 -0
  93. package/dist/routes/device.routes.d.ts +1 -0
  94. package/dist/routes/device.routes.js +11 -0
  95. package/dist/routes/index.d.ts +1 -0
  96. package/dist/routes/index.js +27 -0
  97. package/dist/routes/mcp.routes.d.ts +6 -0
  98. package/dist/routes/mcp.routes.js +70 -0
  99. package/dist/routes/project-stats.routes.d.ts +1 -0
  100. package/dist/routes/project-stats.routes.js +11 -0
  101. package/dist/routes/session.routes.d.ts +1 -0
  102. package/dist/routes/session.routes.js +23 -0
  103. package/dist/routes/upload.routes.d.ts +1 -0
  104. package/dist/routes/upload.routes.js +20 -0
  105. package/dist/routes/user.routes.d.ts +1 -0
  106. package/dist/routes/user.routes.js +11 -0
  107. package/dist/server.d.ts +1 -0
  108. package/dist/server.js +17 -0
  109. package/dist/services/analytics.service.d.ts +88 -0
  110. package/dist/services/analytics.service.js +98 -0
  111. package/dist/services/auth.service.d.ts +27 -0
  112. package/dist/services/auth.service.js +68 -0
  113. package/dist/services/browser.service.d.ts +25 -0
  114. package/dist/services/browser.service.js +43 -0
  115. package/dist/services/device.service.d.ts +18 -0
  116. package/dist/services/device.service.js +58 -0
  117. package/dist/services/diagnostics.service.d.ts +87 -0
  118. package/dist/services/diagnostics.service.js +92 -0
  119. package/dist/services/enrichment.service.d.ts +9 -0
  120. package/dist/services/enrichment.service.js +112 -0
  121. package/dist/services/index.d.ts +20 -0
  122. package/dist/services/index.js +31 -0
  123. package/dist/services/project.service.d.ts +22 -0
  124. package/dist/services/project.service.js +31 -0
  125. package/dist/services/session.service.d.ts +62 -0
  126. package/dist/services/session.service.js +104 -0
  127. package/dist/services/sessions-per-project.service.d.ts +18 -0
  128. package/dist/services/sessions-per-project.service.js +57 -0
  129. package/dist/services/upload.service.d.ts +20 -0
  130. package/dist/services/upload.service.js +29 -0
  131. package/dist/services/user.service.d.ts +17 -0
  132. package/dist/services/user.service.js +39 -0
  133. package/dist/token-cache.d.ts +13 -0
  134. package/dist/token-cache.js +114 -0
  135. package/dist/utils/logger.d.ts +11 -0
  136. package/dist/utils/logger.js +31 -0
  137. package/dist/utils/open-browser.d.ts +6 -0
  138. package/dist/utils/open-browser.js +23 -0
  139. package/dist/utils/time-range.d.ts +11 -0
  140. package/dist/utils/time-range.js +16 -0
  141. package/package.json +42 -0
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ /**
3
+ * Analytics service: sessions per browser, by build, failed count, most active project/user.
4
+ * Time range can be in hours, minutes, or days.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createAnalyticsService = createAnalyticsService;
8
+ function createAnalyticsService(statsAdapter) {
9
+ return {
10
+ async totalTests(token, days) {
11
+ const res = await statsAdapter.getTotalTests(token, days);
12
+ if (res.status !== 200) {
13
+ return { success: false, status: res.status, message: res.error ?? "Failed to get total tests" };
14
+ }
15
+ return { success: true, total: res.total };
16
+ },
17
+ async sessionsPerBrowser(token, timeRange) {
18
+ const res = await statsAdapter.getSessionsPerBrowser(token, timeRange);
19
+ if (res.status !== 200) {
20
+ return { success: false, status: res.status, message: res.error ?? "Failed to get sessions per browser" };
21
+ }
22
+ const data = res.items.map((i) => ({
23
+ browser: i.browser ?? "",
24
+ version: i.version ?? "",
25
+ count: i.count ?? 0,
26
+ }));
27
+ return { success: true, data };
28
+ },
29
+ async sessionsByBuild(token, timeRange) {
30
+ const res = await statsAdapter.getSessionsByBuild(token, timeRange);
31
+ if (res.status !== 200) {
32
+ return { success: false, status: res.status, message: res.error ?? "Failed to get sessions by build" };
33
+ }
34
+ const data = res.items.map((i) => ({
35
+ buildName: i.buildName ?? "Others",
36
+ sessionCount: i.session_count ?? 0,
37
+ }));
38
+ return { success: true, data };
39
+ },
40
+ async failedCount(token, timeRange) {
41
+ const res = await statsAdapter.getCountByStatus(token, timeRange);
42
+ if (res.status !== 200) {
43
+ return { success: false, status: res.status, message: res.error ?? "Failed to get failed count" };
44
+ }
45
+ let count = 0;
46
+ for (const item of res.items) {
47
+ const r = (item.result ?? "").toLowerCase();
48
+ if (r === "failed" || r === "timeout")
49
+ count += item.count ?? 0;
50
+ }
51
+ return { success: true, count };
52
+ },
53
+ async mostActiveProject(token, timeRange) {
54
+ const res = await statsAdapter.getCountPerProject(token, timeRange);
55
+ if (res.status !== 200) {
56
+ return { success: false, status: res.status, message: res.error ?? "Failed to get most active project" };
57
+ }
58
+ const top = res.items.sort((a, b) => (b.count ?? 0) - (a.count ?? 0))[0];
59
+ if (!top) {
60
+ return { success: true, projectName: "N/A", count: 0 };
61
+ }
62
+ return { success: true, projectName: top.project ?? "N/A", count: top.count ?? 0 };
63
+ },
64
+ async mostActiveUser(token, timeRange) {
65
+ const res = await statsAdapter.getCountPerUser(token, timeRange);
66
+ if (res.status !== 200) {
67
+ return { success: false, status: res.status, message: res.error ?? "Failed to get most active user" };
68
+ }
69
+ const top = res.items.sort((a, b) => (b.count ?? 0) - (a.count ?? 0))[0];
70
+ if (!top) {
71
+ return { success: true, userName: "N/A", count: 0 };
72
+ }
73
+ return { success: true, userName: top.user ?? "N/A", count: top.count ?? 0 };
74
+ },
75
+ async testsPerTestName(token, timeRange) {
76
+ const res = await statsAdapter.getCountPerTestName(token, timeRange);
77
+ if (res.status !== 200) {
78
+ return { success: false, status: res.status, message: res.error ?? "Failed to get tests per test name" };
79
+ }
80
+ const data = res.items.map((i) => ({
81
+ testName: i.testName ?? "",
82
+ count: i.count ?? 0,
83
+ }));
84
+ return { success: true, data };
85
+ },
86
+ async manualTestsPerTestName(token, timeRange) {
87
+ const res = await statsAdapter.getManualCountPerTestName(token, timeRange);
88
+ if (res.status !== 200) {
89
+ return { success: false, status: res.status, message: res.error ?? "Failed to get manual tests per test name" };
90
+ }
91
+ const data = res.items.map((i) => ({
92
+ testName: i.testName ?? "",
93
+ count: i.count ?? 0,
94
+ }));
95
+ return { success: true, data };
96
+ },
97
+ };
98
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Authentication service. Delegates to auth adapter; returns DTOs.
3
+ * Exposes customer-friendly auth type (Local, LDAP, OIDC) only; internal realm id is not exposed.
4
+ */
5
+ import type { AuthAdapter } from "../adapters/auth.adapter";
6
+ import type { LoginBody, LoginResponseDto } from "../dto/auth.dto";
7
+ export interface AuthService {
8
+ isMcpAddonEnabled(): Promise<boolean>;
9
+ login(body: LoginBody): Promise<{
10
+ success: true;
11
+ data: LoginResponseDto;
12
+ } | {
13
+ success: false;
14
+ status: number;
15
+ message: string;
16
+ }>;
17
+ validateToken(token: string): Promise<{
18
+ success: true;
19
+ data: LoginResponseDto;
20
+ } | {
21
+ success: false;
22
+ status: number;
23
+ message: string;
24
+ }>;
25
+ pollForToken(pollId: string): Promise<string | null>;
26
+ }
27
+ export declare function createAuthService(adapter: AuthAdapter): AuthService;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ /**
3
+ * Authentication service. Delegates to auth adapter; returns DTOs.
4
+ * Exposes customer-friendly auth type (Local, LDAP, OIDC) only; internal realm id is not exposed.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createAuthService = createAuthService;
8
+ /** Map internal realm id to customer-facing auth type. */
9
+ function realmIdToAuthType(realmId) {
10
+ if (realmId == null || realmId === "")
11
+ return "Local";
12
+ const r = String(realmId).toLowerCase();
13
+ if (r === "0" || r === "local")
14
+ return "Local";
15
+ if (r.includes("ldap"))
16
+ return "LDAP";
17
+ if (r.includes("oidc") || r.includes("openid"))
18
+ return "OIDC";
19
+ return "Local";
20
+ }
21
+ function createAuthService(adapter) {
22
+ return {
23
+ async isMcpAddonEnabled() {
24
+ return adapter.isMcpAddonEnabled();
25
+ },
26
+ async login(body) {
27
+ const payload = { username: body.username, password: body.password };
28
+ const { status, data } = await adapter.login(payload);
29
+ if (status !== 200 || "success" in data && data.success === false) {
30
+ const msg = data.message ?? "Login failed";
31
+ return { success: false, status, message: msg };
32
+ }
33
+ const session = data;
34
+ const dto = {
35
+ token: session.token,
36
+ principal: {
37
+ userId: session.principal?.userId ?? body.username,
38
+ fullname: session.principal?.fullname,
39
+ authType: realmIdToAuthType(session.principal?.realm?.id),
40
+ },
41
+ };
42
+ return { success: true, data: dto };
43
+ },
44
+ async validateToken(token) {
45
+ const { status, data } = await adapter.validateSession(token);
46
+ if (status !== 200 || "success" in data && data.success === false) {
47
+ const msg = data.message ?? "Invalid or expired token";
48
+ return { success: false, status, message: msg };
49
+ }
50
+ const session = data;
51
+ const dto = {
52
+ token: session.token,
53
+ principal: {
54
+ userId: session.principal?.userId ?? "",
55
+ fullname: session.principal?.fullname,
56
+ authType: realmIdToAuthType(session.principal?.realm?.id),
57
+ },
58
+ };
59
+ return { success: true, data: dto };
60
+ },
61
+ async pollForToken(pollId) {
62
+ const { status, data } = await adapter.pollToken(pollId);
63
+ if (status === 200 && data && typeof data.token === "string" && data.token)
64
+ return data.token;
65
+ return null;
66
+ },
67
+ };
68
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Browser service: list supported browsers, Playwright versions.
3
+ * Uses browser adapter; returns business-friendly DTOs.
4
+ */
5
+ import type { BrowserAdapter } from "../adapters/browser.adapter";
6
+ import type { BrowserDto, PlaywrightVersionDto } from "../dto/browser.dto";
7
+ export interface BrowserService {
8
+ listSupportedBrowsers(token: string): Promise<{
9
+ success: true;
10
+ data: BrowserDto[];
11
+ } | {
12
+ success: false;
13
+ status: number;
14
+ message: string;
15
+ }>;
16
+ listPlaywrightVersions(token: string): Promise<{
17
+ success: true;
18
+ data: PlaywrightVersionDto[];
19
+ } | {
20
+ success: false;
21
+ status: number;
22
+ message: string;
23
+ }>;
24
+ }
25
+ export declare function createBrowserService(adapter: BrowserAdapter): BrowserService;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ /**
3
+ * Browser service: list supported browsers, Playwright versions.
4
+ * Uses browser adapter; returns business-friendly DTOs.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createBrowserService = createBrowserService;
8
+ const browser_adapter_1 = require("../adapters/browser.adapter");
9
+ function toPlaywrightDto(raw) {
10
+ return {
11
+ version: raw.version ?? "",
12
+ chromium: raw.chromium ?? "",
13
+ webkit: raw.webkit ?? "",
14
+ firefox: raw.firefox ?? "",
15
+ };
16
+ }
17
+ function createBrowserService(adapter) {
18
+ return {
19
+ async listSupportedBrowsers(token) {
20
+ const results = [];
21
+ for (const name of browser_adapter_1.BROWSER_NAMES) {
22
+ const res = await adapter.getBrowserVersions(name, token);
23
+ if (res.status === 200 && res.versions.length > 0) {
24
+ results.push({
25
+ browserName: res.name,
26
+ version: res.versions[0] ?? "",
27
+ platform: "desktop",
28
+ capabilities: { versions: res.versions },
29
+ });
30
+ }
31
+ }
32
+ return { success: true, data: results };
33
+ },
34
+ async listPlaywrightVersions(token) {
35
+ const res = await adapter.getPlaywrightVersions(token);
36
+ if (res.status !== 200) {
37
+ return { success: false, status: res.status, message: res.error ?? "Playwright is not enabled" };
38
+ }
39
+ const data = res.versions.map(toPlaywrightDto);
40
+ return { success: true, data };
41
+ },
42
+ };
43
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Device service: list supported devices (iOS, Android, mobile web).
3
+ * - iOS: GET /e34/api/mobile/ios/devices (deviceName, iosSDK list) when iOS is enabled / executors attached.
4
+ * - Android: GET /e34/api/mobile/android/devices (avdName, deviceName, completeDeviceName, supportedPlatformVersions).
5
+ */
6
+ import type { DeviceAdapter } from "../adapters/device.adapter";
7
+ import type { DeviceDto } from "../dto/device.dto";
8
+ export interface DeviceService {
9
+ listSupportedDevices(token: string): Promise<{
10
+ success: true;
11
+ data: DeviceDto[];
12
+ } | {
13
+ success: false;
14
+ status: number;
15
+ message: string;
16
+ }>;
17
+ }
18
+ export declare function createDeviceService(adapter: DeviceAdapter): DeviceService;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ /**
3
+ * Device service: list supported devices (iOS, Android, mobile web).
4
+ * - iOS: GET /e34/api/mobile/ios/devices (deviceName, iosSDK list) when iOS is enabled / executors attached.
5
+ * - Android: GET /e34/api/mobile/android/devices (avdName, deviceName, completeDeviceName, supportedPlatformVersions).
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createDeviceService = createDeviceService;
9
+ function iosToDto(raw) {
10
+ const sdks = raw.iosSDK ?? raw.iossdks ?? raw.IOSSDK ?? [];
11
+ const versions = sdks
12
+ .map((s) => (typeof s === "object" && s && "name" in s ? s.name : String(s)))
13
+ .filter((v) => typeof v === "string" && v.length > 0);
14
+ return {
15
+ deviceName: raw.deviceName ?? "Unknown",
16
+ os: "iOS",
17
+ version: versions[0] ?? "",
18
+ supportedPlatformVersions: versions.length > 0 ? versions : undefined,
19
+ };
20
+ }
21
+ function androidToDto(raw) {
22
+ const versions = raw.supportedPlatformVersions ?? [];
23
+ return {
24
+ deviceName: raw.deviceName ?? raw.completeDeviceName ?? "Unknown",
25
+ completeDeviceName: raw.completeDeviceName,
26
+ avdName: raw.avdName,
27
+ os: "Android",
28
+ version: versions[0] ?? "",
29
+ supportedPlatformVersions: versions.length > 0 ? versions : undefined,
30
+ };
31
+ }
32
+ function toDto(raw, platform) {
33
+ return {
34
+ deviceName: raw.deviceName ?? raw.name ?? "Unknown",
35
+ os: raw.os ?? platform,
36
+ version: raw.version ?? "",
37
+ availability: raw.available === true ? "available" : "unavailable",
38
+ };
39
+ }
40
+ function createDeviceService(adapter) {
41
+ return {
42
+ async listSupportedDevices(token) {
43
+ const [ios, android, mobileWeb] = await Promise.all([
44
+ adapter.getIosDevices(token),
45
+ adapter.getAndroidDevices(token),
46
+ adapter.getMobileWebDevices(token),
47
+ ]);
48
+ const list = [];
49
+ if (ios.status === 200)
50
+ ios.devices.forEach((d) => list.push(iosToDto(d)));
51
+ if (android.status === 200)
52
+ android.devices.forEach((d) => list.push(androidToDto(d)));
53
+ if (mobileWeb.status === 200)
54
+ mobileWeb.devices.forEach((d) => list.push(toDto(d, "MobileWeb")));
55
+ return { success: true, data: list };
56
+ },
57
+ };
58
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Diagnostics service: system metrics for the Diagnostics Dashboard.
3
+ * CPU usage, queued tests, active tests, queue waiting time, tests across executors, tests in starting state, executor count.
4
+ */
5
+ import type { DiagnosticsAdapter } from "../adapters/diagnostics.adapter";
6
+ export interface DiagnosticsService {
7
+ getExecutorsCount(token: string): Promise<{
8
+ success: true;
9
+ count: number;
10
+ } | {
11
+ success: false;
12
+ status: number;
13
+ message: string;
14
+ }>;
15
+ getCpuUsagePerNode(token: string, days: number): Promise<{
16
+ success: true;
17
+ data: Array<{
18
+ node: string;
19
+ avgCpu: number;
20
+ timestamp: string;
21
+ }>;
22
+ } | {
23
+ success: false;
24
+ status: number;
25
+ message: string;
26
+ }>;
27
+ getTestsQueuedHourly(token: string, days: number): Promise<{
28
+ success: true;
29
+ data: Array<{
30
+ timestamp: string;
31
+ avgTotal: number;
32
+ maxTotal: number;
33
+ }>;
34
+ } | {
35
+ success: false;
36
+ status: number;
37
+ message: string;
38
+ }>;
39
+ getNodesActiveTests(token: string, days: number): Promise<{
40
+ success: true;
41
+ data: Array<{
42
+ timestamp: string;
43
+ averageTotal: number;
44
+ }>;
45
+ } | {
46
+ success: false;
47
+ status: number;
48
+ message: string;
49
+ }>;
50
+ getQueueWaitingTime(token: string, days: number): Promise<{
51
+ success: true;
52
+ data: Array<{
53
+ timestamp: string;
54
+ avgDuration: number;
55
+ maxDuration: number;
56
+ }>;
57
+ } | {
58
+ success: false;
59
+ status: number;
60
+ message: string;
61
+ }>;
62
+ getTestsActiveGraph(token: string, days: number): Promise<{
63
+ success: true;
64
+ data: Array<{
65
+ timestamp: string;
66
+ avgTotal: number;
67
+ node: string;
68
+ }>;
69
+ } | {
70
+ success: false;
71
+ status: number;
72
+ message: string;
73
+ }>;
74
+ getTestsStartingStats(token: string, days: number): Promise<{
75
+ success: true;
76
+ data: Array<{
77
+ timestamp: string;
78
+ node: string;
79
+ avgTotal: number;
80
+ }>;
81
+ } | {
82
+ success: false;
83
+ status: number;
84
+ message: string;
85
+ }>;
86
+ }
87
+ export declare function createDiagnosticsService(adapter: DiagnosticsAdapter): DiagnosticsService;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ /**
3
+ * Diagnostics service: system metrics for the Diagnostics Dashboard.
4
+ * CPU usage, queued tests, active tests, queue waiting time, tests across executors, tests in starting state, executor count.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createDiagnosticsService = createDiagnosticsService;
8
+ function createDiagnosticsService(adapter) {
9
+ return {
10
+ async getExecutorsCount(token) {
11
+ const res = await adapter.getExecutorsCount(token);
12
+ if (res.status !== 200) {
13
+ return { success: false, status: res.status, message: res.error ?? "Failed to get executors count" };
14
+ }
15
+ return { success: true, count: res.count };
16
+ },
17
+ async getCpuUsagePerNode(token, days) {
18
+ const res = await adapter.getCpuUsagePerNode(token, days);
19
+ if (res.status !== 200) {
20
+ return { success: false, status: res.status, message: res.error ?? "Failed to get CPU usage" };
21
+ }
22
+ const data = res.items.map((i) => ({
23
+ node: i.node ?? "",
24
+ avgCpu: i.avg_cpu ?? 0,
25
+ timestamp: i.timestamp ?? "",
26
+ }));
27
+ return { success: true, data };
28
+ },
29
+ async getTestsQueuedHourly(token, days) {
30
+ const res = await adapter.getTestsQueuedHourly(token, days);
31
+ if (res.status !== 200) {
32
+ return { success: false, status: res.status, message: res.error ?? "Failed to get tests queued hourly" };
33
+ }
34
+ const data = res.items.map((i) => ({
35
+ timestamp: i.timestamp ?? "",
36
+ avgTotal: i.avg_total ?? 0,
37
+ maxTotal: i.max_total ?? 0,
38
+ }));
39
+ return { success: true, data };
40
+ },
41
+ async getNodesActiveTests(token, days) {
42
+ const res = await adapter.getNodesActiveTests(token, days);
43
+ if (res.status !== 200) {
44
+ return { success: false, status: res.status, message: res.error ?? "Failed to get active tests" };
45
+ }
46
+ const data = res.items.map((i) => ({
47
+ timestamp: i.timestamp ?? "",
48
+ averageTotal: i.average_total ?? 0,
49
+ }));
50
+ return { success: true, data };
51
+ },
52
+ async getQueueWaitingTime(token, days) {
53
+ const res = await adapter.getQueueWaitingTime(token, days);
54
+ if (res.status !== 200) {
55
+ return { success: false, status: res.status, message: res.error ?? "Failed to get queue waiting time" };
56
+ }
57
+ const data = res.items.map((i) => {
58
+ const b = i.browsers?.[0];
59
+ return {
60
+ timestamp: i.timestamp ?? "",
61
+ avgDuration: b?.avg_duration ?? 0,
62
+ maxDuration: b?.max_duration ?? 0,
63
+ };
64
+ });
65
+ return { success: true, data };
66
+ },
67
+ async getTestsActiveGraph(token, days) {
68
+ const res = await adapter.getTestsActiveGraph(token, days);
69
+ if (res.status !== 200) {
70
+ return { success: false, status: res.status, message: res.error ?? "Failed to get tests active graph" };
71
+ }
72
+ const data = res.items.map((i) => ({
73
+ timestamp: i.timestamp ?? "",
74
+ avgTotal: i.avg_total ?? 0,
75
+ node: i.node ?? "",
76
+ }));
77
+ return { success: true, data };
78
+ },
79
+ async getTestsStartingStats(token, days) {
80
+ const res = await adapter.getTestsStartingStats(token, days);
81
+ if (res.status !== 200) {
82
+ return { success: false, status: res.status, message: res.error ?? "Failed to get tests starting stats" };
83
+ }
84
+ const data = res.items.map((i) => ({
85
+ timestamp: i.timestamp ?? "",
86
+ node: i.node ?? "",
87
+ avgTotal: i.avg_total ?? 0,
88
+ }));
89
+ return { success: true, data };
90
+ },
91
+ };
92
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Enrichment service: maps raw SBOX API data to business-friendly DTOs.
3
+ * Never exposes internal IDs; always uses names (projectName, userName, browserName, deviceName).
4
+ */
5
+ import type { ESSessionDataRaw } from "../adapters/session.adapter";
6
+ import type { SessionDetailDto } from "../dto/session.dto";
7
+ /** Video files are served at /videos/{ekey}.mp4 (not under /e34/api). */
8
+ export declare function buildVideoUrl(sessionId: string): string;
9
+ export declare function enrichSessionDetail(raw: ESSessionDataRaw, videoUrl: string | null): SessionDetailDto;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ /**
3
+ * Enrichment service: maps raw SBOX API data to business-friendly DTOs.
4
+ * Never exposes internal IDs; always uses names (projectName, userName, browserName, deviceName).
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.buildVideoUrl = buildVideoUrl;
8
+ exports.enrichSessionDetail = enrichSessionDetail;
9
+ const config_1 = require("../config");
10
+ const EXTRAS_PREFIX = "e34:";
11
+ function getExtra(raw, key) {
12
+ const extras = raw.extras ?? {};
13
+ const v = extras[EXTRAS_PREFIX + key];
14
+ if (v === undefined || v === null)
15
+ return null;
16
+ return String(v);
17
+ }
18
+ function getExtraNumber(raw, key) {
19
+ const extras = raw.extras ?? {};
20
+ const v = extras[EXTRAS_PREFIX + key];
21
+ if (v === undefined || v === null)
22
+ return null;
23
+ const n = typeof v === "number" ? v : Number(v);
24
+ return Number.isFinite(n) ? n : null;
25
+ }
26
+ /** Video files are served at /videos/{ekey}.mp4 (not under /e34/api). */
27
+ function buildVideoUrl(sessionId) {
28
+ const base = config_1.config.sboxApiBaseUrl.replace(/\/$/, "");
29
+ return `${base}/videos/${encodeURIComponent(sessionId)}.mp4`;
30
+ }
31
+ /** Internal keys we never expose (IDs and raw longs). */
32
+ const CAPABILITIES_OMIT = new Set([
33
+ "e34:project_Id",
34
+ "e34:u_realmId",
35
+ "e34:arrival",
36
+ ]);
37
+ /**
38
+ * Build a capabilities object from raw session: extras (e34:*) plus top-level session fields.
39
+ * Omits internal IDs (project_Id, u_realmId) and raw arrival long; uses project name and human-readable arrival time.
40
+ */
41
+ function buildCapabilities(raw) {
42
+ const extras = raw.extras ?? {};
43
+ const caps = {};
44
+ for (const [key, value] of Object.entries(extras)) {
45
+ if (CAPABILITIES_OMIT.has(key))
46
+ continue;
47
+ if (key === "e34:projectId") {
48
+ caps.projectName = value;
49
+ continue;
50
+ }
51
+ caps[key] = value;
52
+ }
53
+ if (raw.browser != null)
54
+ caps.browser = raw.browser;
55
+ if (raw.version != null)
56
+ caps.version = raw.version;
57
+ if (raw.browserVersion != null)
58
+ caps.browserVersion = raw.browserVersion;
59
+ if (raw.framework != null)
60
+ caps.framework = raw.framework;
61
+ if (raw.platformName != null)
62
+ caps.platformName = raw.platformName;
63
+ if (raw.deviceName != null)
64
+ caps.deviceName = raw.deviceName;
65
+ if (raw.platformVersion != null)
66
+ caps.platformVersion = raw.platformVersion;
67
+ if (raw.originIp != null)
68
+ caps.originIp = raw.originIp;
69
+ if (raw.node != null)
70
+ caps.node = raw.node;
71
+ if (raw.duration != null)
72
+ caps.duration = raw.duration;
73
+ const arrivalMs = getExtraNumber(raw, "arrival");
74
+ if (arrivalMs != null)
75
+ caps.arrivalTime = new Date(arrivalMs).toISOString();
76
+ return caps;
77
+ }
78
+ function enrichSessionDetail(raw, videoUrl) {
79
+ const sessionId = raw.ekey ?? "";
80
+ const projectName = getExtra(raw, "projectId") ?? "";
81
+ const userName = getExtra(raw, "userId") ?? "";
82
+ const browserName = raw.browser ?? raw.browserVersion ?? "";
83
+ const browserVersion = raw.version ?? raw.browserVersion ?? "";
84
+ const deviceName = raw.deviceName ?? raw.platformName ?? "";
85
+ const status = raw.result ?? "unknown";
86
+ const framework = raw.framework ?? "selenium";
87
+ const arrivalMs = getExtraNumber(raw, "arrival");
88
+ const durationMs = typeof raw.duration === "number" && Number.isFinite(raw.duration) ? raw.duration : null;
89
+ const startTime = arrivalMs != null
90
+ ? new Date(arrivalMs).toISOString()
91
+ : (raw["@timestamp"] ?? getExtra(raw, "startTime") ?? null)
92
+ ? String(raw["@timestamp"] ?? getExtra(raw, "startTime"))
93
+ : null;
94
+ const endTime = startTime && arrivalMs != null && durationMs != null
95
+ ? new Date(arrivalMs + durationMs).toISOString()
96
+ : (getExtra(raw, "endTime") ?? null);
97
+ return {
98
+ sessionId,
99
+ projectName,
100
+ browserName,
101
+ browserVersion,
102
+ deviceName,
103
+ userName,
104
+ status,
105
+ framework,
106
+ startTime,
107
+ endTime,
108
+ durationMs,
109
+ videoUrl,
110
+ capabilities: buildCapabilities(raw),
111
+ };
112
+ }
@@ -0,0 +1,20 @@
1
+ export declare const authService: import("./auth.service").AuthService;
2
+ export declare const sessionService: import("./session.service").SessionService;
3
+ export declare const browserService: import("./browser.service").BrowserService;
4
+ export declare const deviceService: import("./device.service").DeviceService;
5
+ export declare const userService: import("./user.service").UserService;
6
+ export declare const sessionsPerProjectService: import("./sessions-per-project.service").SessionsPerProjectService;
7
+ export declare const uploadService: import("./upload.service").UploadService;
8
+ export declare const analyticsService: import("./analytics.service").AnalyticsService;
9
+ export declare const diagnosticsService: import("./diagnostics.service").DiagnosticsService;
10
+ export declare const projectService: import("./project.service").ProjectService;
11
+ export type { AuthService } from "./auth.service";
12
+ export type { SessionService } from "./session.service";
13
+ export type { BrowserService } from "./browser.service";
14
+ export type { DeviceService } from "./device.service";
15
+ export type { UserService } from "./user.service";
16
+ export type { SessionsPerProjectService } from "./sessions-per-project.service";
17
+ export type { UploadService } from "./upload.service";
18
+ export type { AnalyticsService } from "./analytics.service";
19
+ export type { DiagnosticsService } from "./diagnostics.service";
20
+ export type { ProjectService } from "./project.service";