@jackwener/opencli 1.7.11 → 1.7.12

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 (730) hide show
  1. package/README.zh-CN.md +2 -1
  2. package/cli-manifest.json +1417 -24
  3. package/clis/1688/assets.js +1 -0
  4. package/clis/1688/download.js +1 -0
  5. package/clis/1688/item.js +1 -0
  6. package/clis/1688/search.js +2 -1
  7. package/clis/1688/store.js +1 -0
  8. package/clis/36kr/article.js +1 -0
  9. package/clis/36kr/hot.js +1 -0
  10. package/clis/36kr/news.js +1 -0
  11. package/clis/36kr/search.js +1 -0
  12. package/clis/51job/company.js +1 -0
  13. package/clis/51job/detail.js +1 -0
  14. package/clis/51job/hot.js +1 -0
  15. package/clis/51job/search.js +1 -0
  16. package/clis/amazon/bestsellers.js +1 -0
  17. package/clis/amazon/discussion.js +1 -0
  18. package/clis/amazon/movers-shakers.js +1 -0
  19. package/clis/amazon/new-releases.js +1 -0
  20. package/clis/amazon/offer.js +1 -0
  21. package/clis/amazon/product.js +1 -0
  22. package/clis/amazon/rankings.js +1 -0
  23. package/clis/amazon/search.js +1 -0
  24. package/clis/antigravity/dump.js +1 -0
  25. package/clis/antigravity/extract-code.js +1 -0
  26. package/clis/antigravity/model.js +1 -0
  27. package/clis/antigravity/new.js +1 -0
  28. package/clis/antigravity/read.js +1 -0
  29. package/clis/antigravity/send.js +1 -0
  30. package/clis/antigravity/status.js +1 -0
  31. package/clis/antigravity/watch.js +1 -0
  32. package/clis/apple-podcasts/episodes.js +1 -0
  33. package/clis/apple-podcasts/search.js +1 -0
  34. package/clis/apple-podcasts/top.js +1 -0
  35. package/clis/arxiv/arxiv.test.js +112 -0
  36. package/clis/arxiv/paper.js +4 -3
  37. package/clis/arxiv/recent.js +33 -0
  38. package/clis/arxiv/search.js +19 -7
  39. package/clis/arxiv/utils.js +68 -5
  40. package/clis/baidu-scholar/search.js +1 -0
  41. package/clis/band/bands.js +1 -0
  42. package/clis/band/mentions.js +1 -0
  43. package/clis/band/post.js +1 -0
  44. package/clis/band/posts.js +1 -0
  45. package/clis/barchart/flow.js +1 -0
  46. package/clis/barchart/greeks.js +1 -0
  47. package/clis/barchart/options.js +1 -0
  48. package/clis/barchart/quote.js +1 -0
  49. package/clis/bbc/news.js +1 -0
  50. package/clis/bilibili/comments.js +1 -0
  51. package/clis/bilibili/download.js +1 -0
  52. package/clis/bilibili/dynamic.js +1 -0
  53. package/clis/bilibili/favorite.js +1 -0
  54. package/clis/bilibili/feed.js +2 -0
  55. package/clis/bilibili/following.js +1 -0
  56. package/clis/bilibili/history.js +1 -0
  57. package/clis/bilibili/hot.js +6 -1
  58. package/clis/bilibili/hot.test.js +17 -0
  59. package/clis/bilibili/me.js +1 -1
  60. package/clis/bilibili/ranking.js +1 -0
  61. package/clis/bilibili/search.js +1 -1
  62. package/clis/bilibili/subtitle.js +1 -0
  63. package/clis/bilibili/user-videos.js +1 -0
  64. package/clis/bilibili/video.js +1 -0
  65. package/clis/binance/asks.js +1 -0
  66. package/clis/binance/depth.js +1 -0
  67. package/clis/binance/gainers.js +1 -0
  68. package/clis/binance/klines.js +1 -0
  69. package/clis/binance/losers.js +1 -0
  70. package/clis/binance/pairs.js +1 -0
  71. package/clis/binance/price.js +1 -0
  72. package/clis/binance/prices.js +1 -0
  73. package/clis/binance/ticker.js +1 -0
  74. package/clis/binance/top.js +1 -0
  75. package/clis/binance/trades.js +1 -0
  76. package/clis/bloomberg/businessweek.js +1 -0
  77. package/clis/bloomberg/economics.js +1 -0
  78. package/clis/bloomberg/feeds.js +1 -0
  79. package/clis/bloomberg/industries.js +1 -0
  80. package/clis/bloomberg/main.js +1 -0
  81. package/clis/bloomberg/markets.js +1 -0
  82. package/clis/bloomberg/news.js +1 -0
  83. package/clis/bloomberg/opinions.js +1 -0
  84. package/clis/bloomberg/politics.js +1 -0
  85. package/clis/bloomberg/tech.js +1 -0
  86. package/clis/bluesky/feeds.js +1 -0
  87. package/clis/bluesky/followers.js +1 -0
  88. package/clis/bluesky/following.js +1 -0
  89. package/clis/bluesky/profile.js +1 -0
  90. package/clis/bluesky/search.js +1 -0
  91. package/clis/bluesky/starter-packs.js +1 -0
  92. package/clis/bluesky/thread.js +1 -0
  93. package/clis/bluesky/trending.js +1 -0
  94. package/clis/bluesky/user.js +3 -1
  95. package/clis/boss/batchgreet.js +1 -0
  96. package/clis/boss/chatlist.js +1 -0
  97. package/clis/boss/chatmsg.js +1 -0
  98. package/clis/boss/detail.js +1 -0
  99. package/clis/boss/exchange.js +1 -0
  100. package/clis/boss/greet.js +1 -0
  101. package/clis/boss/invite.js +1 -0
  102. package/clis/boss/joblist.js +1 -0
  103. package/clis/boss/mark.js +1 -0
  104. package/clis/boss/recommend.js +1 -0
  105. package/clis/boss/resume.js +1 -0
  106. package/clis/boss/search.js +1 -0
  107. package/clis/boss/send.js +1 -0
  108. package/clis/boss/stats.js +1 -0
  109. package/clis/chaoxing/assignments.js +1 -0
  110. package/clis/chaoxing/exams.js +1 -0
  111. package/clis/chatgpt/image.js +1 -0
  112. package/clis/chatgpt-app/ask.js +1 -0
  113. package/clis/chatgpt-app/model.js +1 -0
  114. package/clis/chatgpt-app/new.js +1 -0
  115. package/clis/chatgpt-app/read.js +1 -0
  116. package/clis/chatgpt-app/send.js +1 -0
  117. package/clis/chatgpt-app/status.js +1 -0
  118. package/clis/chatwise/ask.js +1 -0
  119. package/clis/chatwise/export.js +1 -0
  120. package/clis/chatwise/history.js +1 -0
  121. package/clis/chatwise/model.js +1 -0
  122. package/clis/chatwise/read.js +1 -0
  123. package/clis/chatwise/send.js +1 -0
  124. package/clis/claude/ask.js +1 -0
  125. package/clis/claude/detail.js +1 -0
  126. package/clis/claude/history.js +1 -0
  127. package/clis/claude/new.js +1 -0
  128. package/clis/claude/read.js +1 -0
  129. package/clis/claude/send.js +1 -0
  130. package/clis/claude/status.js +1 -0
  131. package/clis/cnki/search.js +1 -0
  132. package/clis/codex/ask.js +1 -0
  133. package/clis/codex/export.js +1 -0
  134. package/clis/codex/extract-diff.js +1 -0
  135. package/clis/codex/history.js +1 -0
  136. package/clis/codex/model.js +1 -0
  137. package/clis/codex/read.js +1 -0
  138. package/clis/codex/send.js +1 -0
  139. package/clis/coupang/add-to-cart.js +1 -0
  140. package/clis/coupang/search.js +1 -0
  141. package/clis/ctrip/search.js +1 -0
  142. package/clis/cursor/ask.js +1 -0
  143. package/clis/cursor/composer.js +1 -0
  144. package/clis/cursor/export.js +1 -0
  145. package/clis/cursor/extract-code.js +1 -0
  146. package/clis/cursor/history.js +1 -0
  147. package/clis/cursor/model.js +1 -0
  148. package/clis/cursor/read.js +1 -0
  149. package/clis/cursor/send.js +1 -0
  150. package/clis/dblp/dblp.test.js +397 -0
  151. package/clis/dblp/paper.js +40 -0
  152. package/clis/dblp/search.js +45 -0
  153. package/clis/dblp/utils.js +290 -0
  154. package/clis/deepseek/ask.js +1 -0
  155. package/clis/deepseek/history.js +1 -0
  156. package/clis/deepseek/new.js +1 -0
  157. package/clis/deepseek/read.js +1 -0
  158. package/clis/deepseek/status.js +1 -0
  159. package/clis/devto/devto.test.js +236 -0
  160. package/clis/devto/read.js +103 -0
  161. package/clis/devto/tag.js +5 -1
  162. package/clis/devto/top.js +5 -1
  163. package/clis/devto/user.js +5 -1
  164. package/clis/dianping/__fixtures__/search.html +168 -0
  165. package/clis/dianping/__fixtures__/shop.html +6 -0
  166. package/clis/dianping/dianping.test.js +424 -0
  167. package/clis/dianping/search.js +154 -0
  168. package/clis/dianping/shop.js +173 -0
  169. package/clis/dianping/utils.js +157 -0
  170. package/clis/dictionary/examples.js +1 -0
  171. package/clis/dictionary/search.js +1 -0
  172. package/clis/dictionary/synonyms.js +1 -0
  173. package/clis/discord-app/channels.js +1 -0
  174. package/clis/discord-app/delete.js +1 -0
  175. package/clis/discord-app/members.js +1 -0
  176. package/clis/discord-app/read.js +1 -0
  177. package/clis/discord-app/search.js +1 -0
  178. package/clis/discord-app/send.js +1 -0
  179. package/clis/discord-app/servers.js +1 -0
  180. package/clis/discord-app/status.js +1 -0
  181. package/clis/douban/book-hot.js +1 -0
  182. package/clis/douban/download.js +1 -0
  183. package/clis/douban/marks.js +1 -0
  184. package/clis/douban/movie-hot.js +2 -1
  185. package/clis/douban/movie-hot.test.js +14 -0
  186. package/clis/douban/photos.js +2 -1
  187. package/clis/douban/reviews.js +1 -0
  188. package/clis/douban/search.js +1 -0
  189. package/clis/douban/subject.js +1 -0
  190. package/clis/douban/top250.js +1 -0
  191. package/clis/douban/utils.js +11 -13
  192. package/clis/douban/utils.test.js +79 -0
  193. package/clis/doubao/ask.js +1 -0
  194. package/clis/doubao/detail.js +1 -0
  195. package/clis/doubao/history.js +1 -0
  196. package/clis/doubao/meeting-summary.js +1 -0
  197. package/clis/doubao/meeting-transcript.js +1 -0
  198. package/clis/doubao/new.js +1 -0
  199. package/clis/doubao/read.js +1 -0
  200. package/clis/doubao/send.js +1 -0
  201. package/clis/doubao/status.js +1 -0
  202. package/clis/doubao-app/ask.js +1 -0
  203. package/clis/doubao-app/dump.js +1 -0
  204. package/clis/doubao-app/new.js +1 -0
  205. package/clis/doubao-app/read.js +1 -0
  206. package/clis/doubao-app/screenshot.js +1 -0
  207. package/clis/doubao-app/send.js +1 -0
  208. package/clis/doubao-app/status.js +1 -0
  209. package/clis/douyin/activities.js +1 -0
  210. package/clis/douyin/collections.js +1 -0
  211. package/clis/douyin/delete.js +1 -0
  212. package/clis/douyin/draft.js +1 -0
  213. package/clis/douyin/drafts.js +1 -0
  214. package/clis/douyin/hashtag.js +1 -0
  215. package/clis/douyin/location.js +1 -0
  216. package/clis/douyin/profile.js +1 -0
  217. package/clis/douyin/publish.js +1 -0
  218. package/clis/douyin/stats.js +1 -0
  219. package/clis/douyin/update.js +1 -0
  220. package/clis/douyin/user-videos.js +1 -0
  221. package/clis/douyin/videos.js +1 -0
  222. package/clis/eastmoney/announcement.js +1 -0
  223. package/clis/eastmoney/convertible.js +1 -0
  224. package/clis/eastmoney/etf.js +1 -0
  225. package/clis/eastmoney/holders.js +1 -0
  226. package/clis/eastmoney/hot-rank.js +1 -0
  227. package/clis/eastmoney/index-board.js +1 -0
  228. package/clis/eastmoney/kline.js +1 -0
  229. package/clis/eastmoney/kuaixun.js +1 -0
  230. package/clis/eastmoney/longhu.js +1 -0
  231. package/clis/eastmoney/money-flow.js +1 -0
  232. package/clis/eastmoney/northbound.js +1 -0
  233. package/clis/eastmoney/quote.js +1 -0
  234. package/clis/eastmoney/rank.js +1 -0
  235. package/clis/eastmoney/sectors.js +1 -0
  236. package/clis/facebook/add-friend.js +1 -0
  237. package/clis/facebook/events.js +1 -0
  238. package/clis/facebook/feed.js +1 -0
  239. package/clis/facebook/friends.js +1 -0
  240. package/clis/facebook/groups.js +1 -0
  241. package/clis/facebook/join-group.js +1 -0
  242. package/clis/facebook/marketplace-inbox.js +1 -0
  243. package/clis/facebook/marketplace-listings.js +1 -0
  244. package/clis/facebook/memories.js +1 -0
  245. package/clis/facebook/notifications.js +1 -0
  246. package/clis/facebook/profile.js +1 -0
  247. package/clis/facebook/search.js +1 -0
  248. package/clis/gemini/ask.js +1 -0
  249. package/clis/gemini/deep-research-result.js +1 -0
  250. package/clis/gemini/deep-research.js +1 -0
  251. package/clis/gemini/image.js +1 -0
  252. package/clis/gemini/new.js +1 -0
  253. package/clis/gitee/search.js +1 -0
  254. package/clis/gitee/trending.js +1 -0
  255. package/clis/gitee/user.js +1 -0
  256. package/clis/google/news.js +1 -0
  257. package/clis/google/search.js +1 -0
  258. package/clis/google/suggest.js +1 -0
  259. package/clis/google/trends.js +1 -0
  260. package/clis/google-scholar/cite.js +1 -0
  261. package/clis/google-scholar/profile.js +1 -0
  262. package/clis/google-scholar/search.js +1 -0
  263. package/clis/gov-law/recent.js +1 -0
  264. package/clis/gov-law/search.js +1 -0
  265. package/clis/gov-policy/recent.js +1 -0
  266. package/clis/gov-policy/search.js +1 -0
  267. package/clis/grok/ask.js +1 -0
  268. package/clis/grok/image.ts +1 -0
  269. package/clis/hackernews/ask.js +3 -1
  270. package/clis/hackernews/best.js +3 -1
  271. package/clis/hackernews/hackernews.test.js +132 -0
  272. package/clis/hackernews/jobs.js +3 -1
  273. package/clis/hackernews/new.js +3 -1
  274. package/clis/hackernews/read.js +188 -0
  275. package/clis/hackernews/search.js +3 -1
  276. package/clis/hackernews/show.js +3 -1
  277. package/clis/hackernews/top.js +3 -1
  278. package/clis/hackernews/user.js +1 -0
  279. package/clis/hf/top.js +1 -0
  280. package/clis/hupu/detail.js +1 -0
  281. package/clis/hupu/hot.js +3 -1
  282. package/clis/hupu/like.js +1 -0
  283. package/clis/hupu/mentions.js +2 -1
  284. package/clis/hupu/reply.js +1 -0
  285. package/clis/hupu/search.js +3 -1
  286. package/clis/hupu/unlike.js +1 -0
  287. package/clis/imdb/person.js +1 -0
  288. package/clis/imdb/reviews.js +1 -0
  289. package/clis/imdb/search.js +1 -0
  290. package/clis/imdb/title.js +1 -0
  291. package/clis/imdb/top.js +1 -0
  292. package/clis/imdb/trending.js +1 -0
  293. package/clis/indeed/indeed.test.js +375 -0
  294. package/clis/indeed/job.js +86 -0
  295. package/clis/indeed/search.js +110 -0
  296. package/clis/indeed/utils.js +152 -0
  297. package/clis/instagram/collection-create.js +1 -0
  298. package/clis/instagram/collection-delete.js +1 -0
  299. package/clis/instagram/comment.js +1 -0
  300. package/clis/instagram/download.js +1 -0
  301. package/clis/instagram/explore.js +1 -0
  302. package/clis/instagram/follow.js +1 -0
  303. package/clis/instagram/followers.js +1 -0
  304. package/clis/instagram/following.js +1 -0
  305. package/clis/instagram/like.js +1 -0
  306. package/clis/instagram/note.js +1 -0
  307. package/clis/instagram/post.js +1 -0
  308. package/clis/instagram/profile.js +1 -0
  309. package/clis/instagram/reel.js +1 -0
  310. package/clis/instagram/save.js +1 -0
  311. package/clis/instagram/saved.js +1 -0
  312. package/clis/instagram/search.js +1 -0
  313. package/clis/instagram/story.js +1 -0
  314. package/clis/instagram/unfollow.js +1 -0
  315. package/clis/instagram/unlike.js +1 -0
  316. package/clis/instagram/unsave.js +1 -0
  317. package/clis/instagram/user.js +1 -0
  318. package/clis/jd/add-cart.js +1 -0
  319. package/clis/jd/cart.js +1 -0
  320. package/clis/jd/detail.js +1 -0
  321. package/clis/jd/item.js +1 -0
  322. package/clis/jd/reviews.js +1 -0
  323. package/clis/jd/search.js +1 -0
  324. package/clis/jianyu/detail.js +1 -0
  325. package/clis/jianyu/search.js +1 -0
  326. package/clis/jike/comment.js +1 -0
  327. package/clis/jike/create.js +1 -0
  328. package/clis/jike/feed.js +3 -1
  329. package/clis/jike/like.js +1 -0
  330. package/clis/jike/notifications.js +1 -0
  331. package/clis/jike/post.js +1 -0
  332. package/clis/jike/repost.js +1 -0
  333. package/clis/jike/search.js +3 -1
  334. package/clis/jike/topic.js +1 -0
  335. package/clis/jike/user.js +3 -1
  336. package/clis/jimeng/generate.js +1 -0
  337. package/clis/jimeng/history.js +1 -0
  338. package/clis/jimeng/new.js +1 -0
  339. package/clis/jimeng/workspaces.js +1 -0
  340. package/clis/ke/chengjiao.js +1 -0
  341. package/clis/ke/ershoufang.js +1 -0
  342. package/clis/ke/xiaoqu.js +1 -0
  343. package/clis/ke/zufang.js +1 -0
  344. package/clis/lesswrong/comments.js +1 -0
  345. package/clis/lesswrong/curated.js +1 -0
  346. package/clis/lesswrong/frontpage.js +1 -0
  347. package/clis/lesswrong/new.js +1 -0
  348. package/clis/lesswrong/read.js +1 -0
  349. package/clis/lesswrong/sequences.js +1 -0
  350. package/clis/lesswrong/shortform.js +1 -0
  351. package/clis/lesswrong/tag.js +1 -0
  352. package/clis/lesswrong/tags.js +1 -0
  353. package/clis/lesswrong/top-month.js +1 -0
  354. package/clis/lesswrong/top-week.js +1 -0
  355. package/clis/lesswrong/top-year.js +1 -0
  356. package/clis/lesswrong/top.js +1 -0
  357. package/clis/lesswrong/user-posts.js +1 -0
  358. package/clis/lesswrong/user.js +1 -0
  359. package/clis/linkedin/search.js +1 -0
  360. package/clis/linkedin/timeline.js +1 -0
  361. package/clis/linux-do/categories.js +1 -0
  362. package/clis/linux-do/category.js +1 -0
  363. package/clis/linux-do/feed.js +1 -0
  364. package/clis/linux-do/hot.js +1 -0
  365. package/clis/linux-do/latest.js +1 -0
  366. package/clis/linux-do/search.js +1 -0
  367. package/clis/linux-do/tags.js +2 -1
  368. package/clis/linux-do/topic-content.js +1 -0
  369. package/clis/linux-do/topic.js +1 -0
  370. package/clis/linux-do/user-posts.js +1 -0
  371. package/clis/linux-do/user-topics.js +1 -0
  372. package/clis/lobsters/active.js +4 -1
  373. package/clis/lobsters/hot.js +4 -1
  374. package/clis/lobsters/lobsters.test.js +169 -0
  375. package/clis/lobsters/newest.js +4 -1
  376. package/clis/lobsters/read.js +196 -0
  377. package/clis/lobsters/tag.js +4 -1
  378. package/clis/maimai/search-talents.js +1 -0
  379. package/clis/medium/feed.js +1 -0
  380. package/clis/medium/search.js +1 -0
  381. package/clis/medium/user.js +1 -0
  382. package/clis/mubu/doc.js +1 -0
  383. package/clis/mubu/docs.js +1 -0
  384. package/clis/mubu/notes.js +1 -0
  385. package/clis/mubu/recent.js +1 -0
  386. package/clis/mubu/search.js +1 -0
  387. package/clis/notebooklm/current.js +1 -0
  388. package/clis/notebooklm/get.js +1 -0
  389. package/clis/notebooklm/history.js +1 -0
  390. package/clis/notebooklm/list.js +1 -0
  391. package/clis/notebooklm/note-list.js +1 -0
  392. package/clis/notebooklm/notes-get.js +1 -0
  393. package/clis/notebooklm/open.js +1 -0
  394. package/clis/notebooklm/source-fulltext.js +1 -0
  395. package/clis/notebooklm/source-get.js +1 -0
  396. package/clis/notebooklm/source-guide.js +1 -0
  397. package/clis/notebooklm/source-list.js +1 -0
  398. package/clis/notebooklm/status.js +1 -0
  399. package/clis/notebooklm/summary.js +1 -0
  400. package/clis/notion/export.js +1 -0
  401. package/clis/notion/favorites.js +1 -0
  402. package/clis/notion/new.js +1 -0
  403. package/clis/notion/read.js +1 -0
  404. package/clis/notion/search.js +1 -0
  405. package/clis/notion/sidebar.js +1 -0
  406. package/clis/notion/status.js +1 -0
  407. package/clis/notion/write.js +1 -0
  408. package/clis/nowcoder/companies.js +1 -0
  409. package/clis/nowcoder/creators.js +1 -0
  410. package/clis/nowcoder/detail.js +1 -0
  411. package/clis/nowcoder/experience.js +1 -0
  412. package/clis/nowcoder/hot.js +1 -0
  413. package/clis/nowcoder/jobs.js +1 -0
  414. package/clis/nowcoder/notifications.js +1 -0
  415. package/clis/nowcoder/papers.js +1 -0
  416. package/clis/nowcoder/practice.js +1 -0
  417. package/clis/nowcoder/recommend.js +1 -0
  418. package/clis/nowcoder/referral.js +1 -0
  419. package/clis/nowcoder/salary.js +1 -0
  420. package/clis/nowcoder/search.js +1 -0
  421. package/clis/nowcoder/suggest.js +1 -0
  422. package/clis/nowcoder/topics.js +1 -0
  423. package/clis/nowcoder/trending.js +1 -0
  424. package/clis/ones/login.js +1 -0
  425. package/clis/ones/logout.js +1 -0
  426. package/clis/ones/me.js +1 -0
  427. package/clis/ones/my-tasks.js +1 -0
  428. package/clis/ones/task.js +1 -0
  429. package/clis/ones/tasks.js +1 -0
  430. package/clis/ones/token-info.js +1 -0
  431. package/clis/ones/worklog.js +1 -0
  432. package/clis/openreview/openreview.test.js +345 -0
  433. package/clis/openreview/paper.js +43 -0
  434. package/clis/openreview/reviews.js +131 -0
  435. package/clis/openreview/search.js +46 -0
  436. package/clis/openreview/utils.js +158 -0
  437. package/clis/openreview/venue.js +63 -0
  438. package/clis/paperreview/feedback.js +1 -0
  439. package/clis/paperreview/review.js +1 -0
  440. package/clis/paperreview/submit.js +1 -0
  441. package/clis/pixiv/detail.js +1 -0
  442. package/clis/pixiv/download.js +1 -0
  443. package/clis/pixiv/illusts.js +2 -1
  444. package/clis/pixiv/ranking.js +2 -1
  445. package/clis/pixiv/search.js +2 -1
  446. package/clis/pixiv/user.js +2 -0
  447. package/clis/powerchina/search.js +1 -0
  448. package/clis/producthunt/browse.js +1 -0
  449. package/clis/producthunt/hot.js +1 -0
  450. package/clis/producthunt/posts.js +1 -0
  451. package/clis/producthunt/today.js +1 -0
  452. package/clis/quark/ls.js +1 -0
  453. package/clis/quark/mkdir.js +1 -0
  454. package/clis/quark/mv.js +1 -0
  455. package/clis/quark/rename.js +1 -0
  456. package/clis/quark/rm.js +1 -0
  457. package/clis/quark/save.js +1 -0
  458. package/clis/quark/share-tree.js +1 -0
  459. package/clis/reddit/comment.js +1 -0
  460. package/clis/reddit/frontpage.js +1 -0
  461. package/clis/reddit/hot.js +6 -1
  462. package/clis/reddit/hot.test.js +18 -0
  463. package/clis/reddit/popular.js +1 -0
  464. package/clis/reddit/read.js +1 -0
  465. package/clis/reddit/save.js +1 -0
  466. package/clis/reddit/saved.js +1 -0
  467. package/clis/reddit/search.js +1 -0
  468. package/clis/reddit/subreddit.js +1 -0
  469. package/clis/reddit/subscribe.js +1 -0
  470. package/clis/reddit/upvote.js +1 -0
  471. package/clis/reddit/upvoted.js +1 -0
  472. package/clis/reddit/user-comments.js +1 -0
  473. package/clis/reddit/user-posts.js +1 -0
  474. package/clis/reddit/user.js +1 -0
  475. package/clis/reuters/search.js +1 -0
  476. package/clis/sinablog/article.js +1 -0
  477. package/clis/sinablog/hot.js +1 -0
  478. package/clis/sinablog/search.js +1 -0
  479. package/clis/sinablog/user.js +1 -0
  480. package/clis/sinafinance/news.js +1 -0
  481. package/clis/sinafinance/rolling-news.js +1 -0
  482. package/clis/sinafinance/stock-rank.js +1 -0
  483. package/clis/sinafinance/stock.js +1 -0
  484. package/clis/smzdm/search.js +1 -0
  485. package/clis/spotify/spotify.js +11 -0
  486. package/clis/stackoverflow/bounties.js +11 -3
  487. package/clis/stackoverflow/hot.js +10 -2
  488. package/clis/stackoverflow/read.js +314 -0
  489. package/clis/stackoverflow/search.js +10 -2
  490. package/clis/stackoverflow/stackoverflow.test.js +346 -0
  491. package/clis/stackoverflow/unanswered.js +9 -2
  492. package/clis/steam/top-sellers.js +1 -0
  493. package/clis/substack/feed.js +1 -0
  494. package/clis/substack/publication.js +1 -0
  495. package/clis/substack/search.js +1 -0
  496. package/clis/taobao/add-cart.js +1 -0
  497. package/clis/taobao/cart.js +1 -0
  498. package/clis/taobao/detail.js +1 -0
  499. package/clis/taobao/reviews.js +1 -0
  500. package/clis/taobao/search.js +1 -0
  501. package/clis/tdx/hot-rank.js +1 -0
  502. package/clis/ths/hot-rank.js +1 -0
  503. package/clis/tieba/hot.js +2 -1
  504. package/clis/tieba/posts.js +1 -0
  505. package/clis/tieba/read.js +1 -0
  506. package/clis/tieba/search.js +2 -1
  507. package/clis/tiktok/comment.js +1 -0
  508. package/clis/tiktok/explore.js +1 -0
  509. package/clis/tiktok/follow.js +1 -0
  510. package/clis/tiktok/following.js +1 -0
  511. package/clis/tiktok/friends.js +1 -0
  512. package/clis/tiktok/like.js +1 -0
  513. package/clis/tiktok/live.js +1 -0
  514. package/clis/tiktok/notifications.js +1 -0
  515. package/clis/tiktok/profile.js +1 -0
  516. package/clis/tiktok/save.js +1 -0
  517. package/clis/tiktok/search.js +1 -0
  518. package/clis/tiktok/unfollow.js +1 -0
  519. package/clis/tiktok/unlike.js +1 -0
  520. package/clis/tiktok/unsave.js +1 -0
  521. package/clis/tiktok/user.js +1 -0
  522. package/clis/toutiao/articles.js +1 -0
  523. package/clis/twitter/accept.js +1 -0
  524. package/clis/twitter/article.js +1 -0
  525. package/clis/twitter/block.js +1 -0
  526. package/clis/twitter/bookmark.js +1 -0
  527. package/clis/twitter/bookmarks.js +2 -1
  528. package/clis/twitter/delete.js +1 -0
  529. package/clis/twitter/download.js +1 -0
  530. package/clis/twitter/follow.js +1 -0
  531. package/clis/twitter/followers.js +1 -0
  532. package/clis/twitter/following.js +1 -0
  533. package/clis/twitter/hide-reply.js +1 -0
  534. package/clis/twitter/like.js +1 -0
  535. package/clis/twitter/likes.js +2 -1
  536. package/clis/twitter/list-add.js +1 -0
  537. package/clis/twitter/list-remove.js +1 -0
  538. package/clis/twitter/list-tweets.js +1 -0
  539. package/clis/twitter/lists.js +1 -0
  540. package/clis/twitter/notifications.js +1 -0
  541. package/clis/twitter/post.js +1 -0
  542. package/clis/twitter/profile.js +1 -0
  543. package/clis/twitter/reply-dm.js +1 -0
  544. package/clis/twitter/reply.js +1 -0
  545. package/clis/twitter/search.js +1 -0
  546. package/clis/twitter/thread.js +1 -0
  547. package/clis/twitter/timeline.js +1 -0
  548. package/clis/twitter/trending.js +11 -12
  549. package/clis/twitter/trending.test.js +15 -0
  550. package/clis/twitter/tweets.js +2 -1
  551. package/clis/twitter/tweets.test.js +2 -2
  552. package/clis/twitter/unblock.js +1 -0
  553. package/clis/twitter/unbookmark.js +1 -0
  554. package/clis/twitter/unfollow.js +1 -0
  555. package/clis/uiverse/code.js +1 -0
  556. package/clis/uiverse/preview.js +1 -0
  557. package/clis/v2ex/daily.js +1 -0
  558. package/clis/v2ex/hot.js +1 -0
  559. package/clis/v2ex/latest.js +1 -0
  560. package/clis/v2ex/me.js +1 -0
  561. package/clis/v2ex/member.js +1 -0
  562. package/clis/v2ex/node.js +1 -0
  563. package/clis/v2ex/nodes.js +1 -0
  564. package/clis/v2ex/notifications.js +1 -0
  565. package/clis/v2ex/replies.js +1 -0
  566. package/clis/v2ex/topic.js +1 -0
  567. package/clis/v2ex/user.js +1 -0
  568. package/clis/wanfang/search.js +1 -0
  569. package/clis/web/read.js +1 -0
  570. package/clis/weibo/comments.js +1 -0
  571. package/clis/weibo/favorites.js +1 -0
  572. package/clis/weibo/feed.js +3 -1
  573. package/clis/weibo/hot.js +1 -0
  574. package/clis/weibo/me.js +1 -0
  575. package/clis/weibo/post.js +1 -0
  576. package/clis/weibo/publish.js +1 -0
  577. package/clis/weibo/search.js +8 -2
  578. package/clis/weibo/user.js +1 -0
  579. package/clis/weixin/create-draft.js +1 -0
  580. package/clis/weixin/download.js +1 -0
  581. package/clis/weixin/drafts.js +1 -0
  582. package/clis/weread/ai-outline.js +1 -0
  583. package/clis/weread/book.js +1 -0
  584. package/clis/weread/highlights.js +1 -0
  585. package/clis/weread/notebooks.js +1 -0
  586. package/clis/weread/notes.js +1 -0
  587. package/clis/weread/ranking.js +1 -0
  588. package/clis/weread/search.js +1 -0
  589. package/clis/weread/shelf.js +1 -0
  590. package/clis/wikipedia/random.js +1 -0
  591. package/clis/wikipedia/search.js +1 -0
  592. package/clis/wikipedia/summary.js +1 -0
  593. package/clis/wikipedia/trending.js +1 -0
  594. package/clis/xianyu/chat.js +1 -0
  595. package/clis/xianyu/item.js +1 -0
  596. package/clis/xianyu/search.js +1 -0
  597. package/clis/xiaoe/catalog.js +2 -1
  598. package/clis/xiaoe/content.js +1 -0
  599. package/clis/xiaoe/courses.js +1 -0
  600. package/clis/xiaoe/detail.js +1 -0
  601. package/clis/xiaoe/play-url.js +1 -0
  602. package/clis/xiaohongshu/comments.js +1 -0
  603. package/clis/xiaohongshu/creator-note-detail.js +1 -0
  604. package/clis/xiaohongshu/creator-notes-summary.js +1 -0
  605. package/clis/xiaohongshu/creator-notes.js +1 -0
  606. package/clis/xiaohongshu/creator-profile.js +1 -0
  607. package/clis/xiaohongshu/creator-stats.js +1 -0
  608. package/clis/xiaohongshu/download.js +1 -0
  609. package/clis/xiaohongshu/feed.js +2 -1
  610. package/clis/xiaohongshu/note.js +1 -0
  611. package/clis/xiaohongshu/notifications.js +1 -0
  612. package/clis/xiaohongshu/publish.js +1 -0
  613. package/clis/xiaohongshu/search.js +1 -0
  614. package/clis/xiaohongshu/user.js +1 -0
  615. package/clis/xiaoyuzhou/download.js +1 -0
  616. package/clis/xiaoyuzhou/episode.js +1 -0
  617. package/clis/xiaoyuzhou/podcast-episodes.js +1 -0
  618. package/clis/xiaoyuzhou/podcast.js +1 -0
  619. package/clis/xiaoyuzhou/transcript.js +1 -0
  620. package/clis/xueqiu/comments.js +1 -0
  621. package/clis/xueqiu/earnings-date.js +1 -0
  622. package/clis/xueqiu/feed.js +1 -0
  623. package/clis/xueqiu/fund-holdings.js +1 -0
  624. package/clis/xueqiu/fund-snapshot.js +1 -0
  625. package/clis/xueqiu/groups.js +1 -0
  626. package/clis/xueqiu/hot-stock.js +1 -0
  627. package/clis/xueqiu/hot.js +1 -0
  628. package/clis/xueqiu/kline.js +1 -0
  629. package/clis/xueqiu/search.js +1 -0
  630. package/clis/xueqiu/stock.js +1 -0
  631. package/clis/xueqiu/watchlist.js +1 -0
  632. package/clis/yahoo-finance/quote.js +1 -0
  633. package/clis/yollomi/background.js +1 -0
  634. package/clis/yollomi/edit.js +1 -0
  635. package/clis/yollomi/face-swap.js +1 -0
  636. package/clis/yollomi/generate.js +1 -0
  637. package/clis/yollomi/models.js +1 -0
  638. package/clis/yollomi/object-remover.js +1 -0
  639. package/clis/yollomi/remove-bg.js +1 -0
  640. package/clis/yollomi/restore.js +1 -0
  641. package/clis/yollomi/try-on.js +1 -0
  642. package/clis/yollomi/upload.js +1 -0
  643. package/clis/yollomi/upscale.js +1 -0
  644. package/clis/yollomi/video.js +1 -0
  645. package/clis/youtube/channel.js +1 -0
  646. package/clis/youtube/comments.js +1 -0
  647. package/clis/youtube/feed.js +8 -7
  648. package/clis/youtube/feed.test.js +131 -0
  649. package/clis/youtube/history.js +1 -0
  650. package/clis/youtube/like.js +1 -0
  651. package/clis/youtube/playlist.js +1 -0
  652. package/clis/youtube/search.js +1 -0
  653. package/clis/youtube/subscribe.js +1 -0
  654. package/clis/youtube/subscriptions.js +1 -0
  655. package/clis/youtube/transcript.js +1 -0
  656. package/clis/youtube/unlike.js +1 -0
  657. package/clis/youtube/unsubscribe.js +1 -0
  658. package/clis/youtube/video.js +1 -0
  659. package/clis/youtube/watch-later.js +1 -0
  660. package/clis/yuanbao/ask.js +1 -0
  661. package/clis/yuanbao/new.js +1 -0
  662. package/clis/zhihu/answer.js +1 -0
  663. package/clis/zhihu/collection.js +1 -0
  664. package/clis/zhihu/collections.js +1 -0
  665. package/clis/zhihu/comment.js +1 -0
  666. package/clis/zhihu/download.js +1 -0
  667. package/clis/zhihu/favorite.js +1 -0
  668. package/clis/zhihu/follow.js +1 -0
  669. package/clis/zhihu/hot.js +1 -0
  670. package/clis/zhihu/like.js +1 -0
  671. package/clis/zhihu/question.js +1 -0
  672. package/clis/zhihu/search.js +1 -0
  673. package/clis/zlibrary/info.js +1 -0
  674. package/clis/zlibrary/search.js +1 -0
  675. package/clis/zsxq/dynamics.js +1 -0
  676. package/clis/zsxq/groups.js +1 -0
  677. package/clis/zsxq/search.js +1 -0
  678. package/clis/zsxq/topic.js +1 -0
  679. package/clis/zsxq/topics.js +1 -0
  680. package/dist/src/adapter-source.test.js +1 -1
  681. package/dist/src/browser/analyze.test.js +2 -0
  682. package/dist/src/browser/base-page.d.ts +9 -0
  683. package/dist/src/browser/base-page.js +72 -3
  684. package/dist/src/browser/base-page.test.js +42 -0
  685. package/dist/src/browser/cdp.js +6 -0
  686. package/dist/src/browser/cdp.test.js +3 -0
  687. package/dist/src/browser/page.d.ts +1 -0
  688. package/dist/src/browser/page.js +6 -0
  689. package/dist/src/browser/page.test.js +17 -0
  690. package/dist/src/browser/target-resolver.d.ts +10 -3
  691. package/dist/src/browser/target-resolver.js +16 -10
  692. package/dist/src/browser/verify-fixture.d.ts +6 -1
  693. package/dist/src/browser/verify-fixture.js +87 -0
  694. package/dist/src/browser/verify-fixture.test.js +44 -1
  695. package/dist/src/build-manifest.js +3 -0
  696. package/dist/src/build-manifest.test.js +18 -12
  697. package/dist/src/capabilityRouting.test.js +1 -1
  698. package/dist/src/cli.js +141 -5
  699. package/dist/src/cli.test.js +179 -0
  700. package/dist/src/commanderAdapter.d.ts +1 -1
  701. package/dist/src/commanderAdapter.js +17 -7
  702. package/dist/src/commanderAdapter.test.js +7 -6
  703. package/dist/src/convention-audit.d.ts +50 -0
  704. package/dist/src/convention-audit.js +546 -0
  705. package/dist/src/convention-audit.test.d.ts +1 -0
  706. package/dist/src/convention-audit.test.js +226 -0
  707. package/dist/src/discovery.js +2 -0
  708. package/dist/src/doctor.js +0 -1
  709. package/dist/src/doctor.test.js +1 -1
  710. package/dist/src/engine.test.js +10 -10
  711. package/dist/src/execution.js +1 -1
  712. package/dist/src/execution.test.js +9 -9
  713. package/dist/src/help.d.ts +15 -0
  714. package/dist/src/help.js +149 -0
  715. package/dist/src/main.js +13 -7
  716. package/dist/src/manifest-types.d.ts +2 -0
  717. package/dist/src/plugin.test.js +26 -26
  718. package/dist/src/registry.d.ts +5 -0
  719. package/dist/src/registry.js +8 -0
  720. package/dist/src/registry.test.js +27 -20
  721. package/dist/src/serialization.d.ts +4 -0
  722. package/dist/src/serialization.js +27 -1
  723. package/dist/src/serialization.test.js +24 -2
  724. package/dist/src/types.d.ts +2 -0
  725. package/package.json +4 -1
  726. package/scripts/check-listing-id-pairing.mjs +193 -0
  727. package/scripts/check-silent-column-drop.mjs +105 -0
  728. package/scripts/check-typed-error-lint.mjs +118 -0
  729. package/scripts/silent-column-drop-baseline.json +962 -0
  730. package/scripts/typed-error-lint-baseline.json +1586 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Indeed keyword search.
3
+ *
4
+ * Drives the public `/jobs?q=&l=` listing page through a real browser
5
+ * because Cloudflare returns 403 to bare fetches. Extraction happens
6
+ * inside `page.evaluate` against the rendered DOM (Indeed's JSON-island
7
+ * is heavily obfuscated and changes shape per A/B bucket).
8
+ */
9
+
10
+ import { cli, Strategy } from '@jackwener/opencli/registry';
11
+ import { EmptyResultError, CommandExecutionError } from '@jackwener/opencli/errors';
12
+ import {
13
+ SEARCH_COLUMNS,
14
+ requireQuery,
15
+ requireBoundedInt,
16
+ requireNonNegativeInt,
17
+ requireFromage,
18
+ requireSort,
19
+ buildSearchUrl,
20
+ searchCardToRow,
21
+ } from './utils.js';
22
+
23
+ cli({
24
+ site: 'indeed',
25
+ name: 'search',
26
+ access: 'read',
27
+ description: 'Indeed keyword job search (rendered DOM via browser session, US site)',
28
+ domain: 'www.indeed.com',
29
+ strategy: Strategy.COOKIE,
30
+ browser: true,
31
+ navigateBefore: false,
32
+ args: [
33
+ { name: 'query', positional: true, required: true, help: 'Job keyword (title / skill / company)' },
34
+ { name: 'location', type: 'string', default: '', help: 'Location filter (e.g. "remote", "New York, NY", "San Francisco")' },
35
+ { name: 'fromage', type: 'string', default: '', help: 'Recency filter, days back: 1 / 3 / 7 / 14' },
36
+ { name: 'sort', type: 'string', default: 'relevance', help: 'Sort order: relevance | date' },
37
+ { name: 'start', type: 'int', default: 0, help: 'Pagination offset (multiple of 10, 0-based)' },
38
+ { name: 'limit', type: 'int', default: 15, help: 'Max rows to return (1-25, capped at one page)' },
39
+ ],
40
+ columns: SEARCH_COLUMNS,
41
+ func: async (page, kwargs) => {
42
+ const query = requireQuery(kwargs.query);
43
+ const location = String(kwargs.location ?? '').trim();
44
+ const fromage = requireFromage(kwargs.fromage);
45
+ const sort = requireSort(kwargs.sort);
46
+ const start = requireNonNegativeInt(kwargs.start, 0, 'start');
47
+ const limit = requireBoundedInt(kwargs.limit, 15, 25, 'limit');
48
+
49
+ const url = buildSearchUrl({ query, location, fromage, sort, start });
50
+ await page.goto(url);
51
+ await page.wait(4);
52
+
53
+ let cards;
54
+ try {
55
+ cards = await page.evaluate(`(async () => {
56
+ const hasResults = () => !!document.querySelector('.job_seen_beacon');
57
+ const hasEmptyState = () => {
58
+ const text = document.body?.innerText || '';
59
+ return !!document.querySelector('[data-testid="searchCountPages"], [data-testid="searchCount"], [data-testid="noResultsMessage"], [data-testid="empty-serp-result"]')
60
+ || /did not match any jobs|no jobs found|0 jobs/i.test(text);
61
+ };
62
+ let ready = hasResults() || hasEmptyState();
63
+ for (let i = 0; i < 30; i++) {
64
+ if (ready) break;
65
+ await new Promise(r => setTimeout(r, 500));
66
+ ready = hasResults() || hasEmptyState();
67
+ }
68
+ const blocks = document.querySelectorAll('.job_seen_beacon');
69
+ const seen = new Set();
70
+ const out = [];
71
+ for (const b of blocks) {
72
+ const titleA = b.querySelector('h2.jobTitle a, [class*="jcs-JobTitle"]');
73
+ const jk = titleA?.getAttribute('data-jk');
74
+ if (!jk || seen.has(jk)) continue;
75
+ seen.add(jk);
76
+ const tags = Array.from(b.querySelectorAll('.metadataContainer li span'))
77
+ .map(s => (s.textContent || '').trim())
78
+ .filter(Boolean);
79
+ out.push({
80
+ jk,
81
+ title: b.querySelector('h2.jobTitle span')?.textContent?.trim() ?? '',
82
+ company: b.querySelector('[data-testid="company-name"]')?.textContent?.trim() ?? '',
83
+ location: b.querySelector('[data-testid="text-location"]')?.textContent?.trim() ?? '',
84
+ salary: b.querySelector('.salary-snippet-container span')?.textContent?.trim() ?? '',
85
+ tags,
86
+ });
87
+ }
88
+ const blockedHeadline = document.title || '';
89
+ const challenge = blockedHeadline.includes('Just a moment') || !!document.querySelector('[id^="cf-"]');
90
+ return { cards: out, challenge, ready };
91
+ })()`);
92
+ }
93
+ catch (e) {
94
+ throw new CommandExecutionError(`Failed to scrape Indeed search DOM: ${e?.message ?? e}`, 'The page may not have fully loaded; try again.');
95
+ }
96
+
97
+ if (cards?.challenge) {
98
+ throw new CommandExecutionError('Indeed served a Cloudflare challenge page', 'Open https://www.indeed.com in the connected browser and clear the challenge, then retry.');
99
+ }
100
+ if (!cards?.ready) {
101
+ throw new CommandExecutionError('Indeed search page did not expose result or empty-state markers within 15s', 'Indeed may still be loading or the DOM shape may have changed; retry after opening Indeed in the connected browser.');
102
+ }
103
+
104
+ const list = Array.isArray(cards?.cards) ? cards.cards : [];
105
+ if (list.length === 0) {
106
+ throw new EmptyResultError('indeed search', `No Indeed jobs matched "${query}"${location ? ` in ${location}` : ''}`);
107
+ }
108
+ return list.slice(0, limit).map((c, i) => searchCardToRow(c, start + i + 1));
109
+ },
110
+ });
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Indeed adapter utilities.
3
+ *
4
+ * Indeed sits behind Cloudflare and answers bare fetches with HTTP 403
5
+ * (`cf-mitigated: challenge`). The whole adapter therefore runs through a
6
+ * real browser session (Strategy.COOKIE), and DOM extraction lives inside
7
+ * `page.evaluate` blocks. The helpers below are mostly arg-validation +
8
+ * pure normalizers so they stay unit-testable without a browser.
9
+ */
10
+
11
+ import { ArgumentError } from '@jackwener/opencli/errors';
12
+
13
+ export const INDEED_ORIGIN = 'https://www.indeed.com';
14
+
15
+ /** Job key (jk) shape — 16-char lowercase hex. */
16
+ const JK_PATTERN = /^[a-f0-9]{16}$/;
17
+
18
+ const FROMAGE_VALUES = new Set(['1', '3', '7', '14']);
19
+
20
+ const SORT_VALUES = new Set(['relevance', 'date']);
21
+
22
+ /**
23
+ * Coerce a value to a strict integer. Accepts numeric strings, rejects
24
+ * floats / non-numeric / NaN. Returns NaN on invalid input so callers can
25
+ * decide on the right typed error.
26
+ */
27
+ export function coerceInt(value) {
28
+ if (value === undefined || value === null || value === '') return NaN;
29
+ const n = typeof value === 'number' ? value : Number(value);
30
+ return Number.isFinite(n) && Number.isInteger(n) ? n : NaN;
31
+ }
32
+
33
+ export function requireBoundedInt(value, defaultValue, maxValue, label) {
34
+ const raw = value ?? defaultValue;
35
+ const n = coerceInt(raw);
36
+ if (!Number.isInteger(n) || n <= 0) {
37
+ throw new ArgumentError(`indeed ${label} must be a positive integer`);
38
+ }
39
+ if (n > maxValue) {
40
+ throw new ArgumentError(`indeed ${label} must be <= ${maxValue}`);
41
+ }
42
+ return n;
43
+ }
44
+
45
+ export function requireNonNegativeInt(value, defaultValue, label) {
46
+ const raw = value ?? defaultValue;
47
+ const n = coerceInt(raw);
48
+ if (!Number.isInteger(n) || n < 0) {
49
+ throw new ArgumentError(`indeed ${label} must be a non-negative integer`);
50
+ }
51
+ return n;
52
+ }
53
+
54
+ export function requireJobKey(value) {
55
+ const id = String(value ?? '').trim().toLowerCase();
56
+ if (!id) {
57
+ throw new ArgumentError('indeed job id is required');
58
+ }
59
+ if (!JK_PATTERN.test(id)) {
60
+ throw new ArgumentError(`indeed job id "${value}" is not a valid jk (expected 16-char lowercase hex)`);
61
+ }
62
+ return id;
63
+ }
64
+
65
+ export function requireQuery(value, label = 'query') {
66
+ const q = String(value ?? '').trim();
67
+ if (!q) {
68
+ throw new ArgumentError(`indeed ${label} cannot be empty`);
69
+ }
70
+ return q;
71
+ }
72
+
73
+ /** "1" / "3" / "7" / "14" — accepted by Indeed's `fromage` filter. */
74
+ export function requireFromage(value) {
75
+ if (value === undefined || value === null || value === '') return '';
76
+ const v = String(value).trim();
77
+ if (!FROMAGE_VALUES.has(v)) {
78
+ throw new ArgumentError(`indeed fromage must be one of 1/3/7/14 (days), got "${value}"`);
79
+ }
80
+ return v;
81
+ }
82
+
83
+ export function requireSort(value, defaultValue = 'relevance') {
84
+ const v = String(value ?? defaultValue).trim().toLowerCase();
85
+ if (!SORT_VALUES.has(v)) {
86
+ throw new ArgumentError(`indeed sort must be "relevance" or "date", got "${value}"`);
87
+ }
88
+ return v;
89
+ }
90
+
91
+ /**
92
+ * Build an Indeed search URL with only the user-supplied filters set.
93
+ * Indeed treats absent params as defaults; we never pass empty strings
94
+ * because the page will echo them back into the query and the URL
95
+ * stays cleaner for round-tripping.
96
+ */
97
+ export function buildSearchUrl({ query, location, fromage, sort, start }) {
98
+ const params = new URLSearchParams();
99
+ params.set('q', query);
100
+ if (location) params.set('l', location);
101
+ if (fromage) params.set('fromage', fromage);
102
+ if (sort && sort !== 'relevance') params.set('sort', sort);
103
+ if (start && start > 0) params.set('start', String(start));
104
+ return `${INDEED_ORIGIN}/jobs?${params.toString()}`;
105
+ }
106
+
107
+ export function buildJobUrl(jk) {
108
+ return `${INDEED_ORIGIN}/viewjob?jk=${jk}`;
109
+ }
110
+
111
+ /** Strip a salary-snippet duplicate out of the metadata pill list. */
112
+ export function dedupeTags(tags, salary) {
113
+ const out = [];
114
+ for (const t of tags) {
115
+ const trimmed = String(t || '').trim();
116
+ if (!trimmed) continue;
117
+ if (salary && trimmed === salary) continue;
118
+ if (out.includes(trimmed)) continue;
119
+ out.push(trimmed);
120
+ }
121
+ return out.join(' · ');
122
+ }
123
+
124
+ /**
125
+ * Normalize a parsed search-card object into the row shape declared by
126
+ * `SEARCH_COLUMNS`. Drops nulls into empty strings, defends against
127
+ * Indeed surface drift by treating missing fields as empty rather than
128
+ * silently mislabeling them.
129
+ */
130
+ export function searchCardToRow(card, rank) {
131
+ const jk = String(card?.jk ?? '').trim();
132
+ const salary = String(card?.salary ?? '').trim();
133
+ const tags = Array.isArray(card?.tags) ? card.tags : [];
134
+ return {
135
+ rank,
136
+ id: jk,
137
+ title: String(card?.title ?? '').replace(/\s+/g, ' ').trim(),
138
+ company: String(card?.company ?? '').replace(/\s+/g, ' ').trim(),
139
+ location: String(card?.location ?? '').replace(/\s+/g, ' ').trim(),
140
+ salary,
141
+ tags: dedupeTags(tags, salary),
142
+ url: jk ? buildJobUrl(jk) : '',
143
+ };
144
+ }
145
+
146
+ export const SEARCH_COLUMNS = [
147
+ 'rank', 'id', 'title', 'company', 'location', 'salary', 'tags', 'url',
148
+ ];
149
+
150
+ export const JOB_COLUMNS = [
151
+ 'id', 'title', 'company', 'location', 'salary', 'job_type', 'description', 'url',
152
+ ];
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'collection-create',
5
+ access: 'write',
5
6
  description: 'Create a new Instagram saved-posts collection (folder)',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'collection-delete',
5
+ access: 'write',
5
6
  description: 'Delete an Instagram saved-posts collection (folder) by name or id',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'comment',
5
+ access: 'write',
5
6
  description: 'Comment on an Instagram post',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -196,6 +196,7 @@ async function downloadInstagramMedia(items, outputDir) {
196
196
  cli({
197
197
  site: 'instagram',
198
198
  name: 'download',
199
+ access: 'read',
199
200
  description: 'Download images and videos from Instagram posts and reels',
200
201
  domain: 'www.instagram.com',
201
202
  strategy: Strategy.COOKIE,
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'explore',
5
+ access: 'read',
5
6
  description: 'Instagram explore/discover trending posts',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'follow',
5
+ access: 'write',
5
6
  description: 'Follow an Instagram user',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'followers',
5
+ access: 'read',
5
6
  description: 'List followers of an Instagram user',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'following',
5
+ access: 'read',
5
6
  description: 'List accounts an Instagram user is following',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'like',
5
+ access: 'write',
5
6
  description: 'Like an Instagram post',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -198,6 +198,7 @@ function buildPublishInstagramNoteJs(content) {
198
198
  cli({
199
199
  site: 'instagram',
200
200
  name: 'note',
201
+ access: 'write',
201
202
  description: 'Publish a text Instagram note',
202
203
  domain: 'www.instagram.com',
203
204
  strategy: Strategy.UI,
@@ -1397,6 +1397,7 @@ async function resolveLatestPostUrl(page, existingPostPaths) {
1397
1397
  cli({
1398
1398
  site: 'instagram',
1399
1399
  name: 'post',
1400
+ access: 'write',
1400
1401
  description: 'Post an Instagram feed image or mixed-media carousel',
1401
1402
  domain: 'www.instagram.com',
1402
1403
  strategy: Strategy.UI,
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'profile',
5
+ access: 'read',
5
6
  description: 'Get Instagram user profile info',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -732,6 +732,7 @@ async function resolveLatestReelUrl(page, existingPaths) {
732
732
  cli({
733
733
  site: 'instagram',
734
734
  name: 'reel',
735
+ access: 'write',
735
736
  description: 'Post an Instagram reel video',
736
737
  domain: 'www.instagram.com',
737
738
  strategy: Strategy.UI,
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'save',
5
+ access: 'write',
5
6
  description: 'Save (bookmark) an Instagram post',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'saved',
5
+ access: 'read',
5
6
  description: 'Get your saved Instagram posts (optionally from a specific collection)',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'search',
5
+ access: 'read',
5
6
  description: 'Search Instagram users',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -82,6 +82,7 @@ function buildStorySuccessResult(mediaItem, url) {
82
82
  cli({
83
83
  site: 'instagram',
84
84
  name: 'story',
85
+ access: 'write',
85
86
  description: 'Post a single Instagram story image or video',
86
87
  domain: 'www.instagram.com',
87
88
  strategy: Strategy.UI,
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'unfollow',
5
+ access: 'write',
5
6
  description: 'Unfollow an Instagram user',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'unlike',
5
+ access: 'write',
5
6
  description: 'Unlike an Instagram post',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'unsave',
5
+ access: 'write',
5
6
  description: 'Unsave (remove bookmark) an Instagram post',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'instagram',
4
4
  name: 'user',
5
+ access: 'read',
5
6
  description: 'Get recent posts from an Instagram user',
6
7
  domain: 'www.instagram.com',
7
8
  args: [
@@ -4,6 +4,7 @@ import { clampInt, normalizeNumericId } from '../_shared/common.js';
4
4
  cli({
5
5
  site: 'jd',
6
6
  name: 'add-cart',
7
+ access: 'write',
7
8
  description: '京东加入购物车',
8
9
  domain: 'item.jd.com',
9
10
  strategy: Strategy.COOKIE,
package/clis/jd/cart.js CHANGED
@@ -3,6 +3,7 @@ import { cli, Strategy } from '@jackwener/opencli/registry';
3
3
  cli({
4
4
  site: 'jd',
5
5
  name: 'cart',
6
+ access: 'read',
6
7
  description: '查看京东购物车',
7
8
  domain: 'cart.jd.com',
8
9
  strategy: Strategy.COOKIE,
package/clis/jd/detail.js CHANGED
@@ -3,6 +3,7 @@ import { normalizeNumericId } from '../_shared/common.js';
3
3
  cli({
4
4
  site: 'jd',
5
5
  name: 'detail',
6
+ access: 'read',
6
7
  description: '京东商品详情',
7
8
  domain: 'item.jd.com',
8
9
  strategy: Strategy.COOKIE,
package/clis/jd/item.js CHANGED
@@ -543,6 +543,7 @@ function detectJdPageState(expectedSku) {
543
543
  cli({
544
544
  site: 'jd',
545
545
  name: 'item',
546
+ access: 'read',
546
547
  description: '京东商品详情(价格、店铺、规格参数、主图、详情图)',
547
548
  domain: 'item.jd.com',
548
549
  strategy: Strategy.COOKIE,
@@ -3,6 +3,7 @@ import { clampInt, normalizeNumericId } from '../_shared/common.js';
3
3
  cli({
4
4
  site: 'jd',
5
5
  name: 'reviews',
6
+ access: 'read',
6
7
  description: '京东商品评价',
7
8
  domain: 'item.jd.com',
8
9
  strategy: Strategy.COOKIE,
package/clis/jd/search.js CHANGED
@@ -3,6 +3,7 @@ import { clampInt, requireNonEmptyQuery } from '../_shared/common.js';
3
3
  cli({
4
4
  site: 'jd',
5
5
  name: 'search',
6
+ access: 'read',
6
7
  description: '京东商品搜索',
7
8
  domain: 'search.jd.com',
8
9
  strategy: Strategy.COOKIE,
@@ -3,6 +3,7 @@ import { runProcurementDetail } from './shared/procurement-detail.js';
3
3
  cli({
4
4
  site: 'jianyu',
5
5
  name: 'detail',
6
+ access: 'read',
6
7
  description: '读取剑鱼标讯详情页并抽取证据字段',
7
8
  domain: 'www.jianyu360.cn',
8
9
  strategy: Strategy.COOKIE,
@@ -526,6 +526,7 @@ function collectApiRowsFromResponses(responses) {
526
526
  cli({
527
527
  site: SITE,
528
528
  name: 'search',
529
+ access: 'read',
529
530
  description: '搜索剑鱼标讯公告',
530
531
  domain: DOMAIN,
531
532
  strategy: Strategy.COOKIE,
@@ -8,6 +8,7 @@ import { cli, Strategy } from '@jackwener/opencli/registry';
8
8
  cli({
9
9
  site: 'jike',
10
10
  name: 'comment',
11
+ access: 'write',
11
12
  description: '评论即刻帖子',
12
13
  domain: 'web.okjike.com',
13
14
  strategy: Strategy.UI,
@@ -8,6 +8,7 @@ import { cli, Strategy } from '@jackwener/opencli/registry';
8
8
  cli({
9
9
  site: 'jike',
10
10
  name: 'create',
11
+ access: 'write',
11
12
  description: '发布即刻动态',
12
13
  domain: 'web.okjike.com',
13
14
  strategy: Strategy.UI,
package/clis/jike/feed.js CHANGED
@@ -9,6 +9,7 @@ import { getPostDataJs } from './utils.js';
9
9
  cli({
10
10
  site: 'jike',
11
11
  name: 'feed',
12
+ access: 'read',
12
13
  description: '即刻首页动态流',
13
14
  domain: 'web.okjike.com',
14
15
  strategy: Strategy.COOKIE,
@@ -16,7 +17,7 @@ cli({
16
17
  args: [
17
18
  { name: 'limit', type: 'int', default: 20 },
18
19
  ],
19
- columns: ['author', 'content', 'likes', 'comments', 'time', 'url'],
20
+ columns: ['id', 'author', 'content', 'likes', 'comments', 'time', 'url'],
20
21
  func: async (page, kwargs) => {
21
22
  const limit = kwargs.limit || 20;
22
23
  // 1. 导航到即刻首页,等待 SPA 重定向到 /following
@@ -43,6 +44,7 @@ cli({
43
44
  if (!author && !content) continue;
44
45
 
45
46
  results.push({
47
+ id: data.id,
46
48
  author,
47
49
  content: content.replace(/\\n/g, ' ').slice(0, 120),
48
50
  likes: data.likeCount || 0,
package/clis/jike/like.js CHANGED
@@ -8,6 +8,7 @@ import { cli, Strategy } from '@jackwener/opencli/registry';
8
8
  cli({
9
9
  site: 'jike',
10
10
  name: 'like',
11
+ access: 'write',
11
12
  description: '点赞即刻帖子',
12
13
  domain: 'web.okjike.com',
13
14
  strategy: Strategy.UI,
@@ -21,6 +21,7 @@ function resolveActionLabel(type) {
21
21
  cli({
22
22
  site: 'jike',
23
23
  name: 'notifications',
24
+ access: 'read',
24
25
  description: '即刻通知',
25
26
  domain: 'web.okjike.com',
26
27
  strategy: Strategy.COOKIE,
package/clis/jike/post.js CHANGED
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'jike',
4
4
  name: 'post',
5
+ access: 'read',
5
6
  description: '即刻帖子详情及评论',
6
7
  domain: 'm.okjike.com',
7
8
  browser: true,
@@ -9,6 +9,7 @@ import { cli, Strategy } from '@jackwener/opencli/registry';
9
9
  cli({
10
10
  site: 'jike',
11
11
  name: 'repost',
12
+ access: 'write',
12
13
  description: '转发即刻帖子',
13
14
  domain: 'web.okjike.com',
14
15
  strategy: Strategy.UI,
@@ -9,6 +9,7 @@ import { getPostDataJs } from './utils.js';
9
9
  cli({
10
10
  site: 'jike',
11
11
  name: 'search',
12
+ access: 'read',
12
13
  description: '搜索即刻帖子',
13
14
  domain: 'web.okjike.com',
14
15
  strategy: Strategy.COOKIE,
@@ -17,7 +18,7 @@ cli({
17
18
  { name: 'query', type: 'string', required: true, positional: true },
18
19
  { name: 'limit', type: 'int', default: 20 },
19
20
  ],
20
- columns: ['author', 'content', 'likes', 'comments', 'time', 'url'],
21
+ columns: ['id', 'author', 'content', 'likes', 'comments', 'time', 'url'],
21
22
  func: async (page, kwargs) => {
22
23
  const keyword = kwargs.query;
23
24
  const limit = kwargs.limit || 20;
@@ -43,6 +44,7 @@ cli({
43
44
  if (!author && !content) continue;
44
45
 
45
46
  results.push({
47
+ id: data.id,
46
48
  author,
47
49
  content: content.replace(/\\n/g, ' ').slice(0, 120),
48
50
  likes: data.likeCount || 0,
@@ -2,6 +2,7 @@ import { cli } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'jike',
4
4
  name: 'topic',
5
+ access: 'read',
5
6
  description: '即刻话题/圈子帖子',
6
7
  domain: 'm.okjike.com',
7
8
  browser: true,