@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
@@ -0,0 +1,42 @@
1
+ # 其他垂直源
2
+
3
+ 用于求职、金融、书籍、词典等不适合归入前述分类的场景。
4
+
5
+ ## 站点
6
+
7
+ ### linkedin
8
+
9
+ - 适用:全球职位、英文岗位、跨国公司招聘
10
+ - 使用前先运行:`opencli linkedin -h`
11
+
12
+ ### boss
13
+
14
+ - 适用:国内职位搜索、招聘与岗位信息
15
+ - 使用前先运行:`opencli boss -h`
16
+
17
+ ### xueqiu
18
+
19
+ - 适用:股票、金融讨论、行情相关线索
20
+ - 使用前先运行:`opencli xueqiu -h`
21
+
22
+ ### weread
23
+
24
+ - 适用:中文书籍搜索与书单线索
25
+ - 使用前先运行:`opencli weread -h`
26
+
27
+ ### dictionary
28
+
29
+ - 适用:英文词义、基础词典查询
30
+ - 使用前先运行:`opencli dictionary -h`
31
+
32
+ ### sinablog
33
+
34
+ - 适用:较旧的中文博客内容
35
+ - 使用前先运行:`opencli sinablog -h`
36
+
37
+ ## 路由提示
38
+
39
+ - 求职:全球优先 `linkedin`,国内优先 `boss`
40
+ - 金融:优先 `xueqiu`
41
+ - 书籍:中文优先 `weread`
42
+ - 英文单词定义:优先 `dictionary`
@@ -0,0 +1,31 @@
1
+ # 购物
2
+
3
+ 用于商品搜索、价格、好价、口碑与区域电商结果。
4
+
5
+ ## 站点
6
+
7
+ ### amazon
8
+
9
+ - 适用:全球商品搜索、价格参考、英文电商
10
+ - 使用前先运行:`opencli amazon -h`
11
+
12
+ ### smzdm
13
+
14
+ - 适用:国内好价、优惠、导购、商品讨论
15
+ - 使用前先运行:`opencli smzdm -h`
16
+
17
+ ### coupang
18
+
19
+ - 适用:韩国电商商品搜索
20
+ - 使用前先运行:`opencli coupang -h`
21
+
22
+ ### douban
23
+
24
+ - 适用:图书、影视、音乐类口碑补充,不是标准电商,但可用于消费决策补充
25
+ - 使用前先运行:`opencli douban -h`
26
+
27
+ ## 路由提示
28
+
29
+ - 用户指定平台时直接使用该平台
30
+ - 未指定平台时,全球商品优先 `amazon`,国内好价优先 `smzdm`
31
+ - 若先用 AI 做产品调研,后续可补电商站点拿实际商品结果
@@ -0,0 +1,51 @@
1
+ # 社交媒体
2
+
3
+ 用于需要原始帖子、原始用户结果、中文或英文社区讨论时。
4
+
5
+ ## 站点
6
+
7
+ ### twitter
8
+
9
+ - 适用:Twitter/X 原始帖子、作者、实时讨论
10
+ - 使用前先运行:`opencli twitter -h`
11
+
12
+ ### weibo
13
+
14
+ - 适用:微博热点、话题、中文舆论
15
+ - 使用前先运行:`opencli weibo -h`
16
+
17
+ ### xiaohongshu
18
+
19
+ - 适用:生活方式、穿搭、美妆、旅行、真实体验
20
+ - 使用前先运行:`opencli xiaohongshu -h`
21
+
22
+ ### zhihu
23
+
24
+ - 适用:中文深度问答、专业解释、行业经验
25
+ - 使用前先运行:`opencli zhihu -h`
26
+
27
+ ### tieba
28
+
29
+ - 适用:兴趣圈子、历史帖子、粉丝社区
30
+ - 使用前先运行:`opencli tieba -h`
31
+
32
+ ### instagram
33
+
34
+ - 适用:账号搜索、图片社交线索
35
+ - 使用前先运行:`opencli instagram -h`
36
+
37
+ ### facebook
38
+
39
+ - 适用:主页、人物、帖子线索
40
+ - 使用前先运行:`opencli facebook -h`
41
+
42
+ ### bluesky
43
+
44
+ - 适用:Bluesky 用户与账号搜索
45
+ - 使用前先运行:`opencli bluesky -h`
46
+
47
+ ## 路由提示
48
+
49
+ - 用户明确指定某个平台时,直接用该平台
50
+ - 用户只说“社交媒体上怎么看”时,可先选 `grok` 或 `doubao`
51
+ - AI 只给摘要而没有原始帖子时,再切到对应社交站点
@@ -0,0 +1,42 @@
1
+ # 技术 / 学术
2
+
3
+ 用于技术问题、研究论文、开发者讨论、开源社区信息。
4
+
5
+ ## 站点
6
+
7
+ ### arxiv
8
+
9
+ - 适用:论文、研究、模型、算法、学术背景
10
+ - 使用前先运行:`opencli arxiv -h`
11
+
12
+ ### stackoverflow
13
+
14
+ - 适用:具体报错、API 用法、代码模式、实现细节
15
+ - 使用前先运行:`opencli stackoverflow -h`
16
+
17
+ ### hackernews
18
+
19
+ - 适用:技术社区讨论、开发者观点、创业和产品话题
20
+ - 使用前先运行:`opencli hackernews -h`
21
+
22
+ ### reddit
23
+
24
+ - 适用:英文社区问答、经验贴、推荐、对比讨论
25
+ - 使用前先运行:`opencli reddit -h`
26
+
27
+ ### linux-do
28
+
29
+ - 适用:人工智能、开源工具、中文技术社区
30
+ - 使用前先运行:`opencli linux-do -h`
31
+
32
+ ### v2ex
33
+
34
+ - 适用:中文技术社区
35
+ - 使用前先运行:`opencli linux-do
36
+
37
+ ## 路由提示
38
+
39
+ - 用户明确说“论文”“研究”时,优先 `arxiv`
40
+ - 用户明确说“报错”“API 怎么用”时,优先 `stackoverflow`
41
+ - 用户要“社区讨论”“开发者观点”时,优先 `hackernews` 或 `reddit`
42
+ - 若用户没有明确指定站点,可先选 `gemini` 或 `grok`,内容不足时再补这些专用源
@@ -0,0 +1,20 @@
1
+ # 旅游
2
+
3
+ 用于目的地、景区、酒店联想和旅行线索。
4
+
5
+ ## 站点
6
+
7
+ ### ctrip
8
+
9
+ - 适用:目的地、景区、酒店、旅行联想搜索
10
+ - 使用前先运行:`opencli ctrip -h`
11
+
12
+ ### xiaohongshu
13
+
14
+ - 适用:生活方式、穿搭、美妆、旅行、真实体验
15
+ - 使用前先运行:`opencli xiaohongshu -h`
16
+
17
+ ## 路由提示
18
+
19
+ - 用户明确说“携程”时直接使用 `ctrip`
20
+ - 用户只说“旅行/目的地/景区”时,可先选 `doubao` 做中文语境粗检索,再补 `ctrip`
@@ -27,6 +27,8 @@ import { formatSnapshot } from '../snapshotFormatter.js';
27
27
 
28
28
  export abstract class BasePage implements IPage {
29
29
  protected _lastUrl: string | null = null;
30
+ /** Cached previous snapshot hashes for incremental diff marking */
31
+ private _prevSnapshotHashes: string | null = null;
30
32
 
31
33
  // ── Transport-specific methods (must be implemented by subclasses) ──
32
34
 
@@ -35,14 +37,34 @@ export abstract class BasePage implements IPage {
35
37
  abstract getCookies(opts?: { domain?: string; url?: string }): Promise<BrowserCookie[]>;
36
38
  abstract screenshot(options?: ScreenshotOptions): Promise<string>;
37
39
  abstract tabs(): Promise<unknown[]>;
38
- abstract closeTab(index?: number): Promise<void>;
39
- abstract newTab(): Promise<void>;
40
40
  abstract selectTab(index: number): Promise<void>;
41
41
 
42
42
  // ── Shared DOM helper implementations ──
43
43
 
44
44
  async click(ref: string): Promise<void> {
45
- await this.evaluate(clickJs(ref));
45
+ const result = await this.evaluate(clickJs(ref)) as
46
+ | string
47
+ | { status: string; x?: number; y?: number; w?: number; h?: number; error?: string }
48
+ | null;
49
+
50
+ // Backwards compat: old format returned 'clicked' string
51
+ if (typeof result === 'string' || result == null) return;
52
+
53
+ // JS click succeeded
54
+ if (result.status === 'clicked') return;
55
+
56
+ // JS click failed — try CDP native click if coordinates available
57
+ if (result.x != null && result.y != null) {
58
+ const success = await this.tryNativeClick(result.x, result.y);
59
+ if (success) return;
60
+ }
61
+
62
+ throw new Error(`Click failed: ${result.error ?? 'JS click and CDP fallback both failed'}`);
63
+ }
64
+
65
+ /** Override in subclasses with CDP native click support */
66
+ protected async tryNativeClick(_x: number, _y: number): Promise<boolean> {
67
+ return false;
46
68
  }
47
69
 
48
70
  async typeText(ref: string, text: string): Promise<void> {
@@ -111,17 +133,30 @@ export abstract class BasePage implements IPage {
111
133
 
112
134
  async snapshot(opts: SnapshotOptions = {}): Promise<unknown> {
113
135
  const snapshotJs = generateSnapshotJs({
114
- viewportExpand: opts.viewportExpand ?? 800,
136
+ viewportExpand: opts.viewportExpand ?? 2000,
115
137
  maxDepth: Math.max(1, Math.min(Number(opts.maxDepth) || 50, 200)),
116
138
  interactiveOnly: opts.interactive ?? false,
117
139
  maxTextLength: opts.maxTextLength ?? 120,
118
140
  includeScrollInfo: true,
119
141
  bboxDedup: true,
142
+ previousHashes: this._prevSnapshotHashes,
120
143
  });
121
144
 
122
145
  try {
123
- return await this.evaluate(snapshotJs);
124
- } catch {
146
+ const result = await this.evaluate(snapshotJs);
147
+ // Read back the hashes stored by the snapshot for next diff
148
+ try {
149
+ const hashes = await this.evaluate('window.__opencli_prev_hashes') as string | null;
150
+ this._prevSnapshotHashes = typeof hashes === 'string' ? hashes : null;
151
+ } catch {
152
+ // Non-fatal: diff is best-effort
153
+ }
154
+ return result;
155
+ } catch (err) {
156
+ // Log snapshot failure for debugging, then fallback to basic accessibility tree
157
+ if (process.env.DEBUG_SNAPSHOT) {
158
+ console.error('[snapshot] DOM snapshot failed, falling back to accessibility tree:', (err as Error)?.message?.slice(0, 200));
159
+ }
125
160
  return this._basicSnapshot(opts);
126
161
  }
127
162
  }
@@ -9,7 +9,7 @@ import * as fs from 'node:fs';
9
9
  import type { IPage } from '../types.js';
10
10
  import type { IBrowserFactory } from '../runtime.js';
11
11
  import { Page } from './page.js';
12
- import { isDaemonRunning, isExtensionConnected } from './daemon-client.js';
12
+ import { fetchDaemonStatus, isExtensionConnected } from './daemon-client.js';
13
13
  import { DEFAULT_DAEMON_PORT } from '../constants.js';
14
14
 
15
15
  const DAEMON_SPAWN_TIMEOUT = 10000; // 10s to wait for daemon + extension
@@ -60,14 +60,17 @@ export class BrowserBridge implements IBrowserFactory {
60
60
  const effectiveSeconds = (timeoutSeconds && timeoutSeconds > 0) ? timeoutSeconds : Math.ceil(DAEMON_SPAWN_TIMEOUT / 1000);
61
61
  const timeoutMs = effectiveSeconds * 1000;
62
62
 
63
+ // Single status check instead of two separate fetchDaemonStatus() calls
64
+ const status = await fetchDaemonStatus();
65
+
63
66
  // Fast path: extension already connected
64
- if (await isExtensionConnected()) return;
67
+ if (status?.extensionConnected) return;
65
68
 
66
69
  // Daemon running but no extension — wait for extension with progress
67
- if (await isDaemonRunning()) {
70
+ if (status !== null) {
68
71
  if (process.env.OPENCLI_VERBOSE || process.stderr.isTTY) {
69
- process.stderr.write('⏳ Waiting for Chrome extension to connect...\n');
70
- process.stderr.write(' Make sure Chrome is open and the OpenCLI extension is enabled.\n');
72
+ process.stderr.write('⏳ Waiting for Chrome/Chromium extension to connect...\n');
73
+ process.stderr.write(' Make sure Chrome or Chromium is open and the OpenCLI extension is enabled.\n');
71
74
  }
72
75
  const deadline = Date.now() + timeoutMs;
73
76
  while (Date.now() < deadline) {
@@ -76,7 +79,7 @@ export class BrowserBridge implements IBrowserFactory {
76
79
  }
77
80
  throw new Error(
78
81
  'Daemon is running but the Browser Extension is not connected.\n' +
79
- 'Please install and enable the opencli Browser Bridge extension in Chrome.',
82
+ 'Please install and enable the opencli Browser Bridge extension in Chrome or Chromium.',
80
83
  );
81
84
  }
82
85
 
@@ -110,10 +113,10 @@ export class BrowserBridge implements IBrowserFactory {
110
113
  if (await isExtensionConnected()) return;
111
114
  }
112
115
 
113
- if (await isDaemonRunning()) {
116
+ if ((await fetchDaemonStatus()) !== null) {
114
117
  throw new Error(
115
118
  'Daemon is running but the Browser Extension is not connected.\n' +
116
- 'Please install and enable the opencli Browser Bridge extension in Chrome.',
119
+ 'Please install and enable the opencli Browser Bridge extension in Chrome or Chromium.',
117
120
  );
118
121
  }
119
122
 
@@ -223,14 +223,6 @@ class CDPPage extends BasePage {
223
223
  return [];
224
224
  }
225
225
 
226
- async closeTab(_index?: number): Promise<void> {
227
- // Not supported in direct CDP mode
228
- }
229
-
230
- async newTab(): Promise<void> {
231
- await this.bridge.send('Target.createTarget', { url: 'about:blank' });
232
- }
233
-
234
226
  async selectTab(_index: number): Promise<void> {
235
227
  // Not supported in direct CDP mode
236
228
  }
@@ -274,6 +266,7 @@ function scoreCDPTarget(target: CDPTarget, preferredPattern?: RegExp): number {
274
266
 
275
267
  if (!haystack.trim() && !type) return Number.NEGATIVE_INFINITY;
276
268
  if (haystack.includes('devtools')) return Number.NEGATIVE_INFINITY;
269
+ if (type === 'background_page' || type === 'service_worker') return Number.NEGATIVE_INFINITY;
277
270
 
278
271
  let score = 0;
279
272
 
@@ -21,7 +21,7 @@ function generateId(): string {
21
21
 
22
22
  export interface DaemonCommand {
23
23
  id: string;
24
- action: 'exec' | 'navigate' | 'tabs' | 'cookies' | 'screenshot' | 'close-window' | 'sessions' | 'set-file-input' | 'cdp';
24
+ action: 'exec' | 'navigate' | 'tabs' | 'cookies' | 'screenshot' | 'close-window' | 'sessions' | 'set-file-input' | 'insert-text' | 'bind-current' | 'network-capture-start' | 'network-capture-read' | 'cdp';
25
25
  tabId?: number;
26
26
  code?: string;
27
27
  workspace?: string;
@@ -29,6 +29,8 @@ export interface DaemonCommand {
29
29
  op?: string;
30
30
  index?: number;
31
31
  domain?: string;
32
+ matchDomain?: string;
33
+ matchPathPrefix?: string;
32
34
  format?: 'png' | 'jpeg';
33
35
  quality?: number;
34
36
  fullPage?: boolean;
@@ -37,6 +39,10 @@ export interface DaemonCommand {
37
39
  files?: string[];
38
40
  /** CSS selector for file input element (set-file-input action) */
39
41
  selector?: string;
42
+ /** Raw text payload for insert-text action */
43
+ text?: string;
44
+ /** URL substring filter pattern for network capture */
45
+ pattern?: string;
40
46
  cdpMethod?: string;
41
47
  cdpParams?: Record<string, unknown>;
42
48
  }
@@ -163,3 +169,7 @@ export async function listSessions(): Promise<BrowserSessionInfo[]> {
163
169
  const result = await sendCommand('sessions');
164
170
  return Array.isArray(result) ? result : [];
165
171
  }
172
+
173
+ export async function bindCurrentTab(workspace: string, opts: { matchDomain?: string; matchPathPrefix?: string } = {}): Promise<unknown> {
174
+ return sendCommand('bind-current', { workspace, ...opts });
175
+ }
@@ -5,64 +5,76 @@
5
5
  * to eliminate code duplication for click, type, press, wait, scroll, etc.
6
6
  */
7
7
 
8
- /** Generate JS to click an element by ref */
9
- export function clickJs(ref: string): string {
10
- const safeRef = JSON.stringify(ref);
8
+ /** Shared element lookup JS fragment (4-strategy resolution) */
9
+ function resolveElementJs(safeRef: string, selectorSet: string): string {
11
10
  return `
12
- (() => {
13
11
  const ref = ${safeRef};
14
- // 1. data-opencli-ref (set by snapshot engine)
15
12
  let el = document.querySelector('[data-opencli-ref="' + ref + '"]');
16
- // 2. data-ref (legacy)
17
13
  if (!el) el = document.querySelector('[data-ref="' + ref + '"]');
18
- // 3. CSS selector
19
14
  if (!el && ref.match(/^[a-zA-Z#.\\[]/)) {
20
15
  try { el = document.querySelector(ref); } catch {}
21
16
  }
22
- // 4. Numeric index into interactive elements
23
17
  if (!el) {
24
18
  const idx = parseInt(ref, 10);
25
19
  if (!isNaN(idx)) {
26
- el = document.querySelectorAll('a, button, input, select, textarea, [role="button"], [tabindex]:not([tabindex="-1"])')[idx];
20
+ el = document.querySelectorAll('${selectorSet}')[idx];
27
21
  }
28
- }
22
+ }`;
23
+ }
24
+
25
+ /** Generate JS to click an element by ref.
26
+ * Returns { status, x, y, w, h } for CDP fallback when JS click fails. */
27
+ export function clickJs(ref: string): string {
28
+ const safeRef = JSON.stringify(ref);
29
+ return `
30
+ (() => {
31
+ ${resolveElementJs(safeRef, 'a, button, input, select, textarea, [role="button"], [tabindex]:not([tabindex="-1"])')}
29
32
  if (!el) throw new Error('Element not found: ' + ref);
30
33
  el.scrollIntoView({ behavior: 'instant', block: 'center' });
31
- el.click();
32
- return 'clicked';
34
+ const rect = el.getBoundingClientRect();
35
+ const x = Math.round(rect.left + rect.width / 2);
36
+ const y = Math.round(rect.top + rect.height / 2);
37
+ try {
38
+ el.click();
39
+ return { status: 'clicked', x, y, w: Math.round(rect.width), h: Math.round(rect.height) };
40
+ } catch (e) {
41
+ return { status: 'js_failed', x, y, w: Math.round(rect.width), h: Math.round(rect.height), error: e.message };
42
+ }
33
43
  })()
34
44
  `;
35
45
  }
36
46
 
37
- /** Generate JS to type text into an element by ref */
47
+ /** Generate JS to type text into an element by ref.
48
+ * Uses native setter for React compat + execCommand for contenteditable. */
38
49
  export function typeTextJs(ref: string, text: string): string {
39
50
  const safeRef = JSON.stringify(ref);
40
51
  const safeText = JSON.stringify(text);
41
52
  return `
42
53
  (() => {
43
- const ref = ${safeRef};
44
- // 1. data-opencli-ref (set by snapshot engine)
45
- let el = document.querySelector('[data-opencli-ref="' + ref + '"]');
46
- // 2. data-ref (legacy)
47
- if (!el) el = document.querySelector('[data-ref="' + ref + '"]');
48
- // 3. CSS selector
49
- if (!el && ref.match(/^[a-zA-Z#.\\[]/)) {
50
- try { el = document.querySelector(ref); } catch {}
51
- }
52
- // 4. Numeric index into typeable elements
53
- if (!el) {
54
- const idx = parseInt(ref, 10);
55
- if (!isNaN(idx)) {
56
- el = document.querySelectorAll('input, textarea, [contenteditable="true"]')[idx];
57
- }
58
- }
54
+ ${resolveElementJs(safeRef, 'input, textarea, [contenteditable="true"]')}
59
55
  if (!el) throw new Error('Element not found: ' + ref);
60
56
  el.focus();
61
57
  if (el.isContentEditable) {
62
- el.textContent = ${safeText};
58
+ // Select all content + delete, then insert (supports undo, works with rich text editors)
59
+ const sel = window.getSelection();
60
+ const range = document.createRange();
61
+ range.selectNodeContents(el);
62
+ sel.removeAllRanges();
63
+ sel.addRange(range);
64
+ document.execCommand('delete', false);
65
+ document.execCommand('insertText', false, ${safeText});
63
66
  el.dispatchEvent(new Event('input', { bubbles: true }));
64
67
  } else {
65
- el.value = ${safeText};
68
+ // Use native setter for React/framework compatibility (match element type)
69
+ const proto = el instanceof HTMLTextAreaElement
70
+ ? HTMLTextAreaElement.prototype
71
+ : HTMLInputElement.prototype;
72
+ const nativeSetter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
73
+ if (nativeSetter) {
74
+ nativeSetter.call(el, ${safeText});
75
+ } else {
76
+ el.value = ${safeText};
77
+ }
66
78
  el.dispatchEvent(new Event('input', { bubbles: true }));
67
79
  el.dispatchEvent(new Event('change', { bubbles: true }));
68
80
  }
@@ -377,6 +377,8 @@ export function generateSnapshotJs(opts: DomSnapshotOptions = {}): string {
377
377
  if (role && INTERACTIVE_ROLES.has(role)) return true;
378
378
  if (el.hasAttribute('onclick') || el.hasAttribute('onmousedown') || el.hasAttribute('ontouchstart')) return true;
379
379
  if (el.hasAttribute('tabindex') && el.getAttribute('tabindex') !== '-1') return true;
380
+ // Framework event listener detection (React/Vue/Angular onClick)
381
+ if (hasFrameworkListener(el)) return true;
380
382
  try { if (window.getComputedStyle(el).cursor === 'pointer') return true; } catch {}
381
383
  if (el.isContentEditable && el.getAttribute('contenteditable') !== 'false') return true;
382
384
  // Search element heuristic detection
@@ -384,9 +386,29 @@ export function generateSnapshotJs(opts: DomSnapshotOptions = {}): string {
384
386
  return false;
385
387
  }
386
388
 
389
+ function hasFrameworkListener(el) {
390
+ try {
391
+ // React: __reactProps$xxx / __reactEvents$xxx with onClick/onMouseDown
392
+ for (const key of Object.keys(el)) {
393
+ if (key.startsWith('__reactProps$') || key.startsWith('__reactEvents$')) {
394
+ const props = el[key];
395
+ if (props && (props.onClick || props.onMouseDown || props.onPointerDown)) return true;
396
+ }
397
+ }
398
+ // Vue 3: _vei (Vue Event Invoker) with onClick
399
+ if (el._vei && (el._vei.onClick || el._vei.click || el._vei.onMousedown)) return true;
400
+ // Vue 2: __vue__ instance with $listeners
401
+ if (el.__vue__?.$listeners?.click) return true;
402
+ // Angular: ng-reflect-click binding
403
+ if (el.hasAttribute('ng-reflect-click')) return true;
404
+ } catch { /* ignore errors from cross-origin or frozen objects */ }
405
+ return false;
406
+ }
407
+
387
408
  function isSearchElement(el) {
388
409
  // Check class names for search indicators
389
- const className = el.className?.toLowerCase() || '';
410
+ // Note: SVG elements have className as SVGAnimatedString (not a string), use baseVal
411
+ const className = (typeof el.className === 'string' ? el.className : el.className?.baseVal || '').toLowerCase();
390
412
  const classes = className.split(/\\s+/).filter(Boolean);
391
413
  for (const cls of classes) {
392
414
  const cleaned = cls.replace(/[^a-z0-9-]/g, '');