@browserstack/mcp-server 2.2.3-beta.1 → 2.2.3-beta.2
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.
- package/dist/config.d.ts +4 -1
- package/dist/config.js +15 -2
- package/dist/lib/api.js +3 -1
- package/dist/lib/regional-host.d.ts +40 -0
- package/dist/lib/regional-host.js +51 -0
- package/dist/lib/tm-base-url.js +5 -0
- package/dist/tools/accessiblity-utils/accessibility-rag.js +2 -1
- package/dist/tools/accessiblity-utils/auth-config.js +4 -3
- package/dist/tools/accessiblity-utils/report-fetcher.js +3 -2
- package/dist/tools/accessiblity-utils/scanner.js +3 -2
- package/dist/tools/appautomate-utils/native-execution/appautomate.js +8 -7
- package/dist/tools/appautomate.js +2 -1
- package/dist/tools/automate-utils/fetch-screenshots.js +4 -1
- package/dist/tools/automate-utils/list-session-ids.d.ts +1 -1
- package/dist/tools/automate-utils/list-session-ids.js +7 -4
- package/dist/tools/automate-utils/resolve-hashed-build-id.d.ts +1 -1
- package/dist/tools/automate-utils/resolve-hashed-build-id.js +6 -6
- package/dist/tools/build-insights.js +4 -2
- package/dist/tools/failurelogs-utils/app-automate.js +4 -3
- package/dist/tools/failurelogs-utils/automate.js +4 -3
- package/dist/tools/failurelogs-utils/video.js +3 -1
- package/dist/tools/rca-agent-utils/get-build-id.js +3 -1
- package/dist/tools/rca-agent-utils/get-failed-test-id.d.ts +1 -1
- package/dist/tools/rca-agent-utils/get-failed-test-id.js +2 -2
- package/dist/tools/rca-agent-utils/list-build-id.js +3 -1
- package/dist/tools/rca-agent-utils/rca-data.d.ts +1 -1
- package/dist/tools/rca-agent-utils/rca-data.js +2 -2
- package/dist/tools/rca-agent.js +3 -2
- package/dist/tools/selfheal-utils/fetch-test-code.js +2 -2
- package/dist/tools/selfheal-utils/selfheal.js +4 -3
- package/package.json +1 -1
package/dist/config.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
declare const GRR_REGIONS: readonly ["us", "eu", "in"];
|
|
2
|
+
export type GrrRegion = (typeof GRR_REGIONS)[number];
|
|
1
3
|
/**
|
|
2
4
|
* USE_OWN_LOCAL_BINARY_PROCESS:
|
|
3
5
|
* If true, the system will not start a new local binary process, but will use the user's own process.
|
|
@@ -26,6 +28,7 @@ export declare class Config {
|
|
|
26
28
|
* "deferred" | "elicit". Anything else is ignored.
|
|
27
29
|
*/
|
|
28
30
|
readonly ASK_BROWSERSTACK_RELAY_TRANSPORT: string | undefined;
|
|
31
|
+
readonly BROWSERSTACK_GRR_REGION: GrrRegion;
|
|
29
32
|
constructor(DEV_MODE: boolean, browserstackLocalOptions: Record<string, any>, USE_OWN_LOCAL_BINARY_PROCESS: boolean, REMOTE_MCP: boolean, UPLOAD_BASE_DIR: string | undefined, O11Y_TFA_RCA_BASE_URL: string, BROWSERSTACK_AUTOMATION_BASE_URL: string, BROWSERSTACK_O11Y_UI_BASE_URL: string, ASK_BROWSERSTACK_ATLAS_URL: string | undefined, ASK_BROWSERSTACK_AUTH_TOKEN_URL: string | undefined,
|
|
30
33
|
/**
|
|
31
34
|
* Which relay transport to use, overriding what the deployment implies.
|
|
@@ -39,7 +42,7 @@ export declare class Config {
|
|
|
39
42
|
*
|
|
40
43
|
* "deferred" | "elicit". Anything else is ignored.
|
|
41
44
|
*/
|
|
42
|
-
ASK_BROWSERSTACK_RELAY_TRANSPORT: string | undefined);
|
|
45
|
+
ASK_BROWSERSTACK_RELAY_TRANSPORT: string | undefined, BROWSERSTACK_GRR_REGION?: GrrRegion);
|
|
43
46
|
}
|
|
44
47
|
declare const config: Config;
|
|
45
48
|
export default config;
|
package/dist/config.js
CHANGED
|
@@ -34,6 +34,17 @@ const DEFAULT_O11Y_TFA_RCA_BASE_URL = "https://api-automation.browserstack.com";
|
|
|
34
34
|
const DEFAULT_BROWSERSTACK_AUTOMATION_BASE_URL = "https://api-automation.browserstack.com";
|
|
35
35
|
// Overridable via BROWSERSTACK_O11Y_UI_BASE_URL to point at a staging UI.
|
|
36
36
|
const DEFAULT_BROWSERSTACK_O11Y_UI_BASE_URL = "https://automation.browserstack.com";
|
|
37
|
+
// BROWSERSTACK_GRR_REGION: "us" (default), "eu" or "in". Invalid values fail at startup.
|
|
38
|
+
const GRR_REGIONS = ["us", "eu", "in"];
|
|
39
|
+
function parseGrrRegion(raw) {
|
|
40
|
+
const value = (raw || "").trim().toLowerCase();
|
|
41
|
+
if (!value)
|
|
42
|
+
return "us";
|
|
43
|
+
if (GRR_REGIONS.includes(value)) {
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
throw new Error(`Invalid BROWSERSTACK_GRR_REGION "${raw}". Use one of: ${GRR_REGIONS.join(", ")}.`);
|
|
47
|
+
}
|
|
37
48
|
/**
|
|
38
49
|
* USE_OWN_LOCAL_BINARY_PROCESS:
|
|
39
50
|
* If true, the system will not start a new local binary process, but will use the user's own process.
|
|
@@ -50,6 +61,7 @@ export class Config {
|
|
|
50
61
|
ASK_BROWSERSTACK_ATLAS_URL;
|
|
51
62
|
ASK_BROWSERSTACK_AUTH_TOKEN_URL;
|
|
52
63
|
ASK_BROWSERSTACK_RELAY_TRANSPORT;
|
|
64
|
+
BROWSERSTACK_GRR_REGION;
|
|
53
65
|
constructor(DEV_MODE, browserstackLocalOptions, USE_OWN_LOCAL_BINARY_PROCESS, REMOTE_MCP, UPLOAD_BASE_DIR, O11Y_TFA_RCA_BASE_URL, BROWSERSTACK_AUTOMATION_BASE_URL, BROWSERSTACK_O11Y_UI_BASE_URL,
|
|
54
66
|
// askBrowserStackAI's process-startup settings. Declared here rather than read from
|
|
55
67
|
// process.env inside src/tools/, per rules/tool-design.md — and so the remote wrapper,
|
|
@@ -71,7 +83,7 @@ export class Config {
|
|
|
71
83
|
*
|
|
72
84
|
* "deferred" | "elicit". Anything else is ignored.
|
|
73
85
|
*/
|
|
74
|
-
ASK_BROWSERSTACK_RELAY_TRANSPORT) {
|
|
86
|
+
ASK_BROWSERSTACK_RELAY_TRANSPORT, BROWSERSTACK_GRR_REGION = "us") {
|
|
75
87
|
this.DEV_MODE = DEV_MODE;
|
|
76
88
|
this.browserstackLocalOptions = browserstackLocalOptions;
|
|
77
89
|
this.USE_OWN_LOCAL_BINARY_PROCESS = USE_OWN_LOCAL_BINARY_PROCESS;
|
|
@@ -83,6 +95,7 @@ export class Config {
|
|
|
83
95
|
this.ASK_BROWSERSTACK_ATLAS_URL = ASK_BROWSERSTACK_ATLAS_URL;
|
|
84
96
|
this.ASK_BROWSERSTACK_AUTH_TOKEN_URL = ASK_BROWSERSTACK_AUTH_TOKEN_URL;
|
|
85
97
|
this.ASK_BROWSERSTACK_RELAY_TRANSPORT = ASK_BROWSERSTACK_RELAY_TRANSPORT;
|
|
98
|
+
this.BROWSERSTACK_GRR_REGION = BROWSERSTACK_GRR_REGION;
|
|
86
99
|
}
|
|
87
100
|
}
|
|
88
101
|
const config = new Config(process.env.DEV_MODE === "true", browserstackLocalOptions, process.env.USE_OWN_LOCAL_BINARY_PROCESS === "true", process.env.REMOTE_MCP === "true", process.env.MCP_UPLOAD_BASE_DIR && process.env.MCP_UPLOAD_BASE_DIR.length > 0
|
|
@@ -103,5 +116,5 @@ const config = new Config(process.env.DEV_MODE === "true", browserstackLocalOpti
|
|
|
103
116
|
process.env.ASK_BROWSERSTACK_AUTH_TOKEN_URL.trim().length > 0
|
|
104
117
|
? process.env.ASK_BROWSERSTACK_AUTH_TOKEN_URL
|
|
105
118
|
: undefined, (process.env.ASK_BROWSERSTACK_RELAY_TRANSPORT || "").trim().toLowerCase() ||
|
|
106
|
-
undefined);
|
|
119
|
+
undefined, parseGrrRegion(process.env.BROWSERSTACK_GRR_REGION));
|
|
107
120
|
export default config;
|
package/dist/lib/api.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { getBrowserStackAuth } from "./get-auth.js";
|
|
2
2
|
import { apiClient } from "./apiClient.js";
|
|
3
|
+
import { getRegionalHost } from "./regional-host.js";
|
|
3
4
|
export async function getLatestO11YBuildInfo(buildName, projectName, config) {
|
|
4
|
-
const
|
|
5
|
+
const host = getRegionalHost("observabilityApi");
|
|
6
|
+
const buildsUrl = `${host}/ext/v1/builds/latest?build_name=${encodeURIComponent(buildName)}&project_name=${encodeURIComponent(projectName)}`;
|
|
5
7
|
const authString = getBrowserStackAuth(config);
|
|
6
8
|
const auth = Buffer.from(authString).toString("base64");
|
|
7
9
|
const buildsResponse = await apiClient.get({
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { GrrRegion } from "../config.js";
|
|
2
|
+
type Hosts = Record<GrrRegion, string>;
|
|
3
|
+
declare const HOSTS: {
|
|
4
|
+
readonly automateApi: Hosts;
|
|
5
|
+
readonly appAutomateApi: Hosts;
|
|
6
|
+
readonly appAutomateUpload: {
|
|
7
|
+
readonly us: "https://api-cloud.browserstack.com";
|
|
8
|
+
readonly eu: string;
|
|
9
|
+
readonly in: string;
|
|
10
|
+
};
|
|
11
|
+
readonly appAutomateHub: {
|
|
12
|
+
readonly us: "hub.browserstack.com";
|
|
13
|
+
readonly eu: "hub-eu-only.browserstack.com";
|
|
14
|
+
readonly in: "hub-aps-only.browserstack.com";
|
|
15
|
+
};
|
|
16
|
+
readonly accessibilityApi: {
|
|
17
|
+
readonly us: "https://api-accessibility.browserstack.com";
|
|
18
|
+
readonly eu: "https://api-accessibility-eu.browserstack.com";
|
|
19
|
+
readonly in: "https://api-accessibility-in.browserstack.com";
|
|
20
|
+
};
|
|
21
|
+
readonly accessibilityDashboard: {
|
|
22
|
+
readonly us: "https://accessibility.browserstack.com";
|
|
23
|
+
readonly eu: "https://accessibility-eu.browserstack.com";
|
|
24
|
+
readonly in: "https://accessibility-in.browserstack.com";
|
|
25
|
+
};
|
|
26
|
+
readonly observabilityApi: {
|
|
27
|
+
readonly us: "https://api-observability.browserstack.com";
|
|
28
|
+
readonly eu: "https://api-observability-eu.browserstack.com";
|
|
29
|
+
readonly in: "https://api-observability-in.browserstack.com";
|
|
30
|
+
};
|
|
31
|
+
readonly automationApi: {
|
|
32
|
+
readonly us: "https://api-automation.browserstack.com";
|
|
33
|
+
readonly eu: "https://api-automation-eu.browserstack.com";
|
|
34
|
+
readonly in: "https://api-automation-in.browserstack.com";
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export type RegionalHostKind = keyof typeof HOSTS;
|
|
38
|
+
export declare function hostFor(kind: RegionalHostKind, region: GrrRegion): string;
|
|
39
|
+
export declare function getRegionalHost(kind: RegionalHostKind): string;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import appConfig from "../config.js";
|
|
2
|
+
const AUTOMATE_API = {
|
|
3
|
+
us: "https://api.browserstack.com",
|
|
4
|
+
eu: "https://api-eu-only.browserstack.com",
|
|
5
|
+
in: "https://api-aps-only.browserstack.com",
|
|
6
|
+
};
|
|
7
|
+
const HOSTS = {
|
|
8
|
+
automateApi: AUTOMATE_API,
|
|
9
|
+
appAutomateApi: AUTOMATE_API,
|
|
10
|
+
appAutomateUpload: {
|
|
11
|
+
us: "https://api-cloud.browserstack.com",
|
|
12
|
+
eu: AUTOMATE_API.eu,
|
|
13
|
+
in: AUTOMATE_API.in,
|
|
14
|
+
},
|
|
15
|
+
appAutomateHub: {
|
|
16
|
+
us: "hub.browserstack.com",
|
|
17
|
+
eu: "hub-eu-only.browserstack.com",
|
|
18
|
+
in: "hub-aps-only.browserstack.com",
|
|
19
|
+
},
|
|
20
|
+
accessibilityApi: {
|
|
21
|
+
us: "https://api-accessibility.browserstack.com",
|
|
22
|
+
eu: "https://api-accessibility-eu.browserstack.com",
|
|
23
|
+
in: "https://api-accessibility-in.browserstack.com",
|
|
24
|
+
},
|
|
25
|
+
accessibilityDashboard: {
|
|
26
|
+
us: "https://accessibility.browserstack.com",
|
|
27
|
+
eu: "https://accessibility-eu.browserstack.com",
|
|
28
|
+
in: "https://accessibility-in.browserstack.com",
|
|
29
|
+
},
|
|
30
|
+
observabilityApi: {
|
|
31
|
+
us: "https://api-observability.browserstack.com",
|
|
32
|
+
eu: "https://api-observability-eu.browserstack.com",
|
|
33
|
+
in: "https://api-observability-in.browserstack.com",
|
|
34
|
+
},
|
|
35
|
+
automationApi: {
|
|
36
|
+
us: "https://api-automation.browserstack.com",
|
|
37
|
+
eu: "https://api-automation-eu.browserstack.com",
|
|
38
|
+
in: "https://api-automation-in.browserstack.com",
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
export function hostFor(kind, region) {
|
|
42
|
+
return HOSTS[kind][region];
|
|
43
|
+
}
|
|
44
|
+
export function getRegionalHost(kind) {
|
|
45
|
+
if (kind === "automationApi" &&
|
|
46
|
+
appConfig.BROWSERSTACK_AUTOMATION_BASE_URL &&
|
|
47
|
+
appConfig.BROWSERSTACK_AUTOMATION_BASE_URL !== HOSTS.automationApi.us) {
|
|
48
|
+
return appConfig.BROWSERSTACK_AUTOMATION_BASE_URL;
|
|
49
|
+
}
|
|
50
|
+
return hostFor(kind, appConfig.BROWSERSTACK_GRR_REGION ?? "us");
|
|
51
|
+
}
|
package/dist/lib/tm-base-url.js
CHANGED
|
@@ -9,6 +9,11 @@ const TM_BASE_URLS = [
|
|
|
9
9
|
];
|
|
10
10
|
let cachedBaseUrl = null;
|
|
11
11
|
export async function getTMBaseURL(config) {
|
|
12
|
+
// Unset or "us" falls through to the probe below.
|
|
13
|
+
const grrRegion = appConfig.BROWSERSTACK_GRR_REGION;
|
|
14
|
+
if (grrRegion === "eu" || grrRegion === "in") {
|
|
15
|
+
return `https://test-management-${grrRegion}.browserstack.com`;
|
|
16
|
+
}
|
|
12
17
|
// Skip the module-level cache in remote (multi-tenant) mode: it is process-shared,
|
|
13
18
|
// so the first user's region would be served to every subsequent user — breaking
|
|
14
19
|
// requests for users on a different region's BrowserStack account.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { apiClient } from "../../lib/apiClient.js";
|
|
2
2
|
import { wrapUntrusted } from "../../lib/untrusted-content.js";
|
|
3
3
|
import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
4
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
4
5
|
export async function queryAccessibilityRAG(userQuery, config) {
|
|
5
|
-
const url = "
|
|
6
|
+
const url = `${getRegionalHost("accessibilityDashboard")}/api/tcg-proxy/search`;
|
|
6
7
|
const authString = getBrowserStackAuth(config);
|
|
7
8
|
const auth = Buffer.from(authString).toString("base64");
|
|
8
9
|
const response = await apiClient.post({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { apiClient } from "../../lib/apiClient.js";
|
|
2
2
|
import logger from "../../logger.js";
|
|
3
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
3
4
|
/**
|
|
4
5
|
* Allowlist of non-sensitive fields safe to return to the LLM/tool caller.
|
|
5
6
|
* Everything else (site username/password, selectors, undeclared keys) is
|
|
@@ -52,7 +53,7 @@ export class AccessibilityAuthConfig {
|
|
|
52
53
|
};
|
|
53
54
|
try {
|
|
54
55
|
const response = await apiClient.post({
|
|
55
|
-
url: "
|
|
56
|
+
url: `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/auth_configs`,
|
|
56
57
|
headers: {
|
|
57
58
|
Authorization: "Basic " +
|
|
58
59
|
Buffer.from(`${this.auth.username}:${this.auth.password}`).toString("base64"),
|
|
@@ -91,7 +92,7 @@ export class AccessibilityAuthConfig {
|
|
|
91
92
|
};
|
|
92
93
|
try {
|
|
93
94
|
const response = await apiClient.post({
|
|
94
|
-
url: "
|
|
95
|
+
url: `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/auth_configs`,
|
|
95
96
|
headers: {
|
|
96
97
|
Authorization: "Basic " +
|
|
97
98
|
Buffer.from(`${this.auth.username}:${this.auth.password}`).toString("base64"),
|
|
@@ -120,7 +121,7 @@ export class AccessibilityAuthConfig {
|
|
|
120
121
|
try {
|
|
121
122
|
// No GET-by-id route exists; list and select.
|
|
122
123
|
const response = await apiClient.get({
|
|
123
|
-
url: "
|
|
124
|
+
url: `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/auth_configs`,
|
|
124
125
|
headers: {
|
|
125
126
|
Authorization: "Basic " +
|
|
126
127
|
Buffer.from(`${this.auth.username}:${this.auth.password}`).toString("base64"),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { apiClient } from "../../lib/apiClient.js";
|
|
2
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
2
3
|
export class AccessibilityReportFetcher {
|
|
3
4
|
auth;
|
|
4
5
|
setAuth(auth) {
|
|
@@ -6,7 +7,7 @@ export class AccessibilityReportFetcher {
|
|
|
6
7
|
}
|
|
7
8
|
async getReportLink(scanId, scanRunId) {
|
|
8
9
|
// Initiate CSV link generation
|
|
9
|
-
const initUrl =
|
|
10
|
+
const initUrl = `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/scans/${scanId}/scan_runs/issues?scan_run_id=${scanRunId}`;
|
|
10
11
|
let basicAuthHeader = undefined;
|
|
11
12
|
if (this.auth) {
|
|
12
13
|
const { username, password } = this.auth;
|
|
@@ -23,7 +24,7 @@ export class AccessibilityReportFetcher {
|
|
|
23
24
|
}
|
|
24
25
|
const taskId = initData.data.task_id;
|
|
25
26
|
// Poll for the generated CSV link (task is async, may take a few seconds)
|
|
26
|
-
const reportUrl =
|
|
27
|
+
const reportUrl = `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/scans/${scanId}/scan_runs/issues?task_id=${encodeURIComponent(taskId)}`;
|
|
27
28
|
const maxAttempts = 3;
|
|
28
29
|
const pollIntervalMs = 2000;
|
|
29
30
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import logger from "../../logger.js";
|
|
4
4
|
import { isLocalURL, ensureLocalBinarySetup, killExistingBrowserStackLocalProcesses, } from "../../lib/local.js";
|
|
5
5
|
import config from "../../config.js";
|
|
6
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
6
7
|
export class AccessibilityScanner {
|
|
7
8
|
auth;
|
|
8
9
|
setAuth(auth) {
|
|
@@ -64,7 +65,7 @@ export class AccessibilityScanner {
|
|
|
64
65
|
}
|
|
65
66
|
try {
|
|
66
67
|
const response = await apiClient.post({
|
|
67
|
-
url: "
|
|
68
|
+
url: `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/scans`,
|
|
68
69
|
headers: {
|
|
69
70
|
Authorization: "Basic " +
|
|
70
71
|
Buffer.from(`${this.auth.username}:${this.auth.password}`).toString("base64"),
|
|
@@ -92,7 +93,7 @@ export class AccessibilityScanner {
|
|
|
92
93
|
async pollStatus(scanId, scanRunId) {
|
|
93
94
|
try {
|
|
94
95
|
const response = await apiClient.get({
|
|
95
|
-
url:
|
|
96
|
+
url: `${getRegionalHost("accessibilityApi")}/api/website-scanner/v1/scans/${scanId}/scan_runs/${scanRunId}/status`,
|
|
96
97
|
headers: {
|
|
97
98
|
Authorization: "Basic " +
|
|
98
99
|
Buffer.from(`${this.auth?.username}:${this.auth?.password}`).toString("base64"),
|
|
@@ -5,6 +5,7 @@ import { customFuzzySearch } from "../../../lib/fuzzy.js";
|
|
|
5
5
|
import { getBrowserStackAuth } from "../../../lib/get-auth.js";
|
|
6
6
|
import { validateUploadPath, APP_BINARY_EXTENSIONS, MAX_APP_UPLOAD_BYTES, } from "../../../lib/upload-validator.js";
|
|
7
7
|
import appConfig from "../../../config.js";
|
|
8
|
+
import { getRegionalHost } from "../../../lib/regional-host.js";
|
|
8
9
|
/**
|
|
9
10
|
* Finds devices that exactly match the provided display name.
|
|
10
11
|
* Uses fuzzy search first, and then filters for exact case-insensitive match.
|
|
@@ -84,7 +85,7 @@ export async function uploadApp(appPath, username, password) {
|
|
|
84
85
|
const formData = new FormData();
|
|
85
86
|
formData.append("file", fs.createReadStream(safePath));
|
|
86
87
|
const response = await apiClient.post({
|
|
87
|
-
url: "
|
|
88
|
+
url: `${getRegionalHost("appAutomateUpload")}/app-automate/upload`,
|
|
88
89
|
headers: {
|
|
89
90
|
...formData.getHeaders(),
|
|
90
91
|
Authorization: "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
|
|
@@ -124,25 +125,25 @@ async function uploadFileToBrowserStack(filePath, endpoint, responseKey, config,
|
|
|
124
125
|
}
|
|
125
126
|
//Uploads an Android app (.apk or .aab) to BrowserStack Espresso endpoint and returns the app_url
|
|
126
127
|
export async function uploadEspressoApp(appPath, config) {
|
|
127
|
-
return uploadFileToBrowserStack(appPath, "
|
|
128
|
+
return uploadFileToBrowserStack(appPath, `${getRegionalHost("appAutomateUpload")}/app-automate/espresso/v2/app`, "app_url", config, { allowedExtensions: [".apk", ".aab"] });
|
|
128
129
|
}
|
|
129
130
|
//Uploads an Espresso test suite (.apk) to BrowserStack and returns the test_suite_url
|
|
130
131
|
export async function uploadEspressoTestSuite(testSuitePath, config) {
|
|
131
|
-
return uploadFileToBrowserStack(testSuitePath, "
|
|
132
|
+
return uploadFileToBrowserStack(testSuitePath, `${getRegionalHost("appAutomateUpload")}/app-automate/espresso/v2/test-suite`, "test_suite_url", config, { allowedExtensions: [".apk"] });
|
|
132
133
|
}
|
|
133
134
|
//Uploads an iOS app (.ipa) to BrowserStack XCUITest endpoint and returns the app_url
|
|
134
135
|
export async function uploadXcuiApp(appPath, config) {
|
|
135
|
-
return uploadFileToBrowserStack(appPath, "
|
|
136
|
+
return uploadFileToBrowserStack(appPath, `${getRegionalHost("appAutomateUpload")}/app-automate/xcuitest/v2/app`, "app_url", config, { allowedExtensions: [".ipa"] });
|
|
136
137
|
}
|
|
137
138
|
//Uploads an XCUITest test suite (.zip) to BrowserStack and returns the test_suite_url
|
|
138
139
|
export async function uploadXcuiTestSuite(testSuitePath, config) {
|
|
139
|
-
return uploadFileToBrowserStack(testSuitePath, "
|
|
140
|
+
return uploadFileToBrowserStack(testSuitePath, `${getRegionalHost("appAutomateUpload")}/app-automate/xcuitest/v2/test-suite`, "test_suite_url", config, { allowedExtensions: [".zip"] });
|
|
140
141
|
}
|
|
141
142
|
// Triggers an Espresso test run on BrowserStack and returns the build_id
|
|
142
143
|
export async function triggerEspressoBuild(app_url, test_suite_url, devices, project, config) {
|
|
143
144
|
const authHeader = "Basic " + Buffer.from(getBrowserStackAuth(config)).toString("base64");
|
|
144
145
|
const response = await apiClient.post({
|
|
145
|
-
url: "
|
|
146
|
+
url: `${getRegionalHost("appAutomateUpload")}/app-automate/espresso/v2/build`,
|
|
146
147
|
headers: {
|
|
147
148
|
Authorization: authHeader,
|
|
148
149
|
"Content-Type": "application/json",
|
|
@@ -166,7 +167,7 @@ export async function triggerXcuiBuild(app_url, test_suite_url, devices, project
|
|
|
166
167
|
password: config["browserstack-access-key"],
|
|
167
168
|
};
|
|
168
169
|
const response = await apiClient.post({
|
|
169
|
-
url: "
|
|
170
|
+
url: `${getRegionalHost("appAutomateUpload")}/app-automate/xcuitest/v2/build`,
|
|
170
171
|
headers: {
|
|
171
172
|
Authorization: "Basic " +
|
|
172
173
|
Buffer.from(`${auth.username}:${auth.password}`).toString("base64"),
|
|
@@ -12,6 +12,7 @@ import { Platform, } from "./appautomate-utils/native-execution/types.js";
|
|
|
12
12
|
import { getDevicesAndBrowsers, BrowserStackProducts, } from "../lib/device-cache.js";
|
|
13
13
|
import { findMatchingDevice, getDeviceVersions, resolveVersion, validateArgs, uploadApp, uploadEspressoApp, uploadEspressoTestSuite, triggerEspressoBuild, uploadXcuiApp, uploadXcuiTestSuite, triggerXcuiBuild, } from "./appautomate-utils/native-execution/appautomate.js";
|
|
14
14
|
import { RUN_APP_AUTOMATE_DESCRIPTION, RUN_APP_AUTOMATE_SCHEMA, } from "./appautomate-utils/native-execution/constants.js";
|
|
15
|
+
import { getRegionalHost } from "../lib/regional-host.js";
|
|
15
16
|
/**
|
|
16
17
|
* Launches an app on a selected BrowserStack device and takes a screenshot.
|
|
17
18
|
*/
|
|
@@ -63,7 +64,7 @@ async function takeAppScreenshot(args) {
|
|
|
63
64
|
try {
|
|
64
65
|
driver = await remote({
|
|
65
66
|
protocol: "https",
|
|
66
|
-
hostname: "
|
|
67
|
+
hostname: getRegionalHost("appAutomateHub"),
|
|
67
68
|
port: 443,
|
|
68
69
|
path: "/wd/hub",
|
|
69
70
|
capabilities,
|
|
@@ -3,6 +3,7 @@ import { SessionType } from "../../lib/constants.js";
|
|
|
3
3
|
import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
4
4
|
import { apiClient } from "../../lib/apiClient.js";
|
|
5
5
|
import globalConfig from "../../config.js";
|
|
6
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
6
7
|
import { MAX_LOG_BYTES } from "../failurelogs-utils/utils.js";
|
|
7
8
|
export const MAX_SCREENSHOT_BYTES = globalConfig.REMOTE_MCP
|
|
8
9
|
? 10 * 1024 * 1024
|
|
@@ -10,7 +11,9 @@ export const MAX_SCREENSHOT_BYTES = globalConfig.REMOTE_MCP
|
|
|
10
11
|
async function extractScreenshotUrls(sessionId, sessionType, config) {
|
|
11
12
|
const authString = getBrowserStackAuth(config);
|
|
12
13
|
const auth = Buffer.from(authString).toString("base64");
|
|
13
|
-
const
|
|
14
|
+
const isAutomate = sessionType === SessionType.Automate;
|
|
15
|
+
const host = getRegionalHost(isAutomate ? "automateApi" : "appAutomateApi");
|
|
16
|
+
const baseUrl = `${host}/${isAutomate ? "automate" : "app-automate"}`;
|
|
14
17
|
const url = `${baseUrl}/sessions/${sessionId}/logs`;
|
|
15
18
|
const response = await apiClient.get({
|
|
16
19
|
url,
|
|
@@ -23,6 +23,6 @@ export interface SessionIdRecord {
|
|
|
23
23
|
browserUrl?: string;
|
|
24
24
|
videoUrl?: string;
|
|
25
25
|
}
|
|
26
|
-
export declare function sessionsListUrl(sessionType: SessionType, buildId: string): string;
|
|
26
|
+
export declare function sessionsListUrl(sessionType: SessionType, buildId: string, host?: string): string;
|
|
27
27
|
export declare function mapSessionRecords(payload: unknown, statusFilter?: string): SessionIdRecord[];
|
|
28
28
|
export declare function listSessionIds(args: ListSessionIdsArgs, config: BrowserStackConfig): Promise<SessionIdRecord[]>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SessionType } from "../../lib/constants.js";
|
|
2
2
|
import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
3
3
|
import { apiClient } from "../../lib/apiClient.js";
|
|
4
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
4
5
|
export const DEFAULT_SESSION_LIST_LIMIT = 10;
|
|
5
6
|
/** The REST session list returned 404: no Automate/App Automate build has this hashed id. */
|
|
6
7
|
export class UnknownBuildError extends Error {
|
|
@@ -9,13 +10,13 @@ export class UnknownBuildError extends Error {
|
|
|
9
10
|
this.name = "UnknownBuildError";
|
|
10
11
|
}
|
|
11
12
|
}
|
|
12
|
-
export function sessionsListUrl(sessionType, buildId) {
|
|
13
|
+
export function sessionsListUrl(sessionType, buildId, host) {
|
|
13
14
|
const encodedBuildId = encodeURIComponent(buildId);
|
|
14
15
|
switch (sessionType) {
|
|
15
16
|
case SessionType.Automate:
|
|
16
|
-
return
|
|
17
|
+
return `${host ?? "https://api.browserstack.com"}/automate/builds/${encodedBuildId}/sessions.json`;
|
|
17
18
|
case SessionType.AppAutomate:
|
|
18
|
-
return
|
|
19
|
+
return `${host ?? "https://api-cloud.browserstack.com"}/app-automate/builds/${encodedBuildId}/sessions.json`;
|
|
19
20
|
default: {
|
|
20
21
|
const _exhaustive = sessionType;
|
|
21
22
|
throw new Error(`Unsupported session type: ${_exhaustive}`);
|
|
@@ -66,7 +67,9 @@ export async function listSessionIds(args, config) {
|
|
|
66
67
|
params.offset = args.offset;
|
|
67
68
|
}
|
|
68
69
|
const response = await apiClient.get({
|
|
69
|
-
url: sessionsListUrl(args.sessionType, buildId
|
|
70
|
+
url: sessionsListUrl(args.sessionType, buildId, getRegionalHost(args.sessionType === SessionType.Automate
|
|
71
|
+
? "automateApi"
|
|
72
|
+
: "appAutomateUpload")),
|
|
70
73
|
headers: {
|
|
71
74
|
"Content-Type": "application/json",
|
|
72
75
|
Authorization: `Basic ${auth}`,
|
|
@@ -2,7 +2,7 @@ import { SessionType } from "../../lib/constants.js";
|
|
|
2
2
|
import { BrowserStackConfig } from "../../lib/types.js";
|
|
3
3
|
export declare function isObservabilityBuildUuid(id: string): boolean;
|
|
4
4
|
export declare function isHashedBuildId(id: string): boolean;
|
|
5
|
-
export declare function sessionDetailsUrl(sessionType: SessionType, sessionId: string): string;
|
|
5
|
+
export declare function sessionDetailsUrl(sessionType: SessionType, sessionId: string, host?: string): string;
|
|
6
6
|
/**
|
|
7
7
|
* Resolve the hashed build id that a session belongs to via the Automate /
|
|
8
8
|
* App Automate session detail endpoint. Returns undefined when the session
|
|
@@ -2,8 +2,8 @@ import { SessionType } from "../../lib/constants.js";
|
|
|
2
2
|
import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
3
3
|
import { apiClient } from "../../lib/apiClient.js";
|
|
4
4
|
import logger from "../../logger.js";
|
|
5
|
-
import { getAutomationBaseUrl } from "../rca-agent-utils/constants.js";
|
|
6
5
|
import { extractTestIds } from "../rca-agent-utils/get-failed-test-id.js";
|
|
6
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
7
7
|
// Observability (Test Reporting & Analytics) build ids are usually UUIDs but
|
|
8
8
|
// some are 40-char hex, the same shape as Automate / App Automate "hashed ids".
|
|
9
9
|
// The two are never interchangeable, and the observability build API does not
|
|
@@ -19,13 +19,13 @@ export function isObservabilityBuildUuid(id) {
|
|
|
19
19
|
export function isHashedBuildId(id) {
|
|
20
20
|
return HASHED_BUILD_ID_RE.test(id.trim());
|
|
21
21
|
}
|
|
22
|
-
export function sessionDetailsUrl(sessionType, sessionId) {
|
|
22
|
+
export function sessionDetailsUrl(sessionType, sessionId, host = "https://api.browserstack.com") {
|
|
23
23
|
const encoded = encodeURIComponent(sessionId);
|
|
24
24
|
switch (sessionType) {
|
|
25
25
|
case SessionType.Automate:
|
|
26
|
-
return
|
|
26
|
+
return `${host}/automate/sessions/${encoded}.json`;
|
|
27
27
|
case SessionType.AppAutomate:
|
|
28
|
-
return
|
|
28
|
+
return `${host}/app-automate/sessions/${encoded}.json`;
|
|
29
29
|
default: {
|
|
30
30
|
const _exhaustive = sessionType;
|
|
31
31
|
throw new Error(`Unsupported session type: ${_exhaustive}`);
|
|
@@ -41,7 +41,7 @@ export async function resolveBuildIdFromSession(sessionId, sessionType, config)
|
|
|
41
41
|
const authString = getBrowserStackAuth(config);
|
|
42
42
|
const auth = Buffer.from(authString).toString("base64");
|
|
43
43
|
const response = await apiClient.get({
|
|
44
|
-
url: sessionDetailsUrl(sessionType, sessionId),
|
|
44
|
+
url: sessionDetailsUrl(sessionType, sessionId, getRegionalHost(sessionType === SessionType.Automate ? "automateApi" : "appAutomateApi")),
|
|
45
45
|
headers: {
|
|
46
46
|
"Content-Type": "application/json",
|
|
47
47
|
Authorization: `Basic ${auth}`,
|
|
@@ -66,7 +66,7 @@ export async function resolveBuildIdFromSession(sessionId, sessionType, config)
|
|
|
66
66
|
export async function findSessionIdForObservabilityBuild(observabilityBuildId, config) {
|
|
67
67
|
const authString = getBrowserStackAuth(config);
|
|
68
68
|
const auth = Buffer.from(authString).toString("base64");
|
|
69
|
-
const baseUrl = `${
|
|
69
|
+
const baseUrl = `${getRegionalHost("automationApi")}/ext/v1/builds/${encodeURIComponent(observabilityBuildId)}/testRuns`;
|
|
70
70
|
let nextPage;
|
|
71
71
|
for (let page = 0; page < MAX_TEST_RUN_PAGES; page++) {
|
|
72
72
|
const response = await apiClient.get({
|
|
@@ -3,11 +3,13 @@ import logger from "../logger.js";
|
|
|
3
3
|
import { fetchFromBrowserStackAPI, handleMCPError } from "../lib/utils.js";
|
|
4
4
|
import { trackMCP } from "../lib/instrumentation.js";
|
|
5
5
|
import { resolveHashedBuildId } from "./automate-utils/resolve-hashed-build-id.js";
|
|
6
|
+
import { getRegionalHost } from "../lib/regional-host.js";
|
|
6
7
|
// Tool function that fetches build insights from two APIs
|
|
7
8
|
export async function fetchBuildInsightsTool(args, config) {
|
|
8
9
|
try {
|
|
9
|
-
const
|
|
10
|
-
const
|
|
10
|
+
const host = getRegionalHost("automationApi");
|
|
11
|
+
const buildUrl = `${host}/ext/v1/builds/${args.buildId}`;
|
|
12
|
+
const qualityGateUrl = `${host}/ext/v1/quality-gates/${args.buildId}`;
|
|
11
13
|
// Quality gate data is optional — a failure there should not block build insights
|
|
12
14
|
const [buildData, qualityData] = await Promise.all([
|
|
13
15
|
fetchFromBrowserStackAPI(buildUrl, config),
|
|
@@ -2,9 +2,10 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
|
2
2
|
import { filterLinesByKeywords, MAX_LOG_BYTES, validateLogResponse, } from "./utils.js";
|
|
3
3
|
import { apiClient } from "../../lib/apiClient.js";
|
|
4
4
|
import { wrapUntrusted } from "../../lib/untrusted-content.js";
|
|
5
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
5
6
|
// DEVICE LOGS
|
|
6
7
|
export async function retrieveDeviceLogs(sessionId, buildId, config) {
|
|
7
|
-
const url =
|
|
8
|
+
const url = `${getRegionalHost("appAutomateApi")}/app-automate/builds/${buildId}/sessions/${sessionId}/deviceLogs`;
|
|
8
9
|
const authString = getBrowserStackAuth(config);
|
|
9
10
|
const auth = Buffer.from(authString).toString("base64");
|
|
10
11
|
const response = await apiClient.get({
|
|
@@ -29,7 +30,7 @@ export async function retrieveDeviceLogs(sessionId, buildId, config) {
|
|
|
29
30
|
}
|
|
30
31
|
// APPIUM LOGS
|
|
31
32
|
export async function retrieveAppiumLogs(sessionId, buildId, config) {
|
|
32
|
-
const url =
|
|
33
|
+
const url = `${getRegionalHost("appAutomateApi")}/app-automate/builds/${buildId}/sessions/${sessionId}/appiumlogs`;
|
|
33
34
|
const authString = getBrowserStackAuth(config);
|
|
34
35
|
const auth = Buffer.from(authString).toString("base64");
|
|
35
36
|
const response = await apiClient.get({
|
|
@@ -54,7 +55,7 @@ export async function retrieveAppiumLogs(sessionId, buildId, config) {
|
|
|
54
55
|
}
|
|
55
56
|
// CRASH LOGS
|
|
56
57
|
export async function retrieveCrashLogs(sessionId, buildId, config) {
|
|
57
|
-
const url =
|
|
58
|
+
const url = `${getRegionalHost("appAutomateApi")}/app-automate/builds/${buildId}/sessions/${sessionId}/crashlogs`;
|
|
58
59
|
const authString = getBrowserStackAuth(config);
|
|
59
60
|
const auth = Buffer.from(authString).toString("base64");
|
|
60
61
|
const response = await apiClient.get({
|
|
@@ -2,9 +2,10 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
|
2
2
|
import { wrapUntrusted } from "../../lib/untrusted-content.js";
|
|
3
3
|
import { filterLinesByKeywords, MAX_LOG_BYTES, validateLogResponse, } from "./utils.js";
|
|
4
4
|
import { apiClient } from "../../lib/apiClient.js";
|
|
5
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
5
6
|
// NETWORK LOGS
|
|
6
7
|
export async function retrieveNetworkFailures(sessionId, config) {
|
|
7
|
-
const url =
|
|
8
|
+
const url = `${getRegionalHost("automateApi")}/automate/sessions/${sessionId}/networklogs`;
|
|
8
9
|
const authString = getBrowserStackAuth(config);
|
|
9
10
|
const auth = Buffer.from(authString).toString("base64");
|
|
10
11
|
const response = await apiClient.get({
|
|
@@ -43,7 +44,7 @@ export async function retrieveNetworkFailures(sessionId, config) {
|
|
|
43
44
|
}
|
|
44
45
|
// SESSION LOGS
|
|
45
46
|
export async function retrieveSessionFailures(sessionId, config) {
|
|
46
|
-
const url =
|
|
47
|
+
const url = `${getRegionalHost("automateApi")}/automate/sessions/${sessionId}/logs`;
|
|
47
48
|
const authString = getBrowserStackAuth(config);
|
|
48
49
|
const auth = Buffer.from(authString).toString("base64");
|
|
49
50
|
const response = await apiClient.get({
|
|
@@ -68,7 +69,7 @@ export async function retrieveSessionFailures(sessionId, config) {
|
|
|
68
69
|
}
|
|
69
70
|
// CONSOLE LOGS
|
|
70
71
|
export async function retrieveConsoleFailures(sessionId, config) {
|
|
71
|
-
const url =
|
|
72
|
+
const url = `${getRegionalHost("automateApi")}/automate/sessions/${sessionId}/consolelogs`;
|
|
72
73
|
const authString = getBrowserStackAuth(config);
|
|
73
74
|
const auth = Buffer.from(authString).toString("base64");
|
|
74
75
|
const response = await apiClient.get({
|
|
@@ -2,9 +2,11 @@ import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
|
2
2
|
import { apiClient } from "../../lib/apiClient.js";
|
|
3
3
|
import { SessionType } from "../../lib/constants.js";
|
|
4
4
|
import { validateLogResponse } from "./utils.js";
|
|
5
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
5
6
|
export async function retrieveSessionVideo(sessionId, sessionType, config) {
|
|
6
7
|
const product = sessionType === SessionType.AppAutomate ? "app-automate" : "automate";
|
|
7
|
-
const
|
|
8
|
+
const host = getRegionalHost(sessionType === SessionType.AppAutomate ? "appAutomateApi" : "automateApi");
|
|
9
|
+
const url = `${host}/${product}/sessions/${encodeURIComponent(sessionId)}.json`;
|
|
8
10
|
const authString = getBrowserStackAuth(config);
|
|
9
11
|
const auth = Buffer.from(authString).toString("base64");
|
|
10
12
|
const response = await apiClient.get({
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
1
2
|
export async function getBuildId(projectName, buildName, username, accessKey) {
|
|
2
|
-
const
|
|
3
|
+
const host = getRegionalHost("automationApi");
|
|
4
|
+
const url = new URL(`${host}/ext/v1/builds/latest`);
|
|
3
5
|
url.searchParams.append("project_name", projectName);
|
|
4
6
|
url.searchParams.append("build_name", buildName);
|
|
5
7
|
url.searchParams.append("user_name", username);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { TestStatus, FailedTestInfo, TestDetails } from "./types.js";
|
|
2
|
-
export declare function getTestIds(buildId: string, authString: string, status?: TestStatus, includeFailureDetail?: boolean): Promise<FailedTestInfo[]>;
|
|
2
|
+
export declare function getTestIds(buildId: string, authString: string, status?: TestStatus, includeFailureDetail?: boolean, host?: string): Promise<FailedTestInfo[]>;
|
|
3
3
|
export declare function extractTestIds(hierarchy: TestDetails[], status?: TestStatus, includeFailureDetail?: boolean): FailedTestInfo[];
|
|
4
4
|
export declare const extractFailedTestIds: typeof extractTestIds;
|
|
@@ -5,9 +5,9 @@ import { TestStatus, } from "./types.js";
|
|
|
5
5
|
const ERROR_SUMMARY_MAX = 200;
|
|
6
6
|
// Safety bound on pagination; hitting it is logged, never silent.
|
|
7
7
|
const MAX_PAGES = 100;
|
|
8
|
-
export async function getTestIds(buildId, authString, status, includeFailureDetail = false) {
|
|
8
|
+
export async function getTestIds(buildId, authString, status, includeFailureDetail = false, host = getAutomationBaseUrl()) {
|
|
9
9
|
// No `status` → returns ALL tests; a `status` narrows the query.
|
|
10
|
-
const baseUrl = `${
|
|
10
|
+
const baseUrl = `${host}/ext/v1/builds/${buildId}/testRuns`;
|
|
11
11
|
let url = status ? `${baseUrl}?test_statuses=${status}` : baseUrl;
|
|
12
12
|
let allTests = [];
|
|
13
13
|
let requestNumber = 0;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { apiClient } from "../../lib/apiClient.js";
|
|
2
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
2
3
|
/**
|
|
3
4
|
* Returns the latest build for a project + build name across all users (not just the caller's).
|
|
4
5
|
*/
|
|
5
6
|
export async function listBuildId(projectName, buildName, username, accessKey) {
|
|
6
7
|
const authHeader = "Basic " + Buffer.from(`${username}:${accessKey}`).toString("base64");
|
|
8
|
+
const host = getRegionalHost("automationApi");
|
|
7
9
|
const response = await apiClient.get({
|
|
8
|
-
url:
|
|
10
|
+
url: `${host}/ext/v1/builds/latest`,
|
|
9
11
|
headers: {
|
|
10
12
|
Authorization: authHeader,
|
|
11
13
|
"Content-Type": "application/json",
|
|
@@ -5,5 +5,5 @@ interface ScanProgressContext {
|
|
|
5
5
|
progressToken?: string | number;
|
|
6
6
|
};
|
|
7
7
|
}
|
|
8
|
-
export declare function getRCAData(testIds: number[], authString: string, context?: ScanProgressContext): Promise<RCAResponse>;
|
|
8
|
+
export declare function getRCAData(testIds: number[], authString: string, context?: ScanProgressContext, host?: string): Promise<RCAResponse>;
|
|
9
9
|
export {};
|
|
@@ -177,11 +177,11 @@ async function pollRCAResults(testCases, headers, baseUrl, context, pollInterval
|
|
|
177
177
|
}
|
|
178
178
|
return { testCases };
|
|
179
179
|
}
|
|
180
|
-
export async function getRCAData(testIds, authString, context) {
|
|
180
|
+
export async function getRCAData(testIds, authString, context, host = "https://api-observability.browserstack.com") {
|
|
181
181
|
const pollInterval = 5000;
|
|
182
182
|
const timeout = 40000;
|
|
183
183
|
const initialDelay = 20000;
|
|
184
|
-
const baseUrl =
|
|
184
|
+
const baseUrl = `${host}/ext/v1/testRun/{testId}/testRca`;
|
|
185
185
|
const headers = {
|
|
186
186
|
Authorization: `Basic ${Buffer.from(authString).toString("base64")}`,
|
|
187
187
|
"Content-Type": "application/json",
|
package/dist/tools/rca-agent.js
CHANGED
|
@@ -9,6 +9,7 @@ import { handleMCPError } from "../lib/utils.js";
|
|
|
9
9
|
import { trackMCP } from "../index.js";
|
|
10
10
|
import { NEEDS_BUILD_ID, NEEDS_TEST_IDS } from "./tool-handoff.js";
|
|
11
11
|
import { FETCH_RCA_PARAMS, GET_BUILD_ID_PARAMS, LIST_TEST_IDS_PARAMS, } from "./rca-agent-utils/constants.js";
|
|
12
|
+
import { getRegionalHost } from "../lib/regional-host.js";
|
|
12
13
|
// Tool function to fetch build ID
|
|
13
14
|
export async function getBuildIdTool(args, config) {
|
|
14
15
|
try {
|
|
@@ -75,7 +76,7 @@ export async function fetchRCADataTool(args, config) {
|
|
|
75
76
|
const authString = getBrowserStackAuth(config);
|
|
76
77
|
// Limit to first 3 test IDs for performance
|
|
77
78
|
const testIds = args.testId;
|
|
78
|
-
const rcaData = await getRCAData(testIds, authString);
|
|
79
|
+
const rcaData = await getRCAData(testIds, authString, undefined, getRegionalHost("observabilityApi"));
|
|
79
80
|
const formattedData = formatRCAData(rcaData);
|
|
80
81
|
return {
|
|
81
82
|
content: [
|
|
@@ -105,7 +106,7 @@ export async function listTestIdsTool(args, config) {
|
|
|
105
106
|
const { buildId, status, includeFailureDetail } = args;
|
|
106
107
|
const authString = getBrowserStackAuth(config);
|
|
107
108
|
// Get test IDs
|
|
108
|
-
const testIds = await getTestIds(buildId, authString, status, includeFailureDetail);
|
|
109
|
+
const testIds = await getTestIds(buildId, authString, status, includeFailureDetail, getRegionalHost("automationApi"));
|
|
109
110
|
return {
|
|
110
111
|
content: [
|
|
111
112
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
2
2
|
import { apiClient } from "../../lib/apiClient.js";
|
|
3
3
|
import logger from "../../logger.js";
|
|
4
|
-
|
|
4
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
5
5
|
/**
|
|
6
6
|
* Normalizes the testCode response. The Observability API used to return a
|
|
7
7
|
* bare array of TestCodeEntry, but now wraps it as
|
|
@@ -43,7 +43,7 @@ export async function fetchTestCodeBySession(sessionId, config) {
|
|
|
43
43
|
}
|
|
44
44
|
const authString = getBrowserStackAuth(config);
|
|
45
45
|
const auth = Buffer.from(authString).toString("base64");
|
|
46
|
-
const url = `${
|
|
46
|
+
const url = `${getRegionalHost("automationApi")}/ext/v1/sessions/${encodeURIComponent(sessionId)}/testCode`;
|
|
47
47
|
try {
|
|
48
48
|
const response = await apiClient.get({
|
|
49
49
|
url,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { assertOkResponse } from "../../lib/utils.js";
|
|
2
2
|
import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
3
3
|
import { apiClient } from "../../lib/apiClient.js";
|
|
4
|
-
|
|
4
|
+
import { getRegionalHost } from "../../lib/regional-host.js";
|
|
5
5
|
/**
|
|
6
6
|
* Fetches the self-healing report for a build via the build-scoped API.
|
|
7
7
|
*
|
|
@@ -19,7 +19,7 @@ export async function fetchSelfHealingReportByBuild(buildUuid, config) {
|
|
|
19
19
|
const authString = getBrowserStackAuth(config);
|
|
20
20
|
const auth = Buffer.from(authString).toString("base64");
|
|
21
21
|
const presignedResp = await apiClient.get({
|
|
22
|
-
url: `${
|
|
22
|
+
url: `${getRegionalHost("automationApi")}/ext/v1/builds/${encodeURIComponent(buildUuid)}/selfHealingReport`,
|
|
23
23
|
headers: {
|
|
24
24
|
"Content-Type": "application/json",
|
|
25
25
|
Authorization: `Basic ${auth}`,
|
|
@@ -67,7 +67,8 @@ export async function getSelfHealSelectors(sessionId, config, sessionType = "aut
|
|
|
67
67
|
const authString = getBrowserStackAuth(config);
|
|
68
68
|
const auth = Buffer.from(authString).toString("base64");
|
|
69
69
|
const productPath = sessionType === "app-automate" ? "app-automate" : "automate";
|
|
70
|
-
const
|
|
70
|
+
const host = getRegionalHost(sessionType === "app-automate" ? "appAutomateApi" : "automateApi");
|
|
71
|
+
const url = `${host}/${productPath}/sessions/${sessionId}/logs`;
|
|
71
72
|
const response = await apiClient.get({
|
|
72
73
|
url,
|
|
73
74
|
headers: {
|