brave-mcp 1.7.0 → 1.7.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.
- package/README.md +54 -8
- package/build/src/WaitForHelper.js +1 -1
- package/build/src/telemetry/flagUtils.js +4 -3
- package/build/src/third_party/THIRD_PARTY_NOTICES +1 -1
- package/build/src/third_party/bundled-packages.json +1 -1
- package/build/src/third_party/index.js +44 -22
- package/build/src/tools/pwa.js +4 -0
- package/build/src/version.js +1 -1
- package/package.json +1 -1
- package/build/src/utils/string.js +0 -37
package/README.md
CHANGED
|
@@ -1,16 +1,62 @@
|
|
|
1
1
|
# Brave DevTools for agents
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Full Chrome DevTools MCP parity, rebuilt for Brave.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
(
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
[](https://github.com/triuzzi/brave-devtools-mcp/actions/workflows/run-tests.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/brave-mcp)
|
|
7
|
+
[](https://www.npmjs.com/package/brave-mcp)
|
|
8
|
+
[](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.triuzzi%2Fbrave-mcp)
|
|
9
|
+
[](https://registry.npmjs.org/-/npm/v1/attestations/brave-mcp@1.7.1)
|
|
10
|
+
[](https://github.com/triuzzi/brave-devtools-mcp/compare/ChromeDevTools:main...main)
|
|
11
|
+
[](./LICENSE)
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+

|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
`brave-mcp` gives Claude Code, Codex, Cursor, OpenCode, and other MCP clients direct access to Brave for browser automation, network and console debugging, performance analysis, screenshots, accessibility inspection, and memory profiling. A standalone [`brave-devtools`](docs/cli.md) CLI is included too.
|
|
16
|
+
|
|
17
|
+
## Install in one command
|
|
18
|
+
|
|
19
|
+
### Claude Code
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
claude mcp add brave-devtools --scope user -- npx -y brave-mcp@latest
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Cursor
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
cursor --add-mcp '{"name":"brave-devtools","command":"npx","args":["-y","brave-mcp@latest"]}'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Codex
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
codex mcp add brave-devtools -- npx -y brave-mcp@latest
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### OpenCode
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
opencode mcp add brave-devtools -- npx -y brave-mcp@latest
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Restart your client, then try this prompt:
|
|
44
|
+
|
|
45
|
+
> Open my app in Brave. Find console errors and failed network requests, inspect the accessibility tree, run Lighthouse, and explain the highest-impact issue.
|
|
46
|
+
|
|
47
|
+
## Why use this instead of Chrome DevTools MCP?
|
|
48
|
+
|
|
49
|
+
| Capability | `brave-mcp` | `chrome-devtools-mcp` |
|
|
50
|
+
| ---------------------------------------- | --------------------------------------- | ------------------------------------ |
|
|
51
|
+
| Browser launched and discovered natively | Brave Release, Beta, and Nightly | Chrome Stable, Beta, Dev, and Canary |
|
|
52
|
+
| Attach to an existing browser | Brave profiles, HTTP, or WebSocket | Chrome profiles, HTTP, or WebSocket |
|
|
53
|
+
| Upstream DevTools features | Full parity; currently 0 commits behind | Source implementation |
|
|
54
|
+
| Usage telemetry | Disabled by default | Enabled by default |
|
|
55
|
+
| Standalone CLI | `brave-devtools` | `chrome-devtools` |
|
|
56
|
+
|
|
57
|
+
Use [Chrome DevTools MCP](https://github.com/ChromeDevTools/chrome-devtools-mcp) when your target browser is Chrome. Use `brave-mcp` when your target browser is Brave and you want Brave-native discovery, profiles, channels, naming, and privacy defaults without giving up upstream features.
|
|
58
|
+
|
|
59
|
+
## [Tool reference](./docs/tool-reference.md) | [Changelog](./CHANGELOG.md) | [Contributing](./CONTRIBUTING.md) | [Troubleshooting](./docs/troubleshooting.md) | [Design principles](./docs/design-principles.md)
|
|
14
60
|
|
|
15
61
|
## Key features
|
|
16
62
|
|
|
@@ -71,7 +71,7 @@ export class WaitForHelper {
|
|
|
71
71
|
this.#stableDomTimeout = 3000 * cpuTimeoutMultiplier;
|
|
72
72
|
this.#stableDomFor = 100 * cpuTimeoutMultiplier;
|
|
73
73
|
this.#expectNavigationIn = 100 * cpuTimeoutMultiplier;
|
|
74
|
-
this.#navigationTimeout = 3000 * networkTimeoutMultiplier;
|
|
74
|
+
this.#navigationTimeout = Math.max(page.getDefaultNavigationTimeout(), 3000 * networkTimeoutMultiplier);
|
|
75
75
|
this.#page = page;
|
|
76
76
|
this.#initialUrl = page.url();
|
|
77
77
|
}
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* Copyright 2026 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { DevTools } from '../third_party/index.js';
|
|
7
7
|
import { stripUnderscoreBeforeNumber } from './transformation.js';
|
|
8
|
+
const { StringUtilities } = DevTools.Platform;
|
|
8
9
|
/**
|
|
9
10
|
* For enums, log the value as uppercase.
|
|
10
11
|
* We're going to have an enum for such flags with choices represented
|
|
@@ -30,7 +31,7 @@ export function computeFlagUsage(args, options) {
|
|
|
30
31
|
const usage = {};
|
|
31
32
|
for (const [flagName, config] of Object.entries(options)) {
|
|
32
33
|
const value = args[flagName];
|
|
33
|
-
const snakeCaseName = stripUnderscoreBeforeNumber(toSnakeCase(flagName));
|
|
34
|
+
const snakeCaseName = stripUnderscoreBeforeNumber(StringUtilities.toSnakeCase(flagName));
|
|
34
35
|
// If there isn't a default value provided for the flag,
|
|
35
36
|
// we're going to log whether it's present on the args user
|
|
36
37
|
// provided or not. If there is a default value, we only log presence
|
|
@@ -59,7 +60,7 @@ export function computeFlagUsage(args, options) {
|
|
|
59
60
|
export function getPossibleFlagMetrics(options) {
|
|
60
61
|
const metrics = [];
|
|
61
62
|
for (const [flagName, config] of Object.entries(options)) {
|
|
62
|
-
const snakeCaseName = stripUnderscoreBeforeNumber(toSnakeCase(flagName));
|
|
63
|
+
const snakeCaseName = stripUnderscoreBeforeNumber(StringUtilities.toSnakeCase(flagName));
|
|
63
64
|
// _present is always a possible metric
|
|
64
65
|
metrics.push({
|
|
65
66
|
name: `${snakeCaseName}_present`,
|
|
@@ -854,7 +854,7 @@ License: MIT
|
|
|
854
854
|
|
|
855
855
|
Name: devtools-frontend
|
|
856
856
|
URL: https://github.com/ChromeDevTools/devtools-frontend
|
|
857
|
-
Version:
|
|
857
|
+
Version: 7da4b7eeaea404a21118cd937b23a5c57c367229
|
|
858
858
|
License: Apache-2.0
|
|
859
859
|
|
|
860
860
|
// Copyright 2014 The Chromium Authors
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
"semver": "7.8.5",
|
|
7
7
|
"yargs": "18.1.0",
|
|
8
8
|
"puppeteer-core": "25.5.0",
|
|
9
|
-
"https://github.com/ChromeDevTools/devtools-frontend": "
|
|
9
|
+
"https://github.com/ChromeDevTools/devtools-frontend": "7da4b7eeaea404a21118cd937b23a5c57c367229"
|
|
10
10
|
}
|
|
@@ -82989,11 +82989,6 @@ const UIStrings$1h = {
|
|
|
82989
82989
|
};
|
|
82990
82990
|
const str_$1g = registerUIStrings('core/sdk/sdk-meta.ts', UIStrings$1h);
|
|
82991
82991
|
const i18nLazyString$j = getLazilyComputedLocalizedString.bind(undefined, str_$1g);
|
|
82992
|
-
registerSettingExtension({
|
|
82993
|
-
settingName: 'pause-on-uncaught-exception',
|
|
82994
|
-
settingType: "boolean" ,
|
|
82995
|
-
defaultValue: false,
|
|
82996
|
-
});
|
|
82997
82992
|
registerSettingExtension({
|
|
82998
82993
|
category: "DEBUGGER" ,
|
|
82999
82994
|
title: i18nLazyString$j(UIStrings$1h.disableJavascript),
|
|
@@ -88522,6 +88517,8 @@ function registerCommands(inspectorBackend) {
|
|
|
88522
88517
|
inspectorBackend.registerCommand("Page.setTouchEmulationEnabled", [{ "name": "enabled", "type": "boolean", "optional": false, "description": "Whether the touch event emulation should be enabled.", "typeRef": null }, { "name": "configuration", "type": "string", "optional": true, "description": "Touch/gesture events configuration. Default: current platform.", "typeRef": "Page.SetTouchEmulationEnabledRequestConfiguration" }], [], "Toggles mouse event-based touch event emulation.");
|
|
88523
88518
|
inspectorBackend.registerEnum("Page.StartScreencastRequestFormat", { Jpeg: "jpeg", Png: "png" });
|
|
88524
88519
|
inspectorBackend.registerCommand("Page.startScreencast", [{ "name": "format", "type": "string", "optional": true, "description": "Image compression format.", "typeRef": "Page.StartScreencastRequestFormat" }, { "name": "quality", "type": "number", "optional": true, "description": "Compression quality from range [0..100].", "typeRef": null }, { "name": "maxWidth", "type": "number", "optional": true, "description": "Maximum screenshot width.", "typeRef": null }, { "name": "maxHeight", "type": "number", "optional": true, "description": "Maximum screenshot height.", "typeRef": null }, { "name": "everyNthFrame", "type": "number", "optional": true, "description": "Send every n-th frame.", "typeRef": null }], [], "Starts sending each frame using the `screencastFrame` event.");
|
|
88520
|
+
inspectorBackend.registerCommand("Page.startScreenRecording", [{ "name": "audio", "type": "boolean", "optional": true, "description": "", "typeRef": null }, { "name": "maxWidth", "type": "number", "optional": true, "description": "Maximum frame width in pixels.", "typeRef": null }, { "name": "maxHeight", "type": "number", "optional": true, "description": "Maximum frame height in pixels.", "typeRef": null }, { "name": "frameRate", "type": "number", "optional": true, "description": "Maximum frame rate in frames per second.", "typeRef": null }], ["stream"], "Starts screencast video recording.");
|
|
88521
|
+
inspectorBackend.registerCommand("Page.stopScreenRecording", [], ["stream"], "Stops screencast video recording.");
|
|
88525
88522
|
inspectorBackend.registerCommand("Page.stopLoading", [], [], "Force the page stop all navigations and pending resource fetches.");
|
|
88526
88523
|
inspectorBackend.registerCommand("Page.crash", [], [], "Crashes renderer on the IO thread, generates minidumps.");
|
|
88527
88524
|
inspectorBackend.registerCommand("Page.close", [], [], "Tries to close page, running its beforeunload hooks, if any.");
|
|
@@ -88824,7 +88821,7 @@ function registerCommands(inspectorBackend) {
|
|
|
88824
88821
|
inspectorBackend.registerType("WebAudio.AudioParam", [{ "name": "paramId", "type": "string", "optional": false, "description": "", "typeRef": "WebAudio.GraphObjectId" }, { "name": "nodeId", "type": "string", "optional": false, "description": "", "typeRef": "WebAudio.GraphObjectId" }, { "name": "contextId", "type": "string", "optional": false, "description": "", "typeRef": "WebAudio.GraphObjectId" }, { "name": "paramType", "type": "string", "optional": false, "description": "", "typeRef": "WebAudio.ParamType" }, { "name": "rate", "type": "string", "optional": false, "description": "", "typeRef": "WebAudio.AutomationRate" }, { "name": "defaultValue", "type": "number", "optional": false, "description": "", "typeRef": null }, { "name": "minValue", "type": "number", "optional": false, "description": "", "typeRef": null }, { "name": "maxValue", "type": "number", "optional": false, "description": "", "typeRef": null }]);
|
|
88825
88822
|
inspectorBackend.registerEnum("WebAuthn.AuthenticatorProtocol", { U2f: "u2f", Ctap2: "ctap2" });
|
|
88826
88823
|
inspectorBackend.registerEnum("WebAuthn.Ctap2Version", { Ctap2_0: "ctap2_0", Ctap2_1: "ctap2_1", Ctap2_2: "ctap2_2" });
|
|
88827
|
-
inspectorBackend.registerEnum("WebAuthn.AuthenticatorTransport", { Usb: "usb", Nfc: "nfc", Ble: "ble", Cable: "cable", Internal: "internal" });
|
|
88824
|
+
inspectorBackend.registerEnum("WebAuthn.AuthenticatorTransport", { Usb: "usb", Nfc: "nfc", Ble: "ble", Cable: "cable", Hybrid: "hybrid", SmartCard: "smart-card", Internal: "internal" });
|
|
88828
88825
|
inspectorBackend.registerEvent("WebAuthn.credentialAdded", ["authenticatorId", "credential"]);
|
|
88829
88826
|
inspectorBackend.registerEvent("WebAuthn.credentialDeleted", ["authenticatorId", "credentialId"]);
|
|
88830
88827
|
inspectorBackend.registerEvent("WebAuthn.credentialUpdated", ["authenticatorId", "credential"]);
|
|
@@ -92330,6 +92327,11 @@ const generatedProperties = [
|
|
|
92330
92327
|
"runtime_flag_status": "stable"
|
|
92331
92328
|
},
|
|
92332
92329
|
{
|
|
92330
|
+
"devtools_keywords": [
|
|
92331
|
+
"none",
|
|
92332
|
+
"move",
|
|
92333
|
+
"no-drag"
|
|
92334
|
+
],
|
|
92333
92335
|
"name": "app-region"
|
|
92334
92336
|
},
|
|
92335
92337
|
{
|
|
@@ -96467,7 +96469,8 @@ const generatedProperties = [
|
|
|
96467
96469
|
"inline",
|
|
96468
96470
|
"both",
|
|
96469
96471
|
"mandatory",
|
|
96470
|
-
"proximity"
|
|
96472
|
+
"proximity",
|
|
96473
|
+
"pair"
|
|
96471
96474
|
],
|
|
96472
96475
|
"name": "scroll-snap-type"
|
|
96473
96476
|
},
|
|
@@ -97382,6 +97385,10 @@ const generatedProperties = [
|
|
|
97382
97385
|
"name": "will-change"
|
|
97383
97386
|
},
|
|
97384
97387
|
{
|
|
97388
|
+
"devtools_keywords": [
|
|
97389
|
+
"none",
|
|
97390
|
+
"move"
|
|
97391
|
+
],
|
|
97385
97392
|
"inherited": true,
|
|
97386
97393
|
"keywords": [
|
|
97387
97394
|
"none",
|
|
@@ -97719,6 +97726,13 @@ const generatedPropertyValues = {
|
|
|
97719
97726
|
"none"
|
|
97720
97727
|
]
|
|
97721
97728
|
},
|
|
97729
|
+
"app-region": {
|
|
97730
|
+
"values": [
|
|
97731
|
+
"none",
|
|
97732
|
+
"move",
|
|
97733
|
+
"no-drag"
|
|
97734
|
+
]
|
|
97735
|
+
},
|
|
97722
97736
|
"appearance": {
|
|
97723
97737
|
"values": [
|
|
97724
97738
|
"auto",
|
|
@@ -99998,7 +100012,8 @@ const generatedPropertyValues = {
|
|
|
99998
100012
|
"inline",
|
|
99999
100013
|
"both",
|
|
100000
100014
|
"mandatory",
|
|
100001
|
-
"proximity"
|
|
100015
|
+
"proximity",
|
|
100016
|
+
"pair"
|
|
100002
100017
|
]
|
|
100003
100018
|
},
|
|
100004
100019
|
"scroll-target-group": {
|
|
@@ -100512,8 +100527,7 @@ const generatedPropertyValues = {
|
|
|
100512
100527
|
"window-drag": {
|
|
100513
100528
|
"values": [
|
|
100514
100529
|
"none",
|
|
100515
|
-
"move"
|
|
100516
|
-
"no-drag"
|
|
100530
|
+
"move"
|
|
100517
100531
|
]
|
|
100518
100532
|
},
|
|
100519
100533
|
"word-break": {
|
|
@@ -101050,9 +101064,6 @@ class CSSMetadata {
|
|
|
101050
101064
|
if (Boolean(runtimeFlagStatus) && runtimeFlagStatus !== 'stable') {
|
|
101051
101065
|
continue;
|
|
101052
101066
|
}
|
|
101053
|
-
if (!CSS.supports(propertyName, 'initial')) {
|
|
101054
|
-
continue;
|
|
101055
|
-
}
|
|
101056
101067
|
this.#values.push(propertyName);
|
|
101057
101068
|
if (property.inherited) {
|
|
101058
101069
|
this.#inherited.add(propertyName);
|
|
@@ -101091,7 +101102,7 @@ class CSSMetadata {
|
|
|
101091
101102
|
}
|
|
101092
101103
|
const commonKeywordSet = new Set(CommonKeywords);
|
|
101093
101104
|
for (const propertyName of this.#longhands.keys()) {
|
|
101094
|
-
if (propertyName === 'all' ||
|
|
101105
|
+
if (propertyName === 'all' || propertyName in generatedPropertyValues) {
|
|
101095
101106
|
continue;
|
|
101096
101107
|
}
|
|
101097
101108
|
const longhands = this.#longhands.get(propertyName);
|
|
@@ -101099,6 +101110,10 @@ class CSSMetadata {
|
|
|
101099
101110
|
continue;
|
|
101100
101111
|
}
|
|
101101
101112
|
const values = new Array();
|
|
101113
|
+
const propertyValueSet = propertyValueSets.get(propertyName);
|
|
101114
|
+
if (propertyValueSet) {
|
|
101115
|
+
values.push(...propertyValueSet);
|
|
101116
|
+
}
|
|
101102
101117
|
for (const longhand of longhands) {
|
|
101103
101118
|
const longhandValues = propertyValueSets.get(longhand);
|
|
101104
101119
|
if (!longhandValues) {
|
|
@@ -101119,11 +101134,6 @@ class CSSMetadata {
|
|
|
101119
101134
|
}
|
|
101120
101135
|
}
|
|
101121
101136
|
}
|
|
101122
|
-
for (const commonKeyword of CommonKeywords) {
|
|
101123
|
-
if (!values.has(commonKeyword) && CSS.supports(propertyName, commonKeyword)) {
|
|
101124
|
-
values.add(commonKeyword);
|
|
101125
|
-
}
|
|
101126
|
-
}
|
|
101127
101137
|
this.#propertyValues.set(propertyName, [...values]);
|
|
101128
101138
|
}
|
|
101129
101139
|
for (const name of this.#valuesSet) {
|
|
@@ -107348,6 +107358,11 @@ const pauseOnCaughtExceptionSettingDescriptor = {
|
|
|
107348
107358
|
type: "boolean" ,
|
|
107349
107359
|
defaultValue: false,
|
|
107350
107360
|
};
|
|
107361
|
+
const pauseOnUncaughtExceptionSettingDescriptor = {
|
|
107362
|
+
name: 'pause-on-uncaught-exception',
|
|
107363
|
+
type: "boolean" ,
|
|
107364
|
+
defaultValue: false,
|
|
107365
|
+
};
|
|
107351
107366
|
|
|
107352
107367
|
// Copyright 2020 The Chromium Authors
|
|
107353
107368
|
class IOModel extends SDKModel {
|
|
@@ -121307,7 +121322,8 @@ class DebuggerModel extends SDKModel {
|
|
|
121307
121322
|
settings.resolve(pauseOnCaughtExceptionSettingDescriptor)
|
|
121308
121323
|
.addChangeListener(this.pauseOnExceptionStateChanged, this);
|
|
121309
121324
|
this.#skipAllPausesSetting.addChangeListener(this.skipAllPausesChanged, this);
|
|
121310
|
-
settings.
|
|
121325
|
+
settings.resolve(pauseOnUncaughtExceptionSettingDescriptor)
|
|
121326
|
+
.addChangeListener(this.pauseOnExceptionStateChanged, this);
|
|
121311
121327
|
settings.moduleSetting('disable-async-stack-traces').addChangeListener(this.asyncStackTracesStateChanged, this);
|
|
121312
121328
|
settings.moduleSetting('breakpoints-active').addChangeListener(this.breakpointsActiveChanged, this);
|
|
121313
121329
|
if (!target.suspended()) {
|
|
@@ -121463,7 +121479,7 @@ class DebuggerModel extends SDKModel {
|
|
|
121463
121479
|
const settings = this.target().targetManager().settings;
|
|
121464
121480
|
const pauseOnCaughtEnabled = settings.resolve(pauseOnCaughtExceptionSettingDescriptor).get();
|
|
121465
121481
|
let state;
|
|
121466
|
-
const pauseOnUncaughtEnabled = settings.
|
|
121482
|
+
const pauseOnUncaughtEnabled = settings.resolve(pauseOnUncaughtExceptionSettingDescriptor).get();
|
|
121467
121483
|
if (pauseOnCaughtEnabled && pauseOnUncaughtEnabled) {
|
|
121468
121484
|
state = "all" ;
|
|
121469
121485
|
}
|
|
@@ -121673,6 +121689,10 @@ class DebuggerModel extends SDKModel {
|
|
|
121673
121689
|
this.resume();
|
|
121674
121690
|
return;
|
|
121675
121691
|
}
|
|
121692
|
+
if (this.#skipAllPausesSetting.get()) {
|
|
121693
|
+
this.resume();
|
|
121694
|
+
return;
|
|
121695
|
+
}
|
|
121676
121696
|
const pausedDetails = new DebuggerPausedDetails(this, callFrames, reason, auxData, breakpointIds, asyncStackTrace, asyncStackTraceId);
|
|
121677
121697
|
if (this.continueToLocationCallback) {
|
|
121678
121698
|
const callback = this.continueToLocationCallback;
|
|
@@ -121876,6 +121896,8 @@ class DebuggerModel extends SDKModel {
|
|
|
121876
121896
|
settings.resolve(pauseOnCaughtExceptionSettingDescriptor)
|
|
121877
121897
|
.removeChangeListener(this.pauseOnExceptionStateChanged, this);
|
|
121878
121898
|
this.#skipAllPausesSetting.removeChangeListener(this.skipAllPausesChanged, this);
|
|
121899
|
+
settings.resolve(pauseOnUncaughtExceptionSettingDescriptor)
|
|
121900
|
+
.removeChangeListener(this.pauseOnExceptionStateChanged, this);
|
|
121879
121901
|
settings.moduleSetting('disable-async-stack-traces').removeChangeListener(this.asyncStackTracesStateChanged, this);
|
|
121880
121902
|
}
|
|
121881
121903
|
async suspendModel() {
|
|
@@ -168000,7 +168022,7 @@ const NativeFunctions = [
|
|
|
168000
168022
|
},
|
|
168001
168023
|
{
|
|
168002
168024
|
name: "importNode",
|
|
168003
|
-
signatures: [["node", "?options"]
|
|
168025
|
+
signatures: [["node", "?options"]]
|
|
168004
168026
|
},
|
|
168005
168027
|
{
|
|
168006
168028
|
name: "queryCommandEnabled",
|
package/build/src/tools/pwa.js
CHANGED
|
@@ -98,6 +98,10 @@ export const launchPwa = defineTool({
|
|
|
98
98
|
handler: async (request, response, context) => {
|
|
99
99
|
const { manifestId, url } = request.params;
|
|
100
100
|
const page = await context.launchPWA({ manifestId, url });
|
|
101
|
+
if (url) {
|
|
102
|
+
const expectedUrl = new URL(url).href;
|
|
103
|
+
await page.waitForFunction(requestedUrl => globalThis.location.href === requestedUrl, { timeout: 30_000 }, expectedUrl);
|
|
104
|
+
}
|
|
101
105
|
response.appendResponseLine(`Launched PWA with manifest ID: ${manifestId} (${page.url()})`);
|
|
102
106
|
response.setIncludePages(true);
|
|
103
107
|
},
|
package/build/src/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2026 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Converts a given string to snake_case.
|
|
8
|
-
* This function handles camelCase, PascalCase, and acronyms, including transitions between letters and numbers.
|
|
9
|
-
* It uses Unicode-aware regular expressions (`\p{L}`, `\p{N}`, `\p{Lu}`, `\p{Ll}` with the `u` flag)
|
|
10
|
-
* to correctly process letters and numbers from various languages.
|
|
11
|
-
*
|
|
12
|
-
* @param text The input string to convert to snake_case.
|
|
13
|
-
* @returns The snake_case version of the input string.
|
|
14
|
-
*/
|
|
15
|
-
export function toSnakeCase(text) {
|
|
16
|
-
if (!text) {
|
|
17
|
-
return '';
|
|
18
|
-
}
|
|
19
|
-
// First, handle case-based transformations to insert underscores correctly.
|
|
20
|
-
// 1. Add underscore between a letter and a number.
|
|
21
|
-
// e.g., "version2" -> "version_2"
|
|
22
|
-
// 2. Add underscore between an uppercase letter sequence and a following uppercase+lowercase sequence.
|
|
23
|
-
// e.g., "APIFlags" -> "API_Flags"
|
|
24
|
-
// 3. Add underscore between a lowercase/number and an uppercase letter.
|
|
25
|
-
// e.g., "lastName" -> "last_Name", "version_2Update" -> "version_2_Update"
|
|
26
|
-
// 4. Replace sequences of non-alphanumeric with a single underscore
|
|
27
|
-
// 5. Remove any leading or trailing underscores.
|
|
28
|
-
const result = text
|
|
29
|
-
.replace(/(\p{L})(\p{N})/gu, '$1_$2') // 1
|
|
30
|
-
.replace(/(\p{Lu}+)(\p{Lu}\p{Ll})/gu, '$1_$2') // 2
|
|
31
|
-
.replace(/(\p{Ll}|\p{N})(\p{Lu})/gu, '$1_$2') // 3
|
|
32
|
-
.toLowerCase()
|
|
33
|
-
.replace(/[^\p{L}\p{N}]+/gu, '_') // 4
|
|
34
|
-
.replace(/^_|_$/g, ''); // 5
|
|
35
|
-
return result;
|
|
36
|
-
}
|
|
37
|
-
//# sourceMappingURL=string.js.map
|