@jackwener/opencli 1.0.6 → 1.1.1

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 (400) hide show
  1. package/.agents/skills/cross-project-adapter-migration/SKILL.md +2 -2
  2. package/.github/pull_request_template.md +7 -0
  3. package/.github/workflows/doc-check.yml +36 -0
  4. package/.github/workflows/docs.yml +7 -42
  5. package/CHANGELOG.md +23 -0
  6. package/CLI-EXPLORER.md +9 -8
  7. package/README.md +51 -10
  8. package/README.zh-CN.md +29 -11
  9. package/SKILL.md +102 -33
  10. package/dist/browser/cdp.js +6 -1
  11. package/dist/browser/page.d.ts +4 -1
  12. package/dist/browser/page.js +7 -1
  13. package/dist/build-manifest.js +23 -16
  14. package/dist/cli-manifest.json +951 -296
  15. package/dist/cli.d.ts +6 -0
  16. package/dist/cli.js +225 -148
  17. package/dist/clis/antigravity/serve.js +296 -47
  18. package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
  19. package/dist/clis/apple-podcasts/commands.test.js +76 -0
  20. package/dist/clis/apple-podcasts/search.js +2 -2
  21. package/dist/clis/apple-podcasts/top.js +9 -2
  22. package/dist/clis/arxiv/paper.js +21 -0
  23. package/dist/clis/arxiv/search.js +24 -0
  24. package/dist/clis/arxiv/utils.d.ts +18 -0
  25. package/dist/clis/arxiv/utils.js +49 -0
  26. package/dist/clis/bilibili/dynamic.js +1 -1
  27. package/dist/clis/bilibili/favorite.js +1 -1
  28. package/dist/clis/bilibili/feed.js +1 -1
  29. package/dist/clis/bilibili/following.js +1 -1
  30. package/dist/clis/bilibili/history.js +1 -1
  31. package/dist/clis/bilibili/me.js +1 -1
  32. package/dist/clis/bilibili/ranking.js +1 -1
  33. package/dist/clis/bilibili/search.js +3 -3
  34. package/dist/clis/bilibili/subtitle.js +1 -1
  35. package/dist/clis/bilibili/user-videos.js +1 -1
  36. package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
  37. package/dist/clis/bloomberg/businessweek.d.ts +1 -0
  38. package/dist/clis/bloomberg/businessweek.js +17 -0
  39. package/dist/clis/bloomberg/economics.d.ts +1 -0
  40. package/dist/clis/bloomberg/economics.js +17 -0
  41. package/dist/clis/bloomberg/feeds.d.ts +1 -0
  42. package/dist/clis/bloomberg/feeds.js +15 -0
  43. package/dist/clis/bloomberg/industries.d.ts +1 -0
  44. package/dist/clis/bloomberg/industries.js +17 -0
  45. package/dist/clis/bloomberg/main.d.ts +1 -0
  46. package/dist/clis/bloomberg/main.js +17 -0
  47. package/dist/clis/bloomberg/markets.d.ts +1 -0
  48. package/dist/clis/bloomberg/markets.js +17 -0
  49. package/dist/clis/bloomberg/news.d.ts +1 -0
  50. package/dist/clis/bloomberg/news.js +105 -0
  51. package/dist/clis/bloomberg/opinions.d.ts +1 -0
  52. package/dist/clis/bloomberg/opinions.js +17 -0
  53. package/dist/clis/bloomberg/politics.d.ts +1 -0
  54. package/dist/clis/bloomberg/politics.js +17 -0
  55. package/dist/clis/bloomberg/tech.d.ts +1 -0
  56. package/dist/clis/bloomberg/tech.js +17 -0
  57. package/dist/clis/bloomberg/utils.d.ts +34 -0
  58. package/dist/clis/bloomberg/utils.js +364 -0
  59. package/dist/clis/bloomberg/utils.test.d.ts +1 -0
  60. package/dist/clis/bloomberg/utils.test.js +129 -0
  61. package/dist/clis/boss/batchgreet.d.ts +1 -0
  62. package/dist/clis/boss/batchgreet.js +147 -0
  63. package/dist/clis/boss/chatlist.js +2 -2
  64. package/dist/clis/boss/detail.js +2 -2
  65. package/dist/clis/boss/exchange.d.ts +1 -0
  66. package/dist/clis/boss/exchange.js +111 -0
  67. package/dist/clis/boss/greet.d.ts +1 -0
  68. package/dist/clis/boss/greet.js +175 -0
  69. package/dist/clis/boss/invite.d.ts +1 -0
  70. package/dist/clis/boss/invite.js +158 -0
  71. package/dist/clis/boss/joblist.d.ts +1 -0
  72. package/dist/clis/boss/joblist.js +55 -0
  73. package/dist/clis/boss/mark.d.ts +1 -0
  74. package/dist/clis/boss/mark.js +141 -0
  75. package/dist/clis/boss/recommend.d.ts +1 -0
  76. package/dist/clis/boss/recommend.js +83 -0
  77. package/dist/clis/boss/search.js +1 -1
  78. package/dist/clis/boss/send.js +1 -1
  79. package/dist/clis/boss/stats.d.ts +1 -0
  80. package/dist/clis/boss/stats.js +116 -0
  81. package/dist/clis/chaoxing/assignments.js +1 -1
  82. package/dist/clis/chaoxing/exams.js +1 -1
  83. package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
  84. package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
  85. package/dist/clis/chaoxing/utils.test.d.ts +1 -0
  86. package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
  87. package/dist/clis/chatgpt/read.js +1 -1
  88. package/dist/clis/chatwise/export.js +1 -1
  89. package/dist/clis/chatwise/model.js +2 -2
  90. package/dist/clis/chatwise/screenshot.js +1 -1
  91. package/dist/clis/codex/export.js +1 -1
  92. package/dist/clis/codex/model.js +2 -2
  93. package/dist/clis/codex/screenshot.js +1 -1
  94. package/dist/clis/coupang/add-to-cart.js +3 -4
  95. package/dist/clis/coupang/search.js +2 -4
  96. package/dist/clis/coupang/utils.test.d.ts +1 -0
  97. package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
  98. package/dist/clis/ctrip/search.js +1 -1
  99. package/dist/clis/cursor/export.js +1 -1
  100. package/dist/clis/cursor/model.js +2 -2
  101. package/dist/clis/cursor/screenshot.js +1 -1
  102. package/dist/clis/jike/comment.js +2 -3
  103. package/dist/clis/jike/create.js +1 -2
  104. package/dist/clis/jike/feed.js +0 -1
  105. package/dist/clis/jike/like.js +1 -2
  106. package/dist/clis/jike/notifications.js +0 -1
  107. package/dist/clis/jike/post.yaml +1 -0
  108. package/dist/clis/jike/repost.js +1 -2
  109. package/dist/clis/jike/search.js +2 -3
  110. package/dist/clis/jike/topic.yaml +1 -0
  111. package/dist/clis/jike/user.yaml +1 -0
  112. package/dist/clis/jimeng/history.yaml +0 -1
  113. package/dist/clis/linkedin/search.js +7 -7
  114. package/dist/clis/linux-do/category.yaml +1 -0
  115. package/dist/clis/linux-do/search.yaml +4 -3
  116. package/dist/clis/linux-do/topic.yaml +1 -0
  117. package/dist/clis/notion/export.js +1 -1
  118. package/dist/clis/reddit/comment.js +3 -4
  119. package/dist/clis/reddit/read.js +4 -5
  120. package/dist/clis/reddit/save.js +2 -3
  121. package/dist/clis/reddit/saved.js +0 -1
  122. package/dist/clis/reddit/search.yaml +1 -0
  123. package/dist/clis/reddit/subscribe.js +0 -1
  124. package/dist/clis/reddit/upvote.js +2 -3
  125. package/dist/clis/reddit/upvoted.js +0 -1
  126. package/dist/clis/reddit/user-comments.yaml +1 -0
  127. package/dist/clis/reddit/user-posts.yaml +1 -0
  128. package/dist/clis/reddit/user.yaml +1 -0
  129. package/dist/clis/reuters/search.js +1 -1
  130. package/dist/clis/sinafinance/news.d.ts +7 -0
  131. package/dist/clis/sinafinance/news.js +61 -0
  132. package/dist/clis/smzdm/search.js +2 -3
  133. package/dist/clis/stackoverflow/search.yaml +1 -0
  134. package/dist/clis/steam/top-sellers.yaml +29 -0
  135. package/dist/clis/twitter/accept.js +2 -2
  136. package/dist/clis/twitter/article.js +2 -2
  137. package/dist/clis/twitter/block.d.ts +1 -0
  138. package/dist/clis/twitter/block.js +88 -0
  139. package/dist/clis/twitter/delete.js +1 -1
  140. package/dist/clis/twitter/hide-reply.d.ts +1 -0
  141. package/dist/clis/twitter/hide-reply.js +66 -0
  142. package/dist/clis/twitter/like.js +1 -1
  143. package/dist/clis/twitter/post.js +1 -1
  144. package/dist/clis/twitter/reply-dm.js +1 -1
  145. package/dist/clis/twitter/reply.js +2 -2
  146. package/dist/clis/twitter/search.js +1 -1
  147. package/dist/clis/twitter/thread.js +2 -2
  148. package/dist/clis/twitter/trending.d.ts +1 -0
  149. package/dist/clis/twitter/trending.js +91 -0
  150. package/dist/clis/twitter/unblock.d.ts +1 -0
  151. package/dist/clis/twitter/unblock.js +71 -0
  152. package/dist/clis/v2ex/topic.yaml +1 -0
  153. package/dist/clis/weibo/hot.js +0 -1
  154. package/dist/clis/weread/book.js +1 -1
  155. package/dist/clis/weread/highlights.js +1 -1
  156. package/dist/clis/weread/notes.js +1 -1
  157. package/dist/clis/weread/search.js +1 -1
  158. package/dist/clis/wikipedia/search.d.ts +1 -0
  159. package/dist/clis/wikipedia/search.js +30 -0
  160. package/dist/clis/wikipedia/summary.d.ts +1 -0
  161. package/dist/clis/wikipedia/summary.js +28 -0
  162. package/dist/clis/wikipedia/utils.d.ts +8 -0
  163. package/dist/clis/wikipedia/utils.js +18 -0
  164. package/dist/clis/xiaohongshu/creator-note-detail.d.ts +79 -5
  165. package/dist/clis/xiaohongshu/creator-note-detail.js +323 -70
  166. package/dist/clis/xiaohongshu/creator-note-detail.test.d.ts +1 -0
  167. package/dist/clis/xiaohongshu/creator-note-detail.test.js +258 -0
  168. package/dist/clis/xiaohongshu/creator-notes-summary.d.ts +28 -0
  169. package/dist/clis/xiaohongshu/creator-notes-summary.js +92 -0
  170. package/dist/clis/xiaohongshu/creator-notes-summary.test.d.ts +1 -0
  171. package/dist/clis/xiaohongshu/creator-notes-summary.test.js +49 -0
  172. package/dist/clis/xiaohongshu/creator-notes.d.ts +18 -5
  173. package/dist/clis/xiaohongshu/creator-notes.js +189 -71
  174. package/dist/clis/xiaohongshu/creator-notes.test.d.ts +1 -0
  175. package/dist/clis/xiaohongshu/creator-notes.test.js +191 -0
  176. package/dist/clis/xiaohongshu/creator-profile.js +0 -1
  177. package/dist/clis/xiaohongshu/creator-stats.js +0 -1
  178. package/dist/clis/xiaohongshu/download.js +2 -3
  179. package/dist/clis/xiaohongshu/feed.yaml +0 -1
  180. package/dist/clis/xiaohongshu/notifications.yaml +0 -1
  181. package/dist/clis/xiaohongshu/search.js +2 -2
  182. package/dist/clis/xiaohongshu/user.js +1 -2
  183. package/dist/clis/yahoo-finance/quote.js +0 -1
  184. package/dist/clis/youtube/search.js +1 -1
  185. package/dist/clis/youtube/transcript.js +1 -1
  186. package/dist/clis/youtube/video.js +1 -1
  187. package/dist/clis/zhihu/download.js +1 -2
  188. package/dist/clis/zhihu/question.js +1 -1
  189. package/dist/clis/zhihu/search.yaml +4 -3
  190. package/dist/commanderAdapter.d.ts +21 -0
  191. package/dist/commanderAdapter.js +111 -0
  192. package/dist/{engine.d.ts → discovery.d.ts} +0 -6
  193. package/dist/{engine.js → discovery.js} +1 -98
  194. package/dist/download/index.d.ts +2 -6
  195. package/dist/download/index.js +19 -46
  196. package/dist/engine.test.d.ts +1 -1
  197. package/dist/engine.test.js +8 -7
  198. package/dist/execution.d.ts +22 -0
  199. package/dist/execution.js +129 -0
  200. package/dist/explore.js +121 -107
  201. package/dist/external-clis.yaml +48 -0
  202. package/dist/external.d.ts +25 -0
  203. package/dist/external.js +156 -0
  204. package/dist/main.js +1 -1
  205. package/dist/pipeline/steps/browser.js +8 -2
  206. package/dist/registry.d.ts +2 -0
  207. package/dist/registry.js +2 -0
  208. package/dist/runtime.d.ts +5 -0
  209. package/dist/runtime.js +8 -0
  210. package/dist/serialization.d.ts +34 -0
  211. package/dist/serialization.js +63 -0
  212. package/dist/types.d.ts +4 -1
  213. package/docs/.vitepress/config.mts +14 -3
  214. package/docs/adapters/browser/arxiv.md +27 -0
  215. package/docs/adapters/browser/barchart.md +32 -0
  216. package/docs/adapters/browser/bloomberg.md +70 -0
  217. package/docs/adapters/browser/chaoxing.md +39 -0
  218. package/docs/adapters/browser/grok.md +35 -0
  219. package/docs/adapters/browser/hf.md +42 -0
  220. package/docs/adapters/browser/jike.md +45 -0
  221. package/docs/adapters/browser/jimeng.md +39 -0
  222. package/docs/adapters/browser/linux-do.md +45 -0
  223. package/docs/adapters/browser/sinafinance.md +35 -0
  224. package/docs/adapters/browser/stackoverflow.md +35 -0
  225. package/docs/adapters/browser/steam.md +26 -0
  226. package/docs/adapters/browser/twitter.md +3 -0
  227. package/docs/adapters/browser/weread.md +48 -0
  228. package/docs/adapters/browser/wikipedia.md +30 -0
  229. package/docs/adapters/browser/xiaohongshu.md +5 -1
  230. package/docs/adapters/desktop/chatgpt.md +3 -3
  231. package/docs/adapters/index.md +13 -0
  232. package/docs/advanced/download.md +4 -4
  233. package/docs/developer/architecture.md +17 -4
  234. package/package.json +1 -1
  235. package/scripts/check-doc-coverage.sh +69 -0
  236. package/scripts/copy-yaml.cjs +7 -0
  237. package/src/browser/cdp.ts +9 -4
  238. package/src/browser/page.ts +7 -1
  239. package/src/build-manifest.ts +25 -19
  240. package/src/cli.ts +253 -119
  241. package/src/clis/antigravity/serve.ts +323 -50
  242. package/src/clis/apple-podcasts/commands.test.ts +95 -0
  243. package/src/clis/apple-podcasts/search.ts +2 -2
  244. package/src/clis/apple-podcasts/top.ts +12 -2
  245. package/src/clis/arxiv/paper.ts +21 -0
  246. package/src/clis/arxiv/search.ts +24 -0
  247. package/src/clis/arxiv/utils.ts +63 -0
  248. package/src/clis/bilibili/dynamic.ts +1 -1
  249. package/src/clis/bilibili/favorite.ts +1 -1
  250. package/src/clis/bilibili/feed.ts +1 -1
  251. package/src/clis/bilibili/following.ts +1 -1
  252. package/src/clis/bilibili/history.ts +1 -1
  253. package/src/clis/bilibili/me.ts +1 -1
  254. package/src/clis/bilibili/ranking.ts +1 -1
  255. package/src/clis/bilibili/search.ts +3 -3
  256. package/src/clis/bilibili/subtitle.ts +1 -1
  257. package/src/clis/bilibili/user-videos.ts +1 -1
  258. package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
  259. package/src/clis/bloomberg/businessweek.ts +18 -0
  260. package/src/clis/bloomberg/economics.ts +18 -0
  261. package/src/clis/bloomberg/feeds.ts +16 -0
  262. package/src/clis/bloomberg/industries.ts +18 -0
  263. package/src/clis/bloomberg/main.ts +18 -0
  264. package/src/clis/bloomberg/markets.ts +18 -0
  265. package/src/clis/bloomberg/news.ts +136 -0
  266. package/src/clis/bloomberg/opinions.ts +18 -0
  267. package/src/clis/bloomberg/politics.ts +18 -0
  268. package/src/clis/bloomberg/tech.ts +18 -0
  269. package/src/clis/bloomberg/utils.test.ts +135 -0
  270. package/src/clis/bloomberg/utils.ts +429 -0
  271. package/src/clis/boss/batchgreet.ts +167 -0
  272. package/src/clis/boss/chatlist.ts +2 -2
  273. package/src/clis/boss/detail.ts +2 -2
  274. package/src/clis/boss/exchange.ts +126 -0
  275. package/src/clis/boss/greet.ts +198 -0
  276. package/src/clis/boss/invite.ts +177 -0
  277. package/src/clis/boss/joblist.ts +63 -0
  278. package/src/clis/boss/mark.ts +155 -0
  279. package/src/clis/boss/recommend.ts +94 -0
  280. package/src/clis/boss/search.ts +1 -1
  281. package/src/clis/boss/send.ts +1 -1
  282. package/src/clis/boss/stats.ts +130 -0
  283. package/src/clis/chaoxing/assignments.ts +1 -1
  284. package/src/clis/chaoxing/exams.ts +1 -1
  285. package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
  286. package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
  287. package/src/clis/chatgpt/README.zh-CN.md +3 -3
  288. package/src/clis/chatgpt/read.ts +1 -1
  289. package/src/clis/chatwise/export.ts +1 -1
  290. package/src/clis/chatwise/model.ts +2 -2
  291. package/src/clis/chatwise/screenshot.ts +1 -1
  292. package/src/clis/codex/export.ts +1 -1
  293. package/src/clis/codex/model.ts +2 -2
  294. package/src/clis/codex/screenshot.ts +1 -1
  295. package/src/clis/coupang/add-to-cart.ts +3 -4
  296. package/src/clis/coupang/search.ts +2 -4
  297. package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
  298. package/src/clis/ctrip/search.ts +1 -1
  299. package/src/clis/cursor/export.ts +1 -1
  300. package/src/clis/cursor/model.ts +2 -2
  301. package/src/clis/cursor/screenshot.ts +1 -1
  302. package/src/clis/jike/comment.ts +2 -3
  303. package/src/clis/jike/create.ts +1 -2
  304. package/src/clis/jike/feed.ts +0 -1
  305. package/src/clis/jike/like.ts +1 -2
  306. package/src/clis/jike/notifications.ts +0 -1
  307. package/src/clis/jike/post.yaml +1 -0
  308. package/src/clis/jike/repost.ts +1 -2
  309. package/src/clis/jike/search.ts +2 -3
  310. package/src/clis/jike/topic.yaml +1 -0
  311. package/src/clis/jike/user.yaml +1 -0
  312. package/src/clis/jimeng/history.yaml +0 -1
  313. package/src/clis/linkedin/search.ts +7 -7
  314. package/src/clis/linux-do/category.yaml +1 -0
  315. package/src/clis/linux-do/search.yaml +4 -3
  316. package/src/clis/linux-do/topic.yaml +1 -0
  317. package/src/clis/notion/export.ts +1 -1
  318. package/src/clis/reddit/comment.ts +3 -4
  319. package/src/clis/reddit/read.ts +4 -5
  320. package/src/clis/reddit/save.ts +2 -3
  321. package/src/clis/reddit/saved.ts +0 -1
  322. package/src/clis/reddit/search.yaml +1 -0
  323. package/src/clis/reddit/subscribe.ts +0 -1
  324. package/src/clis/reddit/upvote.ts +2 -3
  325. package/src/clis/reddit/upvoted.ts +0 -1
  326. package/src/clis/reddit/user-comments.yaml +1 -0
  327. package/src/clis/reddit/user-posts.yaml +1 -0
  328. package/src/clis/reddit/user.yaml +1 -0
  329. package/src/clis/reuters/search.ts +1 -1
  330. package/src/clis/sinafinance/news.ts +76 -0
  331. package/src/clis/smzdm/search.ts +2 -3
  332. package/src/clis/stackoverflow/search.yaml +1 -0
  333. package/src/clis/steam/top-sellers.yaml +29 -0
  334. package/src/clis/twitter/accept.ts +2 -2
  335. package/src/clis/twitter/article.ts +2 -2
  336. package/src/clis/twitter/block.ts +92 -0
  337. package/src/clis/twitter/delete.ts +1 -1
  338. package/src/clis/twitter/hide-reply.ts +70 -0
  339. package/src/clis/twitter/like.ts +1 -1
  340. package/src/clis/twitter/post.ts +1 -1
  341. package/src/clis/twitter/reply-dm.ts +1 -1
  342. package/src/clis/twitter/reply.ts +2 -2
  343. package/src/clis/twitter/search.ts +1 -1
  344. package/src/clis/twitter/thread.ts +2 -2
  345. package/src/clis/twitter/trending.ts +113 -0
  346. package/src/clis/twitter/unblock.ts +75 -0
  347. package/src/clis/v2ex/topic.yaml +1 -0
  348. package/src/clis/weibo/hot.ts +0 -1
  349. package/src/clis/weread/book.ts +1 -1
  350. package/src/clis/weread/highlights.ts +1 -1
  351. package/src/clis/weread/notes.ts +1 -1
  352. package/src/clis/weread/search.ts +1 -1
  353. package/src/clis/wikipedia/search.ts +32 -0
  354. package/src/clis/wikipedia/summary.ts +28 -0
  355. package/src/clis/wikipedia/utils.ts +20 -0
  356. package/src/clis/xiaohongshu/creator-note-detail.test.ts +272 -0
  357. package/src/clis/xiaohongshu/creator-note-detail.ts +425 -73
  358. package/src/clis/xiaohongshu/creator-notes-summary.test.ts +54 -0
  359. package/src/clis/xiaohongshu/creator-notes-summary.ts +120 -0
  360. package/src/clis/xiaohongshu/creator-notes.test.ts +211 -0
  361. package/src/clis/xiaohongshu/creator-notes.ts +254 -75
  362. package/src/clis/xiaohongshu/creator-profile.ts +0 -1
  363. package/src/clis/xiaohongshu/creator-stats.ts +0 -1
  364. package/src/clis/xiaohongshu/download.ts +2 -3
  365. package/src/clis/xiaohongshu/feed.yaml +0 -1
  366. package/src/clis/xiaohongshu/notifications.yaml +0 -1
  367. package/src/clis/xiaohongshu/search.ts +2 -2
  368. package/src/clis/xiaohongshu/user.ts +1 -2
  369. package/src/clis/yahoo-finance/quote.ts +0 -1
  370. package/src/clis/youtube/search.ts +1 -1
  371. package/src/clis/youtube/transcript.ts +1 -1
  372. package/src/clis/youtube/video.ts +1 -1
  373. package/src/clis/zhihu/download.ts +1 -2
  374. package/src/clis/zhihu/question.ts +1 -1
  375. package/src/clis/zhihu/search.yaml +4 -3
  376. package/src/commanderAdapter.ts +113 -0
  377. package/src/daemon.ts +3 -3
  378. package/src/{engine.ts → discovery.ts} +1 -108
  379. package/src/download/index.ts +21 -54
  380. package/src/engine.test.ts +8 -7
  381. package/src/execution.ts +138 -0
  382. package/src/explore.ts +135 -109
  383. package/src/external-clis.yaml +48 -0
  384. package/src/external.ts +185 -0
  385. package/src/main.ts +1 -1
  386. package/src/pipeline/steps/browser.ts +7 -2
  387. package/src/registry.ts +5 -0
  388. package/src/runtime.ts +9 -0
  389. package/src/serialization.ts +79 -0
  390. package/src/types.ts +1 -1
  391. package/tests/e2e/browser-public.test.ts +25 -0
  392. package/tests/e2e/public-commands.test.ts +55 -1
  393. package/dist/clis/twitter/trending.yaml +0 -46
  394. package/src/clis/twitter/trending.yaml +0 -46
  395. /package/dist/{chaoxing.test.d.ts → clis/arxiv/paper.d.ts} +0 -0
  396. /package/dist/{coupang.test.d.ts → clis/arxiv/search.d.ts} +0 -0
  397. /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
  398. /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
  399. /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
  400. /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
@@ -0,0 +1,83 @@
1
+ /**
2
+ * BOSS直聘 recommend — view recommended candidates (新招呼/greet sort list).
3
+ *
4
+ * Uses /wapi/zprelation/friend/greetRecSortList to get system-recommended candidates.
5
+ * These are candidates who have greeted or been recommended by the system.
6
+ */
7
+ import { cli, Strategy } from '../../registry.js';
8
+ cli({
9
+ site: 'boss',
10
+ name: 'recommend',
11
+ description: 'BOSS直聘查看推荐候选人(新招呼列表)',
12
+ domain: 'www.zhipin.com',
13
+ strategy: Strategy.COOKIE,
14
+ browser: true,
15
+ args: [
16
+ { name: 'limit', type: 'int', default: 20, help: 'Number of results to return' },
17
+ ],
18
+ columns: ['name', 'job_name', 'last_time', 'labels', 'encrypt_uid', 'security_id', 'encrypt_job_id'],
19
+ func: async (page, kwargs) => {
20
+ if (!page)
21
+ throw new Error('Browser page required');
22
+ const limit = kwargs.limit || 20;
23
+ if (process.env.OPENCLI_VERBOSE) {
24
+ console.error('[opencli:boss] Fetching recommended candidates...');
25
+ }
26
+ await page.goto('https://www.zhipin.com/web/chat/index');
27
+ await page.wait({ time: 2 });
28
+ // Get label definitions for mapping
29
+ const labelData = await page.evaluate(`
30
+ async () => {
31
+ return new Promise((resolve, reject) => {
32
+ const xhr = new XMLHttpRequest();
33
+ xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/label/get', true);
34
+ xhr.withCredentials = true;
35
+ xhr.timeout = 10000;
36
+ xhr.setRequestHeader('Accept', 'application/json');
37
+ xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { resolve({}); } };
38
+ xhr.onerror = () => resolve({});
39
+ xhr.send();
40
+ });
41
+ }
42
+ `);
43
+ const labelMap = {};
44
+ if (labelData.code === 0 && labelData.zpData?.labels) {
45
+ for (const l of labelData.zpData.labels) {
46
+ labelMap[l.labelId] = l.label;
47
+ }
48
+ }
49
+ // Get recommended candidates
50
+ const targetUrl = 'https://www.zhipin.com/wapi/zprelation/friend/greetRecSortList';
51
+ const data = await page.evaluate(`
52
+ async () => {
53
+ return new Promise((resolve, reject) => {
54
+ const xhr = new XMLHttpRequest();
55
+ xhr.open('GET', '${targetUrl}', true);
56
+ xhr.withCredentials = true;
57
+ xhr.timeout = 15000;
58
+ xhr.setRequestHeader('Accept', 'application/json');
59
+ xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(new Error('JSON parse failed')); } };
60
+ xhr.onerror = () => reject(new Error('Network Error'));
61
+ xhr.ontimeout = () => reject(new Error('Timeout'));
62
+ xhr.send();
63
+ });
64
+ }
65
+ `);
66
+ if (data.code !== 0) {
67
+ if (data.code === 7 || data.code === 37) {
68
+ throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
69
+ }
70
+ throw new Error(`API error: ${data.message} (code=${data.code})`);
71
+ }
72
+ const friends = (data.zpData?.friendList || []).slice(0, limit);
73
+ return friends.map((f) => ({
74
+ name: f.name || '',
75
+ job_name: f.jobName || '',
76
+ last_time: f.lastTime || '',
77
+ labels: (f.relationLabelList || []).map((id) => labelMap[id] || String(id)).join(', '),
78
+ encrypt_uid: f.encryptUid || '',
79
+ security_id: f.securityId || '',
80
+ encrypt_job_id: f.encryptJobId || '',
81
+ }));
82
+ },
83
+ });
@@ -69,7 +69,7 @@ cli({
69
69
  strategy: Strategy.COOKIE,
70
70
  browser: true,
71
71
  args: [
72
- { name: 'query', required: true, help: 'Search keyword (e.g. AI agent, 前端)' },
72
+ { name: 'query', required: true, positional: true, help: 'Search keyword (e.g. AI agent, 前端)' },
73
73
  { name: 'city', default: '北京', help: 'City name or code (e.g. 杭州, 上海, 101010100)' },
74
74
  { name: 'experience', default: '', help: 'Experience: 应届/1年以内/1-3年/3-5年/5-10年/10年以上' },
75
75
  { name: 'degree', default: '', help: 'Degree: 大专/本科/硕士/博士' },
@@ -14,7 +14,7 @@ cli({
14
14
  browser: true,
15
15
  args: [
16
16
  { name: 'uid', required: true, help: 'Encrypted UID of the candidate (from chatlist)' },
17
- { name: 'text', required: true, help: 'Message text to send' },
17
+ { name: 'text', required: true, positional: true, help: 'Message text to send' },
18
18
  ],
19
19
  columns: ['status', 'detail'],
20
20
  func: async (page, kwargs) => {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,116 @@
1
+ /**
2
+ * BOSS直聘 stats — job statistics overview.
3
+ *
4
+ * Uses /wapi/zpchat/chatHelper/statistics for total friend count,
5
+ * and /wapi/zpjob/job/chatted/jobList for per-job info.
6
+ * Since BOSS doesn't expose detailed per-job stats via API,
7
+ * we show what's available: job status, chat info, and total stats.
8
+ */
9
+ import { cli, Strategy } from '../../registry.js';
10
+ cli({
11
+ site: 'boss',
12
+ name: 'stats',
13
+ description: 'BOSS直聘职位数据统计',
14
+ domain: 'www.zhipin.com',
15
+ strategy: Strategy.COOKIE,
16
+ browser: true,
17
+ args: [
18
+ { name: 'job-id', default: '', help: 'Encrypted job ID (show all if empty)' },
19
+ ],
20
+ columns: ['job_name', 'salary', 'city', 'status', 'total_chats', 'encrypt_job_id'],
21
+ func: async (page, kwargs) => {
22
+ if (!page)
23
+ throw new Error('Browser page required');
24
+ const filterJobId = kwargs['job-id'] || '';
25
+ if (process.env.OPENCLI_VERBOSE) {
26
+ console.error('[opencli:boss] Fetching job statistics...');
27
+ }
28
+ await page.goto('https://www.zhipin.com/web/chat/index');
29
+ await page.wait({ time: 2 });
30
+ // Get job list
31
+ const jobData = await page.evaluate(`
32
+ async () => {
33
+ return new Promise((resolve, reject) => {
34
+ const xhr = new XMLHttpRequest();
35
+ xhr.open('GET', 'https://www.zhipin.com/wapi/zpjob/job/chatted/jobList', true);
36
+ xhr.withCredentials = true;
37
+ xhr.timeout = 15000;
38
+ xhr.setRequestHeader('Accept', 'application/json');
39
+ xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { reject(new Error('JSON parse failed')); } };
40
+ xhr.onerror = () => reject(new Error('Network Error'));
41
+ xhr.ontimeout = () => reject(new Error('Timeout'));
42
+ xhr.send();
43
+ });
44
+ }
45
+ `);
46
+ if (jobData.code !== 0) {
47
+ if (jobData.code === 7 || jobData.code === 37) {
48
+ throw new Error('Cookie 已过期!请在当前 Chrome 浏览器中重新登录 BOSS 直聘。');
49
+ }
50
+ throw new Error(`API error: ${jobData.message} (code=${jobData.code})`);
51
+ }
52
+ // Get total chat stats
53
+ const chatStats = await page.evaluate(`
54
+ async () => {
55
+ return new Promise((resolve, reject) => {
56
+ const xhr = new XMLHttpRequest();
57
+ xhr.open('GET', 'https://www.zhipin.com/wapi/zpchat/chatHelper/statistics', true);
58
+ xhr.withCredentials = true;
59
+ xhr.timeout = 10000;
60
+ xhr.setRequestHeader('Accept', 'application/json');
61
+ xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { resolve({}); } };
62
+ xhr.onerror = () => resolve({});
63
+ xhr.send();
64
+ });
65
+ }
66
+ `);
67
+ const totalFriends = chatStats.zpData?.totalFriendCount || 0;
68
+ // Get per-job chat counts from friend list
69
+ const friendData = await page.evaluate(`
70
+ async () => {
71
+ return new Promise((resolve, reject) => {
72
+ const xhr = new XMLHttpRequest();
73
+ xhr.open('GET', 'https://www.zhipin.com/wapi/zprelation/friend/getBossFriendListV2.json?page=1&status=0&jobId=0', true);
74
+ xhr.withCredentials = true;
75
+ xhr.timeout = 15000;
76
+ xhr.setRequestHeader('Accept', 'application/json');
77
+ xhr.onload = () => { try { resolve(JSON.parse(xhr.responseText)); } catch(e) { resolve({}); } };
78
+ xhr.onerror = () => resolve({});
79
+ xhr.send();
80
+ });
81
+ }
82
+ `);
83
+ // Count chats per job
84
+ const jobChatCounts = {};
85
+ if (friendData.code === 0) {
86
+ for (const f of (friendData.zpData?.friendList || [])) {
87
+ const jobName = f.jobName || 'unknown';
88
+ jobChatCounts[jobName] = (jobChatCounts[jobName] || 0) + 1;
89
+ }
90
+ }
91
+ let jobs = jobData.zpData || [];
92
+ if (filterJobId) {
93
+ jobs = jobs.filter((j) => j.encryptJobId === filterJobId);
94
+ }
95
+ const results = jobs.map((j) => ({
96
+ job_name: j.jobName || '',
97
+ salary: j.salaryDesc || '',
98
+ city: j.address || '',
99
+ status: j.jobOnlineStatus === 1 ? '在线' : '已关闭',
100
+ total_chats: String(jobChatCounts[j.jobName] || 0),
101
+ encrypt_job_id: j.encryptJobId || '',
102
+ }));
103
+ // Add summary row
104
+ if (!filterJobId && results.length > 0) {
105
+ results.push({
106
+ job_name: '--- 总计 ---',
107
+ salary: '',
108
+ city: '',
109
+ status: `${jobs.length} 个职位`,
110
+ total_chats: String(totalFriends),
111
+ encrypt_job_id: '',
112
+ });
113
+ }
114
+ return results;
115
+ },
116
+ });
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { getCourses, initSession, enterCourse, getTabIframeUrl, parseAssignmentsFromDom, sleep, } from '../../chaoxing.js';
2
+ import { getCourses, initSession, enterCourse, getTabIframeUrl, parseAssignmentsFromDom, sleep, } from './utils.js';
3
3
  cli({
4
4
  site: 'chaoxing',
5
5
  name: 'assignments',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { getCourses, initSession, enterCourse, getTabIframeUrl, parseExamsFromDom, sleep, } from '../../chaoxing.js';
2
+ import { getCourses, initSession, enterCourse, getTabIframeUrl, parseExamsFromDom, sleep, } from './utils.js';
3
3
  cli({
4
4
  site: 'chaoxing',
5
5
  name: 'exams',
@@ -5,7 +5,7 @@
5
5
  * Chaoxing has no flat "list all assignments" API; data is behind session-gated
6
6
  * course pages loaded as iframes.
7
7
  */
8
- import type { IPage } from './types.js';
8
+ import type { IPage } from '../../types.js';
9
9
  /** Sleep for given milliseconds (anti-scraping delay). */
10
10
  export declare function sleep(ms: number): Promise<void>;
11
11
  /** Execute a credentialed fetch in the browser context, returning JSON or text. */
@@ -85,7 +85,6 @@ export async function getCourses(page) {
85
85
  /** Navigate to the interaction page to establish a Chaoxing session. */
86
86
  export async function initSession(page) {
87
87
  await page.goto('https://mooc2-ans.chaoxing.com/mooc2-ans/visit/interaction');
88
- await page.wait(3);
89
88
  }
90
89
  /**
91
90
  * Enter a course via stucoursemiddle redirect (establishes course session + enc).
@@ -95,7 +94,6 @@ export async function enterCourse(page, course) {
95
94
  const url = `https://mooc1.chaoxing.com/visit/stucoursemiddle` +
96
95
  `?courseid=${course.courseId}&clazzid=${course.classId}&cpi=${course.cpi}&ismooc2=1&v=2`;
97
96
  await page.goto(url);
98
- await page.wait(3);
99
97
  }
100
98
  /**
101
99
  * On the course page, click a tab (作业 / 考试) and return the iframe src
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { formatTimestamp, workStatusLabel } from './chaoxing.js';
2
+ import { formatTimestamp, workStatusLabel } from './utils.js';
3
3
  function localDatePrefixFromMillis(ts) {
4
4
  const d = new Date(ts);
5
5
  const yyyy = d.getFullYear();
@@ -4,7 +4,7 @@ import { getVisibleChatMessages } from './ax.js';
4
4
  export const readCommand = cli({
5
5
  site: 'chatgpt',
6
6
  name: 'read',
7
- description: 'Copy the most recent ChatGPT Desktop App response to clipboard and read it',
7
+ description: 'Read the last visible message from the focused ChatGPT Desktop window',
8
8
  domain: 'localhost',
9
9
  strategy: Strategy.PUBLIC,
10
10
  browser: false,
@@ -8,7 +8,7 @@ export const exportCommand = cli({
8
8
  strategy: Strategy.UI,
9
9
  browser: true,
10
10
  args: [
11
- { name: 'output', required: false, positional: true, help: 'Output file (default: /tmp/chatwise-export.md)' },
11
+ { name: 'output', required: false, help: 'Output file (default: /tmp/chatwise-export.md)' },
12
12
  ],
13
13
  columns: ['Status', 'File', 'Messages'],
14
14
  func: async (page, kwargs) => {
@@ -7,11 +7,11 @@ export const modelCommand = cli({
7
7
  strategy: Strategy.UI,
8
8
  browser: true,
9
9
  args: [
10
- { name: 'model_name', required: false, positional: true, help: 'Model to switch to (e.g. gpt-4, claude-3)' },
10
+ { name: 'model-name', required: false, positional: true, help: 'Model to switch to (e.g. gpt-4, claude-3)' },
11
11
  ],
12
12
  columns: ['Status', 'Model'],
13
13
  func: async (page, kwargs) => {
14
- const desiredModel = kwargs.model_name;
14
+ const desiredModel = kwargs['model-name'];
15
15
  if (!desiredModel) {
16
16
  // Read current model
17
17
  const currentModel = await page.evaluate(`
@@ -8,7 +8,7 @@ export const screenshotCommand = cli({
8
8
  strategy: Strategy.UI,
9
9
  browser: true,
10
10
  args: [
11
- { name: 'output', required: false, positional: true, help: 'Output file path (default: /tmp/chatwise-snapshot)' },
11
+ { name: 'output', required: false, help: 'Output file path (default: /tmp/chatwise-snapshot)' },
12
12
  ],
13
13
  columns: ['Status', 'File'],
14
14
  func: async (page, kwargs) => {
@@ -8,7 +8,7 @@ export const exportCommand = cli({
8
8
  strategy: Strategy.UI,
9
9
  browser: true,
10
10
  args: [
11
- { name: 'output', required: false, positional: true, help: 'Output file (default: /tmp/codex-export.md)' },
11
+ { name: 'output', required: false, help: 'Output file (default: /tmp/codex-export.md)' },
12
12
  ],
13
13
  columns: ['Status', 'File', 'Messages'],
14
14
  func: async (page, kwargs) => {
@@ -7,11 +7,11 @@ export const modelCommand = cli({
7
7
  strategy: Strategy.UI,
8
8
  browser: true,
9
9
  args: [
10
- { name: 'model_name', required: false, positional: true, help: 'The ID of the model to switch to (e.g. gpt-4)' }
10
+ { name: 'model-name', required: false, positional: true, help: 'The ID of the model to switch to (e.g. gpt-4)' }
11
11
  ],
12
12
  columns: ['Status', 'Model'],
13
13
  func: async (page, kwargs) => {
14
- const desiredModel = kwargs.model_name;
14
+ const desiredModel = kwargs['model-name'];
15
15
  if (!desiredModel) {
16
16
  // Just read the current model. We traverse iframes/webviews if needed.
17
17
  const currentModel = await page.evaluate(`
@@ -8,7 +8,7 @@ export const screenshotCommand = cli({
8
8
  strategy: Strategy.UI,
9
9
  browser: true,
10
10
  args: [
11
- { name: 'output', required: false, positional: true, help: 'Output file path (default: /tmp/codex-snapshot.txt)' },
11
+ { name: 'output', required: false, help: 'Output file path (default: /tmp/codex-snapshot.txt)' },
12
12
  ],
13
13
  columns: ['Status', 'File'],
14
14
  func: async (page, kwargs) => {
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { canonicalizeProductUrl, normalizeProductId } from '../../coupang.js';
2
+ import { canonicalizeProductUrl, normalizeProductId } from './utils.js';
3
3
  function escapeJsString(value) {
4
4
  return JSON.stringify(value);
5
5
  }
@@ -99,12 +99,12 @@ cli({
99
99
  strategy: Strategy.COOKIE,
100
100
  browser: true,
101
101
  args: [
102
- { name: 'productId', required: false, help: 'Coupang product ID' },
102
+ { name: 'product-id', required: false, help: 'Coupang product ID' },
103
103
  { name: 'url', required: false, help: 'Canonical product URL' },
104
104
  ],
105
105
  columns: ['ok', 'product_id', 'url', 'message'],
106
106
  func: async (page, kwargs) => {
107
- const rawProductId = kwargs.productId ?? kwargs.product_id;
107
+ const rawProductId = kwargs['product-id'] ?? kwargs['product-id'];
108
108
  const productId = normalizeProductId(rawProductId);
109
109
  const targetUrl = canonicalizeProductUrl(kwargs.url, productId);
110
110
  if (!productId && !targetUrl) {
@@ -112,7 +112,6 @@ cli({
112
112
  }
113
113
  const finalUrl = targetUrl || canonicalizeProductUrl('', productId);
114
114
  await page.goto(finalUrl);
115
- await page.wait(3);
116
115
  const result = await page.evaluate(buildAddToCartEvaluate(productId));
117
116
  const loginHints = result?.loginHints ?? {};
118
117
  if (loginHints.hasLoginLink && !loginHints.hasMyCoupang) {
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { mergeSearchItems, normalizeSearchItem, sanitizeSearchItems } from '../../coupang.js';
2
+ import { mergeSearchItems, normalizeSearchItem, sanitizeSearchItems } from './utils.js';
3
3
  function escapeJsString(value) {
4
4
  return JSON.stringify(value);
5
5
  }
@@ -404,7 +404,7 @@ cli({
404
404
  strategy: Strategy.COOKIE,
405
405
  browser: true,
406
406
  args: [
407
- { name: 'query', required: true, help: 'Search keyword' },
407
+ { name: 'query', required: true, positional: true, help: 'Search keyword' },
408
408
  { name: 'page', type: 'int', default: 1, help: 'Search result page number' },
409
409
  { name: 'limit', type: 'int', default: 20, help: 'Max results (max 50)' },
410
410
  { name: 'filter', required: false, help: 'Optional search filter (currently supports: rocket)' },
@@ -420,7 +420,6 @@ cli({
420
420
  const initialPage = filter ? 1 : pageNumber;
421
421
  const url = `https://www.coupang.com/np/search?q=${encodeURIComponent(query)}&channel=user&page=${initialPage}`;
422
422
  await page.goto(url);
423
- await page.wait(3);
424
423
  if (filter) {
425
424
  const filterResult = await page.evaluate(buildApplyFilterEvaluate(filter));
426
425
  if (!filterResult?.ok) {
@@ -432,7 +431,6 @@ cli({
432
431
  const filteredUrl = new URL(locationInfo?.href || url);
433
432
  filteredUrl.searchParams.set('page', String(pageNumber));
434
433
  await page.goto(filteredUrl.toString());
435
- await page.wait(3);
436
434
  }
437
435
  }
438
436
  await page.autoScroll({ times: filter ? 3 : 2, delayMs: 1500 });
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { canonicalizeProductUrl, dedupeSearchItems, normalizeProductId, normalizeSearchItem, sanitizeSearchItems, } from './coupang.js';
2
+ import { canonicalizeProductUrl, dedupeSearchItems, normalizeProductId, normalizeSearchItem, sanitizeSearchItems, } from './utils.js';
3
3
  describe('normalizeProductId', () => {
4
4
  it('extracts product id from canonical path', () => {
5
5
  expect(normalizeProductId('https://www.coupang.com/vp/products/123456789')).toBe('123456789');
@@ -10,7 +10,7 @@ cli({
10
10
  domain: 'www.ctrip.com',
11
11
  strategy: Strategy.COOKIE,
12
12
  args: [
13
- { name: 'query', required: true, help: 'Search keyword (city or attraction)' },
13
+ { name: 'query', required: true, positional: true, help: 'Search keyword (city or attraction)' },
14
14
  { name: 'limit', type: 'int', default: 15, help: 'Number of results' },
15
15
  ],
16
16
  columns: ['rank', 'name', 'type', 'score', 'price', 'url'],
@@ -9,7 +9,7 @@ function makeExportCommand(site, readSelector) {
9
9
  strategy: Strategy.UI,
10
10
  browser: true,
11
11
  args: [
12
- { name: 'output', required: false, positional: true, help: `Output file (default: /tmp/${site}-export.md)` },
12
+ { name: 'output', required: false, help: `Output file (default: /tmp/${site}-export.md)` },
13
13
  ],
14
14
  columns: ['Status', 'File', 'Messages'],
15
15
  func: async (page, kwargs) => {
@@ -7,11 +7,11 @@ export const modelCommand = cli({
7
7
  strategy: Strategy.UI,
8
8
  browser: true,
9
9
  args: [
10
- { name: 'model_name', required: false, positional: true, help: 'The ID of the model to switch to (e.g. claude-3.5-sonnet)' }
10
+ { name: 'model-name', required: false, positional: true, help: 'The ID of the model to switch to (e.g. claude-3.5-sonnet)' }
11
11
  ],
12
12
  columns: ['Status', 'Model'],
13
13
  func: async (page, kwargs) => {
14
- const desiredModel = kwargs.model_name;
14
+ const desiredModel = kwargs['model-name'];
15
15
  if (!desiredModel) {
16
16
  // Just read the current model
17
17
  const currentModel = await page.evaluate(`
@@ -9,7 +9,7 @@ function makeScreenshotCommand(site) {
9
9
  strategy: Strategy.UI,
10
10
  browser: true,
11
11
  args: [
12
- { name: 'output', required: false, positional: true, help: `Output file path (default: /tmp/${site}-snapshot.txt)` },
12
+ { name: 'output', required: false, help: `Output file path (default: /tmp/${site}-snapshot.txt)` },
13
13
  ],
14
14
  columns: ['Status', 'File'],
15
15
  func: async (page, kwargs) => {
@@ -13,13 +13,12 @@ cli({
13
13
  strategy: Strategy.UI,
14
14
  browser: true,
15
15
  args: [
16
- { name: 'id', type: 'string', required: true, help: '帖子 ID' },
17
- { name: 'text', type: 'string', required: true, help: '评论内容' },
16
+ { name: 'id', type: 'string', required: true, positional: true, help: '帖子 ID' },
17
+ { name: 'text', type: 'string', required: true, positional: true, help: '评论内容' },
18
18
  ],
19
19
  columns: ['status', 'message'],
20
20
  func: async (page, kwargs) => {
21
21
  await page.goto(`https://web.okjike.com/originalPost/${kwargs.id}`);
22
- await page.wait(5);
23
22
  // 1. 找到评论输入框并填入文本
24
23
  const inputResult = await page.evaluate(`(async () => {
25
24
  try {
@@ -13,13 +13,12 @@ cli({
13
13
  strategy: Strategy.UI,
14
14
  browser: true,
15
15
  args: [
16
- { name: 'text', type: 'string', required: true, help: '动态正文内容' },
16
+ { name: 'text', type: 'string', required: true, positional: true, help: '动态正文内容' },
17
17
  ],
18
18
  columns: ['status', 'message'],
19
19
  func: async (page, kwargs) => {
20
20
  // 1. 导航到首页(有内联发帖框)
21
21
  await page.goto('https://web.okjike.com');
22
- await page.wait(5);
23
22
  // 2. 在发帖框中输入文本
24
23
  const textResult = await page.evaluate(`(async () => {
25
24
  try {
@@ -21,7 +21,6 @@ cli({
21
21
  const limit = kwargs.limit || 20;
22
22
  // 1. 导航到即刻首页,等待 SPA 重定向到 /following
23
23
  await page.goto('https://web.okjike.com');
24
- await page.wait(5);
25
24
  // 2. 通过 React fiber 提取帖子数据
26
25
  const extract = async () => {
27
26
  return (await page.evaluate(`(() => {
@@ -13,13 +13,12 @@ cli({
13
13
  strategy: Strategy.UI,
14
14
  browser: true,
15
15
  args: [
16
- { name: 'id', type: 'string', required: true, help: '帖子 ID' },
16
+ { name: 'id', type: 'string', required: true, positional: true, help: '帖子 ID' },
17
17
  ],
18
18
  columns: ['status', 'message'],
19
19
  func: async (page, kwargs) => {
20
20
  // 1. 导航到帖子详情页
21
21
  await page.goto(`https://web.okjike.com/originalPost/${kwargs.id}`);
22
- await page.wait(5);
23
22
  // 2. 找到点赞按钮并点击
24
23
  const result = await page.evaluate(`(async () => {
25
24
  try {
@@ -33,7 +33,6 @@ cli({
33
33
  const limit = kwargs.limit || 20;
34
34
  // 1. 直接导航到通知页
35
35
  await page.goto('https://web.okjike.com/notification');
36
- await page.wait(5);
37
36
  // 3. 优先用 React fiber 提取通知数据
38
37
  // 通知 fiber 数据结构与帖子不同,需查找含 type + user 字段的 props
39
38
  const fiberResults = (await page.evaluate(`(() => {
@@ -6,6 +6,7 @@ browser: true
6
6
 
7
7
  args:
8
8
  id:
9
+ positional: true
9
10
  type: string
10
11
  required: true
11
12
  description: Post ID (from post URL)
@@ -14,13 +14,12 @@ cli({
14
14
  strategy: Strategy.UI,
15
15
  browser: true,
16
16
  args: [
17
- { name: 'id', type: 'string', required: true, help: '帖子 ID' },
17
+ { name: 'id', type: 'string', required: true, positional: true, help: '帖子 ID' },
18
18
  { name: 'text', type: 'string', required: false, help: '转发附言(可选)' },
19
19
  ],
20
20
  columns: ['status', 'message'],
21
21
  func: async (page, kwargs) => {
22
22
  await page.goto(`https://web.okjike.com/originalPost/${kwargs.id}`);
23
- await page.wait(5);
24
23
  // 1. 点击操作栏中的转发按钮(第三个子元素)
25
24
  const clickResult = await page.evaluate(`(async () => {
26
25
  try {
@@ -14,17 +14,16 @@ cli({
14
14
  strategy: Strategy.COOKIE,
15
15
  browser: true,
16
16
  args: [
17
- { name: 'keyword', type: 'string', required: true },
17
+ { name: 'query', type: 'string', required: true, positional: true },
18
18
  { name: 'limit', type: 'int', default: 20 },
19
19
  ],
20
20
  columns: ['author', 'content', 'likes', 'comments', 'time', 'url'],
21
21
  func: async (page, kwargs) => {
22
- const keyword = kwargs.keyword;
22
+ const keyword = kwargs.query;
23
23
  const limit = kwargs.limit || 20;
24
24
  // 1. 直接导航到搜索页
25
25
  const encodedKeyword = encodeURIComponent(keyword);
26
26
  await page.goto(`https://web.okjike.com/search?q=${encodedKeyword}`);
27
- await page.wait(5);
28
27
  // 2. 通过 React fiber 提取帖子数据
29
28
  const extract = async () => {
30
29
  return (await page.evaluate(`(() => {
@@ -6,6 +6,7 @@ browser: true
6
6
 
7
7
  args:
8
8
  id:
9
+ positional: true
9
10
  type: string
10
11
  required: true
11
12
  description: Topic ID (from topic URL, e.g. 553870e8e4b0cafb0a1bef68)
@@ -6,6 +6,7 @@ browser: true
6
6
 
7
7
  args:
8
8
  username:
9
+ positional: true
9
10
  type: string
10
11
  required: true
11
12
  description: Username from profile URL (e.g. wenhao1996)
@@ -14,7 +14,6 @@ columns: [prompt, model, status, image_url, created_at]
14
14
 
15
15
  pipeline:
16
16
  - navigate: https://jimeng.jianying.com/ai-tool/generate?type=image&workspace=0
17
- - wait: 3
18
17
  - evaluate: |
19
18
  (async () => {
20
19
  const limit = ${{ args.limit }};