@jackwener/opencli 1.1.0 → 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 (354) 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 +25 -10
  8. package/README.zh-CN.md +26 -11
  9. package/SKILL.md +95 -31
  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 +431 -276
  15. package/dist/cli.d.ts +6 -0
  16. package/dist/cli.js +189 -162
  17. package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
  18. package/dist/clis/apple-podcasts/commands.test.js +76 -0
  19. package/dist/clis/apple-podcasts/search.js +2 -2
  20. package/dist/clis/apple-podcasts/top.js +9 -2
  21. package/dist/clis/arxiv/search.js +1 -1
  22. package/dist/clis/bilibili/dynamic.js +1 -1
  23. package/dist/clis/bilibili/favorite.js +1 -1
  24. package/dist/clis/bilibili/feed.js +1 -1
  25. package/dist/clis/bilibili/following.js +1 -1
  26. package/dist/clis/bilibili/history.js +1 -1
  27. package/dist/clis/bilibili/me.js +1 -1
  28. package/dist/clis/bilibili/ranking.js +1 -1
  29. package/dist/clis/bilibili/search.js +3 -3
  30. package/dist/clis/bilibili/subtitle.js +1 -1
  31. package/dist/clis/bilibili/user-videos.js +1 -1
  32. package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
  33. package/dist/clis/bloomberg/businessweek.js +17 -0
  34. package/dist/clis/bloomberg/economics.js +17 -0
  35. package/dist/clis/bloomberg/feeds.d.ts +1 -0
  36. package/dist/clis/bloomberg/feeds.js +15 -0
  37. package/dist/clis/bloomberg/industries.d.ts +1 -0
  38. package/dist/clis/bloomberg/industries.js +17 -0
  39. package/dist/clis/bloomberg/main.d.ts +1 -0
  40. package/dist/clis/bloomberg/main.js +17 -0
  41. package/dist/clis/bloomberg/markets.d.ts +1 -0
  42. package/dist/clis/bloomberg/markets.js +17 -0
  43. package/dist/clis/bloomberg/news.d.ts +1 -0
  44. package/dist/clis/bloomberg/news.js +105 -0
  45. package/dist/clis/bloomberg/opinions.d.ts +1 -0
  46. package/dist/clis/bloomberg/opinions.js +17 -0
  47. package/dist/clis/bloomberg/politics.d.ts +1 -0
  48. package/dist/clis/bloomberg/politics.js +17 -0
  49. package/dist/clis/bloomberg/tech.d.ts +1 -0
  50. package/dist/clis/bloomberg/tech.js +17 -0
  51. package/dist/clis/bloomberg/utils.d.ts +34 -0
  52. package/dist/clis/bloomberg/utils.js +364 -0
  53. package/dist/clis/bloomberg/utils.test.d.ts +1 -0
  54. package/dist/clis/bloomberg/utils.test.js +129 -0
  55. package/dist/clis/boss/batchgreet.js +2 -2
  56. package/dist/clis/boss/chatlist.js +2 -2
  57. package/dist/clis/boss/detail.js +2 -2
  58. package/dist/clis/boss/greet.js +4 -4
  59. package/dist/clis/boss/search.js +1 -1
  60. package/dist/clis/boss/send.js +1 -1
  61. package/dist/clis/boss/stats.js +2 -2
  62. package/dist/clis/chaoxing/assignments.js +1 -1
  63. package/dist/clis/chaoxing/exams.js +1 -1
  64. package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
  65. package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
  66. package/dist/clis/chaoxing/utils.test.d.ts +1 -0
  67. package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
  68. package/dist/clis/chatgpt/read.js +1 -1
  69. package/dist/clis/chatwise/export.js +1 -1
  70. package/dist/clis/chatwise/model.js +2 -2
  71. package/dist/clis/chatwise/screenshot.js +1 -1
  72. package/dist/clis/codex/export.js +1 -1
  73. package/dist/clis/codex/model.js +2 -2
  74. package/dist/clis/codex/screenshot.js +1 -1
  75. package/dist/clis/coupang/add-to-cart.js +3 -4
  76. package/dist/clis/coupang/search.js +2 -4
  77. package/dist/clis/coupang/utils.test.d.ts +1 -0
  78. package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
  79. package/dist/clis/ctrip/search.js +1 -1
  80. package/dist/clis/cursor/export.js +1 -1
  81. package/dist/clis/cursor/model.js +2 -2
  82. package/dist/clis/cursor/screenshot.js +1 -1
  83. package/dist/clis/jike/comment.js +2 -3
  84. package/dist/clis/jike/create.js +1 -2
  85. package/dist/clis/jike/feed.js +0 -1
  86. package/dist/clis/jike/like.js +1 -2
  87. package/dist/clis/jike/notifications.js +0 -1
  88. package/dist/clis/jike/post.yaml +1 -0
  89. package/dist/clis/jike/repost.js +1 -2
  90. package/dist/clis/jike/search.js +2 -3
  91. package/dist/clis/jike/topic.yaml +1 -0
  92. package/dist/clis/jike/user.yaml +1 -0
  93. package/dist/clis/jimeng/history.yaml +0 -1
  94. package/dist/clis/linkedin/search.js +7 -7
  95. package/dist/clis/linux-do/category.yaml +1 -0
  96. package/dist/clis/linux-do/search.yaml +4 -3
  97. package/dist/clis/linux-do/topic.yaml +1 -0
  98. package/dist/clis/notion/export.js +1 -1
  99. package/dist/clis/reddit/comment.js +3 -4
  100. package/dist/clis/reddit/read.js +4 -5
  101. package/dist/clis/reddit/save.js +2 -3
  102. package/dist/clis/reddit/saved.js +0 -1
  103. package/dist/clis/reddit/search.yaml +1 -0
  104. package/dist/clis/reddit/subscribe.js +0 -1
  105. package/dist/clis/reddit/upvote.js +2 -3
  106. package/dist/clis/reddit/upvoted.js +0 -1
  107. package/dist/clis/reddit/user-comments.yaml +1 -0
  108. package/dist/clis/reddit/user-posts.yaml +1 -0
  109. package/dist/clis/reddit/user.yaml +1 -0
  110. package/dist/clis/reuters/search.js +1 -1
  111. package/dist/clis/smzdm/search.js +2 -3
  112. package/dist/clis/stackoverflow/search.yaml +1 -0
  113. package/dist/clis/steam/top-sellers.yaml +29 -0
  114. package/dist/clis/twitter/accept.js +2 -2
  115. package/dist/clis/twitter/article.js +2 -2
  116. package/dist/clis/twitter/block.d.ts +1 -0
  117. package/dist/clis/twitter/block.js +88 -0
  118. package/dist/clis/twitter/delete.js +1 -1
  119. package/dist/clis/twitter/hide-reply.d.ts +1 -0
  120. package/dist/clis/twitter/hide-reply.js +66 -0
  121. package/dist/clis/twitter/like.js +1 -1
  122. package/dist/clis/twitter/post.js +1 -1
  123. package/dist/clis/twitter/reply-dm.js +1 -1
  124. package/dist/clis/twitter/reply.js +2 -2
  125. package/dist/clis/twitter/search.js +1 -1
  126. package/dist/clis/twitter/thread.js +2 -2
  127. package/dist/clis/twitter/trending.d.ts +1 -0
  128. package/dist/clis/twitter/trending.js +91 -0
  129. package/dist/clis/twitter/unblock.d.ts +1 -0
  130. package/dist/clis/twitter/unblock.js +71 -0
  131. package/dist/clis/v2ex/topic.yaml +1 -0
  132. package/dist/clis/weibo/hot.js +0 -1
  133. package/dist/clis/weread/book.js +1 -1
  134. package/dist/clis/weread/highlights.js +1 -1
  135. package/dist/clis/weread/notes.js +1 -1
  136. package/dist/clis/weread/search.js +1 -1
  137. package/dist/clis/wikipedia/search.js +1 -1
  138. package/dist/clis/xiaohongshu/creator-note-detail.d.ts +15 -0
  139. package/dist/clis/xiaohongshu/creator-note-detail.js +69 -5
  140. package/dist/clis/xiaohongshu/creator-note-detail.test.js +80 -33
  141. package/dist/clis/xiaohongshu/creator-notes.js +35 -5
  142. package/dist/clis/xiaohongshu/creator-notes.test.js +35 -6
  143. package/dist/clis/xiaohongshu/creator-profile.js +0 -1
  144. package/dist/clis/xiaohongshu/creator-stats.js +0 -1
  145. package/dist/clis/xiaohongshu/download.js +2 -3
  146. package/dist/clis/xiaohongshu/feed.yaml +0 -1
  147. package/dist/clis/xiaohongshu/notifications.yaml +0 -1
  148. package/dist/clis/xiaohongshu/search.js +2 -2
  149. package/dist/clis/xiaohongshu/user.js +1 -2
  150. package/dist/clis/yahoo-finance/quote.js +0 -1
  151. package/dist/clis/youtube/search.js +1 -1
  152. package/dist/clis/youtube/transcript.js +1 -1
  153. package/dist/clis/youtube/video.js +1 -1
  154. package/dist/clis/zhihu/download.js +1 -2
  155. package/dist/clis/zhihu/question.js +1 -1
  156. package/dist/clis/zhihu/search.yaml +4 -3
  157. package/dist/commanderAdapter.d.ts +21 -0
  158. package/dist/commanderAdapter.js +111 -0
  159. package/dist/{engine.d.ts → discovery.d.ts} +0 -6
  160. package/dist/{engine.js → discovery.js} +1 -98
  161. package/dist/download/index.d.ts +2 -6
  162. package/dist/download/index.js +19 -46
  163. package/dist/engine.test.d.ts +1 -1
  164. package/dist/engine.test.js +8 -7
  165. package/dist/execution.d.ts +22 -0
  166. package/dist/execution.js +129 -0
  167. package/dist/explore.js +121 -107
  168. package/dist/external-clis.yaml +48 -0
  169. package/dist/external.d.ts +7 -2
  170. package/dist/external.js +11 -14
  171. package/dist/main.js +1 -1
  172. package/dist/pipeline/steps/browser.js +8 -2
  173. package/dist/registry.d.ts +2 -0
  174. package/dist/registry.js +2 -0
  175. package/dist/runtime.d.ts +5 -0
  176. package/dist/runtime.js +8 -0
  177. package/dist/serialization.d.ts +34 -0
  178. package/dist/serialization.js +63 -0
  179. package/dist/types.d.ts +4 -1
  180. package/docs/.vitepress/config.mts +14 -3
  181. package/docs/adapters/browser/arxiv.md +27 -0
  182. package/docs/adapters/browser/barchart.md +32 -0
  183. package/docs/adapters/browser/bloomberg.md +70 -0
  184. package/docs/adapters/browser/chaoxing.md +39 -0
  185. package/docs/adapters/browser/grok.md +35 -0
  186. package/docs/adapters/browser/hf.md +42 -0
  187. package/docs/adapters/browser/jike.md +45 -0
  188. package/docs/adapters/browser/jimeng.md +39 -0
  189. package/docs/adapters/browser/linux-do.md +45 -0
  190. package/docs/adapters/browser/sinafinance.md +35 -0
  191. package/docs/adapters/browser/stackoverflow.md +35 -0
  192. package/docs/adapters/browser/steam.md +26 -0
  193. package/docs/adapters/browser/twitter.md +3 -0
  194. package/docs/adapters/browser/weread.md +48 -0
  195. package/docs/adapters/browser/wikipedia.md +30 -0
  196. package/docs/adapters/browser/xiaohongshu.md +5 -1
  197. package/docs/adapters/desktop/chatgpt.md +3 -3
  198. package/docs/adapters/index.md +13 -0
  199. package/docs/advanced/download.md +4 -4
  200. package/docs/developer/architecture.md +17 -4
  201. package/package.json +1 -1
  202. package/scripts/check-doc-coverage.sh +69 -0
  203. package/scripts/copy-yaml.cjs +7 -0
  204. package/src/browser/cdp.ts +6 -1
  205. package/src/browser/page.ts +7 -1
  206. package/src/build-manifest.ts +25 -19
  207. package/src/cli.ts +218 -139
  208. package/src/clis/apple-podcasts/commands.test.ts +95 -0
  209. package/src/clis/apple-podcasts/search.ts +2 -2
  210. package/src/clis/apple-podcasts/top.ts +12 -2
  211. package/src/clis/arxiv/search.ts +1 -1
  212. package/src/clis/bilibili/dynamic.ts +1 -1
  213. package/src/clis/bilibili/favorite.ts +1 -1
  214. package/src/clis/bilibili/feed.ts +1 -1
  215. package/src/clis/bilibili/following.ts +1 -1
  216. package/src/clis/bilibili/history.ts +1 -1
  217. package/src/clis/bilibili/me.ts +1 -1
  218. package/src/clis/bilibili/ranking.ts +1 -1
  219. package/src/clis/bilibili/search.ts +3 -3
  220. package/src/clis/bilibili/subtitle.ts +1 -1
  221. package/src/clis/bilibili/user-videos.ts +1 -1
  222. package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
  223. package/src/clis/bloomberg/businessweek.ts +18 -0
  224. package/src/clis/bloomberg/economics.ts +18 -0
  225. package/src/clis/bloomberg/feeds.ts +16 -0
  226. package/src/clis/bloomberg/industries.ts +18 -0
  227. package/src/clis/bloomberg/main.ts +18 -0
  228. package/src/clis/bloomberg/markets.ts +18 -0
  229. package/src/clis/bloomberg/news.ts +136 -0
  230. package/src/clis/bloomberg/opinions.ts +18 -0
  231. package/src/clis/bloomberg/politics.ts +18 -0
  232. package/src/clis/bloomberg/tech.ts +18 -0
  233. package/src/clis/bloomberg/utils.test.ts +135 -0
  234. package/src/clis/bloomberg/utils.ts +429 -0
  235. package/src/clis/boss/batchgreet.ts +2 -2
  236. package/src/clis/boss/chatlist.ts +2 -2
  237. package/src/clis/boss/detail.ts +2 -2
  238. package/src/clis/boss/greet.ts +4 -4
  239. package/src/clis/boss/search.ts +1 -1
  240. package/src/clis/boss/send.ts +1 -1
  241. package/src/clis/boss/stats.ts +2 -2
  242. package/src/clis/chaoxing/assignments.ts +1 -1
  243. package/src/clis/chaoxing/exams.ts +1 -1
  244. package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
  245. package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
  246. package/src/clis/chatgpt/README.zh-CN.md +3 -3
  247. package/src/clis/chatgpt/read.ts +1 -1
  248. package/src/clis/chatwise/export.ts +1 -1
  249. package/src/clis/chatwise/model.ts +2 -2
  250. package/src/clis/chatwise/screenshot.ts +1 -1
  251. package/src/clis/codex/export.ts +1 -1
  252. package/src/clis/codex/model.ts +2 -2
  253. package/src/clis/codex/screenshot.ts +1 -1
  254. package/src/clis/coupang/add-to-cart.ts +3 -4
  255. package/src/clis/coupang/search.ts +2 -4
  256. package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
  257. package/src/clis/ctrip/search.ts +1 -1
  258. package/src/clis/cursor/export.ts +1 -1
  259. package/src/clis/cursor/model.ts +2 -2
  260. package/src/clis/cursor/screenshot.ts +1 -1
  261. package/src/clis/jike/comment.ts +2 -3
  262. package/src/clis/jike/create.ts +1 -2
  263. package/src/clis/jike/feed.ts +0 -1
  264. package/src/clis/jike/like.ts +1 -2
  265. package/src/clis/jike/notifications.ts +0 -1
  266. package/src/clis/jike/post.yaml +1 -0
  267. package/src/clis/jike/repost.ts +1 -2
  268. package/src/clis/jike/search.ts +2 -3
  269. package/src/clis/jike/topic.yaml +1 -0
  270. package/src/clis/jike/user.yaml +1 -0
  271. package/src/clis/jimeng/history.yaml +0 -1
  272. package/src/clis/linkedin/search.ts +7 -7
  273. package/src/clis/linux-do/category.yaml +1 -0
  274. package/src/clis/linux-do/search.yaml +4 -3
  275. package/src/clis/linux-do/topic.yaml +1 -0
  276. package/src/clis/notion/export.ts +1 -1
  277. package/src/clis/reddit/comment.ts +3 -4
  278. package/src/clis/reddit/read.ts +4 -5
  279. package/src/clis/reddit/save.ts +2 -3
  280. package/src/clis/reddit/saved.ts +0 -1
  281. package/src/clis/reddit/search.yaml +1 -0
  282. package/src/clis/reddit/subscribe.ts +0 -1
  283. package/src/clis/reddit/upvote.ts +2 -3
  284. package/src/clis/reddit/upvoted.ts +0 -1
  285. package/src/clis/reddit/user-comments.yaml +1 -0
  286. package/src/clis/reddit/user-posts.yaml +1 -0
  287. package/src/clis/reddit/user.yaml +1 -0
  288. package/src/clis/reuters/search.ts +1 -1
  289. package/src/clis/smzdm/search.ts +2 -3
  290. package/src/clis/stackoverflow/search.yaml +1 -0
  291. package/src/clis/steam/top-sellers.yaml +29 -0
  292. package/src/clis/twitter/accept.ts +2 -2
  293. package/src/clis/twitter/article.ts +2 -2
  294. package/src/clis/twitter/block.ts +92 -0
  295. package/src/clis/twitter/delete.ts +1 -1
  296. package/src/clis/twitter/hide-reply.ts +70 -0
  297. package/src/clis/twitter/like.ts +1 -1
  298. package/src/clis/twitter/post.ts +1 -1
  299. package/src/clis/twitter/reply-dm.ts +1 -1
  300. package/src/clis/twitter/reply.ts +2 -2
  301. package/src/clis/twitter/search.ts +1 -1
  302. package/src/clis/twitter/thread.ts +2 -2
  303. package/src/clis/twitter/trending.ts +113 -0
  304. package/src/clis/twitter/unblock.ts +75 -0
  305. package/src/clis/v2ex/topic.yaml +1 -0
  306. package/src/clis/weibo/hot.ts +0 -1
  307. package/src/clis/weread/book.ts +1 -1
  308. package/src/clis/weread/highlights.ts +1 -1
  309. package/src/clis/weread/notes.ts +1 -1
  310. package/src/clis/weread/search.ts +1 -1
  311. package/src/clis/wikipedia/search.ts +1 -1
  312. package/src/clis/xiaohongshu/creator-note-detail.test.ts +82 -33
  313. package/src/clis/xiaohongshu/creator-note-detail.ts +89 -5
  314. package/src/clis/xiaohongshu/creator-notes.test.ts +39 -6
  315. package/src/clis/xiaohongshu/creator-notes.ts +44 -5
  316. package/src/clis/xiaohongshu/creator-profile.ts +0 -1
  317. package/src/clis/xiaohongshu/creator-stats.ts +0 -1
  318. package/src/clis/xiaohongshu/download.ts +2 -3
  319. package/src/clis/xiaohongshu/feed.yaml +0 -1
  320. package/src/clis/xiaohongshu/notifications.yaml +0 -1
  321. package/src/clis/xiaohongshu/search.ts +2 -2
  322. package/src/clis/xiaohongshu/user.ts +1 -2
  323. package/src/clis/yahoo-finance/quote.ts +0 -1
  324. package/src/clis/youtube/search.ts +1 -1
  325. package/src/clis/youtube/transcript.ts +1 -1
  326. package/src/clis/youtube/video.ts +1 -1
  327. package/src/clis/zhihu/download.ts +1 -2
  328. package/src/clis/zhihu/question.ts +1 -1
  329. package/src/clis/zhihu/search.yaml +4 -3
  330. package/src/commanderAdapter.ts +113 -0
  331. package/src/{engine.ts → discovery.ts} +1 -108
  332. package/src/download/index.ts +21 -54
  333. package/src/engine.test.ts +8 -7
  334. package/src/execution.ts +138 -0
  335. package/src/explore.ts +135 -109
  336. package/src/external-clis.yaml +9 -0
  337. package/src/external.ts +15 -12
  338. package/src/main.ts +1 -1
  339. package/src/pipeline/steps/browser.ts +7 -2
  340. package/src/registry.ts +5 -0
  341. package/src/runtime.ts +9 -0
  342. package/src/serialization.ts +79 -0
  343. package/src/types.ts +1 -1
  344. package/tests/e2e/browser-public.test.ts +25 -0
  345. package/tests/e2e/public-commands.test.ts +55 -1
  346. package/dist/clis/twitter/trending.yaml +0 -46
  347. package/docs/public/CNAME +0 -1
  348. package/src/clis/twitter/trending.yaml +0 -46
  349. /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
  350. /package/dist/{chaoxing.test.d.ts → clis/bloomberg/businessweek.d.ts} +0 -0
  351. /package/dist/{coupang.test.d.ts → clis/bloomberg/economics.d.ts} +0 -0
  352. /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
  353. /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
  354. /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
@@ -9,11 +9,11 @@ export const modelCommand = cli({
9
9
  strategy: Strategy.UI,
10
10
  browser: true,
11
11
  args: [
12
- { name: 'model_name', required: false, positional: true, help: 'The ID of the model to switch to (e.g. gpt-4)' }
12
+ { name: 'model-name', required: false, positional: true, help: 'The ID of the model to switch to (e.g. gpt-4)' }
13
13
  ],
14
14
  columns: ['Status', 'Model'],
15
15
  func: async (page: IPage, kwargs: any) => {
16
- const desiredModel = kwargs.model_name as string | undefined;
16
+ const desiredModel = kwargs['model-name'] as string | undefined;
17
17
 
18
18
  if (!desiredModel) {
19
19
  // Just read the current model. We traverse iframes/webviews if needed.
@@ -10,7 +10,7 @@ export const screenshotCommand = cli({
10
10
  strategy: Strategy.UI,
11
11
  browser: true,
12
12
  args: [
13
- { name: 'output', required: false, positional: true, help: 'Output file path (default: /tmp/codex-snapshot.txt)' },
13
+ { name: 'output', required: false, help: 'Output file path (default: /tmp/codex-snapshot.txt)' },
14
14
  ],
15
15
  columns: ['Status', 'File'],
16
16
  func: async (page: IPage, kwargs: any) => {
@@ -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
 
4
4
  function escapeJsString(value: string): string {
5
5
  return JSON.stringify(value);
@@ -102,12 +102,12 @@ cli({
102
102
  strategy: Strategy.COOKIE,
103
103
  browser: true,
104
104
  args: [
105
- { name: 'productId', required: false, help: 'Coupang product ID' },
105
+ { name: 'product-id', required: false, help: 'Coupang product ID' },
106
106
  { name: 'url', required: false, help: 'Canonical product URL' },
107
107
  ],
108
108
  columns: ['ok', 'product_id', 'url', 'message'],
109
109
  func: async (page, kwargs) => {
110
- const rawProductId = kwargs.productId ?? kwargs.product_id;
110
+ const rawProductId = kwargs['product-id'] ?? kwargs['product-id'];
111
111
  const productId = normalizeProductId(rawProductId);
112
112
  const targetUrl = canonicalizeProductUrl(kwargs.url, productId);
113
113
 
@@ -117,7 +117,6 @@ cli({
117
117
 
118
118
  const finalUrl = targetUrl || canonicalizeProductUrl('', productId);
119
119
  await page.goto(finalUrl);
120
- await page.wait(3);
121
120
 
122
121
  const result = await page.evaluate(buildAddToCartEvaluate(productId));
123
122
  const loginHints = result?.loginHints ?? {};
@@ -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
 
4
4
  function escapeJsString(value: string): string {
5
5
  return JSON.stringify(value);
@@ -409,7 +409,7 @@ cli({
409
409
  strategy: Strategy.COOKIE,
410
410
  browser: true,
411
411
  args: [
412
- { name: 'query', required: true, help: 'Search keyword' },
412
+ { name: 'query', required: true, positional: true, help: 'Search keyword' },
413
413
  { name: 'page', type: 'int', default: 1, help: 'Search result page number' },
414
414
  { name: 'limit', type: 'int', default: 20, help: 'Max results (max 50)' },
415
415
  { name: 'filter', required: false, help: 'Optional search filter (currently supports: rocket)' },
@@ -425,7 +425,6 @@ cli({
425
425
  const initialPage = filter ? 1 : pageNumber;
426
426
  const url = `https://www.coupang.com/np/search?q=${encodeURIComponent(query)}&channel=user&page=${initialPage}`;
427
427
  await page.goto(url);
428
- await page.wait(3);
429
428
  if (filter) {
430
429
  const filterResult = await page.evaluate(buildApplyFilterEvaluate(filter));
431
430
  if (!filterResult?.ok) {
@@ -437,7 +436,6 @@ cli({
437
436
  const filteredUrl = new URL(locationInfo?.href || url);
438
437
  filteredUrl.searchParams.set('page', String(pageNumber));
439
438
  await page.goto(filteredUrl.toString());
440
- await page.wait(3);
441
439
  }
442
440
  }
443
441
  await page.autoScroll({ times: filter ? 3 : 2, delayMs: 1500 });
@@ -5,7 +5,7 @@ import {
5
5
  normalizeProductId,
6
6
  normalizeSearchItem,
7
7
  sanitizeSearchItems,
8
- } from './coupang.js';
8
+ } from './utils.js';
9
9
 
10
10
  describe('normalizeProductId', () => {
11
11
  it('extracts product id from canonical path', () => {
@@ -11,7 +11,7 @@ cli({
11
11
  domain: 'www.ctrip.com',
12
12
  strategy: Strategy.COOKIE,
13
13
  args: [
14
- { name: 'query', required: true, help: 'Search keyword (city or attraction)' },
14
+ { name: 'query', required: true, positional: true, help: 'Search keyword (city or attraction)' },
15
15
  { name: 'limit', type: 'int', default: 15, help: 'Number of results' },
16
16
  ],
17
17
  columns: ['rank', 'name', 'type', 'score', 'price', 'url'],
@@ -11,7 +11,7 @@ function makeExportCommand(site: string, readSelector: string) {
11
11
  strategy: Strategy.UI,
12
12
  browser: true,
13
13
  args: [
14
- { name: 'output', required: false, positional: true, help: `Output file (default: /tmp/${site}-export.md)` },
14
+ { name: 'output', required: false, help: `Output file (default: /tmp/${site}-export.md)` },
15
15
  ],
16
16
  columns: ['Status', 'File', 'Messages'],
17
17
  func: async (page: IPage, kwargs: any) => {
@@ -9,11 +9,11 @@ export const modelCommand = cli({
9
9
  strategy: Strategy.UI,
10
10
  browser: true,
11
11
  args: [
12
- { name: 'model_name', required: false, positional: true, help: 'The ID of the model to switch to (e.g. claude-3.5-sonnet)' }
12
+ { name: 'model-name', required: false, positional: true, help: 'The ID of the model to switch to (e.g. claude-3.5-sonnet)' }
13
13
  ],
14
14
  columns: ['Status', 'Model'],
15
15
  func: async (page: IPage, kwargs: any) => {
16
- const desiredModel = kwargs.model_name as string | undefined;
16
+ const desiredModel = kwargs['model-name'] as string | undefined;
17
17
 
18
18
  if (!desiredModel) {
19
19
  // Just read the current model
@@ -11,7 +11,7 @@ function makeScreenshotCommand(site: string) {
11
11
  strategy: Strategy.UI,
12
12
  browser: true,
13
13
  args: [
14
- { name: 'output', required: false, positional: true, help: `Output file path (default: /tmp/${site}-snapshot.txt)` },
14
+ { name: 'output', required: false, help: `Output file path (default: /tmp/${site}-snapshot.txt)` },
15
15
  ],
16
16
  columns: ['Status', 'File'],
17
17
  func: async (page: IPage, kwargs: any) => {
@@ -15,13 +15,12 @@ cli({
15
15
  strategy: Strategy.UI,
16
16
  browser: true,
17
17
  args: [
18
- { name: 'id', type: 'string', required: true, help: '帖子 ID' },
19
- { name: 'text', type: 'string', required: true, help: '评论内容' },
18
+ { name: 'id', type: 'string', required: true, positional: true, help: '帖子 ID' },
19
+ { name: 'text', type: 'string', required: true, positional: true, help: '评论内容' },
20
20
  ],
21
21
  columns: ['status', 'message'],
22
22
  func: async (page, kwargs) => {
23
23
  await page.goto(`https://web.okjike.com/originalPost/${kwargs.id}`);
24
- await page.wait(5);
25
24
 
26
25
  // 1. 找到评论输入框并填入文本
27
26
  const inputResult = await page.evaluate(`(async () => {
@@ -15,13 +15,12 @@ cli({
15
15
  strategy: Strategy.UI,
16
16
  browser: true,
17
17
  args: [
18
- { name: 'text', type: 'string', required: true, help: '动态正文内容' },
18
+ { name: 'text', type: 'string', required: true, positional: true, help: '动态正文内容' },
19
19
  ],
20
20
  columns: ['status', 'message'],
21
21
  func: async (page, kwargs) => {
22
22
  // 1. 导航到首页(有内联发帖框)
23
23
  await page.goto('https://web.okjike.com');
24
- await page.wait(5);
25
24
 
26
25
  // 2. 在发帖框中输入文本
27
26
  const textResult = await page.evaluate(`(async () => {
@@ -24,7 +24,6 @@ cli({
24
24
 
25
25
  // 1. 导航到即刻首页,等待 SPA 重定向到 /following
26
26
  await page.goto('https://web.okjike.com');
27
- await page.wait(5);
28
27
 
29
28
  // 2. 通过 React fiber 提取帖子数据
30
29
  const extract = async (): Promise<JikePost[]> => {
@@ -15,13 +15,12 @@ cli({
15
15
  strategy: Strategy.UI,
16
16
  browser: true,
17
17
  args: [
18
- { name: 'id', type: 'string', required: true, help: '帖子 ID' },
18
+ { name: 'id', type: 'string', required: true, positional: true, help: '帖子 ID' },
19
19
  ],
20
20
  columns: ['status', 'message'],
21
21
  func: async (page, kwargs) => {
22
22
  // 1. 导航到帖子详情页
23
23
  await page.goto(`https://web.okjike.com/originalPost/${kwargs.id}`);
24
- await page.wait(5);
25
24
 
26
25
  // 2. 找到点赞按钮并点击
27
26
  const result = await page.evaluate(`(async () => {
@@ -44,7 +44,6 @@ cli({
44
44
 
45
45
  // 1. 直接导航到通知页
46
46
  await page.goto('https://web.okjike.com/notification');
47
- await page.wait(5);
48
47
 
49
48
  // 3. 优先用 React fiber 提取通知数据
50
49
  // 通知 fiber 数据结构与帖子不同,需查找含 type + user 字段的 props
@@ -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)
@@ -16,13 +16,12 @@ cli({
16
16
  strategy: Strategy.UI,
17
17
  browser: true,
18
18
  args: [
19
- { name: 'id', type: 'string', required: true, help: '帖子 ID' },
19
+ { name: 'id', type: 'string', required: true, positional: true, help: '帖子 ID' },
20
20
  { name: 'text', type: 'string', required: false, help: '转发附言(可选)' },
21
21
  ],
22
22
  columns: ['status', 'message'],
23
23
  func: async (page, kwargs) => {
24
24
  await page.goto(`https://web.okjike.com/originalPost/${kwargs.id}`);
25
- await page.wait(5);
26
25
 
27
26
  // 1. 点击操作栏中的转发按钮(第三个子元素)
28
27
  const clickResult = await page.evaluate(`(async () => {
@@ -16,18 +16,17 @@ cli({
16
16
  strategy: Strategy.COOKIE,
17
17
  browser: true,
18
18
  args: [
19
- { name: 'keyword', type: 'string', required: true },
19
+ { name: 'query', type: 'string', required: true, positional: true },
20
20
  { name: 'limit', type: 'int', default: 20 },
21
21
  ],
22
22
  columns: ['author', 'content', 'likes', 'comments', 'time', 'url'],
23
23
  func: async (page, kwargs) => {
24
- const keyword = kwargs.keyword as string;
24
+ const keyword = kwargs.query as string;
25
25
  const limit = (kwargs.limit as number) || 20;
26
26
 
27
27
  // 1. 直接导航到搜索页
28
28
  const encodedKeyword = encodeURIComponent(keyword);
29
29
  await page.goto(`https://web.okjike.com/search?q=${encodedKeyword}`);
30
- await page.wait(5);
31
30
 
32
31
  // 2. 通过 React fiber 提取帖子数据
33
32
  const extract = async (): Promise<JikePost[]> => {
@@ -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 }};
@@ -368,15 +368,15 @@ cli({
368
368
  strategy: Strategy.HEADER,
369
369
  browser: true,
370
370
  args: [
371
- { name: 'query', type: 'string', required: true, help: 'Job search keywords' },
371
+ { name: 'query', type: 'string', required: true, positional: true, help: 'Job search keywords' },
372
372
  { name: 'location', type: 'string', required: false, help: 'Location text such as San Francisco Bay Area' },
373
373
  { name: 'limit', type: 'int', default: 10, help: 'Number of jobs to return (max 100)' },
374
374
  { name: 'start', type: 'int', default: 0, help: 'Result offset for pagination' },
375
375
  { name: 'details', type: 'bool', default: false, help: 'Include full job description and apply URL (slower)' },
376
376
  { name: 'company', type: 'string', required: false, help: 'Comma-separated company names or LinkedIn company IDs' },
377
- { name: 'experience_level', type: 'string', required: false, help: 'Comma-separated: internship, entry, associate, mid-senior, director, executive' },
378
- { name: 'job_type', type: 'string', required: false, help: 'Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other' },
379
- { name: 'date_posted', type: 'string', required: false, help: 'One of: any, month, week, 24h' },
377
+ { name: 'experience-level', type: 'string', required: false, help: 'Comma-separated: internship, entry, associate, mid-senior, director, executive' },
378
+ { name: 'job-type', type: 'string', required: false, help: 'Comma-separated: full-time, part-time, contract, temporary, volunteer, internship, other' },
379
+ { name: 'date-posted', type: 'string', required: false, help: 'One of: any, month, week, 24h' },
380
380
  { name: 'remote', type: 'string', required: false, help: 'Comma-separated: on-site, hybrid, remote' },
381
381
  ],
382
382
  columns: ['rank', 'title', 'company', 'location', 'listed', 'salary', 'url'],
@@ -402,9 +402,9 @@ cli({
402
402
  limit,
403
403
  start,
404
404
  companyIds,
405
- experienceLevels: mapFilterValues(kwargs.experience_level, EXPERIENCE_LEVELS, 'experience_level'),
406
- jobTypes: mapFilterValues(kwargs.job_type, JOB_TYPES, 'job_type'),
407
- datePostedValues: mapFilterValues(kwargs.date_posted, DATE_POSTED, 'date_posted'),
405
+ experienceLevels: mapFilterValues(kwargs['experience-level'], EXPERIENCE_LEVELS, 'experience_level'),
406
+ jobTypes: mapFilterValues(kwargs['job-type'], JOB_TYPES, 'job_type'),
407
+ datePostedValues: mapFilterValues(kwargs['date-posted'], DATE_POSTED, 'date_posted'),
408
408
  remoteTypes: mapFilterValues(kwargs.remote, REMOTE_TYPES, 'remote'),
409
409
  };
410
410
 
@@ -10,6 +10,7 @@ args:
10
10
  required: true
11
11
  description: Category slug (use 'categories' command to find)
12
12
  id:
13
+ positional: true
13
14
  type: int
14
15
  required: true
15
16
  description: Category ID (use 'categories' command to find)
@@ -5,10 +5,11 @@ domain: linux.do
5
5
  browser: true
6
6
 
7
7
  args:
8
- keyword:
8
+ query:
9
+ positional: true
9
10
  type: str
10
11
  required: true
11
- description: Search keyword
12
+ description: Search query
12
13
  limit:
13
14
  type: int
14
15
  default: 20
@@ -19,7 +20,7 @@ pipeline:
19
20
 
20
21
  - evaluate: |
21
22
  (async () => {
22
- const keyword = ${{ args.keyword | json }};
23
+ const keyword = ${{ args.query | json }};
23
24
  const res = await fetch('/search.json?q=' + encodeURIComponent(keyword), { credentials: 'include' });
24
25
  if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
25
26
  let data;
@@ -6,6 +6,7 @@ browser: true
6
6
 
7
7
  args:
8
8
  id:
9
+ positional: true
9
10
  type: int
10
11
  required: true
11
12
  description: Topic ID
@@ -10,7 +10,7 @@ export const exportCommand = cli({
10
10
  strategy: Strategy.UI,
11
11
  browser: true,
12
12
  args: [
13
- { name: 'output', required: false, positional: true, help: 'Output file (default: /tmp/notion-export.md)' },
13
+ { name: 'output', required: false, help: 'Output file (default: /tmp/notion-export.md)' },
14
14
  ],
15
15
  columns: ['Status', 'File'],
16
16
  func: async (page: IPage, kwargs: any) => {
@@ -8,19 +8,18 @@ cli({
8
8
  strategy: Strategy.COOKIE,
9
9
  browser: true,
10
10
  args: [
11
- { name: 'post_id', type: 'string', required: true, help: 'Post ID (e.g. 1abc123) or fullname (t3_xxx)' },
12
- { name: 'text', type: 'string', required: true, help: 'Comment text' },
11
+ { name: 'post-id', type: 'string', required: true, help: 'Post ID (e.g. 1abc123) or fullname (t3_xxx)' },
12
+ { name: 'text', type: 'string', required: true, positional: true, help: 'Comment text' },
13
13
  ],
14
14
  columns: ['status', 'message'],
15
15
  func: async (page, kwargs) => {
16
16
  if (!page) throw new Error('Requires browser');
17
17
 
18
18
  await page.goto('https://www.reddit.com');
19
- await page.wait(3);
20
19
 
21
20
  const result = await page.evaluate(`(async () => {
22
21
  try {
23
- let postId = ${JSON.stringify(kwargs.post_id)};
22
+ let postId = ${JSON.stringify(kwargs['post-id'])};
24
23
  const urlMatch = postId.match(/comments\\/([a-z0-9]+)/);
25
24
  if (urlMatch) postId = urlMatch[1];
26
25
  const fullname = postId.startsWith('t3_') || postId.startsWith('t1_')
@@ -15,12 +15,12 @@ cli({
15
15
  domain: 'reddit.com',
16
16
  strategy: Strategy.COOKIE,
17
17
  args: [
18
- { name: 'post_id', required: true, help: 'Post ID (e.g. 1abc123) or full URL' },
18
+ { name: 'post-id', required: true, help: 'Post ID (e.g. 1abc123) or full URL' },
19
19
  { name: 'sort', default: 'best', help: 'Comment sort: best, top, new, controversial, old, qa' },
20
20
  { name: 'limit', type: 'int', default: 25, help: 'Number of top-level comments' },
21
21
  { name: 'depth', type: 'int', default: 2, help: 'Max reply depth (1=no replies, 2=one level of replies, etc.)' },
22
22
  { name: 'replies', type: 'int', default: 5, help: 'Max replies shown per comment at each level (sorted by score)' },
23
- { name: 'max_length', type: 'int', default: 2000, help: 'Max characters per comment body (min 100)' },
23
+ { name: 'max-length', type: 'int', default: 2000, help: 'Max characters per comment body (min 100)' },
24
24
  ],
25
25
  columns: ['type', 'author', 'score', 'text'],
26
26
  func: async (page, kwargs) => {
@@ -28,14 +28,13 @@ cli({
28
28
  const limit = Math.max(1, kwargs.limit ?? 25);
29
29
  const maxDepth = Math.max(1, kwargs.depth ?? 2);
30
30
  const maxReplies = Math.max(1, kwargs.replies ?? 5);
31
- const maxLength = Math.max(100, kwargs.max_length ?? 2000);
31
+ const maxLength = Math.max(100, kwargs['max-length'] ?? 2000);
32
32
 
33
33
  await page.goto('https://www.reddit.com');
34
- await page.wait(2);
35
34
 
36
35
  const data = await page.evaluate(`
37
36
  (async function() {
38
- var postId = ${JSON.stringify(kwargs.post_id)};
37
+ var postId = ${JSON.stringify(kwargs['post-id'])};
39
38
  var urlMatch = postId.match(/comments\\/([a-z0-9]+)/);
40
39
  if (urlMatch) postId = urlMatch[1];
41
40
 
@@ -8,7 +8,7 @@ cli({
8
8
  strategy: Strategy.COOKIE,
9
9
  browser: true,
10
10
  args: [
11
- { name: 'post_id', type: 'string', required: true, help: 'Post ID (e.g. 1abc123) or fullname (t3_xxx)' },
11
+ { name: 'post-id', type: 'string', required: true, help: 'Post ID (e.g. 1abc123) or fullname (t3_xxx)' },
12
12
  { name: 'undo', type: 'boolean', default: false, help: 'Unsave instead of save' },
13
13
  ],
14
14
  columns: ['status', 'message'],
@@ -16,11 +16,10 @@ cli({
16
16
  if (!page) throw new Error('Requires browser');
17
17
 
18
18
  await page.goto('https://www.reddit.com');
19
- await page.wait(3);
20
19
 
21
20
  const result = await page.evaluate(`(async () => {
22
21
  try {
23
- let postId = ${JSON.stringify(kwargs.post_id)};
22
+ let postId = ${JSON.stringify(kwargs['post-id'])};
24
23
  const urlMatch = postId.match(/comments\\/([a-z0-9]+)/);
25
24
  if (urlMatch) postId = urlMatch[1];
26
25
  const fullname = postId.startsWith('t3_') || postId.startsWith('t1_')
@@ -15,7 +15,6 @@ cli({
15
15
  if (!page) throw new Error('Requires browser');
16
16
 
17
17
  await page.goto('https://www.reddit.com');
18
- await page.wait(3);
19
18
 
20
19
  const result = await page.evaluate(`(async () => {
21
20
  try {
@@ -7,6 +7,7 @@ browser: true
7
7
 
8
8
  args:
9
9
  query:
10
+ positional: true
10
11
  type: string
11
12
  required: true
12
13
  subreddit:
@@ -16,7 +16,6 @@ cli({
16
16
  if (!page) throw new Error('Requires browser');
17
17
 
18
18
  await page.goto('https://www.reddit.com');
19
- await page.wait(3);
20
19
 
21
20
  const result = await page.evaluate(`(async () => {
22
21
  try {
@@ -8,7 +8,7 @@ cli({
8
8
  strategy: Strategy.COOKIE,
9
9
  browser: true,
10
10
  args: [
11
- { name: 'post_id', type: 'string', required: true, help: 'Post ID (e.g. 1abc123) or fullname (t3_xxx)' },
11
+ { name: 'post-id', type: 'string', required: true, help: 'Post ID (e.g. 1abc123) or fullname (t3_xxx)' },
12
12
  { name: 'direction', type: 'string', default: 'up', help: 'Vote direction: up, down, none' },
13
13
  ],
14
14
  columns: ['status', 'message'],
@@ -16,11 +16,10 @@ cli({
16
16
  if (!page) throw new Error('Requires browser');
17
17
 
18
18
  await page.goto('https://www.reddit.com');
19
- await page.wait(3);
20
19
 
21
20
  const result = await page.evaluate(`(async () => {
22
21
  try {
23
- let postId = ${JSON.stringify(kwargs.post_id)};
22
+ let postId = ${JSON.stringify(kwargs['post-id'])};
24
23
  // Extract ID from URL if needed
25
24
  const urlMatch = postId.match(/comments\\/([a-z0-9]+)/);
26
25
  if (urlMatch) postId = urlMatch[1];
@@ -15,7 +15,6 @@ cli({
15
15
  if (!page) throw new Error('Requires browser');
16
16
 
17
17
  await page.goto('https://www.reddit.com');
18
- await page.wait(3);
19
18
 
20
19
  const result = await page.evaluate(`(async () => {
21
20
  try {
@@ -7,6 +7,7 @@ browser: true
7
7
 
8
8
  args:
9
9
  username:
10
+ positional: true
10
11
  type: string
11
12
  required: true
12
13
  limit:
@@ -7,6 +7,7 @@ browser: true
7
7
 
8
8
  args:
9
9
  username:
10
+ positional: true
10
11
  type: string
11
12
  required: true
12
13
  limit:
@@ -7,6 +7,7 @@ browser: true
7
7
 
8
8
  args:
9
9
  username:
10
+ positional: true
10
11
  type: string
11
12
  required: true
12
13
 
@@ -11,7 +11,7 @@ cli({
11
11
  domain: 'www.reuters.com',
12
12
  strategy: Strategy.COOKIE,
13
13
  args: [
14
- { name: 'query', required: true, help: 'Search query' },
14
+ { name: 'query', required: true, positional: true, help: 'Search query' },
15
15
  { name: 'limit', type: 'int', default: 10, help: 'Number of results (max 40)' },
16
16
  ],
17
17
  columns: ['rank', 'title', 'date', 'section', 'url'],
@@ -14,17 +14,16 @@ cli({
14
14
  domain: 'www.smzdm.com',
15
15
  strategy: Strategy.COOKIE,
16
16
  args: [
17
- { name: 'keyword', required: true, help: 'Search keyword' },
17
+ { name: 'query', required: true, positional: true, help: 'Search keyword' },
18
18
  { name: 'limit', type: 'int', default: 20, help: 'Number of results' },
19
19
  ],
20
20
  columns: ['rank', 'title', 'price', 'mall', 'comments', 'url'],
21
21
  func: async (page, kwargs) => {
22
- const q = encodeURIComponent(kwargs.keyword);
22
+ const q = encodeURIComponent(kwargs.query);
23
23
  const limit = kwargs.limit || 20;
24
24
 
25
25
  // Navigate directly to search results page
26
26
  await page.goto(`https://search.smzdm.com/?c=home&s=${q}&v=b`);
27
- await page.wait(2);
28
27
 
29
28
  const data = await page.evaluate(`
30
29
  (() => {
@@ -7,6 +7,7 @@ browser: false
7
7
 
8
8
  args:
9
9
  query:
10
+ positional: true
10
11
  type: string
11
12
  required: true
12
13
  description: Search query
@@ -0,0 +1,29 @@
1
+ site: steam
2
+ name: top-sellers
3
+ description: Steam top selling games
4
+ domain: store.steampowered.com
5
+ strategy: public
6
+ browser: false
7
+
8
+ args:
9
+ limit:
10
+ type: int
11
+ default: 10
12
+ description: Number of games
13
+
14
+ pipeline:
15
+ - fetch:
16
+ url: https://store.steampowered.com/api/featuredcategories/
17
+
18
+ - select: top_sellers.items
19
+
20
+ - map:
21
+ rank: ${{ index + 1 }}
22
+ name: ${{ item.name }}
23
+ price: ${{ item.final_price }}
24
+ discount: ${{ item.discount_percent }}
25
+ url: https://store.steampowered.com/app/${{ item.id }}
26
+
27
+ - limit: ${{ args.limit }}
28
+
29
+ columns: [rank, name, price, discount, url]
@@ -10,14 +10,14 @@ cli({
10
10
  browser: true,
11
11
  timeoutSeconds: 600, // 10 min — batch operation iterating many conversations
12
12
  args: [
13
- { name: 'keyword', type: 'string', required: true, help: 'Keywords to match (comma-separated for OR, e.g. "群,微信")' },
13
+ { name: 'query', type: 'string', required: true, positional: true, help: 'Keywords to match (comma-separated for OR, e.g. "群,微信")' },
14
14
  { name: 'max', type: 'int', required: false, default: 20, help: 'Maximum number of requests to accept (default: 20)' },
15
15
  ],
16
16
  columns: ['index', 'status', 'user', 'message'],
17
17
  func: async (page: IPage | null, kwargs: any) => {
18
18
  if (!page) throw new Error('Requires browser');
19
19
 
20
- const keywords: string[] = kwargs.keyword.split(',').map((k: string) => k.trim()).filter(Boolean);
20
+ const keywords: string[] = kwargs.query.split(',').map((k: string) => k.trim()).filter(Boolean);
21
21
  const maxAccepts: number = kwargs.max ?? 20;
22
22
  const results: Array<{ index: number; status: string; user: string; message: string }> = [];
23
23
  let acceptCount = 0;