Repository navigation
fix(util): recognize *.bsstag.com hosts as BrowserStack (v8, SDK-6948) - #183
Conversation
getCloudProvider and isBrowserstackInfra only matched browserstack.com, so a session against an internal staging env (*.bsstag.com) was classified as non-BrowserStack — the service skipped its instrumentation + GRR host rewriting and the WebDriver hub stayed on prod. Recognize bsstag/.bsstag.com in both guards. Ports #56/#65 (main/v9) to the v8 release line. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…(SDK-6948)
update_cli runs during bootstrap — before the binary spawns and before GRR
localizes hosts — so it always targets prod api.browserstack.com. On an internal
staging run (BROWSERSTACK_STAGING_ENV) the staging creds are rejected there with
401, leaving the CLI binary path empty and crashing the launcher with
spawn('') -> ERR_INVALID_ARG_VALUE ('file' cannot be empty).
Wrap the update_cli call: on failure, if BROWSERSTACK_BINARY_URL is set, download
the binary from that URL and use it; otherwise re-throw (behaviour unchanged when
the var is unset). Adds unit tests for both paths. Validated end-to-end against
devapplca: update_cli 401 -> fallback download from a served zip -> binary spawned
-> build-start reached.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
See the SDK PR Review Agent's report from your local run. Process integrityThe review gate could NOT certify this run — the verdict is held at
Change map (generated deterministically from the diff)graph LR
subgraph nwdio_service["wdio-service"]
npackages_browserstack_service_src_cli_cliUtils_ts["cliUtils.ts<br/>~81 lines"]
npackages_browserstack_service_tests_cli_cliUtils_test_ts["cliUtils.test.ts<br/>~81 lines"]
npackages_browserstack_service_src_service_ts["service.ts<br/>~49 lines"]
npackages_browserstack_service_tests_service_test_ts["service.test.ts<br/>~49 lines"]
npackages_browserstack_service_src_util_ts["util.ts<br/>~48 lines"]
npackages_browserstack_service_tests_util_test_ts["util.test.ts<br/>~48 lines"]
n_changeset_pr_183_md["pr-183.md<br/>~7 lines"]
n_changeset_sdk_6948_v8_binary_url_fallback_md["sdk-6948-v8-binary-url-fallback.md<br/>~7 lines"]
n_changeset_sdk_6948_v8_bsstag_host_recognition_md["sdk-6948-v8-bsstag-host-recognition.md<br/>~7 lines"]
end
↻ This verdict comment is the review anchor — it's updated in place on each run (the gate posts its status separately). — SDK PR Review Agent |
AakashHotchandani
left a comment
There was a problem hiding this comment.
Automated SDK PR Review — findings at 25529d20
Verdict: 🔴 Fix 2 issues (0 critical · 2 warnings · 0 suggestions). This was a first, full review: 3 of 3 units and 6 of 6 diff regions were judged, and every coverage gate passed.
Both findings are inline below. I checked each one against the code at 25529d20.
Generated by Automated SDK PR review.
| if (!isNullOrEmpty(browserStackBinaryUrl)) { | ||
| const status = (err as { response?: { statusCode?: number } })?.response?.statusCode ?? (err as Error)?.message | ||
| logger.warn(`update_cli request failed (${status}); falling back to BROWSERSTACK_BINARY_URL`) | ||
| const fallbackBinaryPath = await this.downloadLatestBinary(browserStackBinaryUrl as string, cliDir) |
There was a problem hiding this comment.
⚠️ Warning — [MAINTAINABILITY] The update_cli → BROWSERSTACK_BINARY_URL fallback has side effects that aren't documented
Problem
This PR's description covers only the bsstag host matching in util.ts. It also changes how the CLI binary is obtained here: when requestToUpdateCLI throws and BROWSERSTACK_BINARY_URL is set, the version check is skipped and the binary is downloaded from that URL. The changeset sdk-6948-v8-binary-url-fallback.md documents this, but the PR description doesn't.
The fallback is opt-in. With the env var unset, the error propagates as before, and the new test covers that. Two side effects are worth fixing or stating:
- Re-download every time. The fallback calls
downloadLatestBinaryeven when a usableexistingCliPathis already on disk, so each run whereupdate_clifails downloads the binary again. - The original error is lost. If
downloadLatestBinaryalso fails, its error surfaces and the originalupdate_clierror (the 401 from the comment) is never logged. Only its status is in the warn line.
Suggested Fix
- If
existingCliPathexists, return it instead of re-downloading, or at least prefer it. - Wrap the fallback download in a try/catch that logs or rethrows with both errors.
- Add the fallback to the PR description, so reviewers and release notes don't treat this as a pure host-matching port.
Verified against the diff at 25529d20: the fallback path calls downloadLatestBinary without checking existingCliPath, and err is not kept if the download throws.
There was a problem hiding this comment.
Partly fixed in c4d9545.
Fixed:
- The original error is no longer lost. The
update_clierror is logged at debug. If theBROWSERSTACK_BINARY_URLdownload then fails with no binary on disk, both errors are logged together before the download error is rethrown. - A failed download now falls back to the binary on disk.
cliUtils.tsfallbackToBinaryUrlreturns the existing binary when there is one, and the run fails only when there isn't. This matches main/v9 fix(cli): fall back to BROWSERSTACK_BINARY_URL when update_cli fails (SDK-6948) #224. - The PR description and both changesets now describe the fallback.
Not changed: re-downloading instead of reusing the binary on disk. When update_cli fails, BROWSERSTACK_BINARY_URL intentionally wins over the binary on disk:
- With
update_clifailing, the SDK can't tell whether the binary on disk is current. On a staging env it is often an older build that sends session-marking calls toapi-<env>, which returns 404 on non-preprod envs. Reusing it silently brings back the marking bug this work fixes (SDK-7872). - The download only happens when
update_clifails and the user has setBROWSERSTACK_BINARY_URL, so production runs are unaffected. - This is the rule all the SDK-6948 PRs follow (node-agent #2655, python #1282, csharp #1375, javaagent #2296, wdio main fix(cli): fall back to BROWSERSTACK_BINARY_URL when update_cli fails (SDK-6948) #224).
Tests in tests/cli/cliUtils.test.ts: download fails and the binary on disk is used; download fails with nothing on disk and the error is rethrown; a successful reply with no download link and nothing on disk falls back; a successful reply with no download link and a binary on disk keeps it.
|
|
||
| const isBrowserstack = (str: string ): boolean => { | ||
| return str.includes('browserstack.com') | ||
| return str.includes('browserstack.com') || str.includes('bsstag.com') |
There was a problem hiding this comment.
⚠️ Warning — [TEST COVERAGE] No test for the new bsstag matching in getCloudProvider / isBrowserstackInfra
Problem
Both classifiers now treat bsstag hosts as BrowserStack: the single and multiremote branches of getCloudProvider, and isBrowserstack here. Nothing tests these branches. The only test added in this PR covers the CLI fallback. tests/util.test.ts:453-468 covers getCloudProvider only for hub.browserstack.com.
v8 uses .includes() where main/v9 uses === / endsWith, so the v9 tests don't protect this adaptation either. A later edit to either predicate could drop staging recognition without any test failing.
Suggested Fix
Add cases to tests/util.test.ts:
expect(getCloudProvider({ options: { hostname: 'hub-xyz.bsstag.com' } } as any)).toEqual('browserstack')
// multiremote: one instance with a bsstag hostname -> 'browserstack'
expect(isBrowserstackInfra({ hostname: 'hub.bsstag.com' } as any)).toBe(true)Verified: tests/util.test.ts at 25529d20 has no bsstag case. The reviewer graded this as a Suggestion, and the engine's severity table (Test Coverage + KB-medium grounding) raised it to Warning.
There was a problem hiding this comment.
Fixed in c4d9545.
The bsstag matching is also now limited to staging runs, to match main/v9 #275 and the other SDKs. A *.bsstag.com host counts as BrowserStack only when BROWSERSTACK_STAGING_ENV is a bare env name (trimmed, blank means unset) or BROWSERSTACK_ENV is staging, stag, preprod or pre-prod. The host must match exactly and case doesn't matter. This replaces the earlier match on any text containing bsstag, and the logic lives in a new isStagingEnvBsstagHost helper.
New tests in tests/util.test.ts, under 'SDK-6948 BROWSERSTACK_STAGING_ENV bsstag host recognition':
getCloudProviderfor a single instance and for multiremote, andisBrowserstackInfrawith the host in the config and in capabilities- trimming and case-insensitive matching, and the
BROWSERSTACK_ENVvalues - production behaviour unchanged when no staging env is set;
hub.browserstack.comis still recognised - blank or dotted
BROWSERSTACK_STAGING_ENVvalues, and look-alike hosts such ashub.notbsstag.com
The full suite passes locally: 45/45 test files (npm run build && npm test).
- util: treat *.bsstag.com as BrowserStack only on a named staging env
(BROWSERSTACK_STAGING_ENV bare token, trimmed; or BROWSERSTACK_ENV
staging/stag/preprod/pre-prod), exact suffix match, case-insensitive.
Replaces the unconditional `includes('bsstag')` match.
- cli: BROWSERSTACK_BINARY_URL fallback also covers a 2xx update_cli reply
with no url and no cached binary; a failed fallback download uses the
cached binary when present; the update_cli error is logged; the perf span
closes on every path.
- service: _printSessionURL logs a failed or empty session lookup at debug
instead of throwing (v8 uses got, which throws on 404/401).
- tests: bsstag gating, fallback download failures, 2xx-without-url,
_printSessionURL 404 / missing automation_session.
Addresses review comments 4256353854 and 4256353859.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AakashHotchandani
left a comment
There was a problem hiding this comment.
Automated SDK PR Review — re-review at ccb370dd
Engine verdict: 🔴 Fix 1 issue (1 critical · 0 warnings · 0 suggestions). All 9 changed files were re-reviewed (4 units, 11 of 11 regions judged).
The verdict comment above shows
Findings from the previous review (25529d20), both fixed:
cliUtils.tsfallback: a failedBROWSERSTACK_BINARY_URLdownload now falls back to the cached binary and logs both errors, and the changesets document it. ✅bsstagtest coverage:tests/util.test.tsnow covers it. ✅
New: 1 finding, inline below. I checked it against the code at ccb370dd. My view is that its real severity is Warning, not Critical; the reason is in the comment.
Generated by Automated SDK PR review.
| browserUrl = session.browser_url | ||
| } | ||
| } catch (err) { | ||
| // SDK-6948: on a named staging env the session REST host may not serve this session |
There was a problem hiding this comment.
🔴 Critical (reviewer note: should be ⚠️ Warning) — [CORRECTNESS] _update now runs for bsstag sessions with no error handling, while the session GET next to it was made non-fatal
Problem
With this PR, isStagingEnvBsstagHost makes *.bsstag.com sessions count as BrowserStack. isBrowserstackSession(this._browser) is now true there, so _update() (service.ts:791-810) starts sending got.put / got.patch to ${_sessionBaseUrl}/<id>.json. That is the same session REST host this hunk now guards.
This hunk wraps the GET in try/catch because "on a named staging env the session REST host may not serve this session". The writes to that host have no such guard:
_setSessionName→await this._updateJob({ name })(:887), called frombeforeSuite/beforeTest/beforeFeature;after()→await this._updateJob({ status, … })(:554), for the classic non-CLI flow;- the multiremote reload path
await this._update(oldSessionId, …)(:729).
If the host returns 401/404 for these sessions, which is the case this hunk was written for, each of these calls rejects and surfaces as a hook error instead of a debug log.
Why Warning rather than Critical: this only triggers when BROWSERSTACK_STAGING_ENV (a bare env name) or BROWSERSTACK_ENV (staging/stag/preprod/pre-prod) is set. That means internal staging only; production hosts are unchanged. GRR also rewrites APIUtils.BROWSERSTACK_AUTOMATE_API_URL to the staging API, so the call may well succeed there. Runtime was not verified.
Suggested Fix
Make _update non-fatal the same way as _printSessionURL:
return got.put(sessionUrl, { … }).catch((err) => {
const status = err?.response?.statusCode ?? err?.message
BStackLogger.debug(`Could not update Browserstack session (${status}) at ${sessionUrl}`)
})(Apply the same to the got.patch turbo-scale branch.) Add a service.test.ts case where got.put rejects with 401 for a bsstag session.
Verified against ccb370dd: _update has no catch, and its 3 call sites await it without one.
There was a problem hiding this comment.
Correction to the after() bullet above. after() is wrapped in try/catch (service.ts:609), so a rejected _updateJob there is logged as Error in after hook and doesn't surface as a hook failure. It does skip everything after it in that try: Listener.onWorkerEnd(), Percy teardown, and the rest of the worker-end cleanup. The beforeSuite / beforeTest / beforeFeature paths through _setSessionName have no catch. The suggested fix (a non-fatal _update) covers all of these.
There was a problem hiding this comment.
Fixed in d91e729. The finding is valid, and Warning is the right severity: it only triggers on a named staging env.
_update (service.ts) now attaches .catch(onHttpError) to both got.put and the TurboScale got.patch. That covers all three call sites: _setSessionName from beforeSuite / beforeTest / beforeFeature, after(), and the multiremote reload path.
- HTTP error replies (the error has a
response, such as 401 or 404) are logged at debug asCould not update Browserstack session (<status>) at <url>and resolve. The hook doesn't fail, and the rest ofafter()still runs. - Network errors (no
response) still reject.
This matches main/v9 exactly. v9 sends these updates with fetch, which resolves on 4xx/5xx and rejects only on network failures. The v8 difference came only from got rejecting on non-2xx replies, so prod behaviour now lines up with v9 as well.
New tests in tests/service.test.ts under _update: a PUT returning 401 resolves and logs at debug; a TurboScale PATCH returning 404 resolves; a network error still rejects.
npm run build passes, and npm test passes 45/45 test files.
…K-6948) With *.bsstag.com sessions now recognised on named staging envs, _update PUTs/PATCHes the session REST host there. got rejects on a non-2xx reply, so a 401/404 surfaced as a beforeSuite/beforeTest/beforeFeature hook error (via _setSessionName) and skipped the rest of after(). main/v9 uses fetch, which resolves on HTTP errors; match that: log HTTP-status errors at debug and keep rejecting on network errors. Tests: PUT 401 and TurboScale PATCH 404 resolve with a debug log; a network error still rejects. Addresses review comment 4256713959. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AakashHotchandani
left a comment
There was a problem hiding this comment.
Automated SDK PR Review — re-review at 61570d31
Engine verdict: 🔴 Fix 2 issues (1 critical · 1 warning · 0 suggestions). This was an incremental review: only the files changed since ccb370dd were reviewed (the changeset, service.ts, service.test.ts).
The verdict comment above shows
- G3: 18 "no read receipt" violations, all for the 2 units (
cliUtils.ts,util.ts) that the engine deliberately skipped as unchanged. - G6: a KB proposal was made but no proposal PR was opened.
Previous finding (_update with no error handling), fixed in d91e729: got.put / got.patch now .catch(onHttpError). HTTP errors log at debug and network errors still reject, with 3 tests. ✅
New: 2 findings, inline below. I checked both against the code at 61570d31.
Correction to my previous review: I said GRR rewrites APIUtils.BROWSERSTACK_AUTOMATE_API_URL to the staging API. That is only true in the CLI flow (see finding 1).
Generated by Automated SDK PR review.
| // SDK-6948: an HTTP error reply (e.g. 401/404 from a named staging env's session REST host) | ||
| // must not fail the calling hook. got rejects on non-2xx; main/v9 uses fetch, which resolves, | ||
| // so only HTTP-status errors are swallowed here and network errors still propagate. | ||
| const onHttpError = (err: { response?: { statusCode?: number } }) => { |
There was a problem hiding this comment.
🔴 Critical (reviewer note: should be ⚠️ Warning) — [INTENT MISMATCH] In the classic (non-CLI) flow, session marking on bsstag goes to the prod API and is now swallowed, but the changeset says it works
Problem
_sessionBaseUrl is built from APIUtils.BROWSERSTACK_AUTOMATE_API_URL (service.ts:51, and :210/:214 for App Automate and TurboScale). That value is the prod default https://api.browserstack.com (cli/apiUtils.ts:3). The only code that changes it is APIUtils.updateURLSForGRR, called from cli/index.ts:131, which is the CLI flow.
So in the classic non-CLI flow on a bsstag hub, _update (session name, and after() status) now sends the staging session id and credentials to prod. The likely result is 401/404, which onHttpError swallows at debug. The run won't fail, but the session won't be marked. Meanwhile .changeset/pr-183.md says "so session marking and TRA linking work there".
Why Warning rather than Critical: this is internal-staging only (gated on BROWSERSTACK_STAGING_ENV / BROWSERSTACK_ENV), production is unchanged, and the CLI flow is unaffected. It is a claim/behaviour mismatch, not customer breakage. The prod 401 itself was not runtime-verified.
Suggested Fix
Either:
- point the session REST base at the staging API in the non-CLI flow too (derive it from the GRR / env config in the worker), or
- narrow the changeset: on bsstag, session marking works only with the CLI flow; in the classic flow it is skipped at debug.
There was a problem hiding this comment.
Valid. The wording overclaimed. Fixed in d0a8fba by narrowing the changeset, which is your second option.
I confirmed the cause: APIUtils.BROWSERSTACK_AUTOMATE_API_URL is localized only by APIUtils.updateURLSForGRR, which is called only from cli/index.ts:131. In the classic (non-CLI) flow, _sessionBaseUrl stays on prod api.browserstack.com.
What changed:
.changeset/sdk-6948-v8-bsstag-host-recognition.mdand the PR's release notes (which regenerate.changeset/pr-183.md) now say marking and linking work with the CLI flow (the default), where the binary supplies the staging session REST host. The classic non-CLI flow on a named staging env is stated as not supported, and its session update is skipped at debug.- The PR description has a matching Scope note.
Why I didn't add staging-host derivation to the classic flow:
- It is the same scope decision as main/v9 fix: recognize bsstag hosts and localize session REST host on named staging envs (SDK-6948) #275, whose changeset reads: "The classic (non-CLI) flow on a named staging env is not supported". The v8 port keeps parity with that.
- Internal staging runs go through the CLI flow, since the SDK-6948 work is built around the binary supplying the hosts.
- Building the staging API host in the worker would duplicate the binary's per-env host rules (
api<env>vsapi-preprod, binary #1889) in a second place, which could drift. - Production is unaffected.
| expect(debugSpy).toHaveBeenCalledWith(expect.stringContaining('Could not update Browserstack session (401)')) | ||
| }) | ||
|
|
||
| it('logs at debug when a TurboScale session update gets an HTTP error (SDK-6948)', async () => { |
There was a problem hiding this comment.
⚠️ Warning — [TEST COVERAGE] The TurboScale test says "logs at debug" but doesn't assert the debug log
Problem
it('logs at debug when a TurboScale session update gets an HTTP error (SDK-6948)') only checks that _update resolves and that got.patch was called once. There is no BStackLogger.debug spy or assertion, unlike the got.put test above it, so the patch branch of onHttpError logging is untested despite the name.
Suggested Fix
const debugSpy = vi.spyOn(bstackLogger.BStackLogger, 'debug')
...
expect(debugSpy).toHaveBeenCalledWith(expect.stringContaining('Could not update Browserstack session (404)'))Alternatively, rename the test to match what it checks.
Verified at 61570d31.
There was a problem hiding this comment.
Valid. Fixed in d0a8fba. The TurboScale _update test now spies on BStackLogger.debug and asserts 'Could not update Browserstack session (404)', so the patch branch's logging is covered just like the put test. npm test passes 45/45 test files.
… TurboScale debug log (SDK-6948) - Changeset: session marking and TRA linking on named staging envs work in the CLI flow, where the binary supplies the staging session REST host (APIUtils.updateURLSForGRR). The classic non-CLI flow on a named staging env is documented as unsupported (same as main/v9 #275); its session update is skipped at debug. - service.test.ts: the TurboScale _update test now asserts the 'Could not update Browserstack session (404)' debug log. Addresses review comments 4256880250 and 4256880265. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AakashHotchandani
left a comment
There was a problem hiding this comment.
Automated SDK PR Review — re-review at 0f1d1732: ✅ good to go (reviewer assessment)
Reviewer verdict: ✅ Good to go. No blocking issues remain. The one open item (inline below) is a non-blocking changelog nit.
Engine output, for the record: the engine's union says 🔴 Fix 1. The verdict comment above shows
- G3: 18 receipt violations, all for the
cliUtils.ts/util.tsunits it deliberately skipped as unchanged. - G6: 2 KB proposals with no proposal PR.
Its one finding is rated Critical only because the severity table maps any objective, non-cosmetic finding to Critical. I checked it, and it is a duplicate-changelog nit (details inline). I am overriding it to Suggestion.
Previous findings, all resolved:
cliUtils.tsfallback: cached binary, both errors logged. ✅bsstagtests inutil.test.ts. ✅_updateHTTP errors are non-fatal (.catch(onHttpError)). ✅- The changeset claim is narrowed to the CLI flow; the non-CLI flow is documented as unsupported. ✅
- The TurboScale
_updatetest asserts the debug log. ✅
Generated by Automated SDK PR review.
| --- | ||
|
|
||
| - SDK-6948: `*.bsstag.com` hubs are now recognised as BrowserStack on named internal staging envs (`BROWSERSTACK_STAGING_ENV` / `BROWSERSTACK_ENV`), so with the CLI flow (default) session marking and TRA linking work there. The classic (non-CLI) flow on a named staging env is not supported; its session update is skipped at debug. | ||
| - SDK-6948: `update_cli` failures fall back to `BROWSERSTACK_BINARY_URL`. A failed fallback download uses the cached binary. |
There was a problem hiding this comment.
💡 Suggestion (engine: 🔴 Critical, overridden: changelog nit) — [MAINTAINABILITY] The changelog will list each SDK-6948 fix twice
Problem
pr-183.md is auto-generated by .github/workflows/changeset-from-pr.yml from the PR body's ## Release section. It restates the two hand-written changesets, sdk-6948-v8-binary-url-fallback.md and sdk-6948-v8-bsstag-host-recognition.md. All three are patch for @wdio/browserstack-service, so the "Version Packages" changelog will carry each fix twice.
Suggested Fix (non-blocking)
Pick one source:
- Delete the two hand-written changesets and keep the bot's
pr-183.md, which already covers all three points. Don't deletepr-183.mditself: the bot regenerates it on every PR body edit. - Or add the
skip-changesetlabel and keep the hand-written ones.
Verified at 0f1d1732: all three files are in .changeset/, and pr-183.md's commits are by github-actions[bot] ("auto-generate from PR template").

What is this about?
Ports the SDK-6948 staging-env fixes from main/v9 (#224 and #275) to the v8 line. Internal staging consumers such as App LCA (
devapplca) run WebdriverIO v8, so the main/v9 PRs don't reach them.1.
*.bsstag.comhost recognition (util.ts), the v8 port of #275.getCloudProviderandisBrowserstackInfraonly matchedbrowserstack.com. A session on an internal staging hub (hub-<env>.bsstag.com) was therefore treated as non-BrowserStack, so session marking and Automate <-> TRA session linking did not work.isStagingEnvBsstagHost, treats*.bsstag.comas BrowserStack only when:BROWSERSTACK_STAGING_ENVis a bare env name (trimmed; blank means unset), orBROWSERSTACK_ENVisstaging,stag,preprodorpre-prod.*.bsstag.comexactly, ignoring case. Production behaviour is unchanged. This is the same rule node-agent, python, ruby, csharp and javaagent use.APIUtils.updateURLSForGRR. The classic (non-CLI) flow on a named staging env is not supported, the same as main/v9 fix: recognize bsstag hosts and localize session REST host on named staging envs (SDK-6948) #275: its session REST base stays on prod, and the name/status update is logged at debug instead of failing the hook.2.
BROWSERSTACK_BINARY_URLfallback whenupdate_clifails (cli/cliUtils.ts), the v8 port of #224.update_cliruns during bootstrap, before the binary has localized the API hosts, so it always calls prodapi.browserstack.com. Staging credentials get a 401 there, the binary path ends up empty, and the launcher crashes withspawn('') ... 'file' cannot be empty.update_clifails andBROWSERSTACK_BINARY_URLis set, the binary is downloaded from that URL. The same applies when a 2xx reply has no download URL and no binary is cached.3.
_printSessionURLno longer fails the hook (service.ts).got, which throws on a 404 or 401. With (1),bsstaghubs now reach this lookup.automation_session, is now logged at debug instead of failingbefore/afterTest. The session link is informational only._update(session name/status PUT, TurboScale PATCH) likewise logs an HTTP error reply at debug instead of failingbeforeSuite/beforeTest/beforeFeatureor cuttingafter()short. Network errors still reject. This matches main/v9, whosefetchresolves on 4xx/5xx.Related Jira task/s
Release (mandatory for every PR — required for the
ready-for-reviewlabel)Version bump: (required — tick exactly one)
Release notes type: (optional)
Release notes (customer-facing): (optional but encouraged)
Release notes (internal): (required — engineer-facing; what actually changed / why)
*.bsstag.comhubs are now recognised as BrowserStack on named internal staging envs (BROWSERSTACK_STAGING_ENV/BROWSERSTACK_ENV), so with the CLI flow (default) session marking and TRA linking work there. The classic (non-CLI) flow on a named staging env is not supported; its session update is skipped at debug.update_clifailures fall back toBROWSERSTACK_BINARY_URL. A failed fallback download uses the cached binary._printSessionURLand_updatelog a failed session lookup or update (HTTP error) at debug instead of throwing.Checklist
Validation:
npm run buildpasses, including the type check.npm test: 45/45 test files pass. The changed files have 194 util, 42 cliUtils and 104 service tests.PR Validations
Run Tests: Comment RUN_TESTS to trigger sanity tests.
🤖 Generated with Claude Code