@jackwener/opencli 1.2.3 → 1.2.5

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.
@@ -45,26 +45,12 @@ jobs:
45
45
  cd extension-package
46
46
  zip -r ../opencli-extension.zip .
47
47
 
48
- - name: Create Extension CRX
49
- run: |
50
- npm install -g crx3
51
- if [ -n "${{ secrets.CRX_PRIVATE_KEY }}" ]; then
52
- echo "Found CRX_PRIVATE_KEY, signing extension..."
53
- echo "${{ secrets.CRX_PRIVATE_KEY }}" > crx-key.pem
54
- crx3 pack extension-package -o opencli-extension.crx -p crx-key.pem
55
- rm crx-key.pem
56
- else
57
- echo "No CRX_PRIVATE_KEY configured. Generating CRX with a temporary random key..."
58
- crx3 pack extension-package -o opencli-extension.crx
59
- fi
60
-
61
48
  - name: Upload Artifacts (Action Run)
62
49
  uses: actions/upload-artifact@v4
63
50
  with:
64
51
  name: opencli-extension-build
65
52
  path: |
66
53
  opencli-extension.zip
67
- opencli-extension.crx
68
54
  retention-days: 7
69
55
 
70
56
  - name: Attach to GitHub Release
@@ -73,7 +59,6 @@ jobs:
73
59
  with:
74
60
  files: |
75
61
  opencli-extension.zip
76
- opencli-extension.crx
77
62
  draft: false
78
63
  prerelease: false
79
64
  env:
package/README.md CHANGED
@@ -61,11 +61,15 @@ OpenCLI connects to your browser through a lightweight **Browser Bridge** Chrome
61
61
  You can install the extension via either method:
62
62
 
63
63
  **Method 1: Download Pre-built Release (Recommended)**
64
- 1. Go to the GitHub [Releases page](https://github.com/jackwener/opencli/releases) and download the latest `opencli-extension.zip` or `opencli-extension.crx`.
65
- 2. Open `chrome://extensions` and enable **Developer mode** (top-right toggle).
66
- 3. Drag and drop the `.crx` file or the unzipped folder into the extensions page.
64
+ 1. Go to the GitHub [Releases page](https://github.com/jackwener/opencli/releases) and download the latest `opencli-extension.zip`.
65
+ 2. Unzip the file and open `chrome://extensions`, enable **Developer mode** (top-right toggle).
66
+ 3. Click **Load unpacked** and select the unzipped folder.
67
67
 
68
- **Method 2: Load Unpacked Source (For Developers)**
68
+ **Method 2: Load from npm Package**
69
+ 1. After installing opencli via npm, open `chrome://extensions` and enable **Developer mode**.
70
+ 2. Click **Load unpacked** and select `node_modules/@jackwener/opencli/extension` directory.
71
+
72
+ **Method 3: Load Source (For Developers)**
69
73
  1. Open `chrome://extensions` and enable **Developer mode**.
70
74
  2. Click **Load unpacked** and select the `extension/` directory from this repository.
71
75
 
package/README.zh-CN.md CHANGED
@@ -62,11 +62,15 @@ OpenCLI 通过轻量化的 **Browser Bridge** Chrome 扩展 + 微型 daemon 与
62
62
  你可以选择以下任一方式安装扩展:
63
63
 
64
64
  **方式一:下载构建好的安装包(推荐)**
65
- 1. 到 GitHub [Releases 页面](https://github.com/jackwener/opencli/releases) 下载最新的 `opencli-extension.zip` 或 `opencli-extension.crx`。
66
- 2. 打开 Chrome 的 `chrome://extensions`,启用右上角的 **开发者模式**。
67
- 3. `.crx` 拖入浏览器窗口,或将解压后的文件夹拖入即可完成安装。
65
+ 1. 到 GitHub [Releases 页面](https://github.com/jackwener/opencli/releases) 下载最新的 `opencli-extension.zip`。
66
+ 2. 解压后打开 Chrome 的 `chrome://extensions`,启用右上角的 **开发者模式**。
67
+ 3. 点击 **加载已解压的扩展程序**,选择解压后的文件夹。
68
68
 
69
- **方式二:加载源码(针对开发者)**
69
+ **方式二:从 npm 包加载**
70
+ 1. 通过 npm 安装 opencli 后,打开 `chrome://extensions`,启用 **开发者模式**。
71
+ 2. 点击 **加载已解压的扩展程序**,选择 `node_modules/@jackwener/opencli/extension` 目录。
72
+
73
+ **方式三:加载源码(针对开发者)**
70
74
  1. 同样在 `chrome://extensions` 开启 **开发者模式**。
71
75
  2. 点击 **加载已解压的扩展程序**,选择本仓库代码树中的 `extension/` 文件夹。
72
76
 
@@ -8,9 +8,9 @@ OpenCLI connects to your browser through a lightweight **Browser Bridge** Chrome
8
8
 
9
9
  ### Method 1: Download Pre-built Release (Recommended)
10
10
 
11
- 1. Go to the GitHub [Releases page](https://github.com/jackwener/opencli/releases) and download the latest `opencli-extension.zip` or `opencli-extension.crx`.
12
- 2. Open `chrome://extensions` and enable **Developer mode** (top-right toggle).
13
- 3. Drag and drop the `.crx` file or the unzipped folder into the extensions page.
11
+ 1. Go to the GitHub [Releases page](https://github.com/jackwener/opencli/releases) and download the latest `opencli-extension.zip`.
12
+ 2. Unzip the file and open `chrome://extensions`, enable **Developer mode** (top-right toggle).
13
+ 3. Click **Load unpacked** and select the unzipped folder.
14
14
 
15
15
  ### Method 2: Load Unpacked Source (For Developers)
16
16
 
@@ -8,9 +8,9 @@ OpenCLI 通过轻量级 **Browser Bridge** Chrome 扩展 + 微守护进程连接
8
8
 
9
9
  ### 方法 1:下载预构建版本(推荐)
10
10
 
11
- 1. 前往 GitHub [Releases 页面](https://github.com/jackwener/opencli/releases) 下载最新的 `opencli-extension.zip` 或 `opencli-extension.crx`。
12
- 2. 打开 `chrome://extensions`,启用**开发者模式**。
13
- 3. 拖放 `.crx` 文件或解压后的文件夹到扩展页面。
11
+ 1. 前往 GitHub [Releases 页面](https://github.com/jackwener/opencli/releases) 下载最新的 `opencli-extension.zip`。
12
+ 2. 解压后打开 `chrome://extensions`,启用**开发者模式**。
13
+ 3. 点击**加载已解压的扩展程序**,选择解压后的文件夹。
14
14
 
15
15
  ### 方法 2:加载源码(开发者)
16
16
 
@@ -6,7 +6,7 @@ const WS_RECONNECT_MAX_DELAY = 6e4;
6
6
 
7
7
  const attached = /* @__PURE__ */ new Set();
8
8
  function isDebuggableUrl$1(url) {
9
- if (!url) return false;
9
+ if (!url) return true;
10
10
  return !url.startsWith("chrome://") && !url.startsWith("chrome-extension://");
11
11
  }
12
12
  async function ensureAttached(tabId) {
@@ -228,7 +228,7 @@ async function getAutomationWindow(workspace) {
228
228
  }
229
229
  }
230
230
  const win = await chrome.windows.create({
231
- url: "about:blank",
231
+ url: "data:text/html,<html></html>",
232
232
  focused: false,
233
233
  width: 1280,
234
234
  height: 900,
@@ -242,6 +242,7 @@ async function getAutomationWindow(workspace) {
242
242
  automationSessions.set(workspace, session);
243
243
  console.log(`[opencli] Created automation window ${session.windowId} (${workspace})`);
244
244
  resetWindowIdleTimer(workspace);
245
+ await new Promise((resolve) => setTimeout(resolve, 200));
245
246
  return session.windowId;
246
247
  }
247
248
  chrome.windows.onRemoved.addListener((windowId) => {
@@ -302,13 +303,14 @@ async function handleCommand(cmd) {
302
303
  }
303
304
  }
304
305
  function isDebuggableUrl(url) {
305
- if (!url) return false;
306
+ if (!url) return true;
306
307
  return !url.startsWith("chrome://") && !url.startsWith("chrome-extension://");
307
308
  }
308
309
  async function resolveTabId(tabId, workspace) {
309
310
  if (tabId !== void 0) {
310
311
  try {
311
312
  const tab = await chrome.tabs.get(tabId);
313
+ console.log(`[opencli] resolveTabId: explicit tabId=${tabId}, url=${tab.url}`);
312
314
  if (isDebuggableUrl(tab.url)) return tabId;
313
315
  console.warn(`[opencli] Tab ${tabId} URL is not debuggable (${tab.url}), re-resolving`);
314
316
  } catch {
@@ -318,10 +320,14 @@ async function resolveTabId(tabId, workspace) {
318
320
  const windowId = await getAutomationWindow(workspace);
319
321
  const tabs = await chrome.tabs.query({ windowId });
320
322
  const debuggableTab = tabs.find((t) => t.id && isDebuggableUrl(t.url));
321
- if (debuggableTab?.id) return debuggableTab.id;
323
+ if (debuggableTab?.id) {
324
+ console.log(`[opencli] resolveTabId: found debuggable tab ${debuggableTab.id} (${debuggableTab.url})`);
325
+ return debuggableTab.id;
326
+ }
327
+ console.warn(`[opencli] resolveTabId: no debuggable tabs found, tabs: ${tabs.map((t) => `${t.id}=${t.url}`).join(", ")}`);
322
328
  const reuseTab = tabs.find((t) => t.id);
323
329
  if (reuseTab?.id) {
324
- await chrome.tabs.update(reuseTab.id, { url: "about:blank" });
330
+ await chrome.tabs.update(reuseTab.id, { url: "data:text/html,<html></html>" });
325
331
  await new Promise((resolve) => setTimeout(resolve, 300));
326
332
  try {
327
333
  const updated = await chrome.tabs.get(reuseTab.id);
@@ -335,7 +341,7 @@ async function resolveTabId(tabId, workspace) {
335
341
  } catch {
336
342
  }
337
343
  }
338
- const newTab = await chrome.tabs.create({ windowId, url: "about:blank", active: true });
344
+ const newTab = await chrome.tabs.create({ windowId, url: "data:text/html,<html></html>", active: true });
339
345
  if (!newTab.id) throw new Error("Failed to create tab in automation window");
340
346
  return newTab.id;
341
347
  }
@@ -423,7 +429,7 @@ async function handleTabs(cmd, workspace) {
423
429
  }
424
430
  case "new": {
425
431
  const windowId = await getAutomationWindow(workspace);
426
- const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? "about:blank", active: true });
432
+ const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? "data:text/html,<html></html>", active: true });
427
433
  return { id: cmd.id, ok: true, data: { tabId: tab.id, url: tab.url } };
428
434
  }
429
435
  case "close": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "OpenCLI",
4
- "version": "0.2.0",
4
+ "version": "1.2.5",
5
5
  "description": "Bridge between opencli CLI and your browser — execute commands, read cookies, manage tabs.",
6
6
  "permissions": [
7
7
  "debugger",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencli-extension",
3
- "version": "0.2.0",
3
+ "version": "1.2.5",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -135,9 +135,10 @@ async function getAutomationWindow(workspace: string): Promise<number> {
135
135
  }
136
136
  }
137
137
 
138
- // Create a new window with about:blank (not chrome://newtab which blocks scripting)
138
+ // Create a new window with a data: URI that New Tab Override extensions cannot intercept.
139
+ // Using about:blank would be hijacked by extensions like "New Tab Override".
139
140
  const win = await chrome.windows.create({
140
- url: 'about:blank',
141
+ url: 'data:text/html,<html></html>',
141
142
  focused: false,
142
143
  width: 1280,
143
144
  height: 900,
@@ -151,6 +152,8 @@ async function getAutomationWindow(workspace: string): Promise<number> {
151
152
  automationSessions.set(workspace, session);
152
153
  console.log(`[opencli] Created automation window ${session.windowId} (${workspace})`);
153
154
  resetWindowIdleTimer(workspace);
155
+ // Brief delay to let Chrome load the initial data: URI tab
156
+ await new Promise(resolve => setTimeout(resolve, 200));
154
157
  return session.windowId;
155
158
  }
156
159
 
@@ -228,7 +231,7 @@ async function handleCommand(cmd: Command): Promise<Result> {
228
231
 
229
232
  /** Check if a URL can be attached via CDP (not chrome:// or chrome-extension://) */
230
233
  function isDebuggableUrl(url?: string): boolean {
231
- if (!url) return false;
234
+ if (!url) return true; // empty/undefined = tab still loading, allow it
232
235
  return !url.startsWith('chrome://') && !url.startsWith('chrome-extension://');
233
236
  }
234
237
 
@@ -244,6 +247,7 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi
244
247
  if (tabId !== undefined) {
245
248
  try {
246
249
  const tab = await chrome.tabs.get(tabId);
250
+ console.log(`[opencli] resolveTabId: explicit tabId=${tabId}, url=${tab.url}`);
247
251
  if (isDebuggableUrl(tab.url)) return tabId;
248
252
  // Tab exists but URL is not debuggable — fall through to auto-resolve
249
253
  console.warn(`[opencli] Tab ${tabId} URL is not debuggable (${tab.url}), re-resolving`);
@@ -259,7 +263,11 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi
259
263
  // Prefer an existing debuggable tab (about:blank, http://, https://, etc.)
260
264
  const tabs = await chrome.tabs.query({ windowId });
261
265
  const debuggableTab = tabs.find(t => t.id && isDebuggableUrl(t.url));
262
- if (debuggableTab?.id) return debuggableTab.id;
266
+ if (debuggableTab?.id) {
267
+ console.log(`[opencli] resolveTabId: found debuggable tab ${debuggableTab.id} (${debuggableTab.url})`);
268
+ return debuggableTab.id;
269
+ }
270
+ console.warn(`[opencli] resolveTabId: no debuggable tabs found, tabs: ${tabs.map(t => `${t.id}=${t.url}`).join(', ')}`);
263
271
 
264
272
  // No debuggable tab found — this typically happens when a "New Tab Override"
265
273
  // extension replaces about:blank with a chrome-extension:// page.
@@ -267,7 +275,7 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi
267
275
  // accumulating orphan tabs if chrome.tabs.create is also intercepted).
268
276
  const reuseTab = tabs.find(t => t.id);
269
277
  if (reuseTab?.id) {
270
- await chrome.tabs.update(reuseTab.id, { url: 'about:blank' });
278
+ await chrome.tabs.update(reuseTab.id, { url: 'data:text/html,<html></html>' });
271
279
  // Wait for the navigation to take effect
272
280
  await new Promise(resolve => setTimeout(resolve, 300));
273
281
  // Verify the URL is actually debuggable (New Tab Override may have intercepted)
@@ -288,7 +296,7 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi
288
296
  }
289
297
 
290
298
  // Window has no debuggable tabs — create one
291
- const newTab = await chrome.tabs.create({ windowId, url: 'about:blank', active: true });
299
+ const newTab = await chrome.tabs.create({ windowId, url: 'data:text/html,<html></html>', active: true });
292
300
  if (!newTab.id) throw new Error('Failed to create tab in automation window');
293
301
  return newTab.id;
294
302
  }
@@ -397,7 +405,7 @@ async function handleTabs(cmd: Command, workspace: string): Promise<Result> {
397
405
  }
398
406
  case 'new': {
399
407
  const windowId = await getAutomationWindow(workspace);
400
- const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? 'about:blank', active: true });
408
+ const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? 'data:text/html,<html></html>', active: true });
401
409
  return { id: cmd.id, ok: true, data: { tabId: tab.id, url: tab.url } };
402
410
  }
403
411
  case 'close': {
@@ -10,7 +10,7 @@ const attached = new Set<number>();
10
10
 
11
11
  /** Check if a URL can be attached via CDP */
12
12
  function isDebuggableUrl(url?: string): boolean {
13
- if (!url) return false;
13
+ if (!url) return true; // empty/undefined = tab still loading, allow it
14
14
  return !url.startsWith('chrome://') && !url.startsWith('chrome-extension://');
15
15
  }
16
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jackwener/opencli",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },