@seleniumbox/sbox-mcp 0.2.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 +134 -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 +104 -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 +21 -0
  28. package/dist/config/env.js +55 -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 +34 -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 +540 -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 +12 -0
  134. package/dist/token-cache.js +37 -0
  135. package/dist/utils/logger.d.ts +10 -0
  136. package/dist/utils/logger.js +27 -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,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";
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.projectService = exports.diagnosticsService = exports.analyticsService = exports.uploadService = exports.sessionsPerProjectService = exports.userService = exports.deviceService = exports.browserService = exports.sessionService = exports.authService = void 0;
4
+ const auth_adapter_1 = require("../adapters/auth.adapter");
5
+ const session_adapter_1 = require("../adapters/session.adapter");
6
+ const browser_adapter_1 = require("../adapters/browser.adapter");
7
+ const device_adapter_1 = require("../adapters/device.adapter");
8
+ const project_adapter_1 = require("../adapters/project.adapter");
9
+ const upload_adapter_1 = require("../adapters/upload.adapter");
10
+ const stats_adapter_1 = require("../adapters/stats.adapter");
11
+ const diagnostics_adapter_1 = require("../adapters/diagnostics.adapter");
12
+ const auth_service_1 = require("./auth.service");
13
+ const session_service_1 = require("./session.service");
14
+ const browser_service_1 = require("./browser.service");
15
+ const device_service_1 = require("./device.service");
16
+ const user_service_1 = require("./user.service");
17
+ const sessions_per_project_service_1 = require("./sessions-per-project.service");
18
+ const upload_service_1 = require("./upload.service");
19
+ const analytics_service_1 = require("./analytics.service");
20
+ const diagnostics_service_1 = require("./diagnostics.service");
21
+ const project_service_1 = require("./project.service");
22
+ exports.authService = (0, auth_service_1.createAuthService)(auth_adapter_1.authAdapter);
23
+ exports.sessionService = (0, session_service_1.createSessionService)(session_adapter_1.sessionAdapter);
24
+ exports.browserService = (0, browser_service_1.createBrowserService)(browser_adapter_1.browserAdapter);
25
+ exports.deviceService = (0, device_service_1.createDeviceService)(device_adapter_1.deviceAdapter);
26
+ exports.userService = (0, user_service_1.createUserService)(session_adapter_1.sessionAdapter);
27
+ exports.sessionsPerProjectService = (0, sessions_per_project_service_1.createSessionsPerProjectService)(project_adapter_1.projectAdapter, session_adapter_1.sessionAdapter);
28
+ exports.uploadService = (0, upload_service_1.createUploadService)(upload_adapter_1.uploadAdapter, project_adapter_1.projectAdapter);
29
+ exports.analyticsService = (0, analytics_service_1.createAnalyticsService)(stats_adapter_1.statsAdapter);
30
+ exports.diagnosticsService = (0, diagnostics_service_1.createDiagnosticsService)(diagnostics_adapter_1.diagnosticsAdapter);
31
+ exports.projectService = (0, project_service_1.createProjectService)(project_adapter_1.projectAdapter);
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Project service: list projects (all or "my projects" for the current user).
3
+ */
4
+ import type { ProjectAdapter } from "../adapters/project.adapter";
5
+ export interface ProjectDto {
6
+ id: number | string;
7
+ name: string;
8
+ description: string;
9
+ hasToken: boolean;
10
+ tokenPreview?: string;
11
+ }
12
+ export interface ProjectService {
13
+ listMyProjects(token: string): Promise<{
14
+ success: true;
15
+ data: ProjectDto[];
16
+ } | {
17
+ success: false;
18
+ status: number;
19
+ message: string;
20
+ }>;
21
+ }
22
+ export declare function createProjectService(adapter: ProjectAdapter): ProjectService;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ /**
3
+ * Project service: list projects (all or "my projects" for the current user).
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createProjectService = createProjectService;
7
+ function toProjectDto(raw) {
8
+ const id = raw.id ?? 0;
9
+ const name = raw.name ?? "";
10
+ const description = raw.description ?? "";
11
+ const tokenObj = raw.token;
12
+ const hasToken = Boolean(tokenObj?.token);
13
+ const tokenPreview = tokenObj?.token
14
+ ? tokenObj.token.length > 12
15
+ ? tokenObj.token.slice(0, 6) + "…" + tokenObj.token.slice(-6)
16
+ : "***"
17
+ : undefined;
18
+ return { id, name, description, hasToken, tokenPreview };
19
+ }
20
+ function createProjectService(adapter) {
21
+ return {
22
+ async listMyProjects(token) {
23
+ const res = await adapter.listMyProjects(token);
24
+ if (res.status !== 200) {
25
+ return { success: false, status: res.status, message: res.error ?? "Failed to list my projects" };
26
+ }
27
+ const data = res.projects.map(toProjectDto);
28
+ return { success: true, data };
29
+ },
30
+ };
31
+ }