@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,16 @@
1
+ export { authAdapter } from "./auth.adapter";
2
+ export type { AuthAdapter, UserSessionApi } from "./auth.adapter";
3
+ export { sessionAdapter } from "./session.adapter";
4
+ export type { SessionAdapter, ESSessionDataRaw } from "./session.adapter";
5
+ export { browserAdapter, BROWSER_NAMES } from "./browser.adapter";
6
+ export type { BrowserAdapter } from "./browser.adapter";
7
+ export { deviceAdapter } from "./device.adapter";
8
+ export type { DeviceAdapter } from "./device.adapter";
9
+ export { projectAdapter } from "./project.adapter";
10
+ export type { ProjectAdapter } from "./project.adapter";
11
+ export { userAdapter } from "./user.adapter";
12
+ export type { UserAdapter } from "./user.adapter";
13
+ export { statsAdapter } from "./stats.adapter";
14
+ export type { StatsAdapter } from "./stats.adapter";
15
+ export { uploadAdapter } from "./upload.adapter";
16
+ export type { UploadAdapter } from "./upload.adapter";
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uploadAdapter = exports.statsAdapter = exports.userAdapter = exports.projectAdapter = exports.deviceAdapter = exports.BROWSER_NAMES = exports.browserAdapter = exports.sessionAdapter = exports.authAdapter = void 0;
4
+ var auth_adapter_1 = require("./auth.adapter");
5
+ Object.defineProperty(exports, "authAdapter", { enumerable: true, get: function () { return auth_adapter_1.authAdapter; } });
6
+ var session_adapter_1 = require("./session.adapter");
7
+ Object.defineProperty(exports, "sessionAdapter", { enumerable: true, get: function () { return session_adapter_1.sessionAdapter; } });
8
+ var browser_adapter_1 = require("./browser.adapter");
9
+ Object.defineProperty(exports, "browserAdapter", { enumerable: true, get: function () { return browser_adapter_1.browserAdapter; } });
10
+ Object.defineProperty(exports, "BROWSER_NAMES", { enumerable: true, get: function () { return browser_adapter_1.BROWSER_NAMES; } });
11
+ var device_adapter_1 = require("./device.adapter");
12
+ Object.defineProperty(exports, "deviceAdapter", { enumerable: true, get: function () { return device_adapter_1.deviceAdapter; } });
13
+ var project_adapter_1 = require("./project.adapter");
14
+ Object.defineProperty(exports, "projectAdapter", { enumerable: true, get: function () { return project_adapter_1.projectAdapter; } });
15
+ var user_adapter_1 = require("./user.adapter");
16
+ Object.defineProperty(exports, "userAdapter", { enumerable: true, get: function () { return user_adapter_1.userAdapter; } });
17
+ var stats_adapter_1 = require("./stats.adapter");
18
+ Object.defineProperty(exports, "statsAdapter", { enumerable: true, get: function () { return stats_adapter_1.statsAdapter; } });
19
+ var upload_adapter_1 = require("./upload.adapter");
20
+ Object.defineProperty(exports, "uploadAdapter", { enumerable: true, get: function () { return upload_adapter_1.uploadAdapter; } });
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Adapter for SBOX project/testproject APIs. Calls /testprojects and count-per-project.
3
+ * No business logic; only HTTP and response typing.
4
+ */
5
+ export interface TestProjectRaw {
6
+ name?: string;
7
+ id?: number;
8
+ description?: string;
9
+ token?: {
10
+ id?: string;
11
+ token?: string;
12
+ createdAt?: string;
13
+ };
14
+ [key: string]: unknown;
15
+ }
16
+ export interface CountByProjectRaw {
17
+ project?: string;
18
+ count?: number;
19
+ }
20
+ export interface ProjectAdapter {
21
+ listProjects(token: string): Promise<{
22
+ status: number;
23
+ projects: TestProjectRaw[];
24
+ error?: string;
25
+ }>;
26
+ /** My Projects page: projects the user has right to (token:selfservice). */
27
+ listMyProjects(token: string): Promise<{
28
+ status: number;
29
+ projects: TestProjectRaw[];
30
+ error?: string;
31
+ }>;
32
+ getCountPerProject(days: number, token: string): Promise<{
33
+ status: number;
34
+ items: CountByProjectRaw[];
35
+ error?: string;
36
+ }>;
37
+ }
38
+ export declare const projectAdapter: ProjectAdapter;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * Adapter for SBOX project/testproject APIs. Calls /testprojects and count-per-project.
4
+ * No business logic; only HTTP and response typing.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.projectAdapter = void 0;
8
+ const sbox_api_client_1 = require("./sbox-api.client");
9
+ async function listProjects(token) {
10
+ const { data, status } = await (0, sbox_api_client_1.get)("testprojects", token);
11
+ if (status !== 200) {
12
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Failed to list projects";
13
+ return { status, projects: [], error: msg };
14
+ }
15
+ const list = Array.isArray(data) ? data : [];
16
+ return { status, projects: list };
17
+ }
18
+ /** GET /e34/api/test-projects?requestedRight=token:selfservice — matches My Projects page in sbox-fe-v2. */
19
+ async function listMyProjects(token) {
20
+ const path = "test-projects?requestedRight=token:selfservice";
21
+ const { data, status } = await (0, sbox_api_client_1.get)(path, token);
22
+ if (status !== 200) {
23
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Failed to list my projects";
24
+ return { status, projects: [], error: msg };
25
+ }
26
+ const list = Array.isArray(data) ? data : [];
27
+ return { status, projects: list };
28
+ }
29
+ async function getCountPerProject(days, token) {
30
+ const path = `tests/count-per-project?days=${days}`;
31
+ const { data, status } = await (0, sbox_api_client_1.get)(path, token);
32
+ if (status !== 200) {
33
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Failed to get count per project";
34
+ return { status, items: [], error: msg };
35
+ }
36
+ const list = Array.isArray(data) ? data : [];
37
+ return { status, items: list };
38
+ }
39
+ exports.projectAdapter = { listProjects, listMyProjects, getCountPerProject };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * HTTP client for SBOX backend API. All calls go to /e34/api/*.
3
+ * Does not contain business logic; only request/response handling.
4
+ */
5
+ export interface SboxApiResponse<T> {
6
+ data: T;
7
+ status: number;
8
+ }
9
+ export interface SboxApiError {
10
+ success: false;
11
+ message: string;
12
+ type?: string;
13
+ status: number;
14
+ }
15
+ /** GET without sbox-mcp-server header (for MCP add-on check before login redirect). */
16
+ export declare function getPublic<T>(path: string): Promise<SboxApiResponse<T>>;
17
+ export declare function get<T>(path: string, token?: string | null): Promise<SboxApiResponse<T>>;
18
+ export declare function post<T>(path: string, body: unknown, token?: string | null): Promise<SboxApiResponse<T>>;
19
+ export interface MultipartFormDataLike {
20
+ getHeaders?: () => Record<string, string>;
21
+ }
22
+ export declare function postMultipart<T>(path: string, formData: FormData | MultipartFormDataLike, token?: string | null): Promise<SboxApiResponse<T>>;
23
+ export declare function put<T>(path: string, body: unknown, token?: string | null): Promise<SboxApiResponse<T>>;
24
+ export declare function del<T>(path: string, token?: string | null): Promise<SboxApiResponse<T>>;
25
+ export declare function isSboxError(res: unknown): res is {
26
+ success: boolean;
27
+ message: string;
28
+ type?: string;
29
+ };
30
+ export declare function toSboxApiError(err: unknown, defaultStatus: number): SboxApiError;
31
+ export declare function extractToken(authHeader: string | undefined): string | null;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /**
3
+ * HTTP client for SBOX backend API. All calls go to /e34/api/*.
4
+ * Does not contain business logic; only request/response handling.
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.getPublic = getPublic;
11
+ exports.get = get;
12
+ exports.post = post;
13
+ exports.postMultipart = postMultipart;
14
+ exports.put = put;
15
+ exports.del = del;
16
+ exports.isSboxError = isSboxError;
17
+ exports.toSboxApiError = toSboxApiError;
18
+ exports.extractToken = extractToken;
19
+ const axios_1 = __importDefault(require("axios"));
20
+ const config_1 = require("../config");
21
+ function createClient(token, omitContentType = false) {
22
+ const headers = {
23
+ Accept: "application/json",
24
+ [config_1.config.mcpServerHeaderName]: config_1.config.mcpVersion,
25
+ };
26
+ if (!omitContentType)
27
+ headers["Content-Type"] = "application/json";
28
+ if (token)
29
+ headers[config_1.config.jwtHeader] = token;
30
+ return axios_1.default.create({
31
+ baseURL: config_1.config.sboxApiBaseUrl + config_1.config.sboxApiPrefix,
32
+ timeout: config_1.config.requestTimeoutMs,
33
+ headers,
34
+ validateStatus: () => true,
35
+ });
36
+ }
37
+ /** GET without sbox-mcp-server header (for MCP add-on check before login redirect). */
38
+ async function getPublic(path) {
39
+ const url = (path.startsWith("/") ? path : `/${path}`).replace(/^\//, "");
40
+ const fullUrl = config_1.config.sboxApiBaseUrl + config_1.config.sboxApiPrefix + "/" + url;
41
+ const res = await axios_1.default.get(fullUrl, {
42
+ timeout: config_1.config.requestTimeoutMs,
43
+ headers: { Accept: "application/json" },
44
+ validateStatus: () => true,
45
+ });
46
+ return { data: res.data, status: res.status };
47
+ }
48
+ async function get(path, token) {
49
+ const client = createClient(token);
50
+ const url = path.startsWith("/") ? path : `/${path}`;
51
+ const res = await client.get(url);
52
+ return { data: res.data, status: res.status };
53
+ }
54
+ async function post(path, body, token) {
55
+ const client = createClient(token);
56
+ const url = path.startsWith("/") ? path : `/${path}`;
57
+ const res = await client.post(url, body);
58
+ return { data: res.data, status: res.status };
59
+ }
60
+ async function postMultipart(path, formData, token) {
61
+ const client = createClient(token, true);
62
+ const url = path.startsWith("/") ? path : `/${path}`;
63
+ const extraHeaders = formData && typeof formData.getHeaders === "function" ? formData.getHeaders() : {};
64
+ const res = await client.post(url, formData, { headers: extraHeaders });
65
+ return { data: res.data, status: res.status };
66
+ }
67
+ async function put(path, body, token) {
68
+ const client = createClient(token);
69
+ const url = path.startsWith("/") ? path : `/${path}`;
70
+ const res = await client.put(url, body);
71
+ return { data: res.data, status: res.status };
72
+ }
73
+ async function del(path, token) {
74
+ const client = createClient(token);
75
+ const url = path.startsWith("/") ? path : `/${path}`;
76
+ const res = await client.delete(url);
77
+ return { data: res.data, status: res.status };
78
+ }
79
+ function isSboxError(res) {
80
+ return (typeof res === "object" &&
81
+ res !== null &&
82
+ "success" in res &&
83
+ res.success === false &&
84
+ "message" in res);
85
+ }
86
+ function toSboxApiError(err, defaultStatus) {
87
+ if (axios_1.default.isAxiosError(err)) {
88
+ const ax = err;
89
+ const status = ax.response?.status ?? defaultStatus;
90
+ const message = ax.response?.data?.message ?? ax.message ?? "Request failed";
91
+ const data = ax.response?.data;
92
+ return { success: false, message, status, type: data?.type };
93
+ }
94
+ const message = err instanceof Error ? err.message : "Unknown error";
95
+ return { success: false, message, status: defaultStatus };
96
+ }
97
+ function extractToken(authHeader) {
98
+ if (!authHeader || typeof authHeader !== "string")
99
+ return null;
100
+ const v = authHeader.trim();
101
+ if (v.toLowerCase().startsWith("bearer "))
102
+ return v.slice(7).trim();
103
+ return v;
104
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Adapter for SBOX session APIs. Calls session, video, manual, purge, builds/status.
3
+ * No business logic; only HTTP and response typing.
4
+ */
5
+ export interface ESSessionDataRaw {
6
+ ekey?: string;
7
+ browser?: string;
8
+ version?: string;
9
+ browserVersion?: string;
10
+ deviceName?: string;
11
+ platformName?: string;
12
+ platformVersion?: string;
13
+ result?: string;
14
+ duration?: number;
15
+ framework?: string;
16
+ originIp?: string;
17
+ node?: string;
18
+ index?: string;
19
+ extras?: Record<string, unknown>;
20
+ "@timestamp"?: string;
21
+ [key: string]: unknown;
22
+ }
23
+ export interface PaginatedSessionsRaw {
24
+ content?: ESSessionDataRaw[];
25
+ totalElements?: number;
26
+ }
27
+ export interface SessionAdapter {
28
+ getByEkey(ekey: string, token: string): Promise<{
29
+ status: number;
30
+ data: ESSessionDataRaw | null;
31
+ error?: string;
32
+ }>;
33
+ getVideoUrl(sessionId: string, token: string): Promise<{
34
+ status: number;
35
+ videoUrl: string | null;
36
+ error?: string;
37
+ }>;
38
+ /** Update a single session's result via POST /e34/api/test-data?sessionId=&passed= */
39
+ updateSessionStatus(sessionId: string, passed: boolean, token: string): Promise<{
40
+ status: number;
41
+ error?: string;
42
+ }>;
43
+ updateStatusByBuild(params: {
44
+ buildName: string;
45
+ projectId: string;
46
+ status: string;
47
+ }, token: string): Promise<{
48
+ status: number;
49
+ error?: string;
50
+ }>;
51
+ listPaginated(params: {
52
+ projectId?: string;
53
+ size: number;
54
+ offset: number;
55
+ status?: string;
56
+ browser?: string;
57
+ build?: string;
58
+ sortBy?: string;
59
+ sortDirection?: string;
60
+ maxAgeDays?: number;
61
+ }, token: string): Promise<{
62
+ status: number;
63
+ content: ESSessionDataRaw[];
64
+ totalElements: number;
65
+ error?: string;
66
+ }>;
67
+ deleteManualSession(sessionId: string, token: string): Promise<{
68
+ status: number;
69
+ error?: string;
70
+ }>;
71
+ startManualSession(body: Record<string, unknown>, token: string): Promise<{
72
+ status: number;
73
+ sessionId?: string;
74
+ error?: string;
75
+ }>;
76
+ }
77
+ export declare const sessionAdapter: SessionAdapter;
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ /**
3
+ * Adapter for SBOX session APIs. Calls session, video, manual, purge, builds/status.
4
+ * No business logic; only HTTP and response typing.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.sessionAdapter = void 0;
8
+ const sbox_api_client_1 = require("./sbox-api.client");
9
+ async function getByEkey(ekey, token) {
10
+ const path = `session/${encodeURIComponent(ekey)}`;
11
+ const { data, status } = await (0, sbox_api_client_1.get)(path, token);
12
+ if (status !== 200) {
13
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Session not found";
14
+ return { status, data: null, error: msg };
15
+ }
16
+ return { status, data: data };
17
+ }
18
+ async function getVideoUrl(sessionId, token) {
19
+ const { status, data } = await (0, sbox_api_client_1.get)(`video/${encodeURIComponent(sessionId)}`, token);
20
+ if (status === 200 && data && typeof data.url === "string") {
21
+ return { status, videoUrl: data.url };
22
+ }
23
+ if (status === 302 || status === 200) {
24
+ const loc = data?.location;
25
+ if (typeof loc === "string")
26
+ return { status, videoUrl: loc };
27
+ }
28
+ return { status, videoUrl: null };
29
+ }
30
+ /** POST /e34/api/test-data?sessionId=xxx&passed=true|false */
31
+ async function updateSessionStatus(sessionId, passed, token) {
32
+ const path = `test-data?sessionId=${encodeURIComponent(sessionId)}&passed=${passed}`;
33
+ const { status, data } = await (0, sbox_api_client_1.post)(path, {}, token);
34
+ if (status >= 200 && status < 300)
35
+ return { status };
36
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Update session status failed";
37
+ return { status, error: msg };
38
+ }
39
+ async function updateStatusByBuild(params, token) {
40
+ const { status, data } = await (0, sbox_api_client_1.put)("builds/status", params, token);
41
+ if (status >= 200 && status < 300)
42
+ return { status };
43
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Update failed";
44
+ return { status, error: msg };
45
+ }
46
+ async function listPaginated(params, token) {
47
+ const q = new URLSearchParams();
48
+ if (params.projectId)
49
+ q.set("projectId", params.projectId);
50
+ q.set("size", String(params.size));
51
+ q.set("offset", String(params.offset));
52
+ if (params.status)
53
+ q.set("status", params.status);
54
+ if (params.browser)
55
+ q.set("browser", params.browser);
56
+ if (params.build)
57
+ q.set("build", params.build);
58
+ if (params.sortBy)
59
+ q.set("sortBy", params.sortBy);
60
+ if (params.sortDirection)
61
+ q.set("sortDirection", params.sortDirection);
62
+ const path = `v2/paginated/tests-data?${q.toString()}`;
63
+ const { data, status } = await (0, sbox_api_client_1.get)(path, token);
64
+ if (status !== 200) {
65
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "List failed";
66
+ return { status, content: [], totalElements: 0, error: msg };
67
+ }
68
+ const raw = data;
69
+ return { status, content: raw.content ?? [], totalElements: raw.totalElements ?? 0 };
70
+ }
71
+ /** DELETE /e34/api/test-data?sessionId=xxx — soft-delete session result (same as dashboard). */
72
+ async function deleteManualSession(sessionId, token) {
73
+ const { status, data } = await (0, sbox_api_client_1.del)(`test-data?sessionId=${encodeURIComponent(sessionId)}`, token);
74
+ if (status >= 200 && status < 300)
75
+ return { status };
76
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Delete failed";
77
+ return { status, error: msg };
78
+ }
79
+ async function startManualSession(body, token) {
80
+ const { data, status } = await (0, sbox_api_client_1.post)("manual", body, token);
81
+ if (status === 200 && typeof data === "string") {
82
+ return { status, sessionId: data };
83
+ }
84
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Start manual session failed";
85
+ return { status, error: msg };
86
+ }
87
+ exports.sessionAdapter = {
88
+ getByEkey,
89
+ getVideoUrl,
90
+ updateSessionStatus,
91
+ updateStatusByBuild,
92
+ listPaginated,
93
+ deleteManualSession,
94
+ startManualSession,
95
+ };
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Adapter for SBOX stats/count APIs. Supports time range as days or unit+value (hour/minute/day).
3
+ */
4
+ import type { TimeRange } from "../utils/time-range";
5
+ export interface ResultCountRaw {
6
+ result?: string;
7
+ count?: number;
8
+ }
9
+ export interface BrowserVersionCountRaw {
10
+ browser?: string;
11
+ version?: string;
12
+ count?: number;
13
+ }
14
+ export interface BuildCountRaw {
15
+ buildName?: string;
16
+ session_count?: number;
17
+ }
18
+ export interface CountByProjectRaw {
19
+ project?: string;
20
+ count?: number;
21
+ }
22
+ export interface CountByUserRaw {
23
+ user?: string;
24
+ count?: number;
25
+ }
26
+ export interface CountByTestNameRaw {
27
+ testName?: string;
28
+ count?: number;
29
+ }
30
+ export interface StatsAdapter {
31
+ getTotalTests(token: string, days: number): Promise<{
32
+ status: number;
33
+ total: number;
34
+ error?: string;
35
+ }>;
36
+ getCountByStatus(token: string, timeRange: TimeRange): Promise<{
37
+ status: number;
38
+ items: ResultCountRaw[];
39
+ error?: string;
40
+ }>;
41
+ getSessionsPerBrowser(token: string, timeRange: TimeRange): Promise<{
42
+ status: number;
43
+ items: BrowserVersionCountRaw[];
44
+ error?: string;
45
+ }>;
46
+ getSessionsByBuild(token: string, timeRange: TimeRange): Promise<{
47
+ status: number;
48
+ items: BuildCountRaw[];
49
+ error?: string;
50
+ }>;
51
+ getCountPerProject(token: string, timeRange: TimeRange): Promise<{
52
+ status: number;
53
+ items: CountByProjectRaw[];
54
+ error?: string;
55
+ }>;
56
+ getCountPerUser(token: string, timeRange: TimeRange): Promise<{
57
+ status: number;
58
+ items: CountByUserRaw[];
59
+ error?: string;
60
+ }>;
61
+ getCountPerTestName(token: string, timeRange: TimeRange): Promise<{
62
+ status: number;
63
+ items: CountByTestNameRaw[];
64
+ error?: string;
65
+ }>;
66
+ getManualCountPerTestName(token: string, timeRange: TimeRange): Promise<{
67
+ status: number;
68
+ items: CountByTestNameRaw[];
69
+ error?: string;
70
+ }>;
71
+ }
72
+ export declare const statsAdapter: StatsAdapter;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ /**
3
+ * Adapter for SBOX stats/count APIs. Supports time range as days or unit+value (hour/minute/day).
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.statsAdapter = void 0;
7
+ const sbox_api_client_1 = require("./sbox-api.client");
8
+ const time_range_1 = require("../utils/time-range");
9
+ async function getTotalTests(token, days) {
10
+ const d = Math.max(1, Math.min(365, days));
11
+ const path = `tests/count?days=${d}`;
12
+ const { data, status } = await (0, sbox_api_client_1.get)(path, token);
13
+ if (status !== 200) {
14
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Failed to get total tests count";
15
+ return { status, total: 0, error: msg };
16
+ }
17
+ const total = typeof data.totalTestsLast15Days === "number"
18
+ ? data.totalTestsLast15Days
19
+ : 0;
20
+ return { status, total };
21
+ }
22
+ async function getCountByStatus(token, timeRange) {
23
+ const q = (0, time_range_1.buildTimeQuery)(timeRange);
24
+ const path = `tests/count-by-status?${q}`;
25
+ const { data, status } = await (0, sbox_api_client_1.get)(path, token);
26
+ if (status !== 200) {
27
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Failed to get count by status";
28
+ return { status, items: [], error: msg };
29
+ }
30
+ const list = Array.isArray(data) ? data : [];
31
+ return { status, items: list };
32
+ }
33
+ async function getSessionsPerBrowser(token, timeRange) {
34
+ const q = (0, time_range_1.buildTimeQuery)(timeRange);
35
+ const path = `tests/count-by-browser-and-version?${q}`;
36
+ const { data, status } = await (0, sbox_api_client_1.get)(path, token);
37
+ if (status !== 200) {
38
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Failed to get sessions per browser";
39
+ return { status, items: [], error: msg };
40
+ }
41
+ const list = Array.isArray(data) ? data : [];
42
+ return { status, items: list };
43
+ }
44
+ async function getSessionsByBuild(token, timeRange) {
45
+ const q = (0, time_range_1.buildTimeQuery)(timeRange);
46
+ const path = `sessions/count/builds?${q}`;
47
+ const { data, status } = await (0, sbox_api_client_1.get)(path, token);
48
+ if (status !== 200) {
49
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Failed to get sessions by build";
50
+ return { status, items: [], error: msg };
51
+ }
52
+ const list = Array.isArray(data) ? data : [];
53
+ return { status, items: list };
54
+ }
55
+ async function getCountPerProject(token, timeRange) {
56
+ const q = (0, time_range_1.buildTimeQuery)(timeRange);
57
+ const path = `tests/count-per-project?${q}`;
58
+ const { data, status } = await (0, sbox_api_client_1.get)(path, token);
59
+ if (status !== 200) {
60
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Failed to get count per project";
61
+ return { status, items: [], error: msg };
62
+ }
63
+ const list = Array.isArray(data) ? data : [];
64
+ return { status, items: list };
65
+ }
66
+ async function getCountPerUser(token, timeRange) {
67
+ const q = (0, time_range_1.buildTimeQuery)(timeRange);
68
+ const path = `tests/count-per-user?${q}`;
69
+ const { data, status } = await (0, sbox_api_client_1.get)(path, token);
70
+ if (status !== 200) {
71
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Failed to get count per user";
72
+ return { status, items: [], error: msg };
73
+ }
74
+ const list = Array.isArray(data) ? data : [];
75
+ return { status, items: list };
76
+ }
77
+ async function getCountPerTestName(token, timeRange) {
78
+ const q = (0, time_range_1.buildTimeQuery)(timeRange);
79
+ const path = `tests/count-per-test-name?${q}`;
80
+ const { data, status } = await (0, sbox_api_client_1.get)(path, token);
81
+ if (status !== 200) {
82
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Failed to get count per test name";
83
+ return { status, items: [], error: msg };
84
+ }
85
+ const list = Array.isArray(data) ? data : [];
86
+ return { status, items: list };
87
+ }
88
+ async function getManualCountPerTestName(token, timeRange) {
89
+ const q = (0, time_range_1.buildTimeQuery)(timeRange);
90
+ const path = `tests/manual/count-per-test-name?${q}`;
91
+ const { data, status } = await (0, sbox_api_client_1.get)(path, token);
92
+ if (status !== 200) {
93
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Failed to get manual test count per test name";
94
+ return { status, items: [], error: msg };
95
+ }
96
+ const list = Array.isArray(data) ? data : [];
97
+ return { status, items: list };
98
+ }
99
+ exports.statsAdapter = {
100
+ getTotalTests,
101
+ getCountByStatus,
102
+ getSessionsPerBrowser,
103
+ getSessionsByBuild,
104
+ getCountPerProject,
105
+ getCountPerUser,
106
+ getCountPerTestName,
107
+ getManualCountPerTestName,
108
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Adapter for SBOX file upload (mobile apps APK/IPA). Calls POST /e34/api/app (multipart).
3
+ * No business logic; only HTTP and response typing.
4
+ */
5
+ export interface UploadAppResponseRaw {
6
+ success?: boolean;
7
+ uploadedAppIdentifier?: string;
8
+ }
9
+ export interface UploadAdapter {
10
+ uploadMobileApp(projectId: string, fileBuffer: Buffer, filename: string, token: string): Promise<{
11
+ status: number;
12
+ uploadedAppIdentifier?: string;
13
+ error?: string;
14
+ }>;
15
+ }
16
+ export declare const uploadAdapter: UploadAdapter;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /**
3
+ * Adapter for SBOX file upload (mobile apps APK/IPA). Calls POST /e34/api/app (multipart).
4
+ * No business logic; only HTTP and response typing.
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.uploadAdapter = void 0;
11
+ const form_data_1 = __importDefault(require("form-data"));
12
+ const sbox_api_client_1 = require("./sbox-api.client");
13
+ async function uploadMobileApp(projectId, fileBuffer, filename, token) {
14
+ const form = new form_data_1.default();
15
+ form.append("file", fileBuffer, { filename });
16
+ const path = `app?projectId=${encodeURIComponent(projectId)}`;
17
+ const { data, status } = await (0, sbox_api_client_1.postMultipart)(path, form, token);
18
+ if (status !== 200) {
19
+ const msg = (0, sbox_api_client_1.isSboxError)(data) ? data.message : "Upload failed";
20
+ return { status, error: msg };
21
+ }
22
+ const raw = data;
23
+ return { status, uploadedAppIdentifier: raw.uploadedAppIdentifier };
24
+ }
25
+ exports.uploadAdapter = { uploadMobileApp };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Adapter for SBOX user APIs. Calls /users and uses session data for active counts.
3
+ * No business logic; only HTTP and response typing.
4
+ */
5
+ export interface PrincipalRaw {
6
+ userId?: string;
7
+ fullname?: string;
8
+ realm?: {
9
+ id?: string;
10
+ };
11
+ extras?: Record<string, string>;
12
+ }
13
+ export interface UserAdapter {
14
+ searchUsers(params: {
15
+ realmId: string;
16
+ filter?: string;
17
+ size?: number;
18
+ }, token: string): Promise<{
19
+ status: number;
20
+ users: PrincipalRaw[];
21
+ error?: string;
22
+ }>;
23
+ }
24
+ export declare const userAdapter: UserAdapter;