cdp-toolkit 3.0.2 → 3.0.3

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/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to cdp-toolkit are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [3.0.3] - 2026-10-29
11
+
12
+ ### Fixed
13
+
14
+ - `new_page` (and `claim_page` opening a fresh tab) wedged the tab in Vivaldi: blank content, an empty URL, a renderer that never answered again, and a 15 s `new_page` timeout under `claim:true`, until a person reloaded it. Vivaldi breaks any tab that something attaches to in its first few milliseconds, and the activity beacon attached straight after `Target.createTarget`. `newPage` now waits for the tab's first `Target.targetInfoChanged` (capped at 2 s) before returning. Verified live on Vivaldi 8.2 (Chromium 152) through `new_page {claim:true}`: on 3.0.2 two of two Fidelity tabs hung; with the fix three of three loaded in 230 to 390 ms with the beacon installed. (`src/cdp/driver.ts` `createSettledTarget`, `test/new-page-settle.test.ts`)
15
+
16
+ ### Added
17
+
18
+ - `vivaldi` is now a supported `cdp install --browser` choice, wired like `arc`: binary detection (`VIVALDI_BIN`, `/Applications/Vivaldi.app/Contents/MacOS/Vivaldi`, `vivaldi` on PATH, `/usr/bin/vivaldi`), the `cdp-vivaldi` alias, the CLI choice list and help text, and the README. Vivaldi is Chromium, so it takes the same launch flags and the server env is the same as chrome and arc. (`src/install/browser.ts`, `src/install/wizard.ts`, `test/install-browser.test.ts`, `test/install-wizard.test.ts`)
19
+
8
20
  ## [3.0.2] - 2026-10-03
9
21
 
10
22
  ### Fixed
package/README.md CHANGED
@@ -76,7 +76,7 @@ One honest caveat the benchmark also reports: while a navigation to a dead endpo
76
76
  npx -y --package cdp-toolkit cdp install # or `cdp install` from a clone / after a global install
77
77
  ```
78
78
 
79
- It asks for a harness (`claude` | `codex` | `opencode`), a debug port, and a browser (`arc` | `chrome` | `firefox`), then writes an idempotent marker block into your `~/.zshrc`/`~/.bashrc`. The same flags drive it non-interactively: `--harness --browser --port --name --no-alias --yes`. The Firefox alias it writes includes `--marionette` (required for cdp-toolkit's orphaned-session auto-recovery — see [Firefox](#firefox-webdriver-bidi)). It is a CLI subcommand, **not** a 49th tool: `cdp --list` still shows 48.
79
+ It asks for a harness (`claude` | `codex` | `opencode`), a debug port, and a browser (`arc` | `chrome` | `firefox` | `vivaldi`), then writes an idempotent marker block into your `~/.zshrc`/`~/.bashrc`. The same flags drive it non-interactively: `--harness --browser --port --name --no-alias --yes`. The Firefox alias it writes includes `--marionette` (required for cdp-toolkit's orphaned-session auto-recovery — see [Firefox](#firefox-webdriver-bidi)). It is a CLI subcommand, **not** a 49th tool: `cdp --list` still shows 48.
80
80
 
81
81
  Prefer to wire it by hand? The manual path is three steps:
82
82
 
package/dist/cli.js CHANGED
@@ -4869,6 +4869,41 @@ function noSuchTarget(e) {
4869
4869
  throw e;
4870
4870
  throw driverError("no-such-target", e instanceof CdpError ? e.message : String(e));
4871
4871
  }
4872
+ var NEW_TARGET_SETTLE_MS = 2000;
4873
+ async function createSettledTarget(conn, url, settleMs = NEW_TARGET_SETTLE_MS) {
4874
+ const changed = new Set;
4875
+ let id = "";
4876
+ let wake;
4877
+ const off = conn.on("Target.targetInfoChanged", (params) => {
4878
+ const t = params.targetInfo?.targetId;
4879
+ if (!t)
4880
+ return;
4881
+ changed.add(t);
4882
+ if (t === id)
4883
+ wake?.();
4884
+ });
4885
+ try {
4886
+ await conn.send("Target.setDiscoverTargets", { discover: true }).catch(() => {
4887
+ return;
4888
+ });
4889
+ const { targetId } = await conn.send("Target.createTarget", { url });
4890
+ if (!targetId)
4891
+ throw driverError("page-error", "Target.createTarget returned no targetId");
4892
+ id = targetId;
4893
+ if (!changed.has(targetId)) {
4894
+ await new Promise((resolve) => {
4895
+ const timer = setTimeout(resolve, settleMs);
4896
+ wake = () => {
4897
+ clearTimeout(timer);
4898
+ resolve();
4899
+ };
4900
+ });
4901
+ }
4902
+ return targetId;
4903
+ } finally {
4904
+ off();
4905
+ }
4906
+ }
4872
4907
 
4873
4908
  class CdpBrowserDriver {
4874
4909
  scheme = "cdp";
@@ -4883,9 +4918,7 @@ class CdpBrowserDriver {
4883
4918
  }
4884
4919
  async newPage(url) {
4885
4920
  return withBrowserConn(async (conn) => {
4886
- const { targetId } = await conn.send("Target.createTarget", { url: url ?? "about:blank" });
4887
- if (!targetId)
4888
- throw driverError("page-error", "Target.createTarget returned no targetId");
4921
+ const targetId = await createSettledTarget(conn, url ?? "about:blank");
4889
4922
  return { id: targetId, url: url ?? "about:blank", title: "" };
4890
4923
  });
4891
4924
  }
@@ -10760,6 +10793,12 @@ var CANDIDATE_BUILDERS = {
10760
10793
  "chromium"
10761
10794
  ],
10762
10795
  arc: () => [process.env.ARC_BIN, "/Applications/Arc.app/Contents/MacOS/Arc", "arc"],
10796
+ vivaldi: () => [
10797
+ process.env.VIVALDI_BIN,
10798
+ "/Applications/Vivaldi.app/Contents/MacOS/Vivaldi",
10799
+ "vivaldi",
10800
+ "/usr/bin/vivaldi"
10801
+ ],
10763
10802
  firefox: () => [
10764
10803
  process.env.FIREFOX_BIN,
10765
10804
  "/Applications/Firefox.app/Contents/MacOS/firefox",
@@ -11007,7 +11046,7 @@ function isOnPath(bin) {
11007
11046
  // src/install/wizard.ts
11008
11047
  var DEFAULT_NAME = "cdp-toolkit";
11009
11048
  var HARNESSES = ["claude", "codex", "opencode"];
11010
- var BROWSERS = ["arc", "chrome", "firefox"];
11049
+ var BROWSERS = ["arc", "chrome", "firefox", "vivaldi"];
11011
11050
  function defaultPortFor(browser) {
11012
11051
  return browser === "firefox" ? 9223 : 9222;
11013
11052
  }
@@ -11020,10 +11059,10 @@ function parseHarness(v) {
11020
11059
  }
11021
11060
  function parseBrowserChoice(v) {
11022
11061
  if (v === undefined)
11023
- throw new Error("--browser requires a value: arc, chrome, or firefox");
11062
+ throw new Error("--browser requires a value: arc, chrome, firefox, or vivaldi");
11024
11063
  if (BROWSERS.includes(v))
11025
11064
  return v;
11026
- throw new Error(`--browser must be one of arc|chrome|firefox (got '${v}')`);
11065
+ throw new Error(`--browser must be one of arc|chrome|firefox|vivaldi (got '${v}')`);
11027
11066
  }
11028
11067
  function parsePort(v) {
11029
11068
  if (v === undefined)
@@ -11104,14 +11143,14 @@ function installHelpText() {
11104
11143
  browser-launch alias (with the remote-debugging port open) to your shell rc.
11105
11144
 
11106
11145
  Usage:
11107
- cdp install [--harness <claude|codex|opencode>] [--browser <arc|chrome|firefox>]
11146
+ cdp install [--harness <claude|codex|opencode>] [--browser <arc|chrome|firefox|vivaldi>]
11108
11147
  [--port <n>] [--name <server-name>] [--alias-name <name>]
11109
11148
  [--no-alias] [--yes|-y] [--help|-h]
11110
11149
 
11111
11150
  Options:
11112
11151
  --harness <h> Coding harness to register the MCP server with: claude, codex, opencode.
11113
- --browser <b> Browser the alias launches: arc, chrome, firefox.
11114
- --port <n> Remote-debugging port (1-65535). Default: 9222 (chrome/arc), 9223 (firefox).
11152
+ --browser <b> Browser the alias launches: arc, chrome, firefox, vivaldi.
11153
+ --port <n> Remote-debugging port (1-65535). Default: 9222 (chrome/arc/vivaldi), 9223 (firefox).
11115
11154
  --name <name> MCP server name to register. Default: ${DEFAULT_NAME}.
11116
11155
  --alias-name <n> Shell alias name. Default: cdp-<browser> (e.g. cdp-firefox).
11117
11156
  --no-alias Register the MCP server only; skip writing the shell alias.
package/dist/index.js CHANGED
@@ -2267,6 +2267,41 @@ function noSuchTarget(e) {
2267
2267
  throw e;
2268
2268
  throw driverError("no-such-target", e instanceof CdpError ? e.message : String(e));
2269
2269
  }
2270
+ var NEW_TARGET_SETTLE_MS = 2000;
2271
+ async function createSettledTarget(conn, url, settleMs = NEW_TARGET_SETTLE_MS) {
2272
+ const changed = new Set;
2273
+ let id = "";
2274
+ let wake;
2275
+ const off = conn.on("Target.targetInfoChanged", (params) => {
2276
+ const t = params.targetInfo?.targetId;
2277
+ if (!t)
2278
+ return;
2279
+ changed.add(t);
2280
+ if (t === id)
2281
+ wake?.();
2282
+ });
2283
+ try {
2284
+ await conn.send("Target.setDiscoverTargets", { discover: true }).catch(() => {
2285
+ return;
2286
+ });
2287
+ const { targetId } = await conn.send("Target.createTarget", { url });
2288
+ if (!targetId)
2289
+ throw driverError("page-error", "Target.createTarget returned no targetId");
2290
+ id = targetId;
2291
+ if (!changed.has(targetId)) {
2292
+ await new Promise((resolve) => {
2293
+ const timer = setTimeout(resolve, settleMs);
2294
+ wake = () => {
2295
+ clearTimeout(timer);
2296
+ resolve();
2297
+ };
2298
+ });
2299
+ }
2300
+ return targetId;
2301
+ } finally {
2302
+ off();
2303
+ }
2304
+ }
2270
2305
 
2271
2306
  class CdpBrowserDriver {
2272
2307
  scheme = "cdp";
@@ -2281,9 +2316,7 @@ class CdpBrowserDriver {
2281
2316
  }
2282
2317
  async newPage(url) {
2283
2318
  return withBrowserConn(async (conn) => {
2284
- const { targetId } = await conn.send("Target.createTarget", { url: url ?? "about:blank" });
2285
- if (!targetId)
2286
- throw driverError("page-error", "Target.createTarget returned no targetId");
2319
+ const targetId = await createSettledTarget(conn, url ?? "about:blank");
2287
2320
  return { id: targetId, url: url ?? "about:blank", title: "" };
2288
2321
  });
2289
2322
  }
package/dist/mcp.js CHANGED
@@ -4882,6 +4882,41 @@ function noSuchTarget(e) {
4882
4882
  throw e;
4883
4883
  throw driverError("no-such-target", e instanceof CdpError ? e.message : String(e));
4884
4884
  }
4885
+ var NEW_TARGET_SETTLE_MS = 2000;
4886
+ async function createSettledTarget(conn, url, settleMs = NEW_TARGET_SETTLE_MS) {
4887
+ const changed = new Set;
4888
+ let id = "";
4889
+ let wake;
4890
+ const off = conn.on("Target.targetInfoChanged", (params) => {
4891
+ const t = params.targetInfo?.targetId;
4892
+ if (!t)
4893
+ return;
4894
+ changed.add(t);
4895
+ if (t === id)
4896
+ wake?.();
4897
+ });
4898
+ try {
4899
+ await conn.send("Target.setDiscoverTargets", { discover: true }).catch(() => {
4900
+ return;
4901
+ });
4902
+ const { targetId } = await conn.send("Target.createTarget", { url });
4903
+ if (!targetId)
4904
+ throw driverError("page-error", "Target.createTarget returned no targetId");
4905
+ id = targetId;
4906
+ if (!changed.has(targetId)) {
4907
+ await new Promise((resolve) => {
4908
+ const timer = setTimeout(resolve, settleMs);
4909
+ wake = () => {
4910
+ clearTimeout(timer);
4911
+ resolve();
4912
+ };
4913
+ });
4914
+ }
4915
+ return targetId;
4916
+ } finally {
4917
+ off();
4918
+ }
4919
+ }
4885
4920
 
4886
4921
  class CdpBrowserDriver {
4887
4922
  scheme = "cdp";
@@ -4896,9 +4931,7 @@ class CdpBrowserDriver {
4896
4931
  }
4897
4932
  async newPage(url) {
4898
4933
  return withBrowserConn(async (conn) => {
4899
- const { targetId } = await conn.send("Target.createTarget", { url: url ?? "about:blank" });
4900
- if (!targetId)
4901
- throw driverError("page-error", "Target.createTarget returned no targetId");
4934
+ const targetId = await createSettledTarget(conn, url ?? "about:blank");
4902
4935
  return { id: targetId, url: url ?? "about:blank", title: "" };
4903
4936
  });
4904
4937
  }
@@ -11369,7 +11402,7 @@ var TOOL_DOCS = {
11369
11402
  };
11370
11403
 
11371
11404
  // src/version.ts
11372
- var VERSION = "3.0.2";
11405
+ var VERSION = "3.0.3";
11373
11406
 
11374
11407
  // src/mcp.ts
11375
11408
  function isDirectRun() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-toolkit",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "A lightweight chrome-devtools-mcp alternative: an MCP server + CLI that drives the tabs you name, one target per call over one direct WebSocket, with a bounded timeout on every call, so a stuck page can't wedge your agent or force a /mcp restart. Chrome over CDP and Firefox over WebDriver BiDi, with tools a backend can't run absent from tools/list rather than throwing mid-task. 48 tools (only 5 core tools listed by default, plus search_tools/call_tool to reach the rest) including built-in network mocking, a cookie group, tab leases with human-activity detection, real HTML5 drag-and-drop, raw mouse/scroll dispatch, downloads, permission grants, and tab-to-video screen recording so several agents can drive one browser without stealing each other's tabs or a human's. No Puppeteer, no all-target fan-out.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/cdp/driver.ts CHANGED
@@ -1691,6 +1691,56 @@ function noSuchTarget(e: unknown): never {
1691
1691
  if (typeof (e as { code?: unknown } | null | undefined)?.code === "string") throw e;
1692
1692
  throw driverError("no-such-target", e instanceof CdpError ? e.message : String(e));
1693
1693
  }
1694
+ /** Upper bound on waiting for a new tab to settle; see createSettledTarget. */
1695
+ export const NEW_TARGET_SETTLE_MS = 2000;
1696
+
1697
+ /**
1698
+ * Create a tab and return only once the browser has finished adopting it.
1699
+ *
1700
+ * Vivaldi wedges a tab that anything attaches to in the first few milliseconds
1701
+ * after Target.createTarget: the tab keeps an empty URL, its renderer never
1702
+ * answers again (Page.enable, Runtime.evaluate and runIfWaitingForDebugger all
1703
+ * time out), and only a manual reload recovers it. new_page{claim:true} hit this
1704
+ * every time, because the activity beacon attaches straight after creation.
1705
+ * Measured on Vivaldi 8.2 (Chromium 152): an attach at ~0ms wedged 5/5 tabs
1706
+ * (https and about:blank alike, page socket or flattened session alike); an
1707
+ * attach after the tab's first Target.targetInfoChanged loaded 5/5.
1708
+ *
1709
+ * So: discover targets, create, and wait for that first targetInfoChanged for
1710
+ * this id, capped at NEW_TARGET_SETTLE_MS. The listener is registered BEFORE
1711
+ * createTarget because the event can beat the command's response. A browser
1712
+ * that never sends the event costs at most the cap, never a failure.
1713
+ */
1714
+ export async function createSettledTarget(
1715
+ conn: Pick<CdpConnection, "send" | "on">,
1716
+ url: string,
1717
+ settleMs = NEW_TARGET_SETTLE_MS,
1718
+ ): Promise<string> {
1719
+ const changed = new Set<string>();
1720
+ let id = "";
1721
+ let wake: (() => void) | undefined;
1722
+ const off = conn.on("Target.targetInfoChanged", (params) => {
1723
+ const t = (params as { targetInfo?: { targetId?: string } }).targetInfo?.targetId;
1724
+ if (!t) return;
1725
+ changed.add(t);
1726
+ if (t === id) wake?.();
1727
+ });
1728
+ try {
1729
+ await conn.send("Target.setDiscoverTargets", { discover: true }).catch(() => undefined);
1730
+ const { targetId } = await conn.send<{ targetId: string }>("Target.createTarget", { url });
1731
+ if (!targetId) throw driverError("page-error", "Target.createTarget returned no targetId");
1732
+ id = targetId;
1733
+ if (!changed.has(targetId)) {
1734
+ await new Promise<void>((resolve) => {
1735
+ const timer = setTimeout(resolve, settleMs);
1736
+ wake = () => { clearTimeout(timer); resolve(); };
1737
+ });
1738
+ }
1739
+ return targetId;
1740
+ } finally {
1741
+ off();
1742
+ }
1743
+ }
1694
1744
  class CdpBrowserDriver implements BrowserDriver {
1695
1745
  readonly scheme = "cdp";
1696
1746
  readonly lifetime: LifetimeModel = "per-call";
@@ -1706,8 +1756,7 @@ class CdpBrowserDriver implements BrowserDriver {
1706
1756
  }
1707
1757
  async newPage(url?: string): Promise<PageInfo> {
1708
1758
  return withBrowserConn(async (conn) => {
1709
- const { targetId } = await conn.send<{ targetId: string }>("Target.createTarget", { url: url ?? "about:blank" });
1710
- if (!targetId) throw driverError("page-error", "Target.createTarget returned no targetId");
1759
+ const targetId = await createSettledTarget(conn, url ?? "about:blank");
1711
1760
  return { id: targetId, url: url ?? "about:blank", title: "" };
1712
1761
  });
1713
1762
  }
@@ -10,7 +10,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
10
10
  import { homedir } from "node:os";
11
11
  import { delimiter, dirname, join } from "node:path";
12
12
 
13
- export type BrowserChoice = "arc" | "chrome" | "firefox";
13
+ export type BrowserChoice = "arc" | "chrome" | "firefox" | "vivaldi";
14
14
 
15
15
  export interface BrowserInfo {
16
16
  choice: BrowserChoice;
@@ -40,7 +40,7 @@ export interface RcAppendResult {
40
40
 
41
41
  /**
42
42
  * Per-choice candidate lists, built fresh on every call so env var overrides (CHROME_BIN /
43
- * ARC_BIN / FIREFOX_BIN) are read live rather than captured once at module load — important for
43
+ * ARC_BIN / VIVALDI_BIN / FIREFOX_BIN) are read live rather than captured once at module load — important for
44
44
  * tests that set/restore these env vars around a single call.
45
45
  *
46
46
  * macOS-first, Linux fallback. Order mirrors the spec: env override, then the macOS .app bundle
@@ -56,6 +56,12 @@ const CANDIDATE_BUILDERS: Record<BrowserChoice, () => Array<string | undefined>>
56
56
  "chromium",
57
57
  ],
58
58
  arc: () => [process.env.ARC_BIN, "/Applications/Arc.app/Contents/MacOS/Arc", "arc"],
59
+ vivaldi: () => [
60
+ process.env.VIVALDI_BIN,
61
+ "/Applications/Vivaldi.app/Contents/MacOS/Vivaldi",
62
+ "vivaldi",
63
+ "/usr/bin/vivaldi",
64
+ ],
59
65
  firefox: () => [
60
66
  process.env.FIREFOX_BIN,
61
67
  "/Applications/Firefox.app/Contents/MacOS/firefox",
@@ -113,7 +119,7 @@ export function defaultAliasName(choice: BrowserChoice): string {
113
119
  }
114
120
 
115
121
  /**
116
- * The launch flags only (no binary). Chrome and Arc are both Chromium-based and Arc accepts
122
+ * The launch flags only (no binary). Chrome, Arc and Vivaldi are all Chromium-based and accept
117
123
  * Chrome's flags, so they share a shape: --remote-debugging-port plus a DEDICATED
118
124
  * --user-data-dir. The dedicated profile is required, not cosmetic — Chrome silently ignores
119
125
  * --remote-debugging-port when it hands off to an already-running instance on the default
@@ -63,7 +63,7 @@ export interface InstallerIO {
63
63
 
64
64
  const DEFAULT_NAME = "cdp-toolkit";
65
65
  const HARNESSES: readonly HarnessChoice[] = ["claude", "codex", "opencode"];
66
- const BROWSERS: readonly BrowserChoice[] = ["arc", "chrome", "firefox"];
66
+ const BROWSERS: readonly BrowserChoice[] = ["arc", "chrome", "firefox", "vivaldi"];
67
67
 
68
68
  /* --------------------------------- pure: arg parsing --------------------------------- */
69
69
 
@@ -79,9 +79,9 @@ function parseHarness(v: string | undefined): HarnessChoice {
79
79
  }
80
80
 
81
81
  function parseBrowserChoice(v: string | undefined): BrowserChoice {
82
- if (v === undefined) throw new Error("--browser requires a value: arc, chrome, or firefox");
82
+ if (v === undefined) throw new Error("--browser requires a value: arc, chrome, firefox, or vivaldi");
83
83
  if ((BROWSERS as readonly string[]).includes(v)) return v as BrowserChoice;
84
- throw new Error(`--browser must be one of arc|chrome|firefox (got '${v}')`);
84
+ throw new Error(`--browser must be one of arc|chrome|firefox|vivaldi (got '${v}')`);
85
85
  }
86
86
 
87
87
  function parsePort(v: string | undefined): number {
@@ -165,8 +165,8 @@ export function parseInstallArgs(argv: string[]): InstallArgs {
165
165
  * PURE. Build the MCP stdio server config for a given browser + debug port.
166
166
  *
167
167
  * The server always runs `npx -y cdp-toolkit`. Only the env differs by browser, and even then
168
- * chrome and arc are IDENTICAL: both are driven over CDP, and the driver just talks CDP to
169
- * whatever is listening on CDP_BASE — the arc-vs-chrome distinction lives entirely in the shell
168
+ * chrome, arc and vivaldi are IDENTICAL: both are driven over CDP, and the driver just talks CDP to
169
+ * whatever is listening on CDP_BASE — the arc-vs-chrome-vs-vivaldi distinction lives entirely in the shell
170
170
  * ALIAS (which .app it launches), not in the server env. Firefox is the odd one out: it is driven
171
171
  * over WebDriver BiDi, selected by CDP_BROWSER=firefox with the port in CDP_FIREFOX_ENDPOINT.
172
172
  */
@@ -185,14 +185,14 @@ export function installHelpText(): string {
185
185
  browser-launch alias (with the remote-debugging port open) to your shell rc.
186
186
 
187
187
  Usage:
188
- cdp install [--harness <claude|codex|opencode>] [--browser <arc|chrome|firefox>]
188
+ cdp install [--harness <claude|codex|opencode>] [--browser <arc|chrome|firefox|vivaldi>]
189
189
  [--port <n>] [--name <server-name>] [--alias-name <name>]
190
190
  [--no-alias] [--yes|-y] [--help|-h]
191
191
 
192
192
  Options:
193
193
  --harness <h> Coding harness to register the MCP server with: claude, codex, opencode.
194
- --browser <b> Browser the alias launches: arc, chrome, firefox.
195
- --port <n> Remote-debugging port (1-65535). Default: 9222 (chrome/arc), 9223 (firefox).
194
+ --browser <b> Browser the alias launches: arc, chrome, firefox, vivaldi.
195
+ --port <n> Remote-debugging port (1-65535). Default: 9222 (chrome/arc/vivaldi), 9223 (firefox).
196
196
  --name <name> MCP server name to register. Default: ${DEFAULT_NAME}.
197
197
  --alias-name <n> Shell alias name. Default: cdp-<browser> (e.g. cdp-firefox).
198
198
  --no-alias Register the MCP server only; skip writing the shell alias.
package/src/version.ts CHANGED
@@ -6,4 +6,4 @@
6
6
  * ready line; keeping it in its own module means a release bump touches one
7
7
  * line here plus package.json, not a const buried in the server.
8
8
  */
9
- export const VERSION = "3.0.2";
9
+ export const VERSION = "3.0.3";