@browserstack/mcp-server 1.1.0 → 1.1.1

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 (53) hide show
  1. package/dist/config.js +2 -6
  2. package/dist/index.js +29 -34
  3. package/dist/lib/api.js +3 -9
  4. package/dist/lib/constants.js +3 -6
  5. package/dist/lib/device-cache.js +14 -21
  6. package/dist/lib/error.js +3 -6
  7. package/dist/lib/fuzzy.js +1 -4
  8. package/dist/lib/inmemory-store.js +1 -4
  9. package/dist/lib/instrumentation.js +12 -18
  10. package/dist/lib/local.js +27 -35
  11. package/dist/lib/utils.js +6 -15
  12. package/dist/logger.js +4 -6
  13. package/dist/tools/accessibility.js +13 -16
  14. package/dist/tools/accessiblity-utils/report-fetcher.js +7 -14
  15. package/dist/tools/accessiblity-utils/report-parser.js +5 -11
  16. package/dist/tools/accessiblity-utils/scanner.js +9 -16
  17. package/dist/tools/appautomate-utils/appautomate.js +17 -27
  18. package/dist/tools/appautomate.js +29 -35
  19. package/dist/tools/applive-utils/fuzzy-search.js +3 -6
  20. package/dist/tools/applive-utils/start-session.js +14 -20
  21. package/dist/tools/applive-utils/upload-app.js +12 -51
  22. package/dist/tools/applive.js +18 -25
  23. package/dist/tools/automate-utils/fetch-screenshots.js +8 -14
  24. package/dist/tools/automate.js +14 -21
  25. package/dist/tools/bstack-sdk.js +14 -18
  26. package/dist/tools/failurelogs-utils/app-automate.js +15 -26
  27. package/dist/tools/failurelogs-utils/automate.js +13 -23
  28. package/dist/tools/getFailureLogs.js +42 -49
  29. package/dist/tools/live-utils/desktop-filter.js +8 -11
  30. package/dist/tools/live-utils/mobile-filter.js +7 -10
  31. package/dist/tools/live-utils/start-session.js +17 -23
  32. package/dist/tools/live-utils/types.js +2 -5
  33. package/dist/tools/live-utils/version-resolver.js +1 -4
  34. package/dist/tools/live.js +23 -29
  35. package/dist/tools/observability.js +12 -19
  36. package/dist/tools/sdk-utils/constants.js +3 -9
  37. package/dist/tools/sdk-utils/instructions.js +4 -9
  38. package/dist/tools/sdk-utils/types.js +1 -2
  39. package/dist/tools/testmanagement-utils/TCG-utils/api.js +26 -38
  40. package/dist/tools/testmanagement-utils/TCG-utils/config.js +5 -10
  41. package/dist/tools/testmanagement-utils/TCG-utils/helpers.js +3 -8
  42. package/dist/tools/testmanagement-utils/TCG-utils/types.js +5 -8
  43. package/dist/tools/testmanagement-utils/add-test-result.js +17 -24
  44. package/dist/tools/testmanagement-utils/create-project-folder.js +21 -28
  45. package/dist/tools/testmanagement-utils/create-testcase.js +30 -38
  46. package/dist/tools/testmanagement-utils/create-testrun.js +23 -30
  47. package/dist/tools/testmanagement-utils/list-testcases.js +15 -22
  48. package/dist/tools/testmanagement-utils/list-testruns.js +12 -19
  49. package/dist/tools/testmanagement-utils/testcase-from-file.js +16 -22
  50. package/dist/tools/testmanagement-utils/update-testrun.js +15 -22
  51. package/dist/tools/testmanagement-utils/upload-file.js +22 -29
  52. package/dist/tools/testmanagement.js +61 -76
  53. package/package.json +1 -1
@@ -1,25 +1,19 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.AccessibilityScanner = void 0;
7
- const axios_1 = __importDefault(require("axios"));
8
- const config_js_1 = __importDefault(require("../../config.js"));
9
- class AccessibilityScanner {
1
+ import axios from "axios";
2
+ import config from "../../config.js";
3
+ export class AccessibilityScanner {
10
4
  auth = {
11
- username: config_js_1.default.browserstackUsername,
12
- password: config_js_1.default.browserstackAccessKey,
5
+ username: config.browserstackUsername,
6
+ password: config.browserstackAccessKey,
13
7
  };
14
8
  async startScan(name, urlList) {
15
9
  try {
16
- const { data } = await axios_1.default.post("https://api-accessibility.browserstack.com/api/website-scanner/v1/scans", { name, urlList, recurring: false }, { auth: this.auth });
10
+ const { data } = await axios.post("https://api-accessibility.browserstack.com/api/website-scanner/v1/scans", { name, urlList, recurring: false }, { auth: this.auth });
17
11
  if (!data.success)
18
12
  throw new Error(`Unable to start scan: ${data.errors?.join(", ")}`);
19
13
  return data;
20
14
  }
21
15
  catch (err) {
22
- if (axios_1.default.isAxiosError(err) && err.response?.data) {
16
+ if (axios.isAxiosError(err) && err.response?.data) {
23
17
  const msg = err.response.data.error ||
24
18
  err.response.data.message ||
25
19
  err.message;
@@ -30,13 +24,13 @@ class AccessibilityScanner {
30
24
  }
31
25
  async pollStatus(scanId, scanRunId) {
32
26
  try {
33
- const { data } = await axios_1.default.get(`https://api-accessibility.browserstack.com/api/website-scanner/v1/scans/${scanId}/scan_runs/${scanRunId}/status`, { auth: this.auth });
27
+ const { data } = await axios.get(`https://api-accessibility.browserstack.com/api/website-scanner/v1/scans/${scanId}/scan_runs/${scanRunId}/status`, { auth: this.auth });
34
28
  if (!data.success)
35
29
  throw new Error(`Failed to get status: ${data.errors?.join(", ")}`);
36
30
  return data;
37
31
  }
38
32
  catch (err) {
39
- if (axios_1.default.isAxiosError(err) && err.response?.data) {
33
+ if (axios.isAxiosError(err) && err.response?.data) {
40
34
  const msg = err.response.data.message || err.message;
41
35
  throw new Error(`Failed to get scan status: ${msg}`);
42
36
  }
@@ -84,4 +78,3 @@ class AccessibilityScanner {
84
78
  });
85
79
  }
86
80
  }
87
- exports.AccessibilityScanner = AccessibilityScanner;
@@ -1,24 +1,14 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.findMatchingDevice = findMatchingDevice;
7
- exports.getDeviceVersions = getDeviceVersions;
8
- exports.resolveVersion = resolveVersion;
9
- exports.validateArgs = validateArgs;
10
- exports.uploadApp = uploadApp;
11
- const fs_1 = __importDefault(require("fs"));
12
- const axios_1 = __importDefault(require("axios"));
13
- const config_js_1 = __importDefault(require("../../config.js"));
14
- const form_data_1 = __importDefault(require("form-data"));
15
- const fuzzy_js_1 = require("../../lib/fuzzy.js");
1
+ import fs from "fs";
2
+ import axios from "axios";
3
+ import config from "../../config.js";
4
+ import FormData from "form-data";
5
+ import { customFuzzySearch } from "../../lib/fuzzy.js";
16
6
  /**
17
7
  * Finds devices that exactly match the provided display name.
18
8
  * Uses fuzzy search first, and then filters for exact case-insensitive match.
19
9
  */
20
- function findMatchingDevice(devices, deviceName) {
21
- const matches = (0, fuzzy_js_1.customFuzzySearch)(devices, ["display_name"], deviceName, 5);
10
+ export function findMatchingDevice(devices, deviceName) {
11
+ const matches = customFuzzySearch(devices, ["display_name"], deviceName, 5);
22
12
  if (matches.length === 0) {
23
13
  const availableDevices = [
24
14
  ...new Set(devices.map((d) => d.display_name)),
@@ -35,14 +25,14 @@ function findMatchingDevice(devices, deviceName) {
35
25
  /**
36
26
  * Extracts all unique OS versions from a device list and sorts them.
37
27
  */
38
- function getDeviceVersions(devices) {
28
+ export function getDeviceVersions(devices) {
39
29
  return [...new Set(devices.map((d) => d.os_version))].sort();
40
30
  }
41
31
  /**
42
32
  * Resolves the requested platform version against available versions.
43
33
  * Supports 'latest' and 'oldest' as dynamic selectors.
44
34
  */
45
- function resolveVersion(versions, requestedVersion) {
35
+ export function resolveVersion(versions, requestedVersion) {
46
36
  if (requestedVersion === "latest") {
47
37
  return versions[versions.length - 1];
48
38
  }
@@ -59,7 +49,7 @@ function resolveVersion(versions, requestedVersion) {
59
49
  * Validates the input arguments for taking app screenshots.
60
50
  * Checks for presence and correctness of platform, device, and file types.
61
51
  */
62
- function validateArgs(args) {
52
+ export function validateArgs(args) {
63
53
  const { desiredPlatform, desiredPlatformVersion, appPath, desiredPhone } = args;
64
54
  if (!desiredPlatform || !desiredPhone) {
65
55
  throw new Error("Missing required arguments: desiredPlatform and desiredPhone are required");
@@ -80,20 +70,20 @@ function validateArgs(args) {
80
70
  /**
81
71
  * Uploads an application file to AppAutomate and returns the app URL
82
72
  */
83
- async function uploadApp(appPath) {
73
+ export async function uploadApp(appPath) {
84
74
  const filePath = appPath;
85
- if (!fs_1.default.existsSync(filePath)) {
75
+ if (!fs.existsSync(filePath)) {
86
76
  throw new Error(`File not found at path: ${filePath}`);
87
77
  }
88
- const formData = new form_data_1.default();
89
- formData.append("file", fs_1.default.createReadStream(filePath));
90
- const response = await axios_1.default.post("https://api-cloud.browserstack.com/app-automate/upload", formData, {
78
+ const formData = new FormData();
79
+ formData.append("file", fs.createReadStream(filePath));
80
+ const response = await axios.post("https://api-cloud.browserstack.com/app-automate/upload", formData, {
91
81
  headers: {
92
82
  ...formData.getHeaders(),
93
83
  },
94
84
  auth: {
95
- username: config_js_1.default.browserstackUsername,
96
- password: config_js_1.default.browserstackAccessKey,
85
+ username: config.browserstackUsername,
86
+ password: config.browserstackAccessKey,
97
87
  },
98
88
  });
99
89
  if (response.data.app_url) {
@@ -1,17 +1,11 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.default = addAppAutomationTools;
7
- const zod_1 = require("zod");
8
- const logger_js_1 = __importDefault(require("../logger.js"));
9
- const config_js_1 = __importDefault(require("../config.js"));
10
- const instrumentation_js_1 = require("../lib/instrumentation.js");
11
- const utils_js_1 = require("../lib/utils.js");
12
- const webdriverio_1 = require("webdriverio");
13
- const device_cache_js_1 = require("../lib/device-cache.js");
14
- const appautomate_js_1 = require("./appautomate-utils/appautomate.js");
1
+ import { z } from "zod";
2
+ import logger from "../logger.js";
3
+ import config from "../config.js";
4
+ import { trackMCP } from "../lib/instrumentation.js";
5
+ import { maybeCompressBase64 } from "../lib/utils.js";
6
+ import { remote } from "webdriverio";
7
+ import { getDevicesAndBrowsers, BrowserStackProducts, } from "../lib/device-cache.js";
8
+ import { findMatchingDevice, getDeviceVersions, resolveVersion, validateArgs, uploadApp, } from "./appautomate-utils/appautomate.js";
15
9
  var Platform;
16
10
  (function (Platform) {
17
11
  Platform["ANDROID"] = "android";
@@ -23,23 +17,23 @@ var Platform;
23
17
  async function takeAppScreenshot(args) {
24
18
  let driver;
25
19
  try {
26
- (0, appautomate_js_1.validateArgs)(args);
20
+ validateArgs(args);
27
21
  const { desiredPlatform, desiredPhone, appPath } = args;
28
22
  let { desiredPlatformVersion } = args;
29
- const platforms = (await (0, device_cache_js_1.getDevicesAndBrowsers)(device_cache_js_1.BrowserStackProducts.APP_AUTOMATE)).mobile;
23
+ const platforms = (await getDevicesAndBrowsers(BrowserStackProducts.APP_AUTOMATE)).mobile;
30
24
  const platformData = platforms.find((p) => p.os === desiredPlatform.toLowerCase());
31
25
  if (!platformData) {
32
26
  throw new Error(`Platform ${desiredPlatform} not found in device cache.`);
33
27
  }
34
- const matchingDevices = (0, appautomate_js_1.findMatchingDevice)(platformData.devices, desiredPhone);
35
- const availableVersions = (0, appautomate_js_1.getDeviceVersions)(matchingDevices);
36
- desiredPlatformVersion = (0, appautomate_js_1.resolveVersion)(availableVersions, desiredPlatformVersion);
28
+ const matchingDevices = findMatchingDevice(platformData.devices, desiredPhone);
29
+ const availableVersions = getDeviceVersions(matchingDevices);
30
+ desiredPlatformVersion = resolveVersion(availableVersions, desiredPlatformVersion);
37
31
  const selectedDevice = matchingDevices.find((d) => d.os_version === desiredPlatformVersion);
38
32
  if (!selectedDevice) {
39
33
  throw new Error(`Device "${desiredPhone}" with version ${desiredPlatformVersion} not found.`);
40
34
  }
41
- const app_url = await (0, appautomate_js_1.uploadApp)(appPath);
42
- logger_js_1.default.info(`App uploaded. URL: ${app_url}`);
35
+ const app_url = await uploadApp(appPath);
36
+ logger.info(`App uploaded. URL: ${app_url}`);
43
37
  const capabilities = {
44
38
  platformName: desiredPlatform,
45
39
  "appium:platformVersion": selectedDevice.os_version,
@@ -47,13 +41,13 @@ async function takeAppScreenshot(args) {
47
41
  "appium:app": app_url,
48
42
  "appium:autoGrantPermissions": true,
49
43
  "bstack:options": {
50
- userName: config_js_1.default.browserstackUsername,
51
- accessKey: config_js_1.default.browserstackAccessKey,
44
+ userName: config.browserstackUsername,
45
+ accessKey: config.browserstackAccessKey,
52
46
  appiumVersion: "2.0.1",
53
47
  },
54
48
  };
55
- logger_js_1.default.info("Starting WebDriver session on BrowserStack...");
56
- driver = await (0, webdriverio_1.remote)({
49
+ logger.info("Starting WebDriver session on BrowserStack...");
50
+ driver = await remote({
57
51
  protocol: "https",
58
52
  hostname: "hub.browserstack.com",
59
53
  port: 443,
@@ -61,7 +55,7 @@ async function takeAppScreenshot(args) {
61
55
  capabilities,
62
56
  });
63
57
  const screenshotBase64 = await driver.takeScreenshot();
64
- const compressed = await (0, utils_js_1.maybeCompressBase64)(screenshotBase64);
58
+ const compressed = await maybeCompressBase64(screenshotBase64);
65
59
  return {
66
60
  content: [
67
61
  {
@@ -74,12 +68,12 @@ async function takeAppScreenshot(args) {
74
68
  };
75
69
  }
76
70
  catch (error) {
77
- logger_js_1.default.error("Error during app automation or screenshot capture", error);
71
+ logger.error("Error during app automation or screenshot capture", error);
78
72
  throw error;
79
73
  }
80
74
  finally {
81
75
  if (driver) {
82
- logger_js_1.default.info("Cleaning up WebDriver session...");
76
+ logger.info("Cleaning up WebDriver session...");
83
77
  await driver.deleteSession();
84
78
  }
85
79
  }
@@ -87,27 +81,27 @@ async function takeAppScreenshot(args) {
87
81
  /**
88
82
  * Registers the `takeAppScreenshot` tool with the MCP server.
89
83
  */
90
- function addAppAutomationTools(server) {
84
+ export default function addAppAutomationTools(server) {
91
85
  server.tool("takeAppScreenshot", "Use this tool to take a screenshot of an app running on a BrowserStack device. This is useful for visual testing and debugging.", {
92
- desiredPhone: zod_1.z
86
+ desiredPhone: z
93
87
  .string()
94
88
  .describe("The full name of the device to run the app on. Example: 'iPhone 12 Pro' or 'Samsung Galaxy S20'. Always ask the user for the device they want to use."),
95
- desiredPlatformVersion: zod_1.z
89
+ desiredPlatformVersion: z
96
90
  .string()
97
91
  .describe("The platform version to run the app on. Use 'latest' or 'oldest' for dynamic resolution."),
98
- desiredPlatform: zod_1.z
92
+ desiredPlatform: z
99
93
  .enum([Platform.ANDROID, Platform.IOS])
100
94
  .describe("Platform to run the app on. Either 'android' or 'ios'."),
101
- appPath: zod_1.z
95
+ appPath: z
102
96
  .string()
103
97
  .describe("The path to the .apk or .ipa file. Required for app installation."),
104
98
  }, async (args) => {
105
99
  try {
106
- (0, instrumentation_js_1.trackMCP)("takeAppScreenshot", server.server.getClientVersion());
100
+ trackMCP("takeAppScreenshot", server.server.getClientVersion());
107
101
  return await takeAppScreenshot(args);
108
102
  }
109
103
  catch (error) {
110
- (0, instrumentation_js_1.trackMCP)("takeAppScreenshot", server.server.getClientVersion(), error);
104
+ trackMCP("takeAppScreenshot", server.server.getClientVersion(), error);
111
105
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
112
106
  return {
113
107
  content: [
@@ -1,11 +1,8 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fuzzySearchDevices = fuzzySearchDevices;
4
- const fuzzy_js_1 = require("../../lib/fuzzy.js");
1
+ import { customFuzzySearch } from "../../lib/fuzzy.js";
5
2
  /**
6
3
  * Fuzzy searches App Live device entries by name.
7
4
  */
8
- async function fuzzySearchDevices(devices, query, limit = 5) {
9
- const top_match = (0, fuzzy_js_1.customFuzzySearch)(devices, ["device", "display_name"], query, limit);
5
+ export async function fuzzySearchDevices(devices, query, limit = 5) {
6
+ const top_match = customFuzzySearch(devices, ["device", "display_name"], query, limit);
10
7
  return top_match;
11
8
  }
@@ -1,31 +1,25 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.startSession = startSession;
7
- const child_process_1 = __importDefault(require("child_process"));
8
- const logger_js_1 = __importDefault(require("../../logger.js"));
9
- const device_cache_js_1 = require("../../lib/device-cache.js");
10
- const fuzzy_search_js_1 = require("./fuzzy-search.js");
11
- const utils_js_1 = require("../../lib/utils.js");
12
- const upload_app_js_1 = require("./upload-app.js");
1
+ import childProcess from "child_process";
2
+ import logger from "../../logger.js";
3
+ import { BrowserStackProducts, getDevicesAndBrowsers, } from "../../lib/device-cache.js";
4
+ import { fuzzySearchDevices } from "./fuzzy-search.js";
5
+ import { sanitizeUrlParam } from "../../lib/utils.js";
6
+ import { uploadApp } from "./upload-app.js";
13
7
  /**
14
8
  * Starts an App Live session after filtering, fuzzy matching, and launching.
15
9
  * @param args - The arguments for starting the session.
16
10
  * @returns The launch URL for the session.
17
11
  * @throws Will throw an error if no devices are found or if the app URL is invalid.
18
12
  */
19
- async function startSession(args) {
13
+ export async function startSession(args) {
20
14
  const { appPath, desiredPlatform, desiredPhone } = args;
21
15
  let { desiredPlatformVersion } = args;
22
- const data = await (0, device_cache_js_1.getDevicesAndBrowsers)(device_cache_js_1.BrowserStackProducts.APP_LIVE);
16
+ const data = await getDevicesAndBrowsers(BrowserStackProducts.APP_LIVE);
23
17
  const allDevices = data.mobile.flatMap((group) => group.devices.map((dev) => ({ ...dev, os: group.os })));
24
18
  desiredPlatformVersion = resolvePlatformVersion(allDevices, desiredPlatform, desiredPlatformVersion);
25
19
  const filteredDevices = filterDevicesByPlatformAndVersion(allDevices, desiredPlatform, desiredPlatformVersion);
26
- const matches = await (0, fuzzy_search_js_1.fuzzySearchDevices)(filteredDevices, desiredPhone);
20
+ const matches = await fuzzySearchDevices(filteredDevices, desiredPhone);
27
21
  const selectedDevice = validateAndSelectDevice(matches, desiredPhone, desiredPlatform, desiredPlatformVersion);
28
- const { app_url } = await (0, upload_app_js_1.uploadApp)(appPath);
22
+ const { app_url } = await uploadApp(appPath);
29
23
  validateAppUrl(app_url);
30
24
  const launchUrl = constructLaunchUrl(app_url, selectedDevice, desiredPlatform, desiredPlatformVersion);
31
25
  openBrowser(launchUrl);
@@ -120,7 +114,7 @@ function validateAppUrl(appUrl) {
120
114
  * @returns The constructed launch URL.
121
115
  */
122
116
  function constructLaunchUrl(appUrl, device, desiredPlatform, desiredPlatformVersion) {
123
- const deviceParam = (0, utils_js_1.sanitizeUrlParam)(device.display_name.replace(/\s+/g, "+"));
117
+ const deviceParam = sanitizeUrlParam(device.display_name.replace(/\s+/g, "+"));
124
118
  const params = new URLSearchParams({
125
119
  os: desiredPlatform,
126
120
  os_version: desiredPlatformVersion,
@@ -144,16 +138,16 @@ function openBrowser(launchUrl) {
144
138
  ? ["cmd", "/c", "start", launchUrl]
145
139
  : ["xdg-open", launchUrl];
146
140
  // nosemgrep:javascript.lang.security.detect-child-process.detect-child-process
147
- const child = child_process_1.default.spawn(command[0], command.slice(1), {
141
+ const child = childProcess.spawn(command[0], command.slice(1), {
148
142
  stdio: "ignore",
149
143
  detached: true,
150
144
  });
151
145
  child.on("error", (error) => {
152
- logger_js_1.default.error(`Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`);
146
+ logger.error(`Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`);
153
147
  });
154
148
  child.unref();
155
149
  }
156
150
  catch (error) {
157
- logger_js_1.default.error(`Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`);
151
+ logger.error(`Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`);
158
152
  }
159
153
  }
@@ -1,66 +1,27 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.uploadApp = uploadApp;
40
- const axios_1 = __importStar(require("axios"));
41
- const form_data_1 = __importDefault(require("form-data"));
42
- const fs_1 = __importDefault(require("fs"));
43
- const config_js_1 = __importDefault(require("../../config.js"));
44
- async function uploadApp(filePath) {
45
- if (!fs_1.default.existsSync(filePath)) {
1
+ import axios, { AxiosError } from "axios";
2
+ import FormData from "form-data";
3
+ import fs from "fs";
4
+ import config from "../../config.js";
5
+ export async function uploadApp(filePath) {
6
+ if (!fs.existsSync(filePath)) {
46
7
  throw new Error(`File not found at path: ${filePath}`);
47
8
  }
48
- const formData = new form_data_1.default();
49
- formData.append("file", fs_1.default.createReadStream(filePath));
9
+ const formData = new FormData();
10
+ formData.append("file", fs.createReadStream(filePath));
50
11
  try {
51
- const response = await axios_1.default.post("https://api-cloud.browserstack.com/app-live/upload", formData, {
12
+ const response = await axios.post("https://api-cloud.browserstack.com/app-live/upload", formData, {
52
13
  headers: {
53
14
  ...formData.getHeaders(),
54
15
  },
55
16
  auth: {
56
- username: config_js_1.default.browserstackUsername,
57
- password: config_js_1.default.browserstackAccessKey,
17
+ username: config.browserstackUsername,
18
+ password: config.browserstackAccessKey,
58
19
  },
59
20
  });
60
21
  return response.data;
61
22
  }
62
23
  catch (error) {
63
- if (error instanceof axios_1.AxiosError) {
24
+ if (error instanceof AxiosError) {
64
25
  throw new Error(`Failed to upload app: ${error.response?.data?.message || error.message}`);
65
26
  }
66
27
  throw error;
@@ -1,19 +1,12 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.startAppLiveSession = startAppLiveSession;
7
- exports.default = addAppLiveTools;
8
- const zod_1 = require("zod");
9
- const fs_1 = __importDefault(require("fs"));
10
- const start_session_js_1 = require("./applive-utils/start-session.js");
11
- const logger_js_1 = __importDefault(require("../logger.js"));
12
- const instrumentation_js_1 = require("../lib/instrumentation.js");
1
+ import { z } from "zod";
2
+ import fs from "fs";
3
+ import { startSession } from "./applive-utils/start-session.js";
4
+ import logger from "../logger.js";
5
+ import { trackMCP } from "../lib/instrumentation.js";
13
6
  /**
14
7
  * Launches an App Live Session on BrowserStack.
15
8
  */
16
- async function startAppLiveSession(args) {
9
+ export async function startAppLiveSession(args) {
17
10
  if (!args.desiredPlatform) {
18
11
  throw new Error("You must provide a desiredPlatform.");
19
12
  }
@@ -31,16 +24,16 @@ async function startAppLiveSession(args) {
31
24
  }
32
25
  // check if the app path exists && is readable
33
26
  try {
34
- if (!fs_1.default.existsSync(args.appPath)) {
27
+ if (!fs.existsSync(args.appPath)) {
35
28
  throw new Error("The app path does not exist.");
36
29
  }
37
- fs_1.default.accessSync(args.appPath, fs_1.default.constants.R_OK);
30
+ fs.accessSync(args.appPath, fs.constants.R_OK);
38
31
  }
39
32
  catch (error) {
40
- logger_js_1.default.error("The app path does not exist or is not readable: %s", error);
33
+ logger.error("The app path does not exist or is not readable: %s", error);
41
34
  throw new Error("The app path does not exist or is not readable.");
42
35
  }
43
- const launchUrl = await (0, start_session_js_1.startSession)({
36
+ const launchUrl = await startSession({
44
37
  appPath: args.appPath,
45
38
  desiredPlatform: args.desiredPlatform,
46
39
  desiredPhone: args.desiredPhone,
@@ -55,28 +48,28 @@ async function startAppLiveSession(args) {
55
48
  ],
56
49
  };
57
50
  }
58
- function addAppLiveTools(server) {
51
+ export default function addAppLiveTools(server) {
59
52
  server.tool("runAppLiveSession", "Use this tool when user wants to manually check their app on a particular mobile device using BrowserStack's cloud infrastructure. Can be used to debug crashes, slow performance, etc.", {
60
- desiredPhone: zod_1.z
53
+ desiredPhone: z
61
54
  .string()
62
55
  .describe("The full name of the device to run the app on. Example: 'iPhone 12 Pro' or 'Samsung Galaxy S20' or 'Google Pixel 6'. Always ask the user for the device they want to use, do not assume it. "),
63
- desiredPlatformVersion: zod_1.z
56
+ desiredPlatformVersion: z
64
57
  .string()
65
58
  .describe("Specifies the platform version to run the app on. For example, use '12.0' for Android or '16.0' for iOS. If the user says 'latest', 'newest', or similar, normalize it to 'latest'. Likewise, convert terms like 'earliest' or 'oldest' to 'oldest'."),
66
- desiredPlatform: zod_1.z
59
+ desiredPlatform: z
67
60
  .enum(["android", "ios"])
68
61
  .describe("Which platform to run on, examples: 'android', 'ios'. Set this based on the app path provided."),
69
- appPath: zod_1.z
62
+ appPath: z
70
63
  .string()
71
64
  .describe("The path to the .ipa or .apk file to install on the device. Always ask the user for the app path, do not assume it."),
72
65
  }, async (args) => {
73
66
  try {
74
- (0, instrumentation_js_1.trackMCP)("runAppLiveSession", server.server.getClientVersion());
67
+ trackMCP("runAppLiveSession", server.server.getClientVersion());
75
68
  return await startAppLiveSession(args);
76
69
  }
77
70
  catch (error) {
78
- logger_js_1.default.error("App live session failed: %s", error);
79
- (0, instrumentation_js_1.trackMCP)("runAppLiveSession", server.server.getClientVersion(), error);
71
+ logger.error("App live session failed: %s", error);
72
+ trackMCP("runAppLiveSession", server.server.getClientVersion(), error);
80
73
  return {
81
74
  content: [
82
75
  {
@@ -1,17 +1,11 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.fetchAutomationScreenshots = fetchAutomationScreenshots;
7
- const config_js_1 = __importDefault(require("../../config.js"));
8
- const utils_js_1 = require("../../lib/utils.js");
9
- const constants_js_1 = require("../../lib/constants.js");
1
+ import config from "../../config.js";
2
+ import { assertOkResponse, maybeCompressBase64 } from "../../lib/utils.js";
3
+ import { SessionType } from "../../lib/constants.js";
10
4
  //Extracts screenshot URLs from BrowserStack session logs
11
5
  async function extractScreenshotUrls(sessionId, sessionType) {
12
- const credentials = `${config_js_1.default.browserstackUsername}:${config_js_1.default.browserstackAccessKey}`;
6
+ const credentials = `${config.browserstackUsername}:${config.browserstackAccessKey}`;
13
7
  const auth = Buffer.from(credentials).toString("base64");
14
- const baseUrl = `https://api.browserstack.com/${sessionType === constants_js_1.SessionType.Automate ? "automate" : "app-automate"}`;
8
+ const baseUrl = `https://api.browserstack.com/${sessionType === SessionType.Automate ? "automate" : "app-automate"}`;
15
9
  const url = `${baseUrl}/sessions/${sessionId}/logs`;
16
10
  const response = await fetch(url, {
17
11
  headers: {
@@ -19,7 +13,7 @@ async function extractScreenshotUrls(sessionId, sessionType) {
19
13
  Authorization: `Basic ${auth}`,
20
14
  },
21
15
  });
22
- await (0, utils_js_1.assertOkResponse)(response, "Session");
16
+ await assertOkResponse(response, "Session");
23
17
  const text = await response.text();
24
18
  const urls = [];
25
19
  const SCREENSHOT_PATTERN = /REQUEST.*GET.*\/screenshot/;
@@ -45,7 +39,7 @@ async function convertUrlsToBase64(urls) {
45
39
  const arrayBuffer = await response.arrayBuffer();
46
40
  const base64 = Buffer.from(arrayBuffer).toString("base64");
47
41
  // Compress the base64 image if needed
48
- const compressedBase64 = await (0, utils_js_1.maybeCompressBase64)(base64);
42
+ const compressedBase64 = await maybeCompressBase64(base64);
49
43
  return {
50
44
  url,
51
45
  base64: compressedBase64,
@@ -54,7 +48,7 @@ async function convertUrlsToBase64(urls) {
54
48
  return screenshots;
55
49
  }
56
50
  //Fetches and converts screenshot URLs to base64 encoded images
57
- async function fetchAutomationScreenshots(sessionId, sessionType = constants_js_1.SessionType.Automate) {
51
+ export async function fetchAutomationScreenshots(sessionId, sessionType = SessionType.Automate) {
58
52
  const urls = await extractScreenshotUrls(sessionId, sessionType);
59
53
  if (urls.length === 0) {
60
54
  return [];
@@ -1,19 +1,12 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.fetchAutomationScreenshotsTool = fetchAutomationScreenshotsTool;
7
- exports.default = addAutomationTools;
8
- const zod_1 = require("zod");
9
- const fetch_screenshots_js_1 = require("./automate-utils/fetch-screenshots.js");
10
- const constants_js_1 = require("../lib/constants.js");
11
- const instrumentation_js_1 = require("../lib/instrumentation.js");
12
- const logger_js_1 = __importDefault(require("../logger.js"));
1
+ import { z } from "zod";
2
+ import { fetchAutomationScreenshots } from "./automate-utils/fetch-screenshots.js";
3
+ import { SessionType } from "../lib/constants.js";
4
+ import { trackMCP } from "../lib/instrumentation.js";
5
+ import logger from "../logger.js";
13
6
  // Tool function that fetches and processes screenshots from BrowserStack Automate session
14
- async function fetchAutomationScreenshotsTool(args) {
7
+ export async function fetchAutomationScreenshotsTool(args) {
15
8
  try {
16
- const screenshots = await (0, fetch_screenshots_js_1.fetchAutomationScreenshots)(args.sessionId, args.sessionType);
9
+ const screenshots = await fetchAutomationScreenshots(args.sessionId, args.sessionType);
17
10
  if (screenshots.length === 0) {
18
11
  return {
19
12
  content: [
@@ -43,26 +36,26 @@ async function fetchAutomationScreenshotsTool(args) {
43
36
  };
44
37
  }
45
38
  catch (error) {
46
- logger_js_1.default.error("Error during fetching screenshots", error);
39
+ logger.error("Error during fetching screenshots", error);
47
40
  throw error;
48
41
  }
49
42
  }
50
43
  //Registers the fetchAutomationScreenshots tool with the MCP server
51
- function addAutomationTools(server) {
44
+ export default function addAutomationTools(server) {
52
45
  server.tool("fetchAutomationScreenshots", "Fetch and process screenshots from a BrowserStack Automate session", {
53
- sessionId: zod_1.z
46
+ sessionId: z
54
47
  .string()
55
48
  .describe("The BrowserStack session ID to fetch screenshots from"),
56
- sessionType: zod_1.z
57
- .enum([constants_js_1.SessionType.Automate, constants_js_1.SessionType.AppAutomate])
49
+ sessionType: z
50
+ .enum([SessionType.Automate, SessionType.AppAutomate])
58
51
  .describe("Type of BrowserStack session"),
59
52
  }, async (args) => {
60
53
  try {
61
- (0, instrumentation_js_1.trackMCP)("fetchAutomationScreenshots", server.server.getClientVersion());
54
+ trackMCP("fetchAutomationScreenshots", server.server.getClientVersion());
62
55
  return await fetchAutomationScreenshotsTool(args);
63
56
  }
64
57
  catch (error) {
65
- (0, instrumentation_js_1.trackMCP)("fetchAutomationScreenshots", server.server.getClientVersion(), error);
58
+ trackMCP("fetchAutomationScreenshots", server.server.getClientVersion(), error);
66
59
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
67
60
  return {
68
61
  content: [