@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
package/dist/doctor.js CHANGED
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * opencli doctor — diagnose browser connectivity.
3
3
  *
4
- * Simplified for the daemon-based architecture. No more token management,
5
- * MCP path discovery, or config file scanning.
4
+ * Simplified for the daemon-based architecture.
6
5
  */
7
6
  import chalk from 'chalk';
8
7
  import { DEFAULT_DAEMON_PORT } from './constants.js';
@@ -17,11 +16,11 @@ import { getRuntimeLabel } from './runtime-detect.js';
17
16
  export async function checkConnectivity(opts) {
18
17
  const start = Date.now();
19
18
  try {
20
- const mcp = new BrowserBridge();
21
- const page = await mcp.connect({ timeout: opts?.timeout ?? 8 });
19
+ const bridge = new BrowserBridge();
20
+ const page = await bridge.connect({ timeout: opts?.timeout ?? 8 });
22
21
  // Try a simple eval to verify end-to-end connectivity
23
22
  await page.evaluate('1 + 1');
24
- await mcp.close();
23
+ await bridge.close();
25
24
  return { ok: true, durationMs: Date.now() - start };
26
25
  }
27
26
  catch (err) {
@@ -33,9 +32,9 @@ export async function runBrowserDoctor(opts = {}) {
33
32
  let initialStatus = await checkDaemonStatus();
34
33
  if (!initialStatus.running) {
35
34
  try {
36
- const mcp = new BrowserBridge();
37
- await mcp.connect({ timeout: 5 });
38
- await mcp.close();
35
+ const bridge = new BrowserBridge();
36
+ await bridge.connect({ timeout: 5 });
37
+ await bridge.close();
39
38
  }
40
39
  catch {
41
40
  // Auto-start failed; we'll report it below.
package/dist/explore.js CHANGED
@@ -46,7 +46,7 @@ export function slugify(value) {
46
46
  return value.trim().toLowerCase().replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-|-$/g, '') || 'site';
47
47
  }
48
48
  /**
49
- * Parse raw network output from Playwright MCP.
49
+ * Parse raw network output from browser page.
50
50
  * Handles text format: [GET] url => [200]
51
51
  */
52
52
  function parseNetworkRequests(raw) {
package/dist/output.js CHANGED
@@ -24,6 +24,9 @@ export function render(data, opts = {}) {
24
24
  case 'json':
25
25
  renderJson(data);
26
26
  break;
27
+ case 'plain':
28
+ renderPlain(data, opts);
29
+ break;
27
30
  case 'md':
28
31
  case 'markdown':
29
32
  renderMarkdown(data, opts);
@@ -77,6 +80,31 @@ function renderTable(data, opts) {
77
80
  function renderJson(data) {
78
81
  console.log(JSON.stringify(data, null, 2));
79
82
  }
83
+ function renderPlain(data, opts) {
84
+ const rows = normalizeRows(data);
85
+ if (!rows.length)
86
+ return;
87
+ // Single-row single-field shortcuts for chat-style commands.
88
+ if (rows.length === 1) {
89
+ const row = rows[0];
90
+ const entries = Object.entries(row);
91
+ if (entries.length === 1) {
92
+ const [key, value] = entries[0];
93
+ if (key === 'response' || key === 'content' || key === 'text' || key === 'value') {
94
+ console.log(String(value ?? ''));
95
+ return;
96
+ }
97
+ }
98
+ }
99
+ rows.forEach((row, index) => {
100
+ const entries = Object.entries(row).filter(([, value]) => value !== undefined && value !== null && String(value) !== '');
101
+ entries.forEach(([key, value]) => {
102
+ console.log(`${key}: ${value}`);
103
+ });
104
+ if (index < rows.length - 1)
105
+ console.log('');
106
+ });
107
+ }
80
108
  function renderMarkdown(data, opts) {
81
109
  const rows = normalizeRows(data);
82
110
  if (!rows.length)
@@ -77,4 +77,19 @@ describe('render', () => {
77
77
  const calls = log.mock.calls.map(c => c[0]);
78
78
  expect(calls[1]).toBe('test,');
79
79
  });
80
+ it('renders single-field rows in plain mode as the bare value', () => {
81
+ const log = vi.spyOn(console, 'log').mockImplementation(() => { });
82
+ render([{ response: 'Gemini says hi' }], { fmt: 'plain' });
83
+ expect(log).toHaveBeenCalledWith('Gemini says hi');
84
+ });
85
+ it('renders multi-field rows in plain mode as key-value lines', () => {
86
+ const log = vi.spyOn(console, 'log').mockImplementation(() => { });
87
+ render([{ status: 'ok', file: '~/tmp/a.png', link: 'https://example.com' }], { fmt: 'plain' });
88
+ const calls = log.mock.calls.map(c => c[0]);
89
+ expect(calls).toEqual([
90
+ 'status: ok',
91
+ 'file: ~/tmp/a.png',
92
+ 'link: https://example.com',
93
+ ]);
94
+ });
80
95
  });
@@ -5,6 +5,7 @@ import { getStep } from './registry.js';
5
5
  import { log } from '../logger.js';
6
6
  import { ConfigError } from '../errors.js';
7
7
  import { BROWSER_ONLY_STEPS } from '../capabilityRouting.js';
8
+ import { isTransientBrowserError } from '../browser/errors.js';
8
9
  export async function executePipeline(page, pipeline, ctx = {}) {
9
10
  const args = ctx.args ?? {};
10
11
  const debug = ctx.debug ?? false;
@@ -52,13 +53,7 @@ async function executeStepWithRetry(handler, page, params, data, args, op, confi
52
53
  if (attempt >= maxRetries)
53
54
  throw err;
54
55
  // Only retry on transient browser errors
55
- const msg = err instanceof Error ? err.message : '';
56
- const isTransient = msg.includes('Extension disconnected')
57
- || msg.includes('attach failed')
58
- || msg.includes('no longer exists')
59
- || msg.includes('CDP connection')
60
- || msg.includes('Daemon command failed');
61
- if (!isTransient)
56
+ if (!isTransientBrowserError(err))
62
57
  throw err;
63
58
  // Brief delay before retry
64
59
  await new Promise(resolve => setTimeout(resolve, 1000));
@@ -62,7 +62,7 @@ export async function stepSnapshot(page, params, _data, _args) {
62
62
  export async function stepEvaluate(page, params, data, args) {
63
63
  const js = String(render(params, { args, data }));
64
64
  let result = await page.evaluate(js);
65
- // MCP may return JSON as a string — auto-parse it
65
+ // Browser may return JSON as a string — auto-parse it
66
66
  if (typeof result === 'string') {
67
67
  const trimmed = result.trim();
68
68
  if ((trimmed.startsWith('[') && trimmed.endsWith(']')) || (trimmed.startsWith('{') && trimmed.endsWith('}'))) {
@@ -179,6 +179,10 @@ export function resolvePath(pathStr, ctx) {
179
179
  /**
180
180
  * Evaluate arbitrary JS expressions as a last-resort fallback.
181
181
  * Runs inside a `node:vm` sandbox with dynamic code generation disabled.
182
+ *
183
+ * Compiled functions are cached by expression string to avoid re-creating
184
+ * VM contexts on every invocation — critical for loops where the same
185
+ * expression is evaluated hundreds of times.
182
186
  */
183
187
  const FORBIDDEN_EXPR_PATTERNS = /\b(constructor|__proto__|prototype|globalThis|process|require|import|eval)\b/;
184
188
  /**
@@ -197,6 +201,23 @@ function sanitizeContext(obj) {
197
201
  return {};
198
202
  }
199
203
  }
204
+ /** LRU-bounded cache for compiled VM scripts — prevents unbounded memory growth. */
205
+ const MAX_VM_CACHE_SIZE = 256;
206
+ const _vmCache = new Map();
207
+ function getOrCompileScript(expr) {
208
+ let script = _vmCache.get(expr);
209
+ if (script)
210
+ return script;
211
+ // Evict oldest entry when cache is full
212
+ if (_vmCache.size >= MAX_VM_CACHE_SIZE) {
213
+ const firstKey = _vmCache.keys().next().value;
214
+ if (firstKey !== undefined)
215
+ _vmCache.delete(firstKey);
216
+ }
217
+ script = new vm.Script(`(${expr})`);
218
+ _vmCache.set(expr, script);
219
+ return script;
220
+ }
200
221
  function evalJsExpr(expr, ctx) {
201
222
  // Guard against absurdly long expressions that could indicate injection.
202
223
  if (expr.length > 2000)
@@ -209,7 +230,8 @@ function evalJsExpr(expr, ctx) {
209
230
  const data = sanitizeContext(ctx.data);
210
231
  const index = ctx.index ?? 0;
211
232
  try {
212
- return vm.runInNewContext(`(${expr})`, {
233
+ const script = getOrCompileScript(expr);
234
+ const sandbox = vm.createContext({
213
235
  args,
214
236
  item,
215
237
  data,
@@ -224,12 +246,12 @@ function evalJsExpr(expr, ctx) {
224
246
  Array,
225
247
  Date,
226
248
  }, {
227
- timeout: 50,
228
- contextCodeGeneration: {
249
+ codeGeneration: {
229
250
  strings: false,
230
251
  wasm: false,
231
252
  },
232
253
  });
254
+ return script.runInContext(sandbox, { timeout: 50 });
233
255
  }
234
256
  catch {
235
257
  return undefined;
package/dist/record.d.ts CHANGED
@@ -12,10 +12,19 @@
12
12
  * Uses existing exec + navigate actions only.
13
13
  */
14
14
  import type { IPage } from './types.js';
15
+ import { findArrayPath } from './analysis.js';
15
16
  export interface RecordedRequest {
16
17
  url: string;
17
18
  method: string;
18
19
  status: number | null;
20
+ /** Request content type captured at record time, if available. */
21
+ requestContentType: string | null;
22
+ /** Response content type captured at record time, if available. */
23
+ responseContentType: string | null;
24
+ /** Parsed JSON request body for replayable write requests. */
25
+ requestBody: unknown;
26
+ /** Parsed JSON response body captured from the network call. */
27
+ responseBody: unknown;
19
28
  contentType: string;
20
29
  body: unknown;
21
30
  capturedAt: number;
@@ -32,6 +41,46 @@ export interface RecordResult {
32
41
  strategy: string;
33
42
  }>;
34
43
  }
44
+ type RecordedCandidateKind = 'read' | 'write';
45
+ export interface RecordedCandidate {
46
+ kind: RecordedCandidateKind;
47
+ req: RecordedRequest;
48
+ score: number;
49
+ arrayResult: ReturnType<typeof findArrayPath> | null;
50
+ }
51
+ interface GeneratedRecordedCandidate {
52
+ kind: RecordedCandidateKind;
53
+ name: string;
54
+ strategy: string;
55
+ yaml: unknown;
56
+ }
57
+ /** Build one normalized recorded entry from captured request and response values. */
58
+ export declare function createRecordedEntry(input: {
59
+ url: string;
60
+ method: string;
61
+ requestContentType?: string | null;
62
+ requestBodyText?: string | null;
63
+ responseBody: unknown;
64
+ responseContentType?: string | null;
65
+ status?: number | null;
66
+ capturedAt?: number;
67
+ }): RecordedRequest;
68
+ /**
69
+ * Generates a full-capture interceptor that stores {url, method, status, body}
70
+ * for every JSON response. No URL pattern filter — captures everything.
71
+ */
72
+ export declare function generateFullCaptureInterceptorJs(): string;
73
+ /** Analyze recorded requests into read and write candidates. */
74
+ export declare function analyzeRecordedRequests(requests: RecordedRequest[]): {
75
+ candidates: RecordedCandidate[];
76
+ };
77
+ /** Build a minimal YAML candidate for replayable JSON write requests. */
78
+ export declare function buildWriteRecordedYaml(site: string, pageUrl: string, req: RecordedRequest, capName: string): {
79
+ name: string;
80
+ yaml: unknown;
81
+ };
82
+ /** Turn recorded requests into YAML-ready read and write candidates. */
83
+ export declare function generateRecordedCandidates(site: string, pageUrl: string, requests: RecordedRequest[]): GeneratedRecordedCandidate[];
35
84
  export interface RecordOptions {
36
85
  BrowserFactory: new () => {
37
86
  connect(o?: unknown): Promise<IPage>;
@@ -45,3 +94,4 @@ export interface RecordOptions {
45
94
  }
46
95
  export declare function recordSession(opts: RecordOptions): Promise<RecordResult>;
47
96
  export declare function renderRecordSummary(result: RecordResult): string;
97
+ export {};