@jackwener/opencli 1.6.1 → 1.6.2

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 (384) hide show
  1. package/CONTRIBUTING.md +1 -1
  2. package/README.md +27 -45
  3. package/README.zh-CN.md +32 -34
  4. package/autoresearch/browse-tasks.json +18 -20
  5. package/autoresearch/commands/debug.ts +163 -0
  6. package/autoresearch/commands/fix.ts +145 -0
  7. package/autoresearch/commands/plan.ts +88 -0
  8. package/autoresearch/commands/run.ts +138 -0
  9. package/autoresearch/config.ts +82 -0
  10. package/autoresearch/engine.ts +359 -0
  11. package/autoresearch/eval-all.ts +127 -0
  12. package/autoresearch/eval-browse.ts +1 -1
  13. package/autoresearch/eval-publish.ts +238 -0
  14. package/autoresearch/eval-save.ts +249 -0
  15. package/autoresearch/eval-skill.ts +14 -8
  16. package/autoresearch/eval-v2ex.ts +220 -0
  17. package/autoresearch/eval-zhihu.ts +230 -0
  18. package/autoresearch/logger.ts +69 -0
  19. package/autoresearch/presets/combined-reliability.ts +27 -0
  20. package/autoresearch/presets/index.ts +23 -0
  21. package/autoresearch/presets/operate-reliability.ts +24 -0
  22. package/autoresearch/presets/save-reliability.ts +26 -0
  23. package/autoresearch/presets/skill-quality.ts +20 -0
  24. package/autoresearch/presets/v2ex-reliability.ts +24 -0
  25. package/autoresearch/presets/zhihu-reliability.ts +25 -0
  26. package/autoresearch/publish-tasks.json +345 -0
  27. package/autoresearch/run-save.sh +11 -0
  28. package/autoresearch/save-adapters/xhs-explore-deep.ts +64 -0
  29. package/autoresearch/save-adapters/xhs-note-comments.ts +61 -0
  30. package/autoresearch/save-adapters/xhs-search-full.ts +62 -0
  31. package/autoresearch/save-adapters/zhihu-hot-detail.ts +52 -0
  32. package/autoresearch/save-adapters/zhihu-question-full.ts +57 -0
  33. package/autoresearch/save-adapters/zhihu-search-detail.ts +53 -0
  34. package/autoresearch/save-tasks.json +281 -0
  35. package/autoresearch/v2ex-tasks.json +899 -0
  36. package/autoresearch/zhihu-tasks.json +848 -0
  37. package/dist/browser/base-page.d.ts +4 -2
  38. package/dist/browser/base-page.js +37 -4
  39. package/dist/browser/bridge.js +10 -8
  40. package/dist/browser/cdp.js +2 -6
  41. package/dist/browser/daemon-client.d.ts +11 -1
  42. package/dist/browser/daemon-client.js +3 -0
  43. package/dist/browser/dom-helpers.d.ts +4 -2
  44. package/dist/browser/dom-helpers.js +42 -31
  45. package/dist/browser/dom-snapshot.js +23 -1
  46. package/dist/browser/page.d.ts +7 -2
  47. package/dist/browser/page.js +112 -30
  48. package/dist/browser.test.js +1 -1
  49. package/dist/build-manifest.d.ts +1 -0
  50. package/dist/build-manifest.js +1 -0
  51. package/dist/cli-manifest.json +1135 -184
  52. package/dist/cli.d.ts +2 -0
  53. package/dist/cli.js +48 -7
  54. package/dist/cli.test.d.ts +1 -0
  55. package/dist/cli.test.js +88 -0
  56. package/dist/clis/1688/item.d.ts +70 -0
  57. package/dist/clis/1688/item.js +187 -0
  58. package/dist/clis/1688/item.test.d.ts +1 -0
  59. package/dist/clis/1688/item.test.js +67 -0
  60. package/dist/clis/1688/search.d.ts +56 -0
  61. package/dist/clis/1688/search.js +309 -0
  62. package/dist/clis/1688/search.test.d.ts +1 -0
  63. package/dist/clis/1688/search.test.js +75 -0
  64. package/dist/clis/1688/shared.d.ts +112 -0
  65. package/dist/clis/1688/shared.js +514 -0
  66. package/dist/clis/1688/shared.test.d.ts +1 -0
  67. package/dist/clis/1688/shared.test.js +57 -0
  68. package/dist/clis/1688/store.d.ts +45 -0
  69. package/dist/clis/1688/store.js +226 -0
  70. package/dist/clis/1688/store.test.d.ts +1 -0
  71. package/dist/clis/1688/store.test.js +62 -0
  72. package/dist/clis/amazon/bestsellers.d.ts +0 -20
  73. package/dist/clis/amazon/bestsellers.js +6 -129
  74. package/dist/clis/amazon/bestsellers.test.js +12 -3
  75. package/dist/clis/amazon/movers-shakers.d.ts +1 -0
  76. package/dist/clis/amazon/movers-shakers.js +7 -0
  77. package/dist/clis/amazon/new-releases.d.ts +1 -0
  78. package/dist/clis/amazon/new-releases.js +7 -0
  79. package/dist/clis/amazon/rankings.d.ts +59 -0
  80. package/dist/clis/amazon/rankings.js +226 -0
  81. package/dist/clis/amazon/rankings.test.d.ts +1 -0
  82. package/dist/clis/amazon/rankings.test.js +41 -0
  83. package/dist/clis/amazon/shared.d.ts +11 -0
  84. package/dist/clis/amazon/shared.js +121 -11
  85. package/dist/clis/amazon/shared.test.js +11 -0
  86. package/dist/clis/bilibili/comments.js +2 -2
  87. package/dist/clis/bilibili/comments.test.js +3 -2
  88. package/dist/clis/bilibili/download.js +2 -1
  89. package/dist/clis/bilibili/subtitle.js +4 -3
  90. package/dist/clis/bilibili/subtitle.test.js +2 -1
  91. package/dist/clis/bilibili/utils.d.ts +5 -0
  92. package/dist/clis/bilibili/utils.js +30 -0
  93. package/dist/clis/bilibili/utils.test.d.ts +1 -0
  94. package/dist/clis/bilibili/utils.test.js +17 -0
  95. package/dist/clis/douban/marks.js +1 -1
  96. package/dist/clis/douban/subject.yaml +50 -19
  97. package/dist/clis/doubao/utils.js +32 -12
  98. package/dist/clis/douyin/_shared/browser-fetch.test.js +0 -1
  99. package/dist/clis/douyin/_shared/transcode.test.js +0 -2
  100. package/dist/clis/douyin/draft.test.js +0 -2
  101. package/dist/clis/facebook/search.test.js +0 -2
  102. package/dist/clis/gemini/ask.js +9 -3
  103. package/dist/clis/gemini/ask.test.d.ts +1 -0
  104. package/dist/clis/gemini/ask.test.js +100 -0
  105. package/dist/clis/gemini/reply-state.test.d.ts +1 -0
  106. package/dist/clis/gemini/reply-state.test.js +641 -0
  107. package/dist/clis/gemini/utils.d.ts +44 -1
  108. package/dist/clis/gemini/utils.js +528 -61
  109. package/dist/clis/gemini/utils.test.js +149 -2
  110. package/dist/clis/hupu/detail.d.ts +1 -0
  111. package/dist/clis/hupu/detail.js +72 -0
  112. package/dist/clis/hupu/hot.yaml +43 -0
  113. package/dist/clis/hupu/like.d.ts +1 -0
  114. package/dist/clis/hupu/like.js +75 -0
  115. package/dist/clis/hupu/reply.d.ts +1 -0
  116. package/dist/clis/hupu/reply.js +71 -0
  117. package/dist/clis/hupu/search.d.ts +1 -0
  118. package/dist/clis/hupu/search.js +59 -0
  119. package/dist/clis/hupu/unlike.d.ts +1 -0
  120. package/dist/clis/hupu/unlike.js +75 -0
  121. package/dist/clis/hupu/utils.d.ts +20 -0
  122. package/dist/clis/hupu/utils.js +319 -0
  123. package/dist/clis/instagram/_shared/private-publish.d.ts +138 -0
  124. package/dist/clis/instagram/_shared/private-publish.js +1030 -0
  125. package/dist/clis/instagram/_shared/private-publish.test.d.ts +1 -0
  126. package/dist/clis/instagram/_shared/private-publish.test.js +705 -0
  127. package/dist/clis/instagram/_shared/protocol-capture.d.ts +26 -0
  128. package/dist/clis/instagram/_shared/protocol-capture.js +282 -0
  129. package/dist/clis/instagram/_shared/protocol-capture.test.d.ts +1 -0
  130. package/dist/clis/instagram/_shared/protocol-capture.test.js +114 -0
  131. package/dist/clis/instagram/_shared/runtime-info.d.ts +9 -0
  132. package/dist/clis/instagram/_shared/runtime-info.js +81 -0
  133. package/dist/clis/instagram/note.d.ts +1 -0
  134. package/dist/clis/instagram/note.js +222 -0
  135. package/dist/clis/instagram/note.test.d.ts +1 -0
  136. package/dist/clis/instagram/note.test.js +81 -0
  137. package/dist/clis/instagram/post.d.ts +4 -0
  138. package/dist/clis/instagram/post.js +1496 -0
  139. package/dist/clis/instagram/post.test.d.ts +1 -0
  140. package/dist/clis/instagram/post.test.js +1647 -0
  141. package/dist/clis/instagram/reel.d.ts +1 -0
  142. package/dist/clis/instagram/reel.js +826 -0
  143. package/dist/clis/instagram/reel.test.d.ts +1 -0
  144. package/dist/clis/instagram/reel.test.js +167 -0
  145. package/dist/clis/instagram/story.d.ts +1 -0
  146. package/dist/clis/instagram/story.js +115 -0
  147. package/dist/clis/instagram/story.test.d.ts +1 -0
  148. package/dist/clis/instagram/story.test.js +167 -0
  149. package/dist/clis/sinafinance/stock-rank.d.ts +4 -0
  150. package/dist/clis/sinafinance/stock-rank.js +65 -0
  151. package/dist/clis/substack/utils.test.js +0 -2
  152. package/dist/clis/twitter/post.js +72 -45
  153. package/dist/clis/twitter/post.test.d.ts +1 -0
  154. package/dist/clis/twitter/post.test.js +116 -0
  155. package/dist/clis/twitter/reply.d.ts +12 -0
  156. package/dist/clis/twitter/reply.js +257 -35
  157. package/dist/clis/twitter/reply.test.d.ts +1 -0
  158. package/dist/clis/twitter/reply.test.js +151 -0
  159. package/dist/clis/xianyu/chat.d.ts +7 -0
  160. package/dist/clis/xianyu/chat.js +146 -0
  161. package/dist/clis/xianyu/chat.test.d.ts +1 -0
  162. package/dist/clis/xianyu/chat.test.js +15 -0
  163. package/dist/clis/xianyu/item.d.ts +7 -0
  164. package/dist/clis/xianyu/item.js +152 -0
  165. package/dist/clis/xianyu/item.test.d.ts +1 -0
  166. package/dist/clis/xianyu/item.test.js +56 -0
  167. package/dist/clis/xianyu/search.d.ts +10 -0
  168. package/dist/clis/xianyu/search.js +134 -0
  169. package/dist/clis/xianyu/search.test.d.ts +1 -0
  170. package/dist/clis/xianyu/search.test.js +17 -0
  171. package/dist/clis/xianyu/utils.d.ts +1 -0
  172. package/dist/clis/xianyu/utils.js +8 -0
  173. package/dist/clis/xiaoe/catalog.yaml +129 -0
  174. package/dist/clis/xiaoe/content.yaml +43 -0
  175. package/dist/clis/xiaoe/courses.yaml +73 -0
  176. package/dist/clis/xiaoe/detail.yaml +39 -0
  177. package/dist/clis/xiaoe/play-url.yaml +124 -0
  178. package/dist/clis/xiaohongshu/comments.test.js +0 -2
  179. package/dist/clis/xiaohongshu/creator-note-detail.test.js +0 -2
  180. package/dist/clis/xiaohongshu/creator-notes.test.js +0 -2
  181. package/dist/clis/xiaohongshu/download.test.js +0 -2
  182. package/dist/clis/xiaohongshu/note.test.js +0 -2
  183. package/dist/clis/xiaohongshu/publish.test.js +0 -2
  184. package/dist/clis/xiaohongshu/search.js +29 -20
  185. package/dist/clis/xiaohongshu/search.test.js +56 -48
  186. package/dist/clis/yuanbao/ask.d.ts +21 -0
  187. package/dist/clis/yuanbao/ask.js +427 -0
  188. package/dist/clis/yuanbao/ask.test.d.ts +1 -0
  189. package/dist/clis/yuanbao/ask.test.js +124 -0
  190. package/dist/clis/yuanbao/new.d.ts +1 -0
  191. package/dist/clis/yuanbao/new.js +70 -0
  192. package/dist/clis/yuanbao/new.test.d.ts +1 -0
  193. package/dist/clis/yuanbao/new.test.js +30 -0
  194. package/dist/clis/yuanbao/shared.d.ts +13 -0
  195. package/dist/clis/yuanbao/shared.js +49 -0
  196. package/dist/clis/zhihu/question.js +30 -19
  197. package/dist/clis/zhihu/question.test.js +34 -16
  198. package/dist/commanderAdapter.js +8 -4
  199. package/dist/commanderAdapter.test.js +42 -0
  200. package/dist/completion.js +3 -1
  201. package/dist/completion.test.d.ts +1 -0
  202. package/dist/completion.test.js +23 -0
  203. package/dist/doctor.js +1 -1
  204. package/dist/electron-apps.d.ts +2 -0
  205. package/dist/electron-apps.js +7 -1
  206. package/dist/errors.js +1 -1
  207. package/dist/execution.js +25 -35
  208. package/dist/explore.js +1 -1
  209. package/dist/launcher.d.ts +4 -0
  210. package/dist/launcher.js +64 -8
  211. package/dist/launcher.test.js +88 -7
  212. package/dist/output.d.ts +2 -0
  213. package/dist/output.js +10 -1
  214. package/dist/output.test.d.ts +0 -3
  215. package/dist/output.test.js +59 -92
  216. package/dist/pipeline/executor.test.js +0 -2
  217. package/dist/pipeline/steps/download.test.js +0 -2
  218. package/dist/registry.d.ts +2 -0
  219. package/dist/serialization.d.ts +1 -0
  220. package/dist/serialization.js +1 -0
  221. package/dist/types.d.ts +9 -2
  222. package/docs/.vitepress/config.mts +4 -0
  223. package/docs/adapters/browser/1688.md +52 -0
  224. package/docs/adapters/browser/36kr.md +2 -1
  225. package/docs/adapters/browser/doubao.md +5 -1
  226. package/docs/adapters/browser/hupu.md +53 -0
  227. package/docs/adapters/browser/sinafinance.md +32 -2
  228. package/docs/adapters/browser/weibo.md +6 -1
  229. package/docs/adapters/browser/wikipedia.md +2 -0
  230. package/docs/adapters/browser/xianyu.md +42 -0
  231. package/docs/adapters/browser/xiaoe.md +44 -0
  232. package/docs/adapters/browser/yuanbao.md +64 -0
  233. package/docs/adapters/index.md +14 -5
  234. package/docs/comparison.md +1 -1
  235. package/docs/developer/ai-workflow.md +2 -2
  236. package/docs/developer/contributing.md +1 -1
  237. package/docs/developer/testing.md +2 -0
  238. package/docs/guide/plugins.md +1 -0
  239. package/docs/guide/troubleshooting.md +11 -0
  240. package/docs/superpowers/specs/2026-04-03-v2ex-autoresearch-design.md +41 -0
  241. package/docs/zh/guide/plugins.md +1 -0
  242. package/extension/dist/background.js +1127 -0
  243. package/extension/src/background.test.ts +39 -0
  244. package/extension/src/background.ts +223 -34
  245. package/extension/src/cdp.ts +194 -4
  246. package/extension/src/protocol.ts +22 -1
  247. package/package.json +3 -2
  248. package/scripts/postinstall.js +1 -1
  249. package/skills/opencli-explorer/SKILL.md +1 -1
  250. package/skills/opencli-oneshot/SKILL.md +2 -2
  251. package/skills/opencli-operate/SKILL.md +120 -27
  252. package/skills/opencli-usage/SKILL.md +31 -20
  253. package/skills/opencli-usage/browser.md +114 -16
  254. package/skills/opencli-usage/public-api.md +32 -3
  255. package/skills/smart-search/SKILL.md +156 -0
  256. package/skills/smart-search/references/sources-ai.md +74 -0
  257. package/skills/smart-search/references/sources-info.md +43 -0
  258. package/skills/smart-search/references/sources-media.md +50 -0
  259. package/skills/smart-search/references/sources-other.md +42 -0
  260. package/skills/smart-search/references/sources-shopping.md +31 -0
  261. package/skills/smart-search/references/sources-social.md +51 -0
  262. package/skills/smart-search/references/sources-tech.md +42 -0
  263. package/skills/smart-search/references/sources-travel.md +20 -0
  264. package/src/browser/base-page.ts +41 -6
  265. package/src/browser/bridge.ts +11 -8
  266. package/src/browser/cdp.ts +1 -8
  267. package/src/browser/daemon-client.ts +11 -1
  268. package/src/browser/dom-helpers.ts +43 -31
  269. package/src/browser/dom-snapshot.ts +23 -1
  270. package/src/browser/page.ts +115 -31
  271. package/src/browser.test.ts +1 -1
  272. package/src/build-manifest.ts +2 -0
  273. package/src/cli.test.ts +133 -0
  274. package/src/cli.ts +73 -11
  275. package/src/clis/1688/item.test.ts +69 -0
  276. package/src/clis/1688/item.ts +282 -0
  277. package/src/clis/1688/search.test.ts +81 -0
  278. package/src/clis/1688/search.ts +402 -0
  279. package/src/clis/1688/shared.test.ts +75 -0
  280. package/src/clis/1688/shared.ts +623 -0
  281. package/src/clis/1688/store.test.ts +69 -0
  282. package/src/clis/1688/store.ts +300 -0
  283. package/src/clis/amazon/bestsellers.test.ts +12 -3
  284. package/src/clis/amazon/bestsellers.ts +6 -178
  285. package/src/clis/amazon/movers-shakers.ts +8 -0
  286. package/src/clis/amazon/new-releases.ts +8 -0
  287. package/src/clis/amazon/rankings.test.ts +47 -0
  288. package/src/clis/amazon/rankings.ts +312 -0
  289. package/src/clis/amazon/shared.test.ts +16 -0
  290. package/src/clis/amazon/shared.ts +134 -12
  291. package/src/clis/bilibili/comments.test.ts +4 -3
  292. package/src/clis/bilibili/comments.ts +2 -2
  293. package/src/clis/bilibili/download.ts +2 -1
  294. package/src/clis/bilibili/subtitle.test.ts +2 -1
  295. package/src/clis/bilibili/subtitle.ts +4 -3
  296. package/src/clis/bilibili/utils.test.ts +21 -0
  297. package/src/clis/bilibili/utils.ts +27 -0
  298. package/src/clis/douban/marks.ts +1 -1
  299. package/src/clis/douban/subject.yaml +50 -19
  300. package/src/clis/doubao/utils.ts +32 -12
  301. package/src/clis/douyin/_shared/browser-fetch.test.ts +0 -1
  302. package/src/clis/douyin/_shared/transcode.test.ts +0 -2
  303. package/src/clis/douyin/draft.test.ts +0 -2
  304. package/src/clis/facebook/search.test.ts +0 -2
  305. package/src/clis/gemini/ask.test.ts +116 -0
  306. package/src/clis/gemini/ask.ts +10 -3
  307. package/src/clis/gemini/reply-state.test.ts +708 -0
  308. package/src/clis/gemini/utils.test.ts +184 -2
  309. package/src/clis/gemini/utils.ts +588 -60
  310. package/src/clis/hupu/detail.ts +126 -0
  311. package/src/clis/hupu/hot.yaml +43 -0
  312. package/src/clis/hupu/like.ts +76 -0
  313. package/src/clis/hupu/reply.ts +76 -0
  314. package/src/clis/hupu/search.ts +95 -0
  315. package/src/clis/hupu/unlike.ts +76 -0
  316. package/src/clis/hupu/utils.ts +381 -0
  317. package/src/clis/instagram/_shared/private-publish.test.ts +827 -0
  318. package/src/clis/instagram/_shared/private-publish.ts +1303 -0
  319. package/src/clis/instagram/_shared/protocol-capture.test.ts +148 -0
  320. package/src/clis/instagram/_shared/protocol-capture.ts +321 -0
  321. package/src/clis/instagram/_shared/runtime-info.ts +91 -0
  322. package/src/clis/instagram/note.test.ts +96 -0
  323. package/src/clis/instagram/note.ts +254 -0
  324. package/src/clis/instagram/post.test.ts +1716 -0
  325. package/src/clis/instagram/post.ts +1620 -0
  326. package/src/clis/instagram/reel.test.ts +191 -0
  327. package/src/clis/instagram/reel.ts +886 -0
  328. package/src/clis/instagram/story.test.ts +191 -0
  329. package/src/clis/instagram/story.ts +151 -0
  330. package/src/clis/sinafinance/stock-rank.ts +68 -0
  331. package/src/clis/substack/utils.test.ts +0 -2
  332. package/src/clis/twitter/post.test.ts +157 -0
  333. package/src/clis/twitter/post.ts +82 -48
  334. package/src/clis/twitter/reply.test.ts +177 -0
  335. package/src/clis/twitter/reply.ts +285 -39
  336. package/src/clis/xianyu/chat.test.ts +20 -0
  337. package/src/clis/xianyu/chat.ts +175 -0
  338. package/src/clis/xianyu/item.test.ts +67 -0
  339. package/src/clis/xianyu/item.ts +172 -0
  340. package/src/clis/xianyu/search.test.ts +22 -0
  341. package/src/clis/xianyu/search.ts +151 -0
  342. package/src/clis/xianyu/utils.ts +9 -0
  343. package/src/clis/xiaoe/catalog.yaml +129 -0
  344. package/src/clis/xiaoe/content.yaml +43 -0
  345. package/src/clis/xiaoe/courses.yaml +73 -0
  346. package/src/clis/xiaoe/detail.yaml +39 -0
  347. package/src/clis/xiaoe/play-url.yaml +124 -0
  348. package/src/clis/xiaohongshu/comments.test.ts +0 -2
  349. package/src/clis/xiaohongshu/creator-note-detail.test.ts +0 -2
  350. package/src/clis/xiaohongshu/creator-notes.test.ts +0 -2
  351. package/src/clis/xiaohongshu/download.test.ts +0 -2
  352. package/src/clis/xiaohongshu/note.test.ts +0 -2
  353. package/src/clis/xiaohongshu/publish.test.ts +0 -2
  354. package/src/clis/xiaohongshu/search.test.ts +59 -48
  355. package/src/clis/xiaohongshu/search.ts +31 -21
  356. package/src/clis/yuanbao/ask.test.ts +156 -0
  357. package/src/clis/yuanbao/ask.ts +522 -0
  358. package/src/clis/yuanbao/new.test.ts +36 -0
  359. package/src/clis/yuanbao/new.ts +81 -0
  360. package/src/clis/yuanbao/shared.ts +57 -0
  361. package/src/clis/zhihu/question.test.ts +42 -17
  362. package/src/clis/zhihu/question.ts +31 -26
  363. package/src/commanderAdapter.test.ts +51 -0
  364. package/src/commanderAdapter.ts +8 -4
  365. package/src/completion.test.ts +30 -0
  366. package/src/completion.ts +3 -1
  367. package/src/doctor.ts +1 -1
  368. package/src/electron-apps.ts +9 -1
  369. package/src/errors.ts +1 -1
  370. package/src/execution.ts +26 -30
  371. package/src/explore.ts +1 -1
  372. package/src/launcher.test.ts +121 -7
  373. package/src/launcher.ts +87 -9
  374. package/src/output.test.ts +50 -90
  375. package/src/output.ts +10 -1
  376. package/src/pipeline/executor.test.ts +0 -2
  377. package/src/pipeline/steps/download.test.ts +0 -2
  378. package/src/registry.ts +2 -0
  379. package/src/serialization.ts +2 -0
  380. package/src/types.ts +9 -2
  381. package/tests/e2e/browser-auth.test.ts +9 -0
  382. package/CLI-EXPLORER.md +0 -724
  383. package/CLI-ONESHOT.md +0 -216
  384. package/SKILL.md +0 -59
@@ -8,6 +8,27 @@
8
8
 
9
9
  const attached = new Set<number>();
10
10
 
11
+ type NetworkCaptureEntry = {
12
+ kind: 'cdp';
13
+ url: string;
14
+ method: string;
15
+ requestHeaders?: Record<string, string>;
16
+ requestBodyKind?: string;
17
+ requestBodyPreview?: string;
18
+ responseStatus?: number;
19
+ responseContentType?: string;
20
+ responseHeaders?: Record<string, string>;
21
+ responsePreview?: string;
22
+ timestamp: number;
23
+ };
24
+
25
+ type NetworkCaptureState = {
26
+ patterns: string[];
27
+ entries: NetworkCaptureEntry[];
28
+ requestToIndex: Map<string, number>;
29
+ };
30
+
31
+ const networkCaptures = new Map<number, NetworkCaptureState>();
11
32
  /** Check if a URL can be attached via CDP — only allow http(s) and blank pages. */
12
33
  function isDebuggableUrl(url?: string): boolean {
13
34
  if (!url) return true; // empty/undefined = tab still loading, allow it
@@ -79,6 +100,16 @@ export async function ensureAttached(tabId: number, aggressiveRetry: boolean = f
79
100
  }
80
101
 
81
102
  if (lastError) {
103
+ // Log detailed diagnostics for debugging extension conflicts
104
+ let finalUrl = 'unknown';
105
+ let finalWindowId = 'unknown';
106
+ try {
107
+ const tab = await chrome.tabs.get(tabId);
108
+ finalUrl = tab.url ?? 'undefined';
109
+ finalWindowId = String(tab.windowId);
110
+ } catch { /* tab gone */ }
111
+ console.warn(`[opencli] attach failed for tab ${tabId}: url=${finalUrl}, windowId=${finalWindowId}, error=${lastError}`);
112
+
82
113
  const hint = lastError.includes('chrome-extension://')
83
114
  ? '. Tip: another Chrome extension may be interfering — try disabling other extensions'
84
115
  : '';
@@ -121,11 +152,14 @@ export async function evaluate(tabId: number, expression: string, aggressiveRetr
121
152
  } catch (e) {
122
153
  const msg = e instanceof Error ? e.message : String(e);
123
154
  // Only retry on attach/debugger errors, not on JS eval errors
124
- const isAttachError = msg.includes('attach failed') || msg.includes('Debugger is not attached')
125
- || msg.includes('chrome-extension://') || msg.includes('Target closed');
155
+ const isNavigateError = msg.includes('Inspected target navigated') || msg.includes('Target closed');
156
+ const isAttachError = isNavigateError || msg.includes('attach failed') || msg.includes('Debugger is not attached')
157
+ || msg.includes('chrome-extension://');
126
158
  if (isAttachError && attempt < MAX_EVAL_RETRIES) {
127
159
  attached.delete(tabId); // Force re-attach on next attempt
128
- await new Promise(resolve => setTimeout(resolve, 1000));
160
+ // SPA navigations recover quickly; debugger detach needs longer
161
+ const retryMs = isNavigateError ? 200 : 500;
162
+ await new Promise(resolve => setTimeout(resolve, retryMs));
129
163
  continue;
130
164
  }
131
165
  throw e;
@@ -228,18 +262,100 @@ export async function setFileInputFiles(
228
262
  });
229
263
  }
230
264
 
265
+ export async function insertText(
266
+ tabId: number,
267
+ text: string,
268
+ ): Promise<void> {
269
+ await ensureAttached(tabId);
270
+ await chrome.debugger.sendCommand({ tabId }, 'Input.insertText', { text });
271
+ }
272
+
273
+ function normalizeCapturePatterns(pattern?: string): string[] {
274
+ return String(pattern || '')
275
+ .split('|')
276
+ .map((part) => part.trim())
277
+ .filter(Boolean);
278
+ }
279
+
280
+ function shouldCaptureUrl(url: string | undefined, patterns: string[]): boolean {
281
+ if (!url) return false;
282
+ if (!patterns.length) return true;
283
+ return patterns.some((pattern) => url.includes(pattern));
284
+ }
285
+
286
+ function normalizeHeaders(headers: unknown): Record<string, string> {
287
+ if (!headers || typeof headers !== 'object') return {};
288
+ const out: Record<string, string> = {};
289
+ for (const [key, value] of Object.entries(headers as Record<string, unknown>)) {
290
+ out[String(key)] = String(value);
291
+ }
292
+ return out;
293
+ }
294
+
295
+ function getOrCreateNetworkCaptureEntry(tabId: number, requestId: string, fallback?: {
296
+ url?: string;
297
+ method?: string;
298
+ requestHeaders?: Record<string, string>;
299
+ }): NetworkCaptureEntry | null {
300
+ const state = networkCaptures.get(tabId);
301
+ if (!state) return null;
302
+ const existingIndex = state.requestToIndex.get(requestId);
303
+ if (existingIndex !== undefined) {
304
+ return state.entries[existingIndex] || null;
305
+ }
306
+ const url = fallback?.url || '';
307
+ if (!shouldCaptureUrl(url, state.patterns)) return null;
308
+ const entry: NetworkCaptureEntry = {
309
+ kind: 'cdp',
310
+ url,
311
+ method: fallback?.method || 'GET',
312
+ requestHeaders: fallback?.requestHeaders || {},
313
+ timestamp: Date.now(),
314
+ };
315
+ state.entries.push(entry);
316
+ state.requestToIndex.set(requestId, state.entries.length - 1);
317
+ return entry;
318
+ }
319
+
320
+ export async function startNetworkCapture(
321
+ tabId: number,
322
+ pattern?: string,
323
+ ): Promise<void> {
324
+ await ensureAttached(tabId);
325
+ await chrome.debugger.sendCommand({ tabId }, 'Network.enable');
326
+ networkCaptures.set(tabId, {
327
+ patterns: normalizeCapturePatterns(pattern),
328
+ entries: [],
329
+ requestToIndex: new Map(),
330
+ });
331
+ }
332
+
333
+ export async function readNetworkCapture(tabId: number): Promise<NetworkCaptureEntry[]> {
334
+ const state = networkCaptures.get(tabId);
335
+ if (!state) return [];
336
+ const entries = state.entries.slice();
337
+ state.entries = [];
338
+ state.requestToIndex.clear();
339
+ return entries;
340
+ }
341
+
231
342
  export async function detach(tabId: number): Promise<void> {
232
343
  if (!attached.has(tabId)) return;
233
344
  attached.delete(tabId);
345
+ networkCaptures.delete(tabId);
234
346
  try { await chrome.debugger.detach({ tabId }); } catch { /* ignore */ }
235
347
  }
236
348
 
237
349
  export function registerListeners(): void {
238
350
  chrome.tabs.onRemoved.addListener((tabId) => {
239
351
  attached.delete(tabId);
352
+ networkCaptures.delete(tabId);
240
353
  });
241
354
  chrome.debugger.onDetach.addListener((source) => {
242
- if (source.tabId) attached.delete(source.tabId);
355
+ if (source.tabId) {
356
+ attached.delete(source.tabId);
357
+ networkCaptures.delete(source.tabId);
358
+ }
243
359
  });
244
360
  // Invalidate attached cache when tab URL changes to non-debuggable
245
361
  chrome.tabs.onUpdated.addListener(async (tabId, info) => {
@@ -247,4 +363,78 @@ export function registerListeners(): void {
247
363
  await detach(tabId);
248
364
  }
249
365
  });
366
+ chrome.debugger.onEvent.addListener(async (source, method, params) => {
367
+ const tabId = source.tabId;
368
+ if (!tabId) return;
369
+ const state = networkCaptures.get(tabId);
370
+ if (!state) return;
371
+
372
+ if (method === 'Network.requestWillBeSent') {
373
+ const requestId = String(params?.requestId || '');
374
+ const request = params?.request as {
375
+ url?: string;
376
+ method?: string;
377
+ headers?: Record<string, unknown>;
378
+ postData?: string;
379
+ hasPostData?: boolean;
380
+ } | undefined;
381
+ const entry = getOrCreateNetworkCaptureEntry(tabId, requestId, {
382
+ url: request?.url,
383
+ method: request?.method,
384
+ requestHeaders: normalizeHeaders(request?.headers),
385
+ });
386
+ if (!entry) return;
387
+ entry.requestBodyKind = request?.hasPostData ? 'string' : 'empty';
388
+ entry.requestBodyPreview = String(request?.postData || '').slice(0, 4000);
389
+ try {
390
+ const postData = await chrome.debugger.sendCommand({ tabId }, 'Network.getRequestPostData', { requestId }) as { postData?: string };
391
+ if (postData?.postData) {
392
+ entry.requestBodyKind = 'string';
393
+ entry.requestBodyPreview = postData.postData.slice(0, 4000);
394
+ }
395
+ } catch {
396
+ // Optional; some requests do not expose postData.
397
+ }
398
+ return;
399
+ }
400
+
401
+ if (method === 'Network.responseReceived') {
402
+ const requestId = String(params?.requestId || '');
403
+ const response = params?.response as {
404
+ url?: string;
405
+ mimeType?: string;
406
+ status?: number;
407
+ headers?: Record<string, unknown>;
408
+ } | undefined;
409
+ const entry = getOrCreateNetworkCaptureEntry(tabId, requestId, {
410
+ url: response?.url,
411
+ });
412
+ if (!entry) return;
413
+ entry.responseStatus = response?.status;
414
+ entry.responseContentType = response?.mimeType || '';
415
+ entry.responseHeaders = normalizeHeaders(response?.headers);
416
+ return;
417
+ }
418
+
419
+ if (method === 'Network.loadingFinished') {
420
+ const requestId = String(params?.requestId || '');
421
+ const stateEntryIndex = state.requestToIndex.get(requestId);
422
+ if (stateEntryIndex === undefined) return;
423
+ const entry = state.entries[stateEntryIndex];
424
+ if (!entry) return;
425
+ try {
426
+ const body = await chrome.debugger.sendCommand({ tabId }, 'Network.getResponseBody', { requestId }) as {
427
+ body?: string;
428
+ base64Encoded?: boolean;
429
+ };
430
+ if (typeof body?.body === 'string') {
431
+ entry.responsePreview = body.base64Encoded
432
+ ? `base64:${body.body.slice(0, 4000)}`
433
+ : body.body.slice(0, 4000);
434
+ }
435
+ } catch {
436
+ // Optional; bodies are unavailable for some requests (e.g. uploads).
437
+ }
438
+ }
439
+ });
250
440
  }
@@ -5,7 +5,20 @@
5
5
  * Everything else is just JS code sent via 'exec'.
6
6
  */
7
7
 
8
- export type Action = 'exec' | 'navigate' | 'tabs' | 'cookies' | 'screenshot' | 'close-window' | 'sessions' | 'set-file-input' | 'cdp';
8
+ export type Action =
9
+ | 'exec'
10
+ | 'navigate'
11
+ | 'tabs'
12
+ | 'cookies'
13
+ | 'screenshot'
14
+ | 'close-window'
15
+ | 'sessions'
16
+ | 'set-file-input'
17
+ | 'insert-text'
18
+ | 'bind-current'
19
+ | 'network-capture-start'
20
+ | 'network-capture-read'
21
+ | 'cdp';
9
22
 
10
23
  export interface Command {
11
24
  /** Unique request ID */
@@ -26,6 +39,10 @@ export interface Command {
26
39
  index?: number;
27
40
  /** Cookie domain filter */
28
41
  domain?: string;
42
+ /** Optional hostname/domain to require for current-tab binding */
43
+ matchDomain?: string;
44
+ /** Optional pathname prefix to require for current-tab binding */
45
+ matchPathPrefix?: string;
29
46
  /** Screenshot format: png (default) or jpeg */
30
47
  format?: 'png' | 'jpeg';
31
48
  /** JPEG quality (0-100), only for jpeg format */
@@ -36,6 +53,10 @@ export interface Command {
36
53
  files?: string[];
37
54
  /** CSS selector for file input element (set-file-input action) */
38
55
  selector?: string;
56
+ /** Raw text payload for insert-text action */
57
+ text?: string;
58
+ /** URL substring filter pattern for network capture actions */
59
+ pattern?: string;
39
60
  /** CDP method name for 'cdp' action (e.g. 'Accessibility.getFullAXTree') */
40
61
  cdpMethod?: string;
41
62
  /** CDP method params for 'cdp' action */
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@jackwener/opencli",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "description": "Make any website your CLI. AI-powered.",
7
+ "description": "Make any website or Electron App your CLI. AI-powered.",
8
8
  "engines": {
9
9
  "node": ">=20.0.0"
10
10
  },
@@ -30,6 +30,7 @@
30
30
  "postinstall": "node scripts/postinstall.js || true",
31
31
  "typecheck": "tsc --noEmit",
32
32
  "lint": "tsc --noEmit",
33
+ "prepare": "[ -d src ] && npm run build || true",
33
34
  "prepublishOnly": "npm run build",
34
35
  "test": "vitest run --project unit",
35
36
  "test:bun": "bun vitest run --project unit",
@@ -217,7 +217,7 @@ function main() {
217
217
  console.log(' \x1b[1mNext step — Browser Bridge setup\x1b[0m');
218
218
  console.log(' Browser commands (bilibili, zhihu, twitter...) require the extension:');
219
219
  console.log(' 1. Download: https://github.com/jackwener/opencli/releases');
220
- console.log(' 2. Open chrome://extensions → enable Developer Mode → Load unpacked');
220
+ console.log(' 2. In Chrome or Chromium, open chrome://extensions → enable Developer Mode → Load unpacked');
221
221
  console.log('');
222
222
  console.log(' Then run \x1b[36mopencli doctor\x1b[0m to verify.');
223
223
  console.log('');
@@ -10,7 +10,7 @@ tags: [opencli, adapter, browser, api-discovery, cli, web-scraping, automation]
10
10
  > 从零到发布,覆盖 API 发现、方案选择、适配器编写、测试验证全流程。
11
11
 
12
12
  > [!TIP]
13
- > **只想为一个具体页面快速生成一个命令?** 看 [CLI-ONESHOT.md](./CLI-ONESHOT.md)(~150 行,4 步搞定)。
13
+ > **只想为一个具体页面快速生成一个命令?** 看 [opencli-oneshot skill](../opencli-oneshot/SKILL.md)(~150 行,4 步搞定)。
14
14
  > 本文档适合从零探索一个新站点的完整流程。
15
15
 
16
16
  ---
@@ -7,7 +7,7 @@ tags: [opencli, adapter, quick-start, yaml, cli, one-shot, automation]
7
7
  # CLI-ONESHOT — 单点快速 CLI 生成
8
8
 
9
9
  > 给一个 URL + 一句话描述,4 步生成一个 CLI 命令。
10
- > 完整探索式开发请看 [CLI-EXPLORER.md](./CLI-EXPLORER.md)。
10
+ > 完整探索式开发请看 [opencli-explorer skill](../opencli-explorer/SKILL.md)。
11
11
 
12
12
  ---
13
13
 
@@ -219,4 +219,4 @@ opencli mysite mycommand --limit 3 -v # 实际运行
219
219
 
220
220
  ## 就这样,没了
221
221
 
222
- 写完文件 → build → run → 提交。有问题再看 [CLI-EXPLORER.md](./CLI-EXPLORER.md)。
222
+ 写完文件 → build → run → 提交。有问题再看 [opencli-explorer skill](../opencli-explorer/SKILL.md)。
@@ -4,7 +4,7 @@ description: Make websites accessible for AI agents. Navigate, click, type, extr
4
4
  allowed-tools: Bash(opencli:*), Read, Edit, Write
5
5
  ---
6
6
 
7
- # OpenCLI — Make Websites Accessible for AI Agents
7
+ # OpenCLI Operate Browser Automation for AI Agents
8
8
 
9
9
  Control Chrome step-by-step via CLI. Reuses existing login sessions — no passwords needed.
10
10
 
@@ -16,25 +16,61 @@ opencli doctor # Verify extension + daemon connectivity
16
16
 
17
17
  Requires: Chrome running + OpenCLI Browser Bridge extension installed.
18
18
 
19
- ## Quickstart for AI Agents (1 step)
19
+ ## Critical Rules
20
20
 
21
- Point your AI agent to this file. It contains everything needed to operate browsers.
21
+ 1. **ALWAYS use `state` to inspect the page, NEVER use `screenshot`** — `state` returns structured DOM with `[N]` element indices, is instant and costs zero tokens. `screenshot` requires vision processing and is slow. Only use `screenshot` when the user explicitly asks to save a visual.
22
+ 2. **ALWAYS use `click`/`type`/`select` for interaction, NEVER use `eval` to click or type** — `eval "el.click()"` bypasses scrollIntoView and CDP click pipeline, causing failures on off-screen elements. Use `state` to find the `[N]` index, then `click <N>`.
23
+ 3. **Verify inputs with `get value`, not screenshots** — after `type`, run `get value <index>` to confirm.
24
+ 4. **Run `state` after every page change** — after `open`, `click` (on links), `scroll`, always run `state` to see the new elements and their indices. Never guess indices.
25
+ 5. **Chain commands aggressively with `&&`** — combine `open + state`, multiple `type` calls, and `type + get value` into single `&&` chains. Each tool call has overhead; chaining cuts it.
26
+ 6. **`eval` is read-only** — use `eval` ONLY for data extraction (`JSON.stringify(...)`), never for clicking, typing, or navigating. Always wrap in IIFE to avoid variable conflicts: `eval "(function(){ const x = ...; return JSON.stringify(x); })()"`.
27
+ 7. **Minimize total tool calls** — plan your sequence before acting. A good task completion uses 3-5 tool calls, not 15-20. Combine `open + state` as one call. Combine `type + type + click` as one call. Only run `state` separately when you need to discover new indices.
28
+ 8. **Prefer `network` to discover APIs** — most sites have JSON APIs. API-based adapters are more reliable than DOM scraping.
22
29
 
23
- ## Quickstart for Humans (3 steps)
30
+ ## Command Cost Guide
24
31
 
32
+ | Cost | Commands | When to use |
33
+ |------|----------|-------------|
34
+ | **Free & instant** | `state`, `get *`, `eval`, `network`, `scroll`, `keys` | Default — use these |
35
+ | **Free but changes page** | `open`, `click`, `type`, `select`, `back` | Interaction — run `state` after |
36
+ | **Expensive (vision tokens)** | `screenshot` | ONLY when user needs a saved image |
37
+
38
+ ## Action Chaining Rules
39
+
40
+ Commands can be chained with `&&`. The browser persists via daemon, so chaining is safe.
41
+
42
+ **Always chain when possible** — fewer tool calls = faster completion:
25
43
  ```bash
26
- npm install -g @jackwener/opencli # 1. Install
27
- # Install extension from chrome://extensions # 2. Load extension
28
- opencli operate open https://example.com # 3. Go!
44
+ # GOOD: open + inspect in one call (saves 1 round trip)
45
+ opencli operate open https://example.com && opencli operate state
46
+
47
+ # GOOD: fill form in one call (saves 2 round trips)
48
+ opencli operate type 3 "hello" && opencli operate type 4 "world" && opencli operate click 7
49
+
50
+ # GOOD: type + verify in one call
51
+ opencli operate type 5 "test@example.com" && opencli operate get value 5
52
+
53
+ # GOOD: click + wait + state in one call (for page-changing clicks)
54
+ opencli operate click 12 && opencli operate wait time 1 && opencli operate state
55
+
56
+ # BAD: separate calls for each action (wasteful)
57
+ opencli operate type 3 "hello" # Don't do this
58
+ opencli operate type 4 "world" # when you can chain
59
+ opencli operate click 7 # all three together
29
60
  ```
30
61
 
62
+ **Page-changing — always put last** in a chain (subsequent commands see stale indices):
63
+ - `open <url>`, `back`, `click <link/button that navigates>`
64
+
65
+ **Rule**: Chain when you already know the indices. Run `state` separately when you need to discover indices first.
66
+
31
67
  ## Core Workflow
32
68
 
33
69
  1. **Navigate**: `opencli operate open <url>`
34
- 2. **Inspect**: `opencli operate state` → see elements with `[N]` indices
70
+ 2. **Inspect**: `opencli operate state` → elements with `[N]` indices
35
71
  3. **Interact**: use indices — `click`, `type`, `select`, `keys`
36
- 4. **Wait**: `opencli operate wait selector ".loaded"` or `wait text "Success"`
37
- 5. **Verify**: `opencli operate get title` or `opencli operate screenshot`
72
+ 4. **Wait** (if needed): `opencli operate wait selector ".loaded"` or `wait text "Success"`
73
+ 5. **Verify**: `opencli operate state` or `opencli operate get value <N>`
38
74
  6. **Repeat**: browser stays open between commands
39
75
  7. **Save**: write a TS adapter to `~/.opencli/clis/<site>/<command>.ts`
40
76
 
@@ -43,26 +79,26 @@ opencli operate open https://example.com # 3. Go!
43
79
  ### Navigation
44
80
 
45
81
  ```bash
46
- opencli operate open <url> # Open URL
47
- opencli operate back # Go back
82
+ opencli operate open <url> # Open URL (page-changing)
83
+ opencli operate back # Go back (page-changing)
48
84
  opencli operate scroll down # Scroll (up/down, --amount N)
49
85
  opencli operate scroll up --amount 1000
50
86
  ```
51
87
 
52
- ### Inspect
88
+ ### Inspect (free & instant)
53
89
 
54
90
  ```bash
55
- opencli operate state # Elements with [N] indices
56
- opencli operate screenshot [path.png] # Screenshot
91
+ opencli operate state # Structured DOM with [N] indices — PRIMARY tool
92
+ opencli operate screenshot [path.png] # Save visual to file — ONLY for user deliverables
57
93
  ```
58
94
 
59
- ### Get (structured data)
95
+ ### Get (free & instant)
60
96
 
61
97
  ```bash
62
98
  opencli operate get title # Page title
63
99
  opencli operate get url # Current URL
64
100
  opencli operate get text <index> # Element text content
65
- opencli operate get value <index> # Input/textarea value
101
+ opencli operate get value <index> # Input/textarea value (use to verify after type)
66
102
  opencli operate get html # Full page HTML
67
103
  opencli operate get html --selector "h1" # Scoped HTML
68
104
  opencli operate get attributes <index> # Element attributes
@@ -79,18 +115,37 @@ opencli operate keys "Enter" # Press key (Enter, Escape, Tab, Control
79
115
 
80
116
  ### Wait
81
117
 
118
+ Three variants — use the right one for the situation:
119
+
82
120
  ```bash
83
- opencli operate wait selector ".loaded" # Wait for element
84
- opencli operate wait selector ".spinner" --timeout 5000 # With timeout
85
- opencli operate wait text "Success" # Wait for text
86
- opencli operate wait time 3 # Wait N seconds
121
+ opencli operate wait time 3 # Wait N seconds (fixed delay)
122
+ opencli operate wait selector ".loaded" # Wait until element appears in DOM
123
+ opencli operate wait selector ".spinner" --timeout 5000 # With timeout (default 30s)
124
+ opencli operate wait text "Success" # Wait until text appears on page
87
125
  ```
88
126
 
89
- ### Extract
127
+ **When to wait**: After `open` on SPAs, after `click` that triggers async loading, before `eval` on dynamically rendered content.
128
+
129
+ ### Extract (free & instant, read-only)
130
+
131
+ Use `eval` ONLY for reading data. Never use it to click, type, or navigate.
90
132
 
91
133
  ```bash
92
134
  opencli operate eval "document.title"
93
135
  opencli operate eval "JSON.stringify([...document.querySelectorAll('h2')].map(e => e.textContent))"
136
+
137
+ # IMPORTANT: wrap complex logic in IIFE to avoid "already declared" errors
138
+ opencli operate eval "(function(){ const items = [...document.querySelectorAll('.item')]; return JSON.stringify(items.map(e => e.textContent)); })()"
139
+ ```
140
+
141
+ **Selector safety**: Always use fallback selectors — `querySelector` returns `null` on miss:
142
+ ```bash
143
+ # BAD: crashes if selector misses
144
+ opencli operate eval "document.querySelector('.title').textContent"
145
+
146
+ # GOOD: fallback with || or ?.
147
+ opencli operate eval "(document.querySelector('.title') || document.querySelector('h1') || {textContent:''}).textContent"
148
+ opencli operate eval "document.querySelector('.title')?.textContent ?? 'not found'"
94
149
  ```
95
150
 
96
151
  ### Network (API Discovery)
@@ -104,10 +159,14 @@ opencli operate network --all # Include static resources
104
159
  ### Sedimentation (Save as CLI)
105
160
 
106
161
  ```bash
107
- opencli operate init hn/top # Generate adapter scaffold
108
- opencli operate verify hn/top # Test the adapter
162
+ opencli operate init hn/top # Generate adapter scaffold at ~/.opencli/clis/hn/top.ts
163
+ opencli operate verify hn/top # Test the adapter (adds --limit 3 only if `limit` arg is defined)
109
164
  ```
110
165
 
166
+ - `init` auto-detects the domain from the active browser session (no need to specify it)
167
+ - `init` creates the file + populates `site`, `name`, `domain`, and `columns` from current page
168
+ - `verify` runs the adapter end-to-end and prints output; if no `limit` arg exists in the adapter, it won't pass `--limit 3`
169
+
111
170
  ### Session
112
171
 
113
172
  ```bash
@@ -128,8 +187,7 @@ opencli operate close
128
187
  ```bash
129
188
  opencli operate open https://httpbin.org/forms/post
130
189
  opencli operate state # See [3] input "Customer Name", [4] input "Telephone"
131
- opencli operate type 3 "OpenCLI"
132
- opencli operate type 4 "555-0100"
190
+ opencli operate type 3 "OpenCLI" && opencli operate type 4 "555-0100"
133
191
  opencli operate get value 3 # Verify: "OpenCLI"
134
192
  opencli operate close
135
193
  ```
@@ -204,10 +262,45 @@ Save to `~/.opencli/clis/<site>/<command>.ts` → immediately available as `open
204
262
 
205
263
  **Always prefer API over UI** — if you discovered an API during browsing, use `fetch()` directly.
206
264
 
265
+ ## Tips
266
+
267
+ 1. **Always `state` first** — never guess element indices, always inspect first
268
+ 2. **Sessions persist** — browser stays open between commands, no need to re-open
269
+ 3. **Use `eval` for data extraction** — `eval "JSON.stringify(...)"` is faster than multiple `get` calls
270
+ 4. **Use `network` to find APIs** — JSON APIs are more reliable than DOM scraping
271
+ 5. **Alias**: `opencli op` is shorthand for `opencli operate`
272
+
273
+ ## Common Pitfalls
274
+
275
+ 1. **`form.submit()` fails in automation** — Don't use `form.submit()` or `eval` to submit forms. Navigate directly to the search URL instead:
276
+ ```bash
277
+ # BAD: form.submit() often silently fails
278
+ opencli operate eval "document.querySelector('form').submit()"
279
+ # GOOD: construct the URL and navigate
280
+ opencli operate open "https://github.com/search?q=opencli&type=repositories"
281
+ ```
282
+
283
+ 2. **GitHub DOM changes frequently** — Prefer `data-testid` attributes when available; they are more stable than class names or tag structure.
284
+
285
+ 3. **SPA pages need `wait` before extraction** — After `open` or `click` on single-page apps, the DOM isn't ready immediately. Always `wait selector` or `wait text` before `eval`.
286
+
287
+ 4. **Use `state` before clicking** — Run `opencli operate state` to inspect available interactive elements and their indices. Never guess indices from memory.
288
+
289
+ 5. **`evaluate` runs in browser context** — `page.evaluate()` in adapters executes inside the browser. Node.js APIs (`fs`, `path`, `process`) are NOT available. Use `fetch()` for network calls, DOM APIs for page data.
290
+
291
+ 6. **Backticks in `page.evaluate` break JSON storage** — When writing adapters that will be stored/transported as JSON, avoid template literals inside `page.evaluate`. Use string concatenation or function-style evaluate:
292
+ ```typescript
293
+ // BAD: template literal backticks break when adapter is in JSON
294
+ page.evaluate(`document.querySelector("${selector}")`)
295
+ // GOOD: function-style evaluate
296
+ page.evaluate((sel) => document.querySelector(sel), selector)
297
+ ```
298
+
207
299
  ## Troubleshooting
208
300
 
209
301
  | Error | Fix |
210
302
  |-------|-----|
211
303
  | "Browser not connected" | Run `opencli doctor` |
212
304
  | "attach failed: chrome-extension://" | Disable 1Password temporarily |
213
- | Element not found | `opencli operate scroll down` then `opencli operate state` |
305
+ | Element not found | `opencli operate scroll down && opencli operate state` |
306
+ | Stale indices after page change | Run `opencli operate state` again to get fresh indices |