@jackwener/opencli 1.6.0 → 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 (390) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +27 -45
  4. package/README.zh-CN.md +32 -34
  5. package/autoresearch/browse-tasks.json +18 -20
  6. package/autoresearch/commands/debug.ts +163 -0
  7. package/autoresearch/commands/fix.ts +145 -0
  8. package/autoresearch/commands/plan.ts +88 -0
  9. package/autoresearch/commands/run.ts +138 -0
  10. package/autoresearch/config.ts +82 -0
  11. package/autoresearch/engine.ts +359 -0
  12. package/autoresearch/eval-all.ts +127 -0
  13. package/autoresearch/eval-browse.ts +1 -1
  14. package/autoresearch/eval-publish.ts +238 -0
  15. package/autoresearch/eval-save.ts +249 -0
  16. package/autoresearch/eval-skill.ts +14 -8
  17. package/autoresearch/eval-v2ex.ts +220 -0
  18. package/autoresearch/eval-zhihu.ts +230 -0
  19. package/autoresearch/logger.ts +69 -0
  20. package/autoresearch/presets/combined-reliability.ts +27 -0
  21. package/autoresearch/presets/index.ts +23 -0
  22. package/autoresearch/presets/operate-reliability.ts +24 -0
  23. package/autoresearch/presets/save-reliability.ts +26 -0
  24. package/autoresearch/presets/skill-quality.ts +20 -0
  25. package/autoresearch/presets/v2ex-reliability.ts +24 -0
  26. package/autoresearch/presets/zhihu-reliability.ts +25 -0
  27. package/autoresearch/publish-tasks.json +345 -0
  28. package/autoresearch/run-save.sh +11 -0
  29. package/autoresearch/save-adapters/xhs-explore-deep.ts +64 -0
  30. package/autoresearch/save-adapters/xhs-note-comments.ts +61 -0
  31. package/autoresearch/save-adapters/xhs-search-full.ts +62 -0
  32. package/autoresearch/save-adapters/zhihu-hot-detail.ts +52 -0
  33. package/autoresearch/save-adapters/zhihu-question-full.ts +57 -0
  34. package/autoresearch/save-adapters/zhihu-search-detail.ts +53 -0
  35. package/autoresearch/save-tasks.json +281 -0
  36. package/autoresearch/v2ex-tasks.json +899 -0
  37. package/autoresearch/zhihu-tasks.json +848 -0
  38. package/bun.lock +615 -0
  39. package/dist/browser/base-page.d.ts +4 -2
  40. package/dist/browser/base-page.js +37 -4
  41. package/dist/browser/bridge.js +10 -8
  42. package/dist/browser/cdp.js +2 -6
  43. package/dist/browser/daemon-client.d.ts +11 -1
  44. package/dist/browser/daemon-client.js +3 -0
  45. package/dist/browser/dom-helpers.d.ts +4 -2
  46. package/dist/browser/dom-helpers.js +42 -31
  47. package/dist/browser/dom-snapshot.js +23 -1
  48. package/dist/browser/page.d.ts +7 -2
  49. package/dist/browser/page.js +112 -30
  50. package/dist/browser.test.js +1 -1
  51. package/dist/build-manifest.d.ts +1 -0
  52. package/dist/build-manifest.js +1 -0
  53. package/dist/cli-manifest.json +1133 -182
  54. package/dist/cli.d.ts +2 -0
  55. package/dist/cli.js +48 -7
  56. package/dist/cli.test.d.ts +1 -0
  57. package/dist/cli.test.js +88 -0
  58. package/dist/clis/1688/item.d.ts +70 -0
  59. package/dist/clis/1688/item.js +187 -0
  60. package/dist/clis/1688/item.test.d.ts +1 -0
  61. package/dist/clis/1688/item.test.js +67 -0
  62. package/dist/clis/1688/search.d.ts +56 -0
  63. package/dist/clis/1688/search.js +309 -0
  64. package/dist/clis/1688/search.test.d.ts +1 -0
  65. package/dist/clis/1688/search.test.js +75 -0
  66. package/dist/clis/1688/shared.d.ts +112 -0
  67. package/dist/clis/1688/shared.js +514 -0
  68. package/dist/clis/1688/shared.test.d.ts +1 -0
  69. package/dist/clis/1688/shared.test.js +57 -0
  70. package/dist/clis/1688/store.d.ts +45 -0
  71. package/dist/clis/1688/store.js +226 -0
  72. package/dist/clis/1688/store.test.d.ts +1 -0
  73. package/dist/clis/1688/store.test.js +62 -0
  74. package/dist/clis/amazon/bestsellers.d.ts +0 -20
  75. package/dist/clis/amazon/bestsellers.js +6 -129
  76. package/dist/clis/amazon/bestsellers.test.js +12 -3
  77. package/dist/clis/amazon/movers-shakers.d.ts +1 -0
  78. package/dist/clis/amazon/movers-shakers.js +7 -0
  79. package/dist/clis/amazon/new-releases.d.ts +1 -0
  80. package/dist/clis/amazon/new-releases.js +7 -0
  81. package/dist/clis/amazon/rankings.d.ts +59 -0
  82. package/dist/clis/amazon/rankings.js +226 -0
  83. package/dist/clis/amazon/rankings.test.d.ts +1 -0
  84. package/dist/clis/amazon/rankings.test.js +41 -0
  85. package/dist/clis/amazon/shared.d.ts +11 -0
  86. package/dist/clis/amazon/shared.js +121 -11
  87. package/dist/clis/amazon/shared.test.js +11 -0
  88. package/dist/clis/bilibili/comments.js +2 -2
  89. package/dist/clis/bilibili/comments.test.js +3 -2
  90. package/dist/clis/bilibili/download.js +2 -1
  91. package/dist/clis/bilibili/subtitle.js +4 -3
  92. package/dist/clis/bilibili/subtitle.test.js +2 -1
  93. package/dist/clis/bilibili/utils.d.ts +5 -0
  94. package/dist/clis/bilibili/utils.js +30 -0
  95. package/dist/clis/bilibili/utils.test.d.ts +1 -0
  96. package/dist/clis/bilibili/utils.test.js +17 -0
  97. package/dist/clis/douban/marks.js +1 -1
  98. package/dist/clis/douban/subject.yaml +50 -19
  99. package/dist/clis/doubao/utils.js +32 -12
  100. package/dist/clis/douyin/_shared/browser-fetch.test.js +0 -1
  101. package/dist/clis/douyin/_shared/transcode.test.js +0 -2
  102. package/dist/clis/douyin/draft.test.js +0 -2
  103. package/dist/clis/facebook/search.test.js +0 -2
  104. package/dist/clis/gemini/ask.js +9 -3
  105. package/dist/clis/gemini/ask.test.d.ts +1 -0
  106. package/dist/clis/gemini/ask.test.js +100 -0
  107. package/dist/clis/gemini/reply-state.test.d.ts +1 -0
  108. package/dist/clis/gemini/reply-state.test.js +641 -0
  109. package/dist/clis/gemini/utils.d.ts +44 -1
  110. package/dist/clis/gemini/utils.js +528 -61
  111. package/dist/clis/gemini/utils.test.js +149 -2
  112. package/dist/clis/hupu/detail.d.ts +1 -0
  113. package/dist/clis/hupu/detail.js +72 -0
  114. package/dist/clis/hupu/hot.yaml +43 -0
  115. package/dist/clis/hupu/like.d.ts +1 -0
  116. package/dist/clis/hupu/like.js +75 -0
  117. package/dist/clis/hupu/reply.d.ts +1 -0
  118. package/dist/clis/hupu/reply.js +71 -0
  119. package/dist/clis/hupu/search.d.ts +1 -0
  120. package/dist/clis/hupu/search.js +59 -0
  121. package/dist/clis/hupu/unlike.d.ts +1 -0
  122. package/dist/clis/hupu/unlike.js +75 -0
  123. package/dist/clis/hupu/utils.d.ts +20 -0
  124. package/dist/clis/hupu/utils.js +319 -0
  125. package/dist/clis/instagram/_shared/private-publish.d.ts +138 -0
  126. package/dist/clis/instagram/_shared/private-publish.js +1030 -0
  127. package/dist/clis/instagram/_shared/private-publish.test.d.ts +1 -0
  128. package/dist/clis/instagram/_shared/private-publish.test.js +705 -0
  129. package/dist/clis/instagram/_shared/protocol-capture.d.ts +26 -0
  130. package/dist/clis/instagram/_shared/protocol-capture.js +282 -0
  131. package/dist/clis/instagram/_shared/protocol-capture.test.d.ts +1 -0
  132. package/dist/clis/instagram/_shared/protocol-capture.test.js +114 -0
  133. package/dist/clis/instagram/_shared/runtime-info.d.ts +9 -0
  134. package/dist/clis/instagram/_shared/runtime-info.js +81 -0
  135. package/dist/clis/instagram/note.d.ts +1 -0
  136. package/dist/clis/instagram/note.js +222 -0
  137. package/dist/clis/instagram/note.test.d.ts +1 -0
  138. package/dist/clis/instagram/note.test.js +81 -0
  139. package/dist/clis/instagram/post.d.ts +4 -0
  140. package/dist/clis/instagram/post.js +1496 -0
  141. package/dist/clis/instagram/post.test.d.ts +1 -0
  142. package/dist/clis/instagram/post.test.js +1647 -0
  143. package/dist/clis/instagram/reel.d.ts +1 -0
  144. package/dist/clis/instagram/reel.js +826 -0
  145. package/dist/clis/instagram/reel.test.d.ts +1 -0
  146. package/dist/clis/instagram/reel.test.js +167 -0
  147. package/dist/clis/instagram/story.d.ts +1 -0
  148. package/dist/clis/instagram/story.js +115 -0
  149. package/dist/clis/instagram/story.test.d.ts +1 -0
  150. package/dist/clis/instagram/story.test.js +167 -0
  151. package/dist/clis/sinafinance/stock-rank.d.ts +4 -0
  152. package/dist/clis/sinafinance/stock-rank.js +65 -0
  153. package/dist/clis/substack/utils.test.js +0 -2
  154. package/dist/clis/twitter/post.js +72 -45
  155. package/dist/clis/twitter/post.test.d.ts +1 -0
  156. package/dist/clis/twitter/post.test.js +116 -0
  157. package/dist/clis/twitter/reply.d.ts +12 -0
  158. package/dist/clis/twitter/reply.js +257 -35
  159. package/dist/clis/twitter/reply.test.d.ts +1 -0
  160. package/dist/clis/twitter/reply.test.js +151 -0
  161. package/dist/clis/twitter/search.js +67 -5
  162. package/dist/clis/twitter/search.test.js +83 -5
  163. package/dist/clis/xianyu/chat.d.ts +7 -0
  164. package/dist/clis/xianyu/chat.js +146 -0
  165. package/dist/clis/xianyu/chat.test.d.ts +1 -0
  166. package/dist/clis/xianyu/chat.test.js +15 -0
  167. package/dist/clis/xianyu/item.d.ts +7 -0
  168. package/dist/clis/xianyu/item.js +152 -0
  169. package/dist/clis/xianyu/item.test.d.ts +1 -0
  170. package/dist/clis/xianyu/item.test.js +56 -0
  171. package/dist/clis/xianyu/search.d.ts +10 -0
  172. package/dist/clis/xianyu/search.js +134 -0
  173. package/dist/clis/xianyu/search.test.d.ts +1 -0
  174. package/dist/clis/xianyu/search.test.js +17 -0
  175. package/dist/clis/xianyu/utils.d.ts +1 -0
  176. package/dist/clis/xianyu/utils.js +8 -0
  177. package/dist/clis/xiaoe/catalog.yaml +129 -0
  178. package/dist/clis/xiaoe/content.yaml +43 -0
  179. package/dist/clis/xiaoe/courses.yaml +73 -0
  180. package/dist/clis/xiaoe/detail.yaml +39 -0
  181. package/dist/clis/xiaoe/play-url.yaml +124 -0
  182. package/dist/clis/xiaohongshu/comments.test.js +0 -2
  183. package/dist/clis/xiaohongshu/creator-note-detail.test.js +0 -2
  184. package/dist/clis/xiaohongshu/creator-notes.test.js +0 -2
  185. package/dist/clis/xiaohongshu/download.test.js +0 -2
  186. package/dist/clis/xiaohongshu/note.test.js +0 -2
  187. package/dist/clis/xiaohongshu/publish.test.js +0 -2
  188. package/dist/clis/xiaohongshu/search.js +29 -20
  189. package/dist/clis/xiaohongshu/search.test.js +56 -48
  190. package/dist/clis/yuanbao/ask.d.ts +21 -0
  191. package/dist/clis/yuanbao/ask.js +427 -0
  192. package/dist/clis/yuanbao/ask.test.d.ts +1 -0
  193. package/dist/clis/yuanbao/ask.test.js +124 -0
  194. package/dist/clis/yuanbao/new.d.ts +1 -0
  195. package/dist/clis/yuanbao/new.js +70 -0
  196. package/dist/clis/yuanbao/new.test.d.ts +1 -0
  197. package/dist/clis/yuanbao/new.test.js +30 -0
  198. package/dist/clis/yuanbao/shared.d.ts +13 -0
  199. package/dist/clis/yuanbao/shared.js +49 -0
  200. package/dist/clis/zhihu/question.js +30 -19
  201. package/dist/clis/zhihu/question.test.js +34 -16
  202. package/dist/commanderAdapter.js +8 -4
  203. package/dist/commanderAdapter.test.js +42 -0
  204. package/dist/completion.js +3 -1
  205. package/dist/completion.test.d.ts +1 -0
  206. package/dist/completion.test.js +23 -0
  207. package/dist/doctor.js +1 -1
  208. package/dist/electron-apps.d.ts +2 -0
  209. package/dist/electron-apps.js +7 -1
  210. package/dist/errors.js +1 -1
  211. package/dist/execution.js +25 -35
  212. package/dist/explore.js +1 -1
  213. package/dist/launcher.d.ts +4 -0
  214. package/dist/launcher.js +64 -8
  215. package/dist/launcher.test.js +88 -7
  216. package/dist/output.d.ts +2 -0
  217. package/dist/output.js +10 -1
  218. package/dist/output.test.d.ts +0 -3
  219. package/dist/output.test.js +59 -92
  220. package/dist/pipeline/executor.test.js +0 -2
  221. package/dist/pipeline/steps/download.test.js +0 -2
  222. package/dist/registry.d.ts +2 -0
  223. package/dist/serialization.d.ts +1 -0
  224. package/dist/serialization.js +1 -0
  225. package/dist/types.d.ts +9 -2
  226. package/docs/.vitepress/config.mts +4 -0
  227. package/docs/adapters/browser/1688.md +52 -0
  228. package/docs/adapters/browser/36kr.md +2 -1
  229. package/docs/adapters/browser/doubao.md +5 -1
  230. package/docs/adapters/browser/hupu.md +53 -0
  231. package/docs/adapters/browser/sinafinance.md +32 -2
  232. package/docs/adapters/browser/weibo.md +6 -1
  233. package/docs/adapters/browser/wikipedia.md +2 -0
  234. package/docs/adapters/browser/xianyu.md +42 -0
  235. package/docs/adapters/browser/xiaoe.md +44 -0
  236. package/docs/adapters/browser/yuanbao.md +64 -0
  237. package/docs/adapters/index.md +14 -5
  238. package/docs/comparison.md +1 -1
  239. package/docs/developer/ai-workflow.md +2 -2
  240. package/docs/developer/contributing.md +1 -1
  241. package/docs/developer/testing.md +2 -0
  242. package/docs/guide/plugins.md +1 -0
  243. package/docs/guide/troubleshooting.md +11 -0
  244. package/docs/superpowers/specs/2026-04-03-v2ex-autoresearch-design.md +41 -0
  245. package/docs/zh/guide/plugins.md +1 -0
  246. package/extension/dist/background.js +1127 -0
  247. package/extension/src/background.test.ts +39 -0
  248. package/extension/src/background.ts +223 -34
  249. package/extension/src/cdp.ts +194 -4
  250. package/extension/src/protocol.ts +22 -1
  251. package/package.json +3 -2
  252. package/scripts/postinstall.js +1 -1
  253. package/skills/opencli-explorer/SKILL.md +1 -1
  254. package/skills/opencli-oneshot/SKILL.md +2 -2
  255. package/skills/opencli-operate/SKILL.md +120 -27
  256. package/skills/opencli-usage/SKILL.md +31 -20
  257. package/skills/opencli-usage/browser.md +114 -16
  258. package/skills/opencli-usage/public-api.md +32 -3
  259. package/skills/smart-search/SKILL.md +156 -0
  260. package/skills/smart-search/references/sources-ai.md +74 -0
  261. package/skills/smart-search/references/sources-info.md +43 -0
  262. package/skills/smart-search/references/sources-media.md +50 -0
  263. package/skills/smart-search/references/sources-other.md +42 -0
  264. package/skills/smart-search/references/sources-shopping.md +31 -0
  265. package/skills/smart-search/references/sources-social.md +51 -0
  266. package/skills/smart-search/references/sources-tech.md +42 -0
  267. package/skills/smart-search/references/sources-travel.md +20 -0
  268. package/src/browser/base-page.ts +41 -6
  269. package/src/browser/bridge.ts +11 -8
  270. package/src/browser/cdp.ts +1 -8
  271. package/src/browser/daemon-client.ts +11 -1
  272. package/src/browser/dom-helpers.ts +43 -31
  273. package/src/browser/dom-snapshot.ts +23 -1
  274. package/src/browser/page.ts +115 -31
  275. package/src/browser.test.ts +1 -1
  276. package/src/build-manifest.ts +2 -0
  277. package/src/cli.test.ts +133 -0
  278. package/src/cli.ts +73 -11
  279. package/src/clis/1688/item.test.ts +69 -0
  280. package/src/clis/1688/item.ts +282 -0
  281. package/src/clis/1688/search.test.ts +81 -0
  282. package/src/clis/1688/search.ts +402 -0
  283. package/src/clis/1688/shared.test.ts +75 -0
  284. package/src/clis/1688/shared.ts +623 -0
  285. package/src/clis/1688/store.test.ts +69 -0
  286. package/src/clis/1688/store.ts +300 -0
  287. package/src/clis/amazon/bestsellers.test.ts +12 -3
  288. package/src/clis/amazon/bestsellers.ts +6 -178
  289. package/src/clis/amazon/movers-shakers.ts +8 -0
  290. package/src/clis/amazon/new-releases.ts +8 -0
  291. package/src/clis/amazon/rankings.test.ts +47 -0
  292. package/src/clis/amazon/rankings.ts +312 -0
  293. package/src/clis/amazon/shared.test.ts +16 -0
  294. package/src/clis/amazon/shared.ts +134 -12
  295. package/src/clis/bilibili/comments.test.ts +4 -3
  296. package/src/clis/bilibili/comments.ts +2 -2
  297. package/src/clis/bilibili/download.ts +2 -1
  298. package/src/clis/bilibili/subtitle.test.ts +2 -1
  299. package/src/clis/bilibili/subtitle.ts +4 -3
  300. package/src/clis/bilibili/utils.test.ts +21 -0
  301. package/src/clis/bilibili/utils.ts +27 -0
  302. package/src/clis/douban/marks.ts +1 -1
  303. package/src/clis/douban/subject.yaml +50 -19
  304. package/src/clis/doubao/utils.ts +32 -12
  305. package/src/clis/douyin/_shared/browser-fetch.test.ts +0 -1
  306. package/src/clis/douyin/_shared/transcode.test.ts +0 -2
  307. package/src/clis/douyin/draft.test.ts +0 -2
  308. package/src/clis/facebook/search.test.ts +0 -2
  309. package/src/clis/gemini/ask.test.ts +116 -0
  310. package/src/clis/gemini/ask.ts +10 -3
  311. package/src/clis/gemini/reply-state.test.ts +708 -0
  312. package/src/clis/gemini/utils.test.ts +184 -2
  313. package/src/clis/gemini/utils.ts +588 -60
  314. package/src/clis/hupu/detail.ts +126 -0
  315. package/src/clis/hupu/hot.yaml +43 -0
  316. package/src/clis/hupu/like.ts +76 -0
  317. package/src/clis/hupu/reply.ts +76 -0
  318. package/src/clis/hupu/search.ts +95 -0
  319. package/src/clis/hupu/unlike.ts +76 -0
  320. package/src/clis/hupu/utils.ts +381 -0
  321. package/src/clis/instagram/_shared/private-publish.test.ts +827 -0
  322. package/src/clis/instagram/_shared/private-publish.ts +1303 -0
  323. package/src/clis/instagram/_shared/protocol-capture.test.ts +148 -0
  324. package/src/clis/instagram/_shared/protocol-capture.ts +321 -0
  325. package/src/clis/instagram/_shared/runtime-info.ts +91 -0
  326. package/src/clis/instagram/note.test.ts +96 -0
  327. package/src/clis/instagram/note.ts +254 -0
  328. package/src/clis/instagram/post.test.ts +1716 -0
  329. package/src/clis/instagram/post.ts +1620 -0
  330. package/src/clis/instagram/reel.test.ts +191 -0
  331. package/src/clis/instagram/reel.ts +886 -0
  332. package/src/clis/instagram/story.test.ts +191 -0
  333. package/src/clis/instagram/story.ts +151 -0
  334. package/src/clis/sinafinance/stock-rank.ts +68 -0
  335. package/src/clis/substack/utils.test.ts +0 -2
  336. package/src/clis/twitter/post.test.ts +157 -0
  337. package/src/clis/twitter/post.ts +82 -48
  338. package/src/clis/twitter/reply.test.ts +177 -0
  339. package/src/clis/twitter/reply.ts +285 -39
  340. package/src/clis/twitter/search.test.ts +88 -5
  341. package/src/clis/twitter/search.ts +68 -5
  342. package/src/clis/xianyu/chat.test.ts +20 -0
  343. package/src/clis/xianyu/chat.ts +175 -0
  344. package/src/clis/xianyu/item.test.ts +67 -0
  345. package/src/clis/xianyu/item.ts +172 -0
  346. package/src/clis/xianyu/search.test.ts +22 -0
  347. package/src/clis/xianyu/search.ts +151 -0
  348. package/src/clis/xianyu/utils.ts +9 -0
  349. package/src/clis/xiaoe/catalog.yaml +129 -0
  350. package/src/clis/xiaoe/content.yaml +43 -0
  351. package/src/clis/xiaoe/courses.yaml +73 -0
  352. package/src/clis/xiaoe/detail.yaml +39 -0
  353. package/src/clis/xiaoe/play-url.yaml +124 -0
  354. package/src/clis/xiaohongshu/comments.test.ts +0 -2
  355. package/src/clis/xiaohongshu/creator-note-detail.test.ts +0 -2
  356. package/src/clis/xiaohongshu/creator-notes.test.ts +0 -2
  357. package/src/clis/xiaohongshu/download.test.ts +0 -2
  358. package/src/clis/xiaohongshu/note.test.ts +0 -2
  359. package/src/clis/xiaohongshu/publish.test.ts +0 -2
  360. package/src/clis/xiaohongshu/search.test.ts +59 -48
  361. package/src/clis/xiaohongshu/search.ts +31 -21
  362. package/src/clis/yuanbao/ask.test.ts +156 -0
  363. package/src/clis/yuanbao/ask.ts +522 -0
  364. package/src/clis/yuanbao/new.test.ts +36 -0
  365. package/src/clis/yuanbao/new.ts +81 -0
  366. package/src/clis/yuanbao/shared.ts +57 -0
  367. package/src/clis/zhihu/question.test.ts +42 -17
  368. package/src/clis/zhihu/question.ts +31 -26
  369. package/src/commanderAdapter.test.ts +51 -0
  370. package/src/commanderAdapter.ts +8 -4
  371. package/src/completion.test.ts +30 -0
  372. package/src/completion.ts +3 -1
  373. package/src/doctor.ts +1 -1
  374. package/src/electron-apps.ts +9 -1
  375. package/src/errors.ts +1 -1
  376. package/src/execution.ts +26 -30
  377. package/src/explore.ts +1 -1
  378. package/src/launcher.test.ts +121 -7
  379. package/src/launcher.ts +87 -9
  380. package/src/output.test.ts +50 -90
  381. package/src/output.ts +10 -1
  382. package/src/pipeline/executor.test.ts +0 -2
  383. package/src/pipeline/steps/download.test.ts +0 -2
  384. package/src/registry.ts +2 -0
  385. package/src/serialization.ts +2 -0
  386. package/src/types.ts +9 -2
  387. package/tests/e2e/browser-auth.test.ts +9 -0
  388. package/CLI-EXPLORER.md +0 -724
  389. package/CLI-ONESHOT.md +0 -216
  390. package/SKILL.md +0 -59
@@ -1,12 +1,24 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { probeCDP, detectProcess, discoverAppPath } from './launcher.js';
2
+ import { detectProcess, discoverAppPath, launchDetachedApp, launchElectronApp, probeCDP, resolveExecutableCandidates } from './launcher.js';
3
+ function createMockChildProcess() {
4
+ const listeners = new Map();
5
+ return {
6
+ once: vi.fn((event, handler) => {
7
+ listeners.set(event, [...(listeners.get(event) ?? []), handler]);
8
+ }),
9
+ off: vi.fn((event, handler) => {
10
+ listeners.set(event, (listeners.get(event) ?? []).filter((listener) => listener !== handler));
11
+ }),
12
+ unref: vi.fn(),
13
+ emit: (event, value) => {
14
+ for (const listener of listeners.get(event) ?? [])
15
+ listener(value);
16
+ },
17
+ };
18
+ }
3
19
  vi.mock('node:child_process', () => ({
4
20
  execFileSync: vi.fn(),
5
- spawn: vi.fn(() => ({
6
- unref: vi.fn(),
7
- pid: 12345,
8
- on: vi.fn(),
9
- })),
21
+ spawn: vi.fn(),
10
22
  }));
11
23
  const cp = vi.mocked(await import('node:child_process'));
12
24
  describe('probeCDP', () => {
@@ -28,7 +40,7 @@ describe('detectProcess', () => {
28
40
  const result = detectProcess('NonExistentApp');
29
41
  expect(result).toBe(false);
30
42
  });
31
- it('returns true when pgrep finds a process', () => {
43
+ it.skipIf(process.platform === 'win32')('returns true when pgrep finds a process', () => {
32
44
  cp.execFileSync.mockReturnValue('12345\n');
33
45
  const result = detectProcess('Cursor');
34
46
  expect(result).toBe(true);
@@ -55,3 +67,72 @@ describe('discoverAppPath', () => {
55
67
  expect(result).toBeNull();
56
68
  });
57
69
  });
70
+ describe('launchDetachedApp', () => {
71
+ beforeEach(() => {
72
+ vi.restoreAllMocks();
73
+ cp.spawn.mockReset();
74
+ });
75
+ it('unrefs the process after spawn succeeds', async () => {
76
+ const child = createMockChildProcess();
77
+ cp.spawn.mockImplementation(() => {
78
+ queueMicrotask(() => child.emit('spawn'));
79
+ return child;
80
+ });
81
+ await expect(launchDetachedApp('/Applications/Antigravity.app/Contents/MacOS/Antigravity', ['--remote-debugging-port=9234'], 'Antigravity'))
82
+ .resolves
83
+ .toBeUndefined();
84
+ expect(child.unref).toHaveBeenCalledTimes(1);
85
+ });
86
+ it('converts ENOENT into a controlled launch error', async () => {
87
+ const child = createMockChildProcess();
88
+ cp.spawn.mockImplementation(() => {
89
+ queueMicrotask(() => child.emit('error', Object.assign(new Error('missing binary'), { code: 'ENOENT' })));
90
+ return child;
91
+ });
92
+ await expect(launchDetachedApp('/Applications/Antigravity.app/Contents/MacOS/Antigravity', ['--remote-debugging-port=9234'], 'Antigravity'))
93
+ .rejects
94
+ .toThrow('Could not launch Antigravity');
95
+ expect(child.unref).not.toHaveBeenCalled();
96
+ });
97
+ });
98
+ describe('resolveExecutableCandidates', () => {
99
+ it('prefers explicit executable candidates over processName', () => {
100
+ const app = {
101
+ port: 9234,
102
+ processName: 'Antigravity',
103
+ executableNames: ['Electron', 'Antigravity'],
104
+ };
105
+ expect(resolveExecutableCandidates('/Applications/Antigravity.app', app)).toEqual([
106
+ '/Applications/Antigravity.app/Contents/MacOS/Electron',
107
+ '/Applications/Antigravity.app/Contents/MacOS/Antigravity',
108
+ ]);
109
+ });
110
+ });
111
+ describe('launchElectronApp', () => {
112
+ beforeEach(() => {
113
+ vi.restoreAllMocks();
114
+ cp.spawn.mockReset();
115
+ });
116
+ it('falls back to the next executable candidate when the first is missing', async () => {
117
+ const firstChild = createMockChildProcess();
118
+ const secondChild = createMockChildProcess();
119
+ const app = {
120
+ port: 9234,
121
+ processName: 'Antigravity',
122
+ executableNames: ['Electron', 'Antigravity'],
123
+ };
124
+ cp.spawn
125
+ .mockImplementationOnce(() => {
126
+ queueMicrotask(() => firstChild.emit('error', Object.assign(new Error('missing binary'), { code: 'ENOENT' })));
127
+ return firstChild;
128
+ })
129
+ .mockImplementationOnce(() => {
130
+ queueMicrotask(() => secondChild.emit('spawn'));
131
+ return secondChild;
132
+ });
133
+ await expect(launchElectronApp('/Applications/Antigravity.app', app, ['--remote-debugging-port=9234'], 'Antigravity')).resolves.toBeUndefined();
134
+ expect(cp.spawn).toHaveBeenNthCalledWith(1, '/Applications/Antigravity.app/Contents/MacOS/Electron', ['--remote-debugging-port=9234'], { detached: true, stdio: 'ignore' });
135
+ expect(cp.spawn).toHaveBeenNthCalledWith(2, '/Applications/Antigravity.app/Contents/MacOS/Antigravity', ['--remote-debugging-port=9234'], { detached: true, stdio: 'ignore' });
136
+ expect(secondChild.unref).toHaveBeenCalledTimes(1);
137
+ });
138
+ });
package/dist/output.d.ts CHANGED
@@ -3,6 +3,8 @@
3
3
  */
4
4
  export interface RenderOptions {
5
5
  fmt?: string;
6
+ /** True when the user explicitly passed -f on the command line */
7
+ fmtExplicit?: boolean;
6
8
  columns?: string[];
7
9
  title?: string;
8
10
  elapsed?: number;
package/dist/output.js CHANGED
@@ -15,7 +15,16 @@ function resolveColumns(rows, opts) {
15
15
  return opts.columns ?? Object.keys(rows[0] ?? {});
16
16
  }
17
17
  export function render(data, opts = {}) {
18
- const fmt = opts.fmt ?? 'table';
18
+ let fmt = opts.fmt ?? 'table';
19
+ // Non-TTY auto-downgrade only when format was NOT explicitly passed by user.
20
+ // Priority: explicit -f (any value) > OUTPUT env var > TTY auto-detect > table
21
+ if (!opts.fmtExplicit) {
22
+ const envFmt = process.env.OUTPUT?.trim().toLowerCase();
23
+ if (envFmt)
24
+ fmt = envFmt;
25
+ else if (fmt === 'table' && !process.stdout.isTTY)
26
+ fmt = 'yaml';
27
+ }
19
28
  if (data === null || data === undefined) {
20
29
  console.log(data);
21
30
  return;
@@ -1,4 +1 @@
1
- /**
2
- * Tests for output.ts: render function format coverage.
3
- */
4
1
  export {};
@@ -1,95 +1,62 @@
1
- /**
2
- * Tests for output.ts: render function format coverage.
3
- */
4
- import { describe, it, expect, vi, afterEach } from 'vitest';
1
+ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
5
2
  import { render } from './output.js';
6
- afterEach(() => {
7
- vi.restoreAllMocks();
8
- });
9
- describe('render', () => {
10
- it('renders JSON output', () => {
11
- const log = vi.spyOn(console, 'log').mockImplementation(() => { });
12
- render([{ title: 'Hello', rank: 1 }], { fmt: 'json' });
13
- expect(log).toHaveBeenCalledOnce();
14
- const output = log.mock.calls[0]?.[0];
15
- const parsed = JSON.parse(output);
16
- expect(parsed).toEqual([{ title: 'Hello', rank: 1 }]);
17
- });
18
- it('renders Markdown table output', () => {
19
- const log = vi.spyOn(console, 'log').mockImplementation(() => { });
20
- render([{ name: 'Alice', score: 100 }], { fmt: 'md', columns: ['name', 'score'] });
21
- const calls = log.mock.calls.map(c => c[0]);
22
- expect(calls[0]).toContain('| name | score |');
23
- expect(calls[1]).toContain('| --- | --- |');
24
- expect(calls[2]).toContain('| Alice | 100 |');
25
- });
26
- it('renders CSV output with proper quoting', () => {
27
- const log = vi.spyOn(console, 'log').mockImplementation(() => { });
28
- render([{ name: 'Alice, Bob', value: 'say "hi"' }], { fmt: 'csv' });
29
- const calls = log.mock.calls.map(c => c[0]);
30
- // Header
31
- expect(calls[0]).toBe('name,value');
32
- // Values with commas/quotes are quoted
33
- expect(calls[1]).toContain('"Alice, Bob"');
34
- expect(calls[1]).toContain('"say ""hi"""');
35
- });
36
- it('handles null and undefined data', () => {
37
- const log = vi.spyOn(console, 'log').mockImplementation(() => { });
38
- render(null, { fmt: 'json' });
39
- expect(log).toHaveBeenCalledWith(null);
40
- });
41
- it('renders single object as single-row table', () => {
42
- const log = vi.spyOn(console, 'log').mockImplementation(() => { });
43
- render({ title: 'Test' }, { fmt: 'json' });
44
- const output = log.mock.calls[0]?.[0];
45
- const parsed = JSON.parse(output);
46
- expect(parsed).toEqual({ title: 'Test' });
47
- });
48
- it('handles empty array gracefully', () => {
49
- const log = vi.spyOn(console, 'log').mockImplementation(() => { });
50
- render([], { fmt: 'table' });
51
- // Should show "(no data)" for empty arrays
52
- expect(log).toHaveBeenCalled();
53
- });
54
- it('uses custom columns for CSV', () => {
55
- const log = vi.spyOn(console, 'log').mockImplementation(() => { });
56
- render([{ a: 1, b: 2, c: 3 }], { fmt: 'csv', columns: ['a', 'c'] });
57
- const calls = log.mock.calls.map(c => c[0]);
58
- expect(calls[0]).toBe('a,c');
59
- expect(calls[1]).toBe('1,3');
60
- });
61
- it('renders YAML output', () => {
62
- const log = vi.spyOn(console, 'log').mockImplementation(() => { });
63
- render([{ title: 'Hello', rank: 1 }], { fmt: 'yaml' });
64
- expect(log).toHaveBeenCalledOnce();
65
- expect(log.mock.calls[0]?.[0]).toContain('- title: Hello');
66
- expect(log.mock.calls[0]?.[0]).toContain('rank: 1');
67
- });
68
- it('renders yml alias as YAML output', () => {
69
- const log = vi.spyOn(console, 'log').mockImplementation(() => { });
70
- render({ title: 'Hello' }, { fmt: 'yml' });
71
- expect(log).toHaveBeenCalledOnce();
72
- expect(log.mock.calls[0]?.[0]).toContain('title: Hello');
73
- });
74
- it('handles null values in CSV cells', () => {
75
- const log = vi.spyOn(console, 'log').mockImplementation(() => { });
76
- render([{ name: 'test', value: null }], { fmt: 'csv' });
77
- const calls = log.mock.calls.map(c => c[0]);
78
- expect(calls[1]).toBe('test,');
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
- ]);
3
+ describe('output TTY detection', () => {
4
+ const originalIsTTY = process.stdout.isTTY;
5
+ const originalEnv = process.env.OUTPUT;
6
+ let logSpy;
7
+ beforeEach(() => {
8
+ logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
9
+ });
10
+ afterEach(() => {
11
+ Object.defineProperty(process.stdout, 'isTTY', { value: originalIsTTY, writable: true });
12
+ if (originalEnv === undefined)
13
+ delete process.env.OUTPUT;
14
+ else
15
+ process.env.OUTPUT = originalEnv;
16
+ logSpy.mockRestore();
17
+ });
18
+ it('outputs YAML in non-TTY when format is default table', () => {
19
+ Object.defineProperty(process.stdout, 'isTTY', { value: false, writable: true });
20
+ // commanderAdapter always passes fmt:'table' as default this must still trigger downgrade
21
+ render([{ name: 'alice', score: 10 }], { fmt: 'table', columns: ['name', 'score'] });
22
+ const out = logSpy.mock.calls.map((c) => c[0]).join('\n');
23
+ expect(out).toContain('name: alice');
24
+ expect(out).toContain('score: 10');
25
+ });
26
+ it('outputs table in TTY when format is default table', () => {
27
+ Object.defineProperty(process.stdout, 'isTTY', { value: true, writable: true });
28
+ render([{ name: 'alice', score: 10 }], { fmt: 'table', columns: ['name', 'score'] });
29
+ const out = logSpy.mock.calls.map((c) => c[0]).join('\n');
30
+ expect(out).toContain('alice');
31
+ });
32
+ it('respects explicit -f json even in non-TTY', () => {
33
+ Object.defineProperty(process.stdout, 'isTTY', { value: false, writable: true });
34
+ render([{ name: 'alice' }], { fmt: 'json' });
35
+ const out = logSpy.mock.calls.map((c) => c[0]).join('\n');
36
+ expect(JSON.parse(out)).toEqual([{ name: 'alice' }]);
37
+ });
38
+ it('OUTPUT env var overrides default table in non-TTY', () => {
39
+ Object.defineProperty(process.stdout, 'isTTY', { value: false, writable: true });
40
+ process.env.OUTPUT = 'json';
41
+ render([{ name: 'alice' }], { fmt: 'table' });
42
+ const out = logSpy.mock.calls.map((c) => c[0]).join('\n');
43
+ expect(JSON.parse(out)).toEqual([{ name: 'alice' }]);
44
+ });
45
+ it('explicit -f flag takes precedence over OUTPUT env var', () => {
46
+ Object.defineProperty(process.stdout, 'isTTY', { value: false, writable: true });
47
+ process.env.OUTPUT = 'json';
48
+ render([{ name: 'alice' }], { fmt: 'csv', fmtExplicit: true });
49
+ const out = logSpy.mock.calls.map((c) => c[0]).join('\n');
50
+ expect(out).toContain('name');
51
+ expect(out).toContain('alice');
52
+ expect(out).not.toContain('"name"'); // not JSON
53
+ });
54
+ it('explicit -f table overrides non-TTY auto-downgrade', () => {
55
+ Object.defineProperty(process.stdout, 'isTTY', { value: false, writable: true });
56
+ render([{ name: 'alice' }], { fmt: 'table', fmtExplicit: true, columns: ['name'] });
57
+ const out = logSpy.mock.calls.map((c) => c[0]).join('\n');
58
+ // Should be table output, not YAML
59
+ expect(out).not.toContain('name: alice');
60
+ expect(out).toContain('alice');
94
61
  });
95
62
  });
@@ -17,8 +17,6 @@ function createMockPage(overrides = {}) {
17
17
  getFormState: vi.fn().mockResolvedValue({}),
18
18
  wait: vi.fn(),
19
19
  tabs: vi.fn().mockResolvedValue([]),
20
- closeTab: vi.fn(),
21
- newTab: vi.fn(),
22
20
  selectTab: vi.fn(),
23
21
  networkRequests: vi.fn().mockResolvedValue([]),
24
22
  consoleMessages: vi.fn().mockResolvedValue(''),
@@ -29,8 +29,6 @@ function createMockPage(getCookies) {
29
29
  getFormState: vi.fn().mockResolvedValue({}),
30
30
  wait: vi.fn(),
31
31
  tabs: vi.fn().mockResolvedValue([]),
32
- closeTab: vi.fn(),
33
- newTab: vi.fn(),
34
32
  selectTab: vi.fn(),
35
33
  networkRequests: vi.fn().mockResolvedValue([]),
36
34
  consoleMessages: vi.fn().mockResolvedValue([]),
@@ -14,6 +14,7 @@ export interface Arg {
14
14
  type?: string;
15
15
  default?: unknown;
16
16
  required?: boolean;
17
+ valueRequired?: boolean;
17
18
  positional?: boolean;
18
19
  help?: string;
19
20
  choices?: string[];
@@ -40,6 +41,7 @@ export interface CliCommand {
40
41
  source?: string;
41
42
  footerExtra?: (kwargs: CommandArgs) => string | undefined;
42
43
  requiredEnv?: RequiredEnv[];
44
+ validateArgs?: (kwargs: CommandArgs) => void;
43
45
  /** Deprecation note shown in help / execution warnings. */
44
46
  deprecated?: boolean | string;
45
47
  /** Preferred replacement command, if any. */
@@ -9,6 +9,7 @@ export type SerializedArg = {
9
9
  name: string;
10
10
  type: string;
11
11
  required: boolean;
12
+ valueRequired: boolean;
12
13
  positional: boolean;
13
14
  choices: string[];
14
15
  default: unknown;
@@ -11,6 +11,7 @@ export function serializeArg(a) {
11
11
  name: a.name,
12
12
  type: a.type ?? 'string',
13
13
  required: !!a.required,
14
+ valueRequired: !!a.valueRequired,
14
15
  positional: !!a.positional,
15
16
  choices: a.choices ?? [],
16
17
  default: a.default ?? null,
package/dist/types.d.ts CHANGED
@@ -56,8 +56,8 @@ export interface IPage {
56
56
  getFormState(): Promise<any>;
57
57
  wait(options: number | WaitOptions): Promise<void>;
58
58
  tabs(): Promise<any>;
59
- closeTab(index?: number): Promise<void>;
60
- newTab(): Promise<void>;
59
+ closeTab?(index?: number): Promise<void>;
60
+ newTab?(): Promise<void>;
61
61
  selectTab(index: number): Promise<void>;
62
62
  networkRequests(includeStatic?: boolean): Promise<any>;
63
63
  consoleMessages(level?: string): Promise<any>;
@@ -70,11 +70,18 @@ export interface IPage {
70
70
  getInterceptedRequests(): Promise<any[]>;
71
71
  waitForCapture(timeout?: number): Promise<void>;
72
72
  screenshot(options?: ScreenshotOptions): Promise<string>;
73
+ startNetworkCapture?(pattern?: string): Promise<void>;
74
+ readNetworkCapture?(): Promise<unknown[]>;
73
75
  /**
74
76
  * Set local file paths on a file input element via CDP DOM.setFileInputFiles.
75
77
  * Chrome reads the files directly — no base64 encoding or payload size limits.
76
78
  */
77
79
  setFileInput?(files: string[], selector?: string): Promise<void>;
80
+ /**
81
+ * Insert text via native CDP Input.insertText into the currently focused element.
82
+ * Useful for rich editors that ignore synthetic DOM value/text mutations.
83
+ */
84
+ insertText?(text: string): Promise<void>;
78
85
  closeWindow?(): Promise<void>;
79
86
  /** Returns the current page URL, or null if unavailable. */
80
87
  getCurrentUrl?(): Promise<string | null>;
@@ -54,6 +54,7 @@ export default defineConfig({
54
54
  { text: 'Bilibili', link: '/adapters/browser/bilibili' },
55
55
  { text: 'Zhihu', link: '/adapters/browser/zhihu' },
56
56
  { text: 'Xiaohongshu', link: '/adapters/browser/xiaohongshu' },
57
+ { text: 'Xiaoe', link: '/adapters/browser/xiaoe' },
57
58
  { text: 'Weibo', link: '/adapters/browser/weibo' },
58
59
  { text: 'YouTube', link: '/adapters/browser/youtube' },
59
60
  { text: 'Xueqiu', link: '/adapters/browser/xueqiu' },
@@ -73,7 +74,9 @@ export default defineConfig({
73
74
  { text: 'Chaoxing', link: '/adapters/browser/chaoxing' },
74
75
  { text: 'Grok', link: '/adapters/browser/grok' },
75
76
  { text: 'Amazon', link: '/adapters/browser/amazon' },
77
+ { text: '1688', link: '/adapters/browser/1688' },
76
78
  { text: 'Gemini', link: '/adapters/browser/gemini' },
79
+ { text: 'Yuanbao', link: '/adapters/browser/yuanbao' },
77
80
  { text: 'NotebookLM', link: '/adapters/browser/notebooklm' },
78
81
  { text: 'WeRead', link: '/adapters/browser/weread' },
79
82
  { text: 'Douban', link: '/adapters/browser/douban' },
@@ -91,6 +94,7 @@ export default defineConfig({
91
94
  { text: 'TikTok', link: '/adapters/browser/tiktok' },
92
95
  { text: 'Web (Generic)', link: '/adapters/browser/web' },
93
96
  { text: 'Weixin', link: '/adapters/browser/weixin' },
97
+ { text: 'Xianyu', link: '/adapters/browser/xianyu' },
94
98
  ],
95
99
  },
96
100
  {
@@ -0,0 +1,52 @@
1
+ # 1688
2
+
3
+ **Mode**: 🔐 Browser · **Domain**: `1688.com`
4
+
5
+ ## Commands
6
+
7
+ | Command | Description |
8
+ |---------|-------------|
9
+ | `opencli 1688 search "<query>" --limit <n>` | Search public product candidates with price, MOQ, seller link, and visible badges |
10
+ | `opencli 1688 item <url-or-offer-id>` | Read a public product detail page with price tiers, MOQ, delivery text, and seller basics |
11
+ | `opencli 1688 store <url-or-member-id>` | Read a public supplier/store page with company info, years on platform, categories, and visible service signals |
12
+
13
+ ## Usage Examples
14
+
15
+ ```bash
16
+ # Search products
17
+ opencli 1688 search "桌面置物架 宿舍 收纳" --limit 10
18
+
19
+ # JSON output
20
+ opencli 1688 search "桌面置物架 宿舍 收纳" --limit 10 -f json
21
+
22
+ # Read an item by offer id
23
+ opencli 1688 item 841141931191 -f json
24
+
25
+ # Read an item by URL
26
+ opencli 1688 item https://detail.1688.com/offer/841141931191.html -f json
27
+
28
+ # Read a supplier store
29
+ opencli 1688 store https://shop52908bfw19166.1688.com/ -f json
30
+
31
+ # Read a supplier by member id
32
+ opencli 1688 store b2b-22154705262941f196 -f json
33
+ ```
34
+
35
+ ## Prerequisites
36
+
37
+ - Chrome running and **logged into** `1688.com`
38
+ - [Browser Bridge extension](/guide/browser-bridge) installed
39
+
40
+ ## Notes
41
+
42
+ - This adapter only returns fields visible on public pages. It does not send inquiries, place orders, or access seller back office data.
43
+ - Prefer stable identifiers such as `offer_id`, `member_id`, and `shop_id` for follow-up workflows.
44
+ - `search --limit` defaults to `20` and is capped at `100`.
45
+ - `search` deduplicates with key priority: `offer_id` first, then canonical `item_url`.
46
+ - `item` can be more sensitive to the active browser target than `search` or `store`.
47
+
48
+ ## Troubleshooting
49
+
50
+ - If `opencli 1688 item` reports `did not expose product context`, first make sure the open page is a real `detail.1688.com` item page.
51
+ - If the browser target is too broad, retry with `OPENCLI_CDP_TARGET=detail.1688.com`.
52
+ - If you hit a slider or verification page, refresh the real page in Chrome and retry.
@@ -44,4 +44,5 @@ opencli 36kr hot -f json
44
44
 
45
45
  ## Prerequisites
46
46
 
47
- - No browser required — uses public API
47
+ - `news`: No browser required — uses public RSS feed
48
+ - `hot`, `search`, `article`: Chrome running with [Browser Bridge extension](/guide/browser-bridge) installed
@@ -11,12 +11,16 @@ Browser adapter for [Doubao Chat](https://www.doubao.com/chat).
11
11
  | `opencli doubao send "..."` | Send a message to the current Doubao chat |
12
12
  | `opencli doubao read` | Read the visible Doubao conversation |
13
13
  | `opencli doubao ask "..."` | Send a prompt and wait for a reply |
14
+ | `opencli doubao detail <id>` | 对话详情 |
15
+ | `opencli doubao history` | 历史对话列表 |
16
+ | `opencli doubao meeting-summary <id>` | 会议总结 |
17
+ | `opencli doubao meeting-transcript <id>` | 会议记录 |
14
18
 
15
19
  ## Prerequisites
16
20
 
17
21
  - Chrome is running
18
22
  - You are already logged into [doubao.com](https://www.doubao.com/)
19
- - Playwright MCP Bridge / browser bridge is configured for OpenCLI
23
+ - Browser Bridge extension is installed and enabled for OpenCLI
20
24
 
21
25
  ## Examples
22
26
 
@@ -0,0 +1,53 @@
1
+ # Hupu (虎扑)
2
+
3
+ **Mode**: 🌐 Public / 🔐 Browser · **Domain**: `bbs.hupu.com`
4
+
5
+ ## Commands
6
+
7
+ | Command | Description |
8
+ |---------|-------------|
9
+ | `opencli hupu hot` | Read Hupu hot threads |
10
+ | `opencli hupu search <keyword>` | Search Hupu threads by keyword |
11
+ | `opencli hupu detail <tid>` | Read one thread and optional hot replies |
12
+ | `opencli hupu reply <tid> <text>` | Reply to a thread or quote one reply |
13
+ | `opencli hupu like <tid> <pid>` | Like one reply |
14
+ | `opencli hupu unlike <tid> <pid>` | Cancel like on one reply |
15
+
16
+ ## Usage Examples
17
+
18
+ ```bash
19
+ # Hot threads
20
+ opencli hupu hot --limit 5
21
+
22
+ # Search threads
23
+ opencli hupu search 湖人 --limit 10
24
+
25
+ # Read one thread and include hot replies
26
+ opencli hupu detail 638234927 --replies true
27
+
28
+ # Reply to the thread
29
+ opencli hupu reply 638234927 "hello from opencli" --topic_id 502
30
+
31
+ # Quote one hot reply by pid
32
+ opencli hupu reply 638234927 "replying to this comment" --topic_id 502 --quote_id 174908
33
+
34
+ # Like / unlike one reply
35
+ opencli hupu like 638234927 174908 --fid 4860
36
+ opencli hupu unlike 638234927 174908 --fid 4860
37
+
38
+ # JSON output
39
+ opencli hupu detail 638234927 -f json
40
+ ```
41
+
42
+ ## Notes
43
+
44
+ - `reply --topic_id` maps to Hupu's API `topicId`, for example `502` for Basketball News
45
+ - `reply --quote_id` is the quoted reply `pid`
46
+ - `like` / `unlike --fid` uses the forum ID from thread metadata
47
+ - `detail --replies true` appends top hot replies to the content field
48
+
49
+ ## Prerequisites
50
+
51
+ - Chrome running and able to open `bbs.hupu.com`
52
+ - [Browser Bridge extension](/guide/browser-bridge) installed
53
+ - For `reply`, `like`, and `unlike`, a valid Hupu login session in Chrome is required
@@ -9,6 +9,7 @@
9
9
  | `opencli sinafinance news` | 新浪财经 7×24 小时实时快讯 | 🌐 Public |
10
10
  | `opencli sinafinance rolling-news` | 新浪财经滚动新闻 | 🔐 Browser |
11
11
  | `opencli sinafinance stock` | 新浪财经行情(A股/港股/美股) | 🌐 Public |
12
+ | `opencli sinafinance stock-rank` | 新浪财经热搜榜 | 🔐 Browser |
12
13
 
13
14
  ## Usage Examples
14
15
 
@@ -56,6 +57,28 @@ opencli sinafinance stock 招商证券
56
57
  opencli sinafinance stock 贵州茅台 -f json
57
58
  ```
58
59
 
60
+ ### stock-rank - 热搜榜
61
+
62
+ ```bash
63
+ # Default A股热搜榜
64
+ opencli sinafinance stock-rank
65
+
66
+ # 港股热搜榜
67
+ opencli sinafinance stock-rank --market hk
68
+
69
+ # 美股热搜榜
70
+ opencli sinafinance stock-rank --market us
71
+
72
+ # 外汇热搜榜
73
+ opencli sinafinance stock-rank --market ft
74
+
75
+ # 期货热搜榜
76
+ opencli sinafinance stock-rank --market wh
77
+
78
+ # JSON output
79
+ opencli sinafinance stock-rank -f json
80
+ ```
81
+
59
82
  ## Options
60
83
 
61
84
  ### news
@@ -71,11 +94,17 @@ opencli sinafinance stock 贵州茅台 -f json
71
94
  |--------|-------------|
72
95
  | `--market` | Market: `cn`, `hk`, `us`, `auto` (default: auto). When `auto`, searches in cn, hk, us order |
73
96
 
97
+ ### stock-rank
98
+
99
+ | Option | Description |
100
+ |--------|-------------|
101
+ | `--market` | Market: `cn` (A股, 默认), `ft` (期货), `us` (美股), `wh` (外汇), `hk` (港股) |
102
+
74
103
  ## Prerequisites
75
104
 
76
105
  - `news` & `stock`: No browser required — uses public API
77
- - `rolling-news`: Chrome running and **logged into** `finance.sina.com.cn`
78
- - For `rolling-news`: [Browser Bridge extension](/guide/browser-bridge) installed
106
+ - `rolling-news` & `stock-rank`: Chrome running and **logged into** `finance.sina.com.cn`
107
+ - For `rolling-news` & `stock-rank`: [Browser Bridge extension](/guide/browser-bridge) installed
79
108
 
80
109
  ## Notes
81
110
 
@@ -83,3 +112,4 @@ opencli sinafinance stock 贵州茅台 -f json
83
112
  - `stock` supports Chinese names, Chinese codes, and ticker symbols; auto-detects market
84
113
  - Market priority for auto-detection: cn (A股) → hk (港股) → us (美股)
85
114
  - US stock `High`/`Low` columns show 52-week range; A股/港股 show today's range
115
+ - `stock-rank` scrapes the hot search list from the Sina Finance homepage; requires browser login
@@ -6,8 +6,13 @@
6
6
 
7
7
  | Command | Description |
8
8
  |---------|-------------|
9
- | `opencli weibo hot` | |
9
+ | `opencli weibo hot` | 微博热搜 |
10
10
  | `opencli weibo search` | Search Weibo posts by keyword |
11
+ | `opencli weibo feed` | 首页时间线 |
12
+ | `opencli weibo user` | 用户信息 |
13
+ | `opencli weibo me` | 我的信息 |
14
+ | `opencli weibo post` | 发微博 |
15
+ | `opencli weibo comments` | 微博评论 |
11
16
 
12
17
  ## Usage Examples
13
18
 
@@ -8,6 +8,8 @@
8
8
  |---------|-------------|
9
9
  | `opencli wikipedia search` | Search Wikipedia articles |
10
10
  | `opencli wikipedia summary` | Get Wikipedia article summary |
11
+ | `opencli wikipedia random` | Random Wikipedia article |
12
+ | `opencli wikipedia trending` | Trending Wikipedia articles |
11
13
 
12
14
  ## Usage Examples
13
15