@jackwener/opencli 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +20 -1
  2. package/README.zh-CN.md +20 -1
  3. package/dist/browser/daemon-client.d.ts +1 -1
  4. package/dist/browser/index.d.ts +1 -2
  5. package/dist/browser/index.js +1 -5
  6. package/dist/browser/mcp.d.ts +5 -8
  7. package/dist/browser/mcp.js +9 -10
  8. package/dist/browser/page.d.ts +8 -1
  9. package/dist/browser/page.js +23 -17
  10. package/dist/browser.test.js +6 -6
  11. package/dist/cli-manifest.json +394 -14
  12. package/dist/clis/apple-podcasts/episodes.d.ts +1 -0
  13. package/dist/clis/apple-podcasts/episodes.js +28 -0
  14. package/dist/clis/apple-podcasts/search.d.ts +1 -0
  15. package/dist/clis/apple-podcasts/search.js +29 -0
  16. package/dist/clis/apple-podcasts/top.d.ts +1 -0
  17. package/dist/clis/apple-podcasts/top.js +34 -0
  18. package/dist/clis/apple-podcasts/utils.d.ts +11 -0
  19. package/dist/clis/apple-podcasts/utils.js +30 -0
  20. package/dist/clis/apple-podcasts/utils.test.d.ts +1 -0
  21. package/dist/clis/apple-podcasts/utils.test.js +57 -0
  22. package/dist/clis/chatwise/history.js +18 -1
  23. package/dist/clis/discord-app/channels.js +33 -21
  24. package/dist/clis/twitter/accept.d.ts +1 -0
  25. package/dist/clis/twitter/accept.js +202 -0
  26. package/dist/clis/twitter/followers.js +30 -22
  27. package/dist/clis/twitter/following.js +19 -14
  28. package/dist/clis/twitter/notifications.js +29 -22
  29. package/dist/clis/twitter/reply-dm.d.ts +1 -0
  30. package/dist/clis/twitter/reply-dm.js +181 -0
  31. package/dist/clis/twitter/search.js +50 -12
  32. package/dist/clis/weread/book.d.ts +1 -0
  33. package/dist/clis/weread/book.js +26 -0
  34. package/dist/clis/weread/highlights.d.ts +1 -0
  35. package/dist/clis/weread/highlights.js +23 -0
  36. package/dist/clis/weread/notebooks.d.ts +1 -0
  37. package/dist/clis/weread/notebooks.js +21 -0
  38. package/dist/clis/weread/notes.d.ts +1 -0
  39. package/dist/clis/weread/notes.js +29 -0
  40. package/dist/clis/weread/ranking.d.ts +1 -0
  41. package/dist/clis/weread/ranking.js +28 -0
  42. package/dist/clis/weread/search.d.ts +1 -0
  43. package/dist/clis/weread/search.js +25 -0
  44. package/dist/clis/weread/shelf.d.ts +1 -0
  45. package/dist/clis/weread/shelf.js +24 -0
  46. package/dist/clis/weread/utils.d.ts +20 -0
  47. package/dist/clis/weread/utils.js +72 -0
  48. package/dist/clis/weread/utils.test.d.ts +1 -0
  49. package/dist/clis/weread/utils.test.js +85 -0
  50. package/dist/daemon.js +2 -2
  51. package/dist/doctor.d.ts +0 -21
  52. package/dist/doctor.js +2 -24
  53. package/dist/main.js +6 -16
  54. package/dist/runtime.d.ts +1 -4
  55. package/dist/runtime.js +1 -4
  56. package/dist/setup.js +2 -2
  57. package/extension/dist/background.js +484 -0
  58. package/extension/manifest.json +1 -1
  59. package/extension/package.json +1 -1
  60. package/extension/src/background.ts +99 -22
  61. package/extension/src/protocol.ts +1 -1
  62. package/package.json +1 -1
  63. package/src/browser/daemon-client.ts +1 -1
  64. package/src/browser/index.ts +1 -6
  65. package/src/browser/mcp.ts +14 -15
  66. package/src/browser/page.ts +23 -17
  67. package/src/browser.test.ts +6 -6
  68. package/src/clis/apple-podcasts/episodes.ts +28 -0
  69. package/src/clis/apple-podcasts/search.ts +29 -0
  70. package/src/clis/apple-podcasts/top.ts +34 -0
  71. package/src/clis/apple-podcasts/utils.test.ts +72 -0
  72. package/src/clis/apple-podcasts/utils.ts +37 -0
  73. package/src/clis/chatwise/history.ts +15 -1
  74. package/src/clis/discord-app/channels.ts +33 -21
  75. package/src/clis/twitter/accept.ts +213 -0
  76. package/src/clis/twitter/followers.ts +36 -29
  77. package/src/clis/twitter/following.ts +25 -20
  78. package/src/clis/twitter/notifications.ts +34 -27
  79. package/src/clis/twitter/reply-dm.ts +193 -0
  80. package/src/clis/twitter/search.ts +53 -13
  81. package/src/clis/weread/book.ts +28 -0
  82. package/src/clis/weread/highlights.ts +25 -0
  83. package/src/clis/weread/notebooks.ts +23 -0
  84. package/src/clis/weread/notes.ts +31 -0
  85. package/src/clis/weread/ranking.ts +29 -0
  86. package/src/clis/weread/search.ts +26 -0
  87. package/src/clis/weread/shelf.ts +26 -0
  88. package/src/clis/weread/utils.test.ts +104 -0
  89. package/src/clis/weread/utils.ts +74 -0
  90. package/src/daemon.ts +2 -2
  91. package/src/doctor.ts +2 -19
  92. package/src/main.ts +5 -11
  93. package/src/runtime.ts +2 -6
  94. package/src/setup.ts +2 -2
  95. package/tests/e2e/public-commands.test.ts +68 -1
  96. package/dist/clis/grok/debug.d.ts +0 -1
  97. package/dist/clis/grok/debug.js +0 -45
  98. package/src/clis/grok/debug.ts +0 -49
package/README.md CHANGED
@@ -22,6 +22,7 @@ Turn ANY Electron application into a CLI tool! Recombine, script, and extend app
22
22
  - [Prerequisites](#prerequisites)
23
23
  - [Quick Start](#quick-start)
24
24
  - [Built-in Commands](#built-in-commands)
25
+ - [Desktop App Adapters](#desktop-app-adapters)
25
26
  - [Download Support](#download-support)
26
27
  - [Output Formats](#output-formats)
27
28
  - [For AI Agents (Developer Guide)](#for-ai-agents-developer-guide)
@@ -106,7 +107,7 @@ Run `opencli list` for the live registry.
106
107
 
107
108
  | Site | Commands | Mode |
108
109
  |------|----------|------|
109
- | **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` | 🔐 Browser |
110
+ | **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` `accept` `reply-dm` | 🔐 Browser |
110
111
  | **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 🔐 Browser |
111
112
  | **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` | 🖥️ Desktop |
112
113
  | **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | 🔐 Browser |
@@ -119,6 +120,7 @@ Run `opencli list` for the live registry.
119
120
  | **antigravity** | `status` `send` `read` `new` `evaluate` | 🖥️ Desktop |
120
121
  | **chatgpt** | `status` `new` `send` `read` `ask` | 🖥️ Desktop |
121
122
  | **xiaohongshu** | `search` `notifications` `feed` `me` `user` `download` | 🔐 Browser |
123
+ | **apple-podcasts** | `search` `episodes` `top` | 🌐 Public |
122
124
  | **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | 🌐 Public |
123
125
  | **zhihu** | `hot` `search` `question` `download` | 🔐 Browser |
124
126
  | **youtube** | `search` `video` `transcript` | 🔐 Browser |
@@ -134,6 +136,23 @@ Run `opencli list` for the live registry.
134
136
  | **weibo** | `hot` | 🔐 Browser |
135
137
  | **yahoo-finance** | `quote` | 🔐 Browser |
136
138
 
139
+ ### Desktop App Adapters
140
+
141
+ Each desktop adapter has its own detailed documentation with commands reference, setup guide, and examples:
142
+
143
+ | App | Description | Doc |
144
+ |-----|-------------|-----|
145
+ | **Cursor** | Control Cursor IDE — Composer, chat, code extraction | [README](./src/clis/cursor/README.md) |
146
+ | **Codex** | Drive OpenAI Codex CLI agent headlessly | [README](./src/clis/codex/README.md) |
147
+ | **Antigravity** | Control Antigravity Ultra from terminal | [README](./src/clis/antigravity/README.md) |
148
+ | **ChatGPT** | Automate ChatGPT macOS desktop app | [README](./src/clis/chatgpt/README.md) |
149
+ | **ChatWise** | Multi-LLM client (GPT-4, Claude, Gemini) | [README](./src/clis/chatwise/README.md) |
150
+ | **Notion** | Search, read, write Notion pages | [README](./src/clis/notion/README.md) |
151
+ | **Discord** | Discord Desktop — messages, channels, servers | [README](./src/clis/discord-app/README.md) |
152
+ | **Feishu** | 飞书/Lark Desktop via AppleScript | [README](./src/clis/feishu/README.md) |
153
+ | **WeChat** | 微信 Desktop via AppleScript + Accessibility | [README](./src/clis/wechat/README.md) |
154
+ | **NeteaseMusic** | 网易云音乐 Desktop via CEF/CDP | [README](./src/clis/neteasemusic/README.md) |
155
+
137
156
  ## Download Support
138
157
 
139
158
  OpenCLI supports downloading images, videos, and articles from supported platforms.
package/README.zh-CN.md CHANGED
@@ -24,6 +24,7 @@ CLI all electron!现在支持把所有 electron 应用 CLI 化,从而组合
24
24
  - [前置要求](#前置要求)
25
25
  - [快速开始](#快速开始)
26
26
  - [内置命令](#内置命令)
27
+ - [桌面应用适配器](#桌面应用适配器)
27
28
  - [下载支持](#下载支持)
28
29
  - [输出格式](#输出格式)
29
30
  - [致 AI Agent(开发者指南)](#致-ai-agent开发者指南)
@@ -107,7 +108,7 @@ npm install -g @jackwener/opencli@latest
107
108
 
108
109
  | 站点 | 命令 | 模式 |
109
110
  |------|------|------|
110
- | **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` | 🔐 浏览器 |
111
+ | **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` `accept` `reply-dm` | 🔐 浏览器 |
111
112
  | **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 🔐 浏览器 |
112
113
  | **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` | 🖥️ 桌面端 |
113
114
  | **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | 🔐 浏览器 |
@@ -120,6 +121,7 @@ npm install -g @jackwener/opencli@latest
120
121
  | **antigravity** | `status` `send` `read` `new` `evaluate` | 🖥️ 桌面端 |
121
122
  | **chatgpt** | `status` `new` `send` `read` `ask` | 🖥️ 桌面端 |
122
123
  | **xiaohongshu** | `search` `notifications` `feed` `me` `user` `download` | 🔐 浏览器 |
124
+ | **apple-podcasts** | `search` `episodes` `top` | 🌐 公开 |
123
125
  | **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | 🌐 公开 |
124
126
  | **zhihu** | `hot` `search` `question` `download` | 🔐 浏览器 |
125
127
  | **youtube** | `search` `video` `transcript` | 🔐 浏览器 |
@@ -135,6 +137,23 @@ npm install -g @jackwener/opencli@latest
135
137
  | **weibo** | `hot` | 🔐 浏览器 |
136
138
  | **yahoo-finance** | `quote` | 🔐 浏览器 |
137
139
 
140
+ ### 桌面应用适配器
141
+
142
+ 每个桌面适配器都有自己详细的文档说明,包括命令参考、启动配置与使用示例:
143
+
144
+ | 应用 | 描述 | 文档 |
145
+ |-----|-------------|-----|
146
+ | **Cursor** | 控制 Cursor IDE — Composer、对话、代码提取等 | [README](./src/clis/cursor/README.md) |
147
+ | **Codex** | 在后台(无头)驱动 OpenAI Codex CLI Agent | [README](./src/clis/codex/README.md) |
148
+ | **Antigravity** | 在终端直接控制 Antigravity Ultra | [README](./src/clis/antigravity/README.md) |
149
+ | **ChatGPT** | 自动化操作 ChatGPT macOS 桌面客户端 | [README](./src/clis/chatgpt/README.md) |
150
+ | **ChatWise** | 多 LLM 客户端(GPT-4、Claude、Gemini) | [README](./src/clis/chatwise/README.md) |
151
+ | **Notion** | 搜索、读取、写入 Notion 页面 | [README](./src/clis/notion/README.md) |
152
+ | **Discord** | Discord 桌面版 — 消息、频道、服务器 | [README](./src/clis/discord-app/README.md) |
153
+ | **Feishu** | 飞书/Lark 桌面版 (AppleScript 驱动) | [README](./src/clis/feishu/README.md) |
154
+ | **WeChat** | 微信 Mac 桌面端 (AppleScript + 无障碍接口) | [README](./src/clis/wechat/README.md) |
155
+ | **NeteaseMusic** | 网易云音乐 (CEF/CDP 驱动) | [README](./src/clis/neteasemusic/README.md) |
156
+
138
157
  ## 下载支持
139
158
 
140
159
  OpenCLI 支持从各平台下载图片、视频和文章。
@@ -5,7 +5,7 @@
5
5
  */
6
6
  export interface DaemonCommand {
7
7
  id: string;
8
- action: 'exec' | 'navigate' | 'tabs' | 'cookies' | 'screenshot';
8
+ action: 'exec' | 'navigate' | 'tabs' | 'cookies' | 'screenshot' | 'close-window';
9
9
  tabId?: number;
10
10
  code?: string;
11
11
  url?: string;
@@ -5,9 +5,8 @@
5
5
  * External code should import from './browser/index.js' (or './browser.js' via Node resolution).
6
6
  */
7
7
  export { Page } from './page.js';
8
- export { PlaywrightMCP } from './mcp.js';
8
+ export { BrowserBridge, BrowserBridge as PlaywrightMCP } from './mcp.js';
9
9
  export { isDaemonRunning } from './daemon-client.js';
10
- export declare function getTokenFingerprint(_token: string | undefined): string | null;
11
10
  import { extractTabEntries, diffTabIndexes, appendLimited } from './tabs.js';
12
11
  import { withTimeoutMs } from '../runtime.js';
13
12
  export declare const __test__: {
@@ -5,12 +5,8 @@
5
5
  * External code should import from './browser/index.js' (or './browser.js' via Node resolution).
6
6
  */
7
7
  export { Page } from './page.js';
8
- export { PlaywrightMCP } from './mcp.js';
8
+ export { BrowserBridge, BrowserBridge as PlaywrightMCP } from './mcp.js';
9
9
  export { isDaemonRunning } from './daemon-client.js';
10
- // Backward compatibility: getTokenFingerprint is no longer needed but kept as no-op export
11
- export function getTokenFingerprint(_token) {
12
- return null;
13
- }
14
10
  import { extractTabEntries, diffTabIndexes, appendLimited } from './tabs.js';
15
11
  import { withTimeoutMs } from '../runtime.js';
16
12
  export const __test__ = {
@@ -1,24 +1,21 @@
1
1
  /**
2
2
  * Browser session manager — auto-spawns daemon and provides IPage.
3
- *
4
- * Replaces the old PlaywrightMCP class. Still exports as PlaywrightMCP
5
- * for backward compatibility with main.ts and other consumers.
6
3
  */
7
4
  import type { IPage } from '../types.js';
8
- export type PlaywrightMCPState = 'idle' | 'connecting' | 'connected' | 'closing' | 'closed';
5
+ export type BrowserBridgeState = 'idle' | 'connecting' | 'connected' | 'closing' | 'closed';
9
6
  /**
10
7
  * Browser factory: manages daemon lifecycle and provides IPage instances.
11
- *
12
- * Kept as `PlaywrightMCP` class name for backward compatibility.
13
8
  */
14
- export declare class PlaywrightMCP {
9
+ export declare class BrowserBridge {
15
10
  private _state;
16
11
  private _page;
17
12
  private _daemonProc;
18
- get state(): PlaywrightMCPState;
13
+ get state(): BrowserBridgeState;
19
14
  connect(opts?: {
20
15
  timeout?: number;
21
16
  }): Promise<IPage>;
22
17
  close(): Promise<void>;
23
18
  private _ensureDaemon;
24
19
  }
20
+ /** @deprecated Use BrowserBridge instead */
21
+ export declare const PlaywrightMCP: typeof BrowserBridge;
@@ -1,8 +1,5 @@
1
1
  /**
2
2
  * Browser session manager — auto-spawns daemon and provides IPage.
3
- *
4
- * Replaces the old PlaywrightMCP class. Still exports as PlaywrightMCP
5
- * for backward compatibility with main.ts and other consumers.
6
3
  */
7
4
  import { spawn } from 'node:child_process';
8
5
  import { fileURLToPath } from 'node:url';
@@ -13,10 +10,8 @@ import { isDaemonRunning, isExtensionConnected } from './daemon-client.js';
13
10
  const DAEMON_SPAWN_TIMEOUT = 10000; // 10s to wait for daemon + extension
14
11
  /**
15
12
  * Browser factory: manages daemon lifecycle and provides IPage instances.
16
- *
17
- * Kept as `PlaywrightMCP` class name for backward compatibility.
18
13
  */
19
- export class PlaywrightMCP {
14
+ export class BrowserBridge {
20
15
  _state = 'idle';
21
16
  _page = null;
22
17
  _daemonProc = null;
@@ -68,10 +63,12 @@ export class PlaywrightMCP {
68
63
  if (process.env.OPENCLI_VERBOSE) {
69
64
  console.error(`[opencli] Starting daemon (${isTs ? 'ts' : 'js'})...`);
70
65
  }
71
- // Use the current runtime to spawn daemon avoids slow npx resolution.
72
- // If already running under tsx (dev), process.execPath is tsx's node.
73
- // If running compiled (node dist/), process.execPath is node.
74
- this._daemonProc = spawn(process.execPath, [daemonPath], {
66
+ // For compiled .js, use the current node binary directly (fast).
67
+ // For .ts dev mode, node can't run .ts files — use tsx via --import.
68
+ const spawnArgs = isTs
69
+ ? [process.execPath, '--import', 'tsx/esm', daemonPath]
70
+ : [process.execPath, daemonPath];
71
+ this._daemonProc = spawn(spawnArgs[0], spawnArgs.slice(1), {
75
72
  detached: true,
76
73
  stdio: 'ignore',
77
74
  env: { ...process.env },
@@ -94,3 +91,5 @@ export class PlaywrightMCP {
94
91
  'Make sure port 19825 is available.');
95
92
  }
96
93
  }
94
+ /** @deprecated Use BrowserBridge instead */
95
+ export const PlaywrightMCP = BrowserBridge;
@@ -19,6 +19,8 @@ export declare class Page implements IPage {
19
19
  /** Helper: spread tabId into command params if we have one */
20
20
  private _tabOpt;
21
21
  goto(url: string): Promise<void>;
22
+ /** Close the automation window in the extension */
23
+ closeWindow(): Promise<void>;
22
24
  evaluate(js: string): Promise<any>;
23
25
  snapshot(opts?: {
24
26
  interactive?: boolean;
@@ -39,7 +41,12 @@ export declare class Page implements IPage {
39
41
  newTab(): Promise<void>;
40
42
  selectTab(index: number): Promise<void>;
41
43
  networkRequests(includeStatic?: boolean): Promise<any>;
42
- consoleMessages(level?: string): Promise<any>;
44
+ /**
45
+ * Console messages are not available in lightweight daemon mode.
46
+ * Would require CDP Runtime.consoleAPICalled event listener.
47
+ * @returns Always returns empty array.
48
+ */
49
+ consoleMessages(_level?: string): Promise<any>;
43
50
  /**
44
51
  * Capture a screenshot via CDP Page.captureScreenshot.
45
52
  * @param options.format - 'png' (default) or 'jpeg'
@@ -31,6 +31,15 @@ export class Page {
31
31
  this._tabId = result.tabId;
32
32
  }
33
33
  }
34
+ /** Close the automation window in the extension */
35
+ async closeWindow() {
36
+ try {
37
+ await sendCommand('close-window', {});
38
+ }
39
+ catch {
40
+ // Window may already be closed or daemon may be down
41
+ }
42
+ }
34
43
  async evaluate(js) {
35
44
  const code = wrapForEval(js);
36
45
  return sendCommand('exec', { code, ...this._tabOpt() });
@@ -168,12 +177,12 @@ export class Page {
168
177
  `;
169
178
  return sendCommand('exec', { code, ...this._tabOpt() });
170
179
  }
171
- async consoleMessages(level = 'info') {
172
- // Console messages can't be retrospectively read via CDP Runtime.evaluate.
173
- // Would need Runtime.consoleAPICalled event listener, which is not yet implemented.
174
- if (process.env.OPENCLI_VERBOSE) {
175
- console.error('[page] consoleMessages() not supported in lightweight mode — returning empty');
176
- }
180
+ /**
181
+ * Console messages are not available in lightweight daemon mode.
182
+ * Would require CDP Runtime.consoleAPICalled event listener.
183
+ * @returns Always returns empty array.
184
+ */
185
+ async consoleMessages(_level = 'info') {
177
186
  return [];
178
187
  }
179
188
  /**
@@ -234,20 +243,17 @@ export class Page {
234
243
  }
235
244
  async installInterceptor(pattern) {
236
245
  const { generateInterceptorJs } = await import('../interceptor.js');
237
- await sendCommand('exec', {
238
- code: generateInterceptorJs(JSON.stringify(pattern), {
239
- arrayName: '__opencli_xhr',
240
- patchGuard: '__opencli_interceptor_patched',
241
- }),
242
- ...this._tabOpt(),
243
- });
246
+ // Must use evaluate() so wrapForEval() converts the arrow function into an IIFE;
247
+ // sendCommand('exec') sends the code as-is, and CDP never executes a bare arrow.
248
+ await this.evaluate(generateInterceptorJs(JSON.stringify(pattern), {
249
+ arrayName: '__opencli_xhr',
250
+ patchGuard: '__opencli_interceptor_patched',
251
+ }));
244
252
  }
245
253
  async getInterceptedRequests() {
246
254
  const { generateReadInterceptedJs } = await import('../interceptor.js');
247
- const result = await sendCommand('exec', {
248
- code: generateReadInterceptedJs('__opencli_xhr'),
249
- ...this._tabOpt(),
250
- });
255
+ // Same as installInterceptor: must go through evaluate() for IIFE wrapping
256
+ const result = await this.evaluate(generateReadInterceptedJs('__opencli_xhr'));
251
257
  return result || [];
252
258
  }
253
259
  }
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { PlaywrightMCP, __test__ } from './browser/index.js';
2
+ import { BrowserBridge, __test__ } from './browser/index.js';
3
3
  describe('browser helpers', () => {
4
4
  it('extracts tab entries from string snapshots', () => {
5
5
  const entries = __test__.extractTabEntries('Tab 0 https://example.com\nTab 1 Chrome Extension');
@@ -31,25 +31,25 @@ describe('browser helpers', () => {
31
31
  await expect(__test__.withTimeoutMs(new Promise(() => { }), 10, 'timeout')).rejects.toThrow('timeout');
32
32
  });
33
33
  });
34
- describe('PlaywrightMCP state', () => {
34
+ describe('BrowserBridge state', () => {
35
35
  it('transitions to closed after close()', async () => {
36
- const mcp = new PlaywrightMCP();
36
+ const mcp = new BrowserBridge();
37
37
  expect(mcp.state).toBe('idle');
38
38
  await mcp.close();
39
39
  expect(mcp.state).toBe('closed');
40
40
  });
41
41
  it('rejects connect() after the session has been closed', async () => {
42
- const mcp = new PlaywrightMCP();
42
+ const mcp = new BrowserBridge();
43
43
  await mcp.close();
44
44
  await expect(mcp.connect()).rejects.toThrow('Session is closed');
45
45
  });
46
46
  it('rejects connect() while already connecting', async () => {
47
- const mcp = new PlaywrightMCP();
47
+ const mcp = new BrowserBridge();
48
48
  mcp._state = 'connecting';
49
49
  await expect(mcp.connect()).rejects.toThrow('Already connecting');
50
50
  });
51
51
  it('rejects connect() while closing', async () => {
52
- const mcp = new PlaywrightMCP();
52
+ const mcp = new BrowserBridge();
53
53
  mcp._state = 'closing';
54
54
  await expect(mcp.connect()).rejects.toThrow('Session is closing');
55
55
  });