@jackwener/opencli 1.5.8 → 1.5.9

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 (194) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +17 -1
  3. package/README.zh-CN.md +17 -1
  4. package/dist/browser/base-page.d.ts +48 -0
  5. package/dist/browser/base-page.js +160 -0
  6. package/dist/browser/cdp.js +4 -106
  7. package/dist/browser/daemon-client.d.ts +1 -7
  8. package/dist/browser/daemon-client.js +2 -9
  9. package/dist/browser/discover.d.ts +1 -4
  10. package/dist/browser/discover.js +1 -4
  11. package/dist/browser/errors.d.ts +4 -0
  12. package/dist/browser/errors.js +20 -0
  13. package/dist/browser/index.d.ts +1 -1
  14. package/dist/browser/index.js +1 -1
  15. package/dist/browser/page.d.ts +6 -35
  16. package/dist/browser/page.js +10 -189
  17. package/dist/browser/tabs.js +5 -5
  18. package/dist/browser.test.js +15 -15
  19. package/dist/cli-manifest.json +294 -22
  20. package/dist/clis/amazon/bestsellers.d.ts +21 -0
  21. package/dist/clis/amazon/bestsellers.js +130 -0
  22. package/dist/clis/amazon/bestsellers.test.js +20 -0
  23. package/dist/clis/amazon/discussion.d.ts +20 -0
  24. package/dist/clis/amazon/discussion.js +91 -0
  25. package/dist/clis/amazon/discussion.test.js +36 -0
  26. package/dist/clis/amazon/offer.d.ts +23 -0
  27. package/dist/clis/amazon/offer.js +140 -0
  28. package/dist/clis/amazon/offer.test.d.ts +1 -0
  29. package/dist/clis/amazon/offer.test.js +29 -0
  30. package/dist/clis/amazon/product.d.ts +18 -0
  31. package/dist/clis/amazon/product.js +92 -0
  32. package/dist/clis/amazon/product.test.d.ts +1 -0
  33. package/dist/clis/amazon/product.test.js +24 -0
  34. package/dist/clis/amazon/search.d.ts +18 -0
  35. package/dist/clis/amazon/search.js +87 -0
  36. package/dist/clis/amazon/search.test.d.ts +1 -0
  37. package/dist/clis/amazon/search.test.js +22 -0
  38. package/dist/clis/amazon/shared.d.ts +64 -0
  39. package/dist/clis/amazon/shared.js +255 -0
  40. package/dist/clis/amazon/shared.test.d.ts +1 -0
  41. package/dist/clis/amazon/shared.test.js +33 -0
  42. package/dist/clis/gemini/ask.d.ts +1 -0
  43. package/dist/clis/gemini/ask.js +40 -0
  44. package/dist/clis/gemini/image.d.ts +1 -0
  45. package/dist/clis/gemini/image.js +105 -0
  46. package/dist/clis/gemini/new.d.ts +1 -0
  47. package/dist/clis/gemini/new.js +20 -0
  48. package/dist/clis/gemini/utils.d.ts +34 -0
  49. package/dist/clis/gemini/utils.js +463 -0
  50. package/dist/clis/gemini/utils.test.d.ts +1 -0
  51. package/dist/clis/gemini/utils.test.js +31 -0
  52. package/dist/clis/notebooklm/compat.test.d.ts +1 -1
  53. package/dist/clis/notebooklm/compat.test.js +3 -3
  54. package/dist/clis/notebooklm/current.js +2 -3
  55. package/dist/clis/notebooklm/get.js +2 -3
  56. package/dist/clis/notebooklm/history.js +2 -3
  57. package/dist/clis/notebooklm/note-list.js +2 -3
  58. package/dist/clis/notebooklm/notes-get.js +2 -3
  59. package/dist/clis/notebooklm/open.d.ts +1 -0
  60. package/dist/clis/notebooklm/open.js +41 -0
  61. package/dist/clis/notebooklm/open.test.d.ts +1 -0
  62. package/dist/clis/notebooklm/open.test.js +63 -0
  63. package/dist/clis/notebooklm/source-fulltext.js +2 -3
  64. package/dist/clis/notebooklm/source-get.js +2 -3
  65. package/dist/clis/notebooklm/source-guide.js +2 -3
  66. package/dist/clis/notebooklm/source-list.js +2 -3
  67. package/dist/clis/notebooklm/status.js +1 -2
  68. package/dist/clis/notebooklm/summary.js +2 -3
  69. package/dist/clis/notebooklm/utils.d.ts +2 -1
  70. package/dist/clis/notebooklm/utils.js +20 -21
  71. package/dist/clis/xiaohongshu/creator-note-detail.test.js +11 -11
  72. package/dist/clis/xiaohongshu/creator-notes-summary.test.js +6 -6
  73. package/dist/clis/xiaohongshu/creator-notes.test.js +22 -22
  74. package/dist/commanderAdapter.js +6 -3
  75. package/dist/commanderAdapter.test.js +33 -0
  76. package/dist/commands/daemon.js +1 -1
  77. package/dist/commands/daemon.test.js +1 -1
  78. package/dist/doctor.d.ts +1 -2
  79. package/dist/doctor.js +7 -8
  80. package/dist/explore.js +1 -1
  81. package/dist/output.js +28 -0
  82. package/dist/output.test.js +15 -0
  83. package/dist/pipeline/executor.js +2 -7
  84. package/dist/pipeline/steps/browser.js +1 -1
  85. package/dist/pipeline/template.js +25 -3
  86. package/dist/record.d.ts +50 -0
  87. package/dist/record.js +298 -57
  88. package/dist/record.test.d.ts +1 -0
  89. package/dist/record.test.js +293 -0
  90. package/dist/registry.d.ts +2 -0
  91. package/dist/registry.js +1 -0
  92. package/dist/registry.test.js +10 -0
  93. package/dist/runtime.js +3 -3
  94. package/dist/snapshotFormatter.d.ts +1 -1
  95. package/dist/snapshotFormatter.js +4 -4
  96. package/dist/snapshotFormatter.test.d.ts +1 -1
  97. package/dist/snapshotFormatter.test.js +2 -2
  98. package/dist/types.d.ts +3 -1
  99. package/dist/types.js +1 -1
  100. package/docs/.vitepress/config.mts +2 -0
  101. package/docs/adapters/browser/amazon.md +53 -0
  102. package/docs/adapters/browser/gemini.md +72 -0
  103. package/docs/adapters/browser/notebooklm.md +5 -5
  104. package/docs/adapters/index.md +3 -1
  105. package/extension/dist/background.js +5 -143
  106. package/extension/src/background.test.ts +7 -163
  107. package/extension/src/background.ts +7 -157
  108. package/extension/src/protocol.ts +1 -5
  109. package/package.json +1 -1
  110. package/skills/opencli-explorer/SKILL.md +847 -0
  111. package/skills/opencli-oneshot/SKILL.md +216 -0
  112. package/skills/opencli-usage/SKILL.md +71 -0
  113. package/skills/opencli-usage/browser.md +429 -0
  114. package/skills/opencli-usage/desktop.md +118 -0
  115. package/skills/opencli-usage/plugins.md +82 -0
  116. package/skills/opencli-usage/public-api.md +149 -0
  117. package/src/browser/base-page.ts +197 -0
  118. package/src/browser/cdp.ts +7 -131
  119. package/src/browser/daemon-client.ts +3 -14
  120. package/src/browser/discover.ts +1 -4
  121. package/src/browser/errors.ts +22 -0
  122. package/src/browser/index.ts +1 -1
  123. package/src/browser/page.ts +13 -212
  124. package/src/browser/tabs.ts +5 -5
  125. package/src/browser.test.ts +15 -15
  126. package/src/clis/amazon/bestsellers.test.ts +22 -0
  127. package/src/clis/amazon/bestsellers.ts +180 -0
  128. package/src/clis/amazon/discussion.test.ts +38 -0
  129. package/src/clis/amazon/discussion.ts +131 -0
  130. package/src/clis/amazon/offer.test.ts +35 -0
  131. package/src/clis/amazon/offer.ts +185 -0
  132. package/src/clis/amazon/product.test.ts +26 -0
  133. package/src/clis/amazon/product.ts +131 -0
  134. package/src/clis/amazon/search.test.ts +24 -0
  135. package/src/clis/amazon/search.ts +128 -0
  136. package/src/clis/amazon/shared.test.ts +37 -0
  137. package/src/clis/amazon/shared.ts +316 -0
  138. package/src/clis/gemini/ask.ts +46 -0
  139. package/src/clis/gemini/image.ts +115 -0
  140. package/src/clis/gemini/new.ts +22 -0
  141. package/src/clis/gemini/utils.test.ts +36 -0
  142. package/src/clis/gemini/utils.ts +523 -0
  143. package/src/clis/notebooklm/compat.test.ts +3 -3
  144. package/src/clis/notebooklm/current.ts +2 -3
  145. package/src/clis/notebooklm/get.ts +1 -3
  146. package/src/clis/notebooklm/history.ts +1 -3
  147. package/src/clis/notebooklm/note-list.ts +1 -3
  148. package/src/clis/notebooklm/notes-get.ts +1 -3
  149. package/src/clis/notebooklm/open.test.ts +78 -0
  150. package/src/clis/notebooklm/open.ts +61 -0
  151. package/src/clis/notebooklm/source-fulltext.ts +1 -3
  152. package/src/clis/notebooklm/source-get.ts +1 -3
  153. package/src/clis/notebooklm/source-guide.ts +1 -3
  154. package/src/clis/notebooklm/source-list.ts +1 -3
  155. package/src/clis/notebooklm/status.ts +1 -2
  156. package/src/clis/notebooklm/summary.ts +1 -3
  157. package/src/clis/notebooklm/utils.ts +29 -20
  158. package/src/clis/xiaohongshu/creator-note-detail.test.ts +11 -11
  159. package/src/clis/xiaohongshu/creator-notes-summary.test.ts +6 -6
  160. package/src/clis/xiaohongshu/creator-notes.test.ts +22 -22
  161. package/src/commanderAdapter.test.ts +47 -0
  162. package/src/commanderAdapter.ts +7 -3
  163. package/src/commands/daemon.test.ts +1 -1
  164. package/src/commands/daemon.ts +1 -1
  165. package/src/doctor.ts +7 -8
  166. package/src/explore.ts +1 -1
  167. package/src/output.test.ts +17 -0
  168. package/src/output.ts +27 -0
  169. package/src/pipeline/executor.ts +2 -7
  170. package/src/pipeline/steps/browser.ts +1 -1
  171. package/src/pipeline/template.ts +27 -4
  172. package/src/record.test.ts +362 -0
  173. package/src/record.ts +341 -62
  174. package/src/registry.test.ts +12 -0
  175. package/src/registry.ts +3 -0
  176. package/src/runtime.ts +3 -3
  177. package/src/snapshotFormatter.test.ts +2 -2
  178. package/src/snapshotFormatter.ts +4 -4
  179. package/src/types.ts +3 -1
  180. package/.agents/skills/cross-project-adapter-migration/SKILL.md +0 -249
  181. package/.agents/workflows/cross-project-adapter-migration.md +0 -54
  182. package/SKILL.md +0 -879
  183. package/dist/clis/notebooklm/bind-current.js +0 -29
  184. package/dist/clis/notebooklm/bind-current.test.d.ts +0 -1
  185. package/dist/clis/notebooklm/bind-current.test.js +0 -35
  186. package/dist/clis/notebooklm/binding.test.js +0 -44
  187. package/src/clis/notebooklm/bind-current.test.ts +0 -43
  188. package/src/clis/notebooklm/bind-current.ts +0 -36
  189. package/src/clis/notebooklm/binding.test.ts +0 -53
  190. /package/dist/browser/{mcp.d.ts → bridge.d.ts} +0 -0
  191. /package/dist/browser/{mcp.js → bridge.js} +0 -0
  192. /package/dist/clis/{notebooklm/bind-current.d.ts → amazon/bestsellers.test.d.ts} +0 -0
  193. /package/dist/clis/{notebooklm/binding.test.d.ts → amazon/discussion.test.d.ts} +0 -0
  194. /package/src/browser/{mcp.ts → bridge.ts} +0 -0
@@ -9,13 +9,12 @@
9
9
  * where resolveTabId() in the extension picks a chrome:// or
10
10
  * chrome-extension:// tab that can't be debugged.
11
11
  */
12
- import { formatSnapshot } from '../snapshotFormatter.js';
13
12
  import { sendCommand } from './daemon-client.js';
14
13
  import { wrapForEval } from './utils.js';
15
14
  import { saveBase64ToFile } from '../utils.js';
16
- import { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
17
15
  import { generateStealthJs } from './stealth.js';
18
- import { clickJs, typeTextJs, pressKeyJs, waitForTextJs, waitForCaptureJs, waitForSelectorJs, scrollJs, autoScrollJs, networkRequestsJs, waitForDomStableJs, } from './dom-helpers.js';
16
+ import { waitForDomStableJs } from './dom-helpers.js';
17
+ import { BasePage } from './base-page.js';
19
18
  export function isRetryableSettleError(err) {
20
19
  const message = err instanceof Error ? err.message : String(err);
21
20
  return message.includes('Inspected target navigated or closed')
@@ -24,15 +23,14 @@ export function isRetryableSettleError(err) {
24
23
  /**
25
24
  * Page — implements IPage by talking to the daemon via HTTP.
26
25
  */
27
- export class Page {
26
+ export class Page extends BasePage {
28
27
  workspace;
29
28
  constructor(workspace = 'default') {
29
+ super();
30
30
  this.workspace = workspace;
31
31
  }
32
32
  /** Active tab ID, set after navigate and used in all subsequent commands */
33
33
  _tabId;
34
- /** Last navigated URL, tracked in-memory to avoid extra round-trips */
35
- _lastUrl = null;
36
34
  /** Helper: spread workspace into command params */
37
35
  _wsOpt() {
38
36
  return { workspace: this.workspace };
@@ -95,29 +93,8 @@ export class Page {
95
93
  }
96
94
  }
97
95
  }
98
- async getCurrentUrl() {
99
- if (this._lastUrl)
100
- return this._lastUrl;
101
- try {
102
- const current = await this.evaluate('window.location.href');
103
- if (typeof current === 'string' && current) {
104
- this._lastUrl = current;
105
- return current;
106
- }
107
- }
108
- catch {
109
- // Best-effort: some commands may run before a debuggable tab is ready.
110
- }
111
- return null;
112
- }
113
- /** Close the automation window in the extension */
114
- async closeWindow() {
115
- try {
116
- await sendCommand('close-window', { ...this._wsOpt() });
117
- }
118
- catch {
119
- // Window may already be closed or daemon may be down
120
- }
96
+ getActiveTabId() {
97
+ return this._tabId;
121
98
  }
122
99
  async evaluate(js) {
123
100
  const code = wrapForEval(js);
@@ -135,120 +112,13 @@ export class Page {
135
112
  const result = await sendCommand('cookies', { ...this._wsOpt(), ...opts });
136
113
  return Array.isArray(result) ? result : [];
137
114
  }
138
- async snapshot(opts = {}) {
139
- // Primary: use the advanced DOM snapshot engine with multi-layer pruning
140
- const snapshotJs = generateSnapshotJs({
141
- viewportExpand: opts.viewportExpand ?? 800,
142
- maxDepth: Math.max(1, Math.min(Number(opts.maxDepth) || 50, 200)),
143
- interactiveOnly: opts.interactive ?? false,
144
- maxTextLength: opts.maxTextLength ?? 120,
145
- includeScrollInfo: true,
146
- bboxDedup: true,
147
- });
115
+ /** Close the automation window in the extension */
116
+ async closeWindow() {
148
117
  try {
149
- const result = await sendCommand('exec', { code: snapshotJs, ...this._cmdOpts() });
150
- // The advanced engine already produces a clean, pruned, LLM-friendly output.
151
- // Do NOT pass through formatSnapshot — its format is incompatible.
152
- return result;
118
+ await sendCommand('close-window', { ...this._wsOpt() });
153
119
  }
154
120
  catch {
155
- // Fallback: basic DOM snapshot (original implementation)
156
- return this._basicSnapshot(opts);
157
- }
158
- }
159
- /** Fallback basic snapshot — original buildTree approach */
160
- async _basicSnapshot(opts = {}) {
161
- const maxDepth = Math.max(1, Math.min(Number(opts.maxDepth) || 50, 200));
162
- const code = `
163
- (async () => {
164
- function buildTree(node, depth) {
165
- if (depth > ${maxDepth}) return '';
166
- const role = node.getAttribute?.('role') || node.tagName?.toLowerCase() || 'generic';
167
- const name = node.getAttribute?.('aria-label') || node.getAttribute?.('alt') || node.textContent?.trim().slice(0, 80) || '';
168
- const isInteractive = ['a', 'button', 'input', 'select', 'textarea'].includes(node.tagName?.toLowerCase()) || node.getAttribute?.('tabindex') != null;
169
-
170
- ${opts.interactive ? 'if (!isInteractive && !node.children?.length) return "";' : ''}
171
-
172
- let indent = ' '.repeat(depth);
173
- let line = indent + role;
174
- if (name) line += ' "' + name.replace(/"/g, '\\\\\\"') + '"';
175
- if (node.tagName?.toLowerCase() === 'a' && node.href) line += ' [' + node.href + ']';
176
- if (node.tagName?.toLowerCase() === 'input') line += ' [' + (node.type || 'text') + ']';
177
-
178
- let result = line + '\\n';
179
- if (node.children) {
180
- for (const child of node.children) {
181
- result += buildTree(child, depth + 1);
182
- }
183
- }
184
- return result;
185
- }
186
- return buildTree(document.body, 0);
187
- })()
188
- `;
189
- const raw = await sendCommand('exec', { code, ...this._cmdOpts() });
190
- if (opts.raw)
191
- return raw;
192
- if (typeof raw === 'string')
193
- return formatSnapshot(raw, opts);
194
- return raw;
195
- }
196
- async click(ref) {
197
- const code = clickJs(ref);
198
- await sendCommand('exec', { code, ...this._cmdOpts() });
199
- }
200
- async typeText(ref, text) {
201
- const code = typeTextJs(ref, text);
202
- await sendCommand('exec', { code, ...this._cmdOpts() });
203
- }
204
- async pressKey(key) {
205
- const code = pressKeyJs(key);
206
- await sendCommand('exec', { code, ...this._cmdOpts() });
207
- }
208
- async scrollTo(ref) {
209
- const code = scrollToRefJs(ref);
210
- return sendCommand('exec', { code, ...this._cmdOpts() });
211
- }
212
- async getFormState() {
213
- const code = getFormStateJs();
214
- return (await sendCommand('exec', { code, ...this._cmdOpts() }));
215
- }
216
- async wait(options) {
217
- if (typeof options === 'number') {
218
- if (options >= 1) {
219
- // For waits >= 1s, use DOM-stable check: return early when the page
220
- // stops mutating, with the original wait time as the hard cap.
221
- // This turns e.g. `page.wait(5)` from a fixed 5s sleep into
222
- // "wait until DOM is stable, max 5s" — often completing in <1s.
223
- try {
224
- const maxMs = options * 1000;
225
- await sendCommand('exec', {
226
- code: waitForDomStableJs(maxMs, Math.min(500, maxMs)),
227
- ...this._cmdOpts(),
228
- });
229
- return;
230
- }
231
- catch {
232
- // Fallback: fixed sleep (e.g. if page has no DOM yet)
233
- }
234
- }
235
- await new Promise(resolve => setTimeout(resolve, options * 1000));
236
- return;
237
- }
238
- if (typeof options.time === 'number') {
239
- await new Promise(resolve => setTimeout(resolve, options.time * 1000));
240
- return;
241
- }
242
- if (options.selector) {
243
- const timeout = (options.timeout ?? 10) * 1000;
244
- const code = waitForSelectorJs(options.selector, timeout);
245
- await sendCommand('exec', { code, ...this._cmdOpts() });
246
- return;
247
- }
248
- if (options.text) {
249
- const timeout = (options.timeout ?? 30) * 1000;
250
- const code = waitForTextJs(options.text, timeout);
251
- await sendCommand('exec', { code, ...this._cmdOpts() });
121
+ // Window may already be closed or daemon may be down
252
122
  }
253
123
  }
254
124
  async tabs() {
@@ -271,25 +141,8 @@ export class Page {
271
141
  if (result?.selected)
272
142
  this._tabId = result.selected;
273
143
  }
274
- async networkRequests(includeStatic = false) {
275
- const code = networkRequestsJs(includeStatic);
276
- const result = await sendCommand('exec', { code, ...this._cmdOpts() });
277
- return Array.isArray(result) ? result : [];
278
- }
279
- /**
280
- * Console messages are not available in lightweight daemon mode.
281
- * Would require CDP Runtime.consoleAPICalled event listener.
282
- * @returns Always returns empty array.
283
- */
284
- async consoleMessages(_level = 'info') {
285
- return [];
286
- }
287
144
  /**
288
145
  * Capture a screenshot via CDP Page.captureScreenshot.
289
- * @param options.format - 'png' (default) or 'jpeg'
290
- * @param options.quality - JPEG quality 0-100
291
- * @param options.fullPage - capture full scrollable page
292
- * @param options.path - save to file path (returns base64 if omitted)
293
146
  */
294
147
  async screenshot(options = {}) {
295
148
  const base64 = await sendCommand('screenshot', {
@@ -303,31 +156,6 @@ export class Page {
303
156
  }
304
157
  return base64;
305
158
  }
306
- async scroll(direction = 'down', amount = 500) {
307
- const code = scrollJs(direction, amount);
308
- await sendCommand('exec', { code, ...this._cmdOpts() });
309
- }
310
- async autoScroll(options = {}) {
311
- const times = options.times ?? 3;
312
- const delayMs = options.delayMs ?? 2000;
313
- const code = autoScrollJs(times, delayMs);
314
- await sendCommand('exec', { code, ...this._cmdOpts() });
315
- }
316
- async installInterceptor(pattern) {
317
- const { generateInterceptorJs } = await import('../interceptor.js');
318
- // Must use evaluate() so wrapForEval() converts the arrow function into an IIFE;
319
- // sendCommand('exec') sends the code as-is, and CDP never executes a bare arrow.
320
- await this.evaluate(generateInterceptorJs(JSON.stringify(pattern), {
321
- arrayName: '__opencli_xhr',
322
- patchGuard: '__opencli_interceptor_patched',
323
- }));
324
- }
325
- async getInterceptedRequests() {
326
- const { generateReadInterceptedJs } = await import('../interceptor.js');
327
- // Same as installInterceptor: must go through evaluate() for IIFE wrapping
328
- const result = await this.evaluate(generateReadInterceptedJs('__opencli_xhr'));
329
- return Array.isArray(result) ? result : [];
330
- }
331
159
  /**
332
160
  * Set local file paths on a file input element via CDP DOM.setFileInputFiles.
333
161
  * Chrome reads the files directly from the local filesystem, avoiding the
@@ -343,12 +171,5 @@ export class Page {
343
171
  throw new Error('setFileInput returned no count — command may not be supported by the extension');
344
172
  }
345
173
  }
346
- async waitForCapture(timeout = 10) {
347
- const maxMs = timeout * 1000;
348
- await sendCommand('exec', {
349
- code: waitForCaptureJs(maxMs),
350
- ...this._cmdOpts(),
351
- });
352
- }
353
174
  }
354
175
  // (End of file)
@@ -19,12 +19,12 @@ export function extractTabEntries(raw) {
19
19
  .map(line => line.trim())
20
20
  .filter(Boolean)
21
21
  .map(line => {
22
- // Match actual Playwright MCP format: "- 0: (current) [title](url)" or "- 1: [title](url)"
23
- const mcpMatch = line.match(/^-\s+(\d+):\s*(.*)$/);
24
- if (mcpMatch) {
22
+ // Match tab list format: "- 0: (current) [title](url)" or "- 1: [title](url)"
23
+ const tabMatch = line.match(/^-\s+(\d+):\s*(.*)$/);
24
+ if (tabMatch) {
25
25
  return {
26
- index: parseInt(mcpMatch[1], 10),
27
- identity: mcpMatch[2].trim() || `tab-${mcpMatch[1]}`,
26
+ index: parseInt(tabMatch[1], 10),
27
+ identity: tabMatch[2].trim() || `tab-${tabMatch[1]}`,
28
28
  };
29
29
  }
30
30
  // Legacy format: "Tab 0 ..."
@@ -84,31 +84,31 @@ describe('browser helpers', () => {
84
84
  });
85
85
  describe('BrowserBridge state', () => {
86
86
  it('transitions to closed after close()', async () => {
87
- const mcp = new BrowserBridge();
88
- expect(mcp.state).toBe('idle');
89
- await mcp.close();
90
- expect(mcp.state).toBe('closed');
87
+ const bridge = new BrowserBridge();
88
+ expect(bridge.state).toBe('idle');
89
+ await bridge.close();
90
+ expect(bridge.state).toBe('closed');
91
91
  });
92
92
  it('rejects connect() after the session has been closed', async () => {
93
- const mcp = new BrowserBridge();
94
- await mcp.close();
95
- await expect(mcp.connect()).rejects.toThrow('Session is closed');
93
+ const bridge = new BrowserBridge();
94
+ await bridge.close();
95
+ await expect(bridge.connect()).rejects.toThrow('Session is closed');
96
96
  });
97
97
  it('rejects connect() while already connecting', async () => {
98
- const mcp = new BrowserBridge();
99
- mcp._state = 'connecting';
100
- await expect(mcp.connect()).rejects.toThrow('Already connecting');
98
+ const bridge = new BrowserBridge();
99
+ bridge._state = 'connecting';
100
+ await expect(bridge.connect()).rejects.toThrow('Already connecting');
101
101
  });
102
102
  it('rejects connect() while closing', async () => {
103
- const mcp = new BrowserBridge();
104
- mcp._state = 'closing';
105
- await expect(mcp.connect()).rejects.toThrow('Session is closing');
103
+ const bridge = new BrowserBridge();
104
+ bridge._state = 'closing';
105
+ await expect(bridge.connect()).rejects.toThrow('Session is closing');
106
106
  });
107
107
  it('fails fast when daemon is running but extension is disconnected', async () => {
108
108
  vi.spyOn(daemonClient, 'isExtensionConnected').mockResolvedValue(false);
109
109
  vi.spyOn(daemonClient, 'isDaemonRunning').mockResolvedValue(true);
110
- const mcp = new BrowserBridge();
111
- await expect(mcp.connect({ timeout: 0.1 })).rejects.toThrow('Browser Extension is not connected');
110
+ const bridge = new BrowserBridge();
111
+ await expect(bridge.connect({ timeout: 0.1 })).rejects.toThrow('Browser Extension is not connected');
112
112
  });
113
113
  });
114
114
  describe('stealth anti-detection', () => {
@@ -111,6 +111,163 @@
111
111
  "type": "ts",
112
112
  "modulePath": "36kr/search.js"
113
113
  },
114
+ {
115
+ "site": "amazon",
116
+ "name": "bestsellers",
117
+ "description": "Amazon Best Sellers pages for category candidate discovery",
118
+ "domain": "amazon.com",
119
+ "strategy": "cookie",
120
+ "browser": true,
121
+ "args": [
122
+ {
123
+ "name": "input",
124
+ "type": "str",
125
+ "required": false,
126
+ "positional": true,
127
+ "help": "Best sellers URL or /zgbs path. Omit to use the root Best Sellers page."
128
+ },
129
+ {
130
+ "name": "limit",
131
+ "type": "int",
132
+ "default": 100,
133
+ "required": false,
134
+ "help": "Maximum number of ranked items to return (default 100)"
135
+ }
136
+ ],
137
+ "columns": [
138
+ "rank",
139
+ "asin",
140
+ "title",
141
+ "price_text",
142
+ "rating_value",
143
+ "review_count"
144
+ ],
145
+ "type": "ts",
146
+ "modulePath": "amazon/bestsellers.js",
147
+ "navigateBefore": false
148
+ },
149
+ {
150
+ "site": "amazon",
151
+ "name": "discussion",
152
+ "description": "Amazon review summary and sample customer discussion from product review pages",
153
+ "domain": "amazon.com",
154
+ "strategy": "cookie",
155
+ "browser": true,
156
+ "args": [
157
+ {
158
+ "name": "input",
159
+ "type": "str",
160
+ "required": true,
161
+ "positional": true,
162
+ "help": "ASIN or product URL, for example B0FJS72893"
163
+ },
164
+ {
165
+ "name": "limit",
166
+ "type": "int",
167
+ "default": 10,
168
+ "required": false,
169
+ "help": "Maximum number of review samples to return (default 10)"
170
+ }
171
+ ],
172
+ "columns": [
173
+ "asin",
174
+ "average_rating_value",
175
+ "total_review_count"
176
+ ],
177
+ "type": "ts",
178
+ "modulePath": "amazon/discussion.js",
179
+ "navigateBefore": false
180
+ },
181
+ {
182
+ "site": "amazon",
183
+ "name": "offer",
184
+ "description": "Amazon seller, buy box, and fulfillment facts from the product page",
185
+ "domain": "amazon.com",
186
+ "strategy": "cookie",
187
+ "browser": true,
188
+ "args": [
189
+ {
190
+ "name": "input",
191
+ "type": "str",
192
+ "required": true,
193
+ "positional": true,
194
+ "help": "ASIN or product URL, for example B0FJS72893"
195
+ }
196
+ ],
197
+ "columns": [
198
+ "asin",
199
+ "price_text",
200
+ "sold_by",
201
+ "ships_from",
202
+ "is_amazon_sold",
203
+ "is_amazon_fulfilled"
204
+ ],
205
+ "type": "ts",
206
+ "modulePath": "amazon/offer.js",
207
+ "navigateBefore": false
208
+ },
209
+ {
210
+ "site": "amazon",
211
+ "name": "product",
212
+ "description": "Amazon product page facts for candidate validation",
213
+ "domain": "amazon.com",
214
+ "strategy": "cookie",
215
+ "browser": true,
216
+ "args": [
217
+ {
218
+ "name": "input",
219
+ "type": "str",
220
+ "required": true,
221
+ "positional": true,
222
+ "help": "ASIN or product URL, for example B0FJS72893"
223
+ }
224
+ ],
225
+ "columns": [
226
+ "asin",
227
+ "title",
228
+ "price_text",
229
+ "rating_value",
230
+ "review_count"
231
+ ],
232
+ "type": "ts",
233
+ "modulePath": "amazon/product.js",
234
+ "navigateBefore": false
235
+ },
236
+ {
237
+ "site": "amazon",
238
+ "name": "search",
239
+ "description": "Amazon search results for product discovery and coarse filtering",
240
+ "domain": "amazon.com",
241
+ "strategy": "cookie",
242
+ "browser": true,
243
+ "args": [
244
+ {
245
+ "name": "query",
246
+ "type": "str",
247
+ "required": true,
248
+ "positional": true,
249
+ "help": "Search query, for example \"desk shelf organizer\""
250
+ },
251
+ {
252
+ "name": "limit",
253
+ "type": "int",
254
+ "default": 20,
255
+ "required": false,
256
+ "help": "Maximum number of results to return (default 20)"
257
+ }
258
+ ],
259
+ "columns": [
260
+ "rank",
261
+ "asin",
262
+ "title",
263
+ "price_text",
264
+ "rating_value",
265
+ "review_count"
266
+ ],
267
+ "type": "ts",
268
+ "modulePath": "amazon/search.js",
269
+ "navigateBefore": false
270
+ },
114
271
  {
115
272
  "site": "antigravity",
116
273
  "name": "dump",
@@ -5218,6 +5375,114 @@
5218
5375
  ],
5219
5376
  "type": "yaml"
5220
5377
  },
5378
+ {
5379
+ "site": "gemini",
5380
+ "name": "ask",
5381
+ "description": "Send a prompt to Gemini and return only the assistant response",
5382
+ "domain": "gemini.google.com",
5383
+ "strategy": "cookie",
5384
+ "browser": true,
5385
+ "args": [
5386
+ {
5387
+ "name": "prompt",
5388
+ "type": "str",
5389
+ "required": true,
5390
+ "positional": true,
5391
+ "help": "Prompt to send"
5392
+ },
5393
+ {
5394
+ "name": "timeout",
5395
+ "type": "str",
5396
+ "default": "60",
5397
+ "required": false,
5398
+ "help": "Max seconds to wait (default: 60)"
5399
+ },
5400
+ {
5401
+ "name": "new",
5402
+ "type": "str",
5403
+ "default": "false",
5404
+ "required": false,
5405
+ "help": "Start a new chat first (true/false, default: false)"
5406
+ }
5407
+ ],
5408
+ "columns": [
5409
+ "response"
5410
+ ],
5411
+ "timeout": 180,
5412
+ "type": "ts",
5413
+ "modulePath": "gemini/ask.js",
5414
+ "navigateBefore": false
5415
+ },
5416
+ {
5417
+ "site": "gemini",
5418
+ "name": "image",
5419
+ "description": "Generate images with Gemini web and save them locally",
5420
+ "domain": "gemini.google.com",
5421
+ "strategy": "cookie",
5422
+ "browser": true,
5423
+ "args": [
5424
+ {
5425
+ "name": "prompt",
5426
+ "type": "str",
5427
+ "required": true,
5428
+ "positional": true,
5429
+ "help": "Image prompt to send to Gemini"
5430
+ },
5431
+ {
5432
+ "name": "rt",
5433
+ "type": "str",
5434
+ "default": "1:1",
5435
+ "required": false,
5436
+ "help": "Ratio shorthand for aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3)"
5437
+ },
5438
+ {
5439
+ "name": "st",
5440
+ "type": "str",
5441
+ "default": "",
5442
+ "required": false,
5443
+ "help": "Style shorthand, e.g. anime, icon, watercolor"
5444
+ },
5445
+ {
5446
+ "name": "op",
5447
+ "type": "str",
5448
+ "default": "/home/runner/tmp/gemini-images",
5449
+ "required": false,
5450
+ "help": "Output directory shorthand"
5451
+ },
5452
+ {
5453
+ "name": "sd",
5454
+ "type": "boolean",
5455
+ "default": false,
5456
+ "required": false,
5457
+ "help": "Skip download shorthand; only show Gemini page link"
5458
+ }
5459
+ ],
5460
+ "columns": [
5461
+ "status",
5462
+ "file",
5463
+ "link"
5464
+ ],
5465
+ "timeout": 240,
5466
+ "type": "ts",
5467
+ "modulePath": "gemini/image.js",
5468
+ "navigateBefore": false
5469
+ },
5470
+ {
5471
+ "site": "gemini",
5472
+ "name": "new",
5473
+ "description": "Start a new conversation in Gemini web chat",
5474
+ "domain": "gemini.google.com",
5475
+ "strategy": "cookie",
5476
+ "browser": true,
5477
+ "args": [],
5478
+ "columns": [
5479
+ "Status",
5480
+ "Action"
5481
+ ],
5482
+ "type": "ts",
5483
+ "modulePath": "gemini/new.js",
5484
+ "navigateBefore": false
5485
+ },
5221
5486
  {
5222
5487
  "site": "google",
5223
5488
  "name": "news",
@@ -8079,28 +8344,6 @@
8079
8344
  "type": "ts",
8080
8345
  "modulePath": "medium/user.js"
8081
8346
  },
8082
- {
8083
- "site": "notebooklm",
8084
- "name": "bind-current",
8085
- "aliases": [
8086
- "use"
8087
- ],
8088
- "description": "Bind the current active NotebookLM notebook tab into the site:notebooklm workspace",
8089
- "domain": "notebooklm.google.com",
8090
- "strategy": "cookie",
8091
- "browser": true,
8092
- "args": [],
8093
- "columns": [
8094
- "workspace",
8095
- "tab_id",
8096
- "notebook_id",
8097
- "title",
8098
- "url"
8099
- ],
8100
- "type": "ts",
8101
- "modulePath": "notebooklm/bind-current.js",
8102
- "navigateBefore": false
8103
- },
8104
8347
  {
8105
8348
  "site": "notebooklm",
8106
8349
  "name": "current",
@@ -8231,6 +8474,35 @@
8231
8474
  "modulePath": "notebooklm/notes-get.js",
8232
8475
  "navigateBefore": false
8233
8476
  },
8477
+ {
8478
+ "site": "notebooklm",
8479
+ "name": "open",
8480
+ "aliases": [
8481
+ "select"
8482
+ ],
8483
+ "description": "Open one NotebookLM notebook in the automation workspace by id or URL",
8484
+ "domain": "notebooklm.google.com",
8485
+ "strategy": "cookie",
8486
+ "browser": true,
8487
+ "args": [
8488
+ {
8489
+ "name": "notebook",
8490
+ "type": "str",
8491
+ "required": true,
8492
+ "positional": true,
8493
+ "help": "Notebook id from list output, or a full NotebookLM notebook URL"
8494
+ }
8495
+ ],
8496
+ "columns": [
8497
+ "id",
8498
+ "title",
8499
+ "url",
8500
+ "source"
8501
+ ],
8502
+ "type": "ts",
8503
+ "modulePath": "notebooklm/open.js",
8504
+ "navigateBefore": false
8505
+ },
8234
8506
  {
8235
8507
  "site": "notebooklm",
8236
8508
  "name": "source-fulltext",
@@ -0,0 +1,21 @@
1
+ interface BestsellersPagePayload {
2
+ href?: string;
3
+ title?: string;
4
+ list_title?: string;
5
+ cards?: Array<{
6
+ rank_text?: string | null;
7
+ asin?: string | null;
8
+ title?: string | null;
9
+ href?: string | null;
10
+ price_text?: string | null;
11
+ rating_text?: string | null;
12
+ review_count_text?: string | null;
13
+ card_text?: string | null;
14
+ }>;
15
+ page_links?: string[];
16
+ }
17
+ declare function normalizeBestsellerCandidate(candidate: NonNullable<BestsellersPagePayload['cards']>[number], rank: number, listTitle: string | null, sourceUrl: string): Record<string, unknown>;
18
+ export declare const __test__: {
19
+ normalizeBestsellerCandidate: typeof normalizeBestsellerCandidate;
20
+ };
21
+ export {};