publishport-opencli 1.0.20 → 1.0.21

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 (1041) hide show
  1. package/clis/12306/me.js +2 -2
  2. package/clis/12306/orders.js +3 -3
  3. package/clis/12306/passengers.js +3 -3
  4. package/clis/12306/price.js +1 -1
  5. package/clis/12306/stations.js +1 -1
  6. package/clis/12306/train.js +1 -1
  7. package/clis/12306/trains.js +1 -1
  8. package/clis/12306/utils.js +1 -1
  9. package/clis/1688/download.js +1 -1
  10. package/clis/1688/item.js +4 -4
  11. package/clis/1688/search.js +4 -4
  12. package/clis/1688/store.js +4 -4
  13. package/clis/1point3acres/forum.js +1 -1
  14. package/clis/1point3acres/forums.js +1 -1
  15. package/clis/1point3acres/notifications.js +1 -1
  16. package/clis/1point3acres/search.js +1 -1
  17. package/clis/1point3acres/thread.js +2 -2
  18. package/clis/1point3acres/user.js +1 -1
  19. package/clis/1point3acres/utils.js +2 -2
  20. package/clis/36kr/article.js +2 -2
  21. package/clis/36kr/hot.js +2 -2
  22. package/clis/36kr/news.js +1 -1
  23. package/clis/36kr/search.js +2 -2
  24. package/clis/51job/company.js +2 -2
  25. package/clis/51job/detail.js +2 -2
  26. package/clis/51job/hot.js +1 -1
  27. package/clis/51job/search.js +1 -1
  28. package/clis/51job/utils.js +7 -7
  29. package/clis/_atlassian/shared.js +9 -9
  30. package/clis/_shared/article/auth.js +4 -4
  31. package/clis/_shared/article/douban-md2draft.js +4 -4
  32. package/clis/_shared/article/format.js +1 -1
  33. package/clis/_shared/article/images.js +7 -7
  34. package/clis/_shared/article/login.js +1 -1
  35. package/clis/_shared/article/publish.js +11 -11
  36. package/clis/_shared/common.js +1 -1
  37. package/clis/_shared/content-guard.js +1 -1
  38. package/clis/_shared/desktop-commands.js +1 -1
  39. package/clis/_shared/file-inject.js +12 -12
  40. package/clis/_shared/self-hosted-sites.js +2 -2
  41. package/clis/_shared/site-auth.js +1 -1
  42. package/clis/_shared/token-auth.js +2 -2
  43. package/clis/_shared/video-publish.js +9 -9
  44. package/clis/amazon/offer.js +3 -3
  45. package/clis/amazon/product.js +5 -5
  46. package/clis/amazon/shared.js +6 -6
  47. package/clis/antigravity/_actions.js +13 -13
  48. package/clis/antigravity/audit-extras.js +10 -10
  49. package/clis/antigravity/delete.js +1 -1
  50. package/clis/antigravity/model.js +6 -6
  51. package/clis/antigravity/new.js +2 -2
  52. package/clis/antigravity/read.js +2 -2
  53. package/clis/antigravity/rename.js +1 -1
  54. package/clis/antigravity/send.js +3 -3
  55. package/clis/antigravity/serve.js +17 -17
  56. package/clis/antigravity/storage.js +6 -6
  57. package/clis/antigravity/watch.js +2 -2
  58. package/clis/apple-podcasts/episodes.js +1 -1
  59. package/clis/apple-podcasts/top.js +1 -1
  60. package/clis/apple-podcasts/utils.js +1 -1
  61. package/clis/archive/item.js +1 -1
  62. package/clis/archive/search.js +1 -1
  63. package/clis/archive/snapshots.js +1 -1
  64. package/clis/archive/wayback.js +1 -1
  65. package/clis/arxiv/author.js +1 -1
  66. package/clis/arxiv/utils.js +1 -1
  67. package/clis/autohome/brand.js +1 -1
  68. package/clis/autohome/score.js +1 -1
  69. package/clis/autohome/utils.js +1 -1
  70. package/clis/baijiahao/article.js +1 -1
  71. package/clis/baijiahao/lists.js +1 -1
  72. package/clis/baijiahao/login.js +1 -1
  73. package/clis/baijiahao/publish.js +9 -9
  74. package/clis/baijiahao/stats.js +1 -1
  75. package/clis/baijiahao/whoami.js +1 -1
  76. package/clis/band/bands.js +2 -2
  77. package/clis/band/mentions.js +2 -2
  78. package/clis/band/post.js +4 -4
  79. package/clis/band/posts.js +3 -3
  80. package/clis/barchart/flow.js +3 -3
  81. package/clis/barchart/greeks.js +2 -2
  82. package/clis/barchart/quote.js +3 -3
  83. package/clis/bbc/news.js +1 -1
  84. package/clis/bbc/topic.js +1 -1
  85. package/clis/bbc/utils.js +1 -1
  86. package/clis/bilibili/article.js +1 -1
  87. package/clis/bilibili/categories.js +1 -1
  88. package/clis/bilibili/coin.js +1 -1
  89. package/clis/bilibili/comment-del.js +1 -1
  90. package/clis/bilibili/comment-like.js +1 -1
  91. package/clis/bilibili/comment.js +1 -1
  92. package/clis/bilibili/comments.js +1 -1
  93. package/clis/bilibili/dm-list.js +1 -1
  94. package/clis/bilibili/dm-read.js +1 -1
  95. package/clis/bilibili/dm-send.js +1 -1
  96. package/clis/bilibili/download.js +2 -2
  97. package/clis/bilibili/drafts.js +1 -1
  98. package/clis/bilibili/dynamic-post.js +3 -3
  99. package/clis/bilibili/favorite-add.js +1 -1
  100. package/clis/bilibili/favorite.js +1 -1
  101. package/clis/bilibili/feed.js +2 -2
  102. package/clis/bilibili/follow.js +1 -1
  103. package/clis/bilibili/following.js +1 -1
  104. package/clis/bilibili/like.js +1 -1
  105. package/clis/bilibili/partitions.js +1 -1
  106. package/clis/bilibili/stats.js +1 -1
  107. package/clis/bilibili/subtitle.js +3 -3
  108. package/clis/bilibili/summary.js +1 -1
  109. package/clis/bilibili/topics.js +1 -1
  110. package/clis/bilibili/unfollow.js +1 -1
  111. package/clis/bilibili/upload.js +1 -1
  112. package/clis/bilibili/utils.js +10 -10
  113. package/clis/bilibili/video-drafts.js +1 -1
  114. package/clis/bilibili/video.js +1 -1
  115. package/clis/bloomberg/businessweek.js +4 -4
  116. package/clis/bloomberg/news.js +2 -2
  117. package/clis/bloomberg/utils.js +13 -13
  118. package/clis/bluesky/post.js +1 -1
  119. package/clis/bluesky/shared.js +11 -11
  120. package/clis/bluesky/whoami.js +1 -1
  121. package/clis/booking/search.js +2 -2
  122. package/clis/boss/accept.js +1 -1
  123. package/clis/boss/applied.js +1 -1
  124. package/clis/boss/auth.js +1 -1
  125. package/clis/boss/batchgreet.js +1 -1
  126. package/clis/boss/bossinfo.js +1 -1
  127. package/clis/boss/chatlist.js +1 -1
  128. package/clis/boss/chatmsg.js +1 -1
  129. package/clis/boss/cities.js +1 -1
  130. package/clis/boss/detail.js +1 -1
  131. package/clis/boss/exchange.js +1 -1
  132. package/clis/boss/greet.js +1 -1
  133. package/clis/boss/interviews.js +1 -1
  134. package/clis/boss/invite.js +1 -1
  135. package/clis/boss/joblist.js +1 -1
  136. package/clis/boss/mark.js +1 -1
  137. package/clis/boss/me.js +2 -2
  138. package/clis/boss/recommend.js +1 -1
  139. package/clis/boss/resumedelete.js +1 -1
  140. package/clis/boss/resumelist.js +1 -1
  141. package/clis/boss/resumeupload.js +11 -11
  142. package/clis/boss/search.js +1 -1
  143. package/clis/boss/send.js +1 -1
  144. package/clis/boss/sendresume.js +1 -1
  145. package/clis/boss/stats.js +1 -1
  146. package/clis/boss/unread.js +1 -1
  147. package/clis/boss/utils.js +13 -13
  148. package/clis/chaoxing/assignments.js +1 -1
  149. package/clis/chaoxing/exams.js +1 -1
  150. package/clis/chaoxing/utils.js +9 -9
  151. package/clis/chatgpt/ask.js +1 -1
  152. package/clis/chatgpt/auth.js +2 -2
  153. package/clis/chatgpt/detail.js +1 -1
  154. package/clis/chatgpt/image.js +1 -1
  155. package/clis/chatgpt/read.js +1 -1
  156. package/clis/chatgpt/send.js +1 -1
  157. package/clis/chatgpt/utils.js +59 -59
  158. package/clis/chatgpt-app/ask.js +1 -1
  159. package/clis/chatgpt-app/send.js +1 -1
  160. package/clis/chatwise/model.js +6 -6
  161. package/clis/chess/analyze.js +1 -1
  162. package/clis/chess/game.js +1 -1
  163. package/clis/chess/games.js +1 -1
  164. package/clis/chess/stats.js +1 -1
  165. package/clis/chess/utils.js +1 -1
  166. package/clis/claude/ask.js +2 -2
  167. package/clis/claude/detail.js +1 -1
  168. package/clis/claude/new.js +1 -1
  169. package/clis/claude/read.js +1 -1
  170. package/clis/claude/send.js +1 -1
  171. package/clis/claude/utils.js +23 -23
  172. package/clis/cnblogs/article.js +1 -1
  173. package/clis/cnblogs/login.js +1 -1
  174. package/clis/cnblogs/stats.js +7 -7
  175. package/clis/cnblogs/whoami.js +1 -1
  176. package/clis/codex/_actions.js +3 -3
  177. package/clis/codex/archive.js +1 -1
  178. package/clis/codex/ask.js +6 -6
  179. package/clis/codex/model.js +8 -8
  180. package/clis/codex/pin.js +1 -1
  181. package/clis/codex/rename.js +4 -4
  182. package/clis/codex/send.js +3 -3
  183. package/clis/codex/sidebar.js +1 -1
  184. package/clis/coingecko/categories.js +1 -1
  185. package/clis/coingecko/coin.js +1 -1
  186. package/clis/coingecko/derivatives.js +1 -1
  187. package/clis/coingecko/exchanges.js +1 -1
  188. package/clis/coingecko/global.js +1 -1
  189. package/clis/coingecko/trending.js +1 -1
  190. package/clis/confluence/search.js +1 -1
  191. package/clis/coupang/product.js +3 -3
  192. package/clis/coupang/utils.js +1 -1
  193. package/clis/crates/crate.js +1 -1
  194. package/clis/crates/search.js +1 -1
  195. package/clis/crates/utils.js +1 -1
  196. package/clis/csdn/article.js +13 -13
  197. package/clis/csdn/columns.js +1 -1
  198. package/clis/csdn/login.js +1 -1
  199. package/clis/csdn/stats.js +4 -4
  200. package/clis/csdn/whoami.js +1 -1
  201. package/clis/cto51/article.js +1 -1
  202. package/clis/cto51/whoami.js +1 -1
  203. package/clis/ctrip/flight.js +2 -2
  204. package/clis/ctrip/hotel-search.js +3 -3
  205. package/clis/ctrip/hotel-suggest.js +1 -1
  206. package/clis/ctrip/search.js +1 -1
  207. package/clis/ctrip/utils.js +6 -6
  208. package/clis/cursor/ask.js +5 -5
  209. package/clis/cursor/composer.js +2 -2
  210. package/clis/cursor/model.js +4 -4
  211. package/clis/cursor/send.js +2 -2
  212. package/clis/dblp/author.js +1 -1
  213. package/clis/dblp/paper.js +1 -1
  214. package/clis/dblp/search.js +1 -1
  215. package/clis/dblp/utils.js +1 -1
  216. package/clis/dblp/venue.js +1 -1
  217. package/clis/deepseek/ask.js +1 -1
  218. package/clis/deepseek/auth.js +2 -2
  219. package/clis/deepseek/detail.js +1 -1
  220. package/clis/deepseek/new.js +1 -1
  221. package/clis/deepseek/read.js +1 -1
  222. package/clis/deepseek/send.js +7 -7
  223. package/clis/deepseek/utils.js +25 -25
  224. package/clis/defillama/protocol.js +1 -1
  225. package/clis/defillama/protocols.js +1 -1
  226. package/clis/defillama/utils.js +1 -1
  227. package/clis/devto/auth.js +2 -2
  228. package/clis/devto/latest.js +1 -1
  229. package/clis/devto/publish.js +5 -5
  230. package/clis/devto/read.js +2 -2
  231. package/clis/devto/stats.js +3 -3
  232. package/clis/dianping/auth.js +2 -2
  233. package/clis/dianping/cityResolver.js +2 -2
  234. package/clis/dianping/search.js +1 -1
  235. package/clis/dianping/shop.js +1 -1
  236. package/clis/dianping/utils.js +1 -1
  237. package/clis/discord-app/delete.js +3 -3
  238. package/clis/discord-app/read.js +1 -1
  239. package/clis/discord-app/thread-read.js +1 -1
  240. package/clis/discord-web/auth.js +2 -2
  241. package/clis/discord-web/send.js +5 -5
  242. package/clis/dockerhub/image.js +1 -1
  243. package/clis/dockerhub/search.js +1 -1
  244. package/clis/dockerhub/utils.js +1 -1
  245. package/clis/dongchedi/koubei.js +1 -1
  246. package/clis/dongchedi/models.js +1 -1
  247. package/clis/dongchedi/score.js +1 -1
  248. package/clis/dongchedi/search.js +1 -1
  249. package/clis/dongchedi/series.js +1 -1
  250. package/clis/dongchedi/specs.js +1 -1
  251. package/clis/dongchedi/utils.js +1 -1
  252. package/clis/douban/article.js +3 -3
  253. package/clis/douban/auth.js +2 -2
  254. package/clis/douban/download.js +1 -1
  255. package/clis/douban/utils.js +23 -23
  256. package/clis/doubao/auth.js +3 -3
  257. package/clis/doubao/utils.js +39 -39
  258. package/clis/doubao-app/ask.js +1 -1
  259. package/clis/doubao-app/utils.js +4 -4
  260. package/clis/douyin/_shared/browser-fetch.js +7 -7
  261. package/clis/douyin/_shared/draft-cache.js +1 -1
  262. package/clis/douyin/_shared/imagex-upload.js +1 -1
  263. package/clis/douyin/_shared/sts2.js +1 -1
  264. package/clis/douyin/_shared/text-extra.js +1 -1
  265. package/clis/douyin/_shared/tos-upload.js +4 -4
  266. package/clis/douyin/_shared/transcode.js +1 -1
  267. package/clis/douyin/_shared/vod-upload.js +4 -4
  268. package/clis/douyin/draft.js +18 -18
  269. package/clis/douyin/drafts.js +1 -1
  270. package/clis/douyin/publish-image.js +12 -12
  271. package/clis/douyin/publish.js +9 -9
  272. package/clis/douyin/search.js +3 -3
  273. package/clis/douyin/videos.js +1 -1
  274. package/clis/eastmoney/_secid.js +1 -1
  275. package/clis/eastmoney/announcement.js +1 -1
  276. package/clis/eastmoney/article.js +1 -1
  277. package/clis/eastmoney/convertible.js +1 -1
  278. package/clis/eastmoney/etf.js +1 -1
  279. package/clis/eastmoney/holders.js +1 -1
  280. package/clis/eastmoney/index-board.js +1 -1
  281. package/clis/eastmoney/kline.js +1 -1
  282. package/clis/eastmoney/kuaixun.js +1 -1
  283. package/clis/eastmoney/longhu.js +1 -1
  284. package/clis/eastmoney/money-flow.js +1 -1
  285. package/clis/eastmoney/northbound.js +1 -1
  286. package/clis/eastmoney/quote.js +1 -1
  287. package/clis/eastmoney/rank.js +1 -1
  288. package/clis/eastmoney/sectors.js +1 -1
  289. package/clis/endoflife/product.js +1 -1
  290. package/clis/endoflife/utils.js +1 -1
  291. package/clis/facebook/comment.js +8 -8
  292. package/clis/facebook/feed.js +4 -4
  293. package/clis/facebook/like.js +5 -5
  294. package/clis/facebook/notifications.js +3 -3
  295. package/clis/facebook/post.js +11 -11
  296. package/clis/facebook/search.js +3 -3
  297. package/clis/flathub/app.js +1 -1
  298. package/clis/flathub/search.js +1 -1
  299. package/clis/flathub/utils.js +1 -1
  300. package/clis/flomo/memos.js +1 -1
  301. package/clis/gemini/deep-research-result.js +4 -4
  302. package/clis/gemini/deep-research.js +1 -1
  303. package/clis/gemini/detail.js +1 -1
  304. package/clis/gemini/history.js +1 -1
  305. package/clis/gemini/image.js +2 -2
  306. package/clis/gemini/status.js +1 -1
  307. package/clis/gemini/utils.js +43 -43
  308. package/clis/geogebra/hexagon.js +1 -1
  309. package/clis/geogebra/info.js +3 -3
  310. package/clis/geogebra/triangle.js +1 -1
  311. package/clis/geogebra/utils.js +11 -11
  312. package/clis/ghost/login.js +1 -1
  313. package/clis/ghost/shared.js +2 -2
  314. package/clis/gitee/auth.js +2 -2
  315. package/clis/github-trending/repos.js +1 -1
  316. package/clis/google/news.js +1 -1
  317. package/clis/google/search.js +2 -2
  318. package/clis/google/suggest.js +1 -1
  319. package/clis/google/trends.js +1 -1
  320. package/clis/google/utils.js +1 -1
  321. package/clis/goproxy/module.js +1 -1
  322. package/clis/goproxy/utils.js +1 -1
  323. package/clis/goproxy/versions.js +1 -1
  324. package/clis/gov-policy/recent.js +2 -2
  325. package/clis/gov-policy/search.js +2 -2
  326. package/clis/grok/ask.js +1 -1
  327. package/clis/grok/detail.js +1 -1
  328. package/clis/grok/image.js +8 -8
  329. package/clis/grok/pin.js +1 -1
  330. package/clis/grok/send.js +1 -1
  331. package/clis/grok/status.js +1 -1
  332. package/clis/grok/utils.js +21 -21
  333. package/clis/guazi/browse.js +1 -1
  334. package/clis/guazi/car.js +1 -1
  335. package/clis/guazi/utils.js +1 -1
  336. package/clis/hackernews/auth.js +2 -2
  337. package/clis/hackernews/comment.js +8 -8
  338. package/clis/hackernews/read.js +7 -7
  339. package/clis/hackernews/submit.js +7 -7
  340. package/clis/hashnode/auth.js +3 -3
  341. package/clis/hashnode/publish.js +14 -14
  342. package/clis/hf/auth.js +2 -2
  343. package/clis/hf/models.js +1 -1
  344. package/clis/hf/paper.js +1 -1
  345. package/clis/hf/spaces.js +1 -1
  346. package/clis/hf/top.js +1 -1
  347. package/clis/homebrew/cask.js +1 -1
  348. package/clis/homebrew/formula.js +1 -1
  349. package/clis/homebrew/popular.js +1 -1
  350. package/clis/homebrew/utils.js +1 -1
  351. package/clis/huodongxing/events.js +1 -1
  352. package/clis/hupu/detail.js +4 -4
  353. package/clis/hupu/hot.js +3 -3
  354. package/clis/hupu/reply.js +1 -1
  355. package/clis/hupu/search.js +1 -1
  356. package/clis/hupu/unlike.js +1 -1
  357. package/clis/hupu/utils.js +10 -10
  358. package/clis/imdb/person.js +2 -2
  359. package/clis/imdb/reviews.js +3 -3
  360. package/clis/imdb/search.js +2 -2
  361. package/clis/imdb/title.js +1 -1
  362. package/clis/imdb/top.js +1 -1
  363. package/clis/imdb/trending.js +1 -1
  364. package/clis/imdb/utils.js +13 -13
  365. package/clis/imooc/article.js +6 -6
  366. package/clis/imooc/login.js +1 -1
  367. package/clis/imooc/whoami.js +1 -1
  368. package/clis/indeed/job.js +2 -2
  369. package/clis/indeed/search.js +2 -2
  370. package/clis/indeed/utils.js +1 -1
  371. package/clis/infoq/article.js +2 -2
  372. package/clis/infoq/login.js +1 -1
  373. package/clis/infoq/whoami.js +1 -1
  374. package/clis/instagram/_shared/private-publish.js +4 -4
  375. package/clis/instagram/_shared/protocol-capture.js +9 -9
  376. package/clis/instagram/detail.js +3 -3
  377. package/clis/instagram/post.js +25 -25
  378. package/clis/instagram/reel.js +18 -18
  379. package/clis/instagram/story.js +4 -4
  380. package/clis/instagram/user.js +2 -2
  381. package/clis/jd/item.js +65 -65
  382. package/clis/jianshu/article.js +1 -1
  383. package/clis/jianshu/login.js +1 -1
  384. package/clis/jianshu/notebooks.js +1 -1
  385. package/clis/jianshu/whoami.js +1 -1
  386. package/clis/jianyu/search.js +6 -6
  387. package/clis/jianyu/shared/procurement-contract.js +1 -1
  388. package/clis/jike/api.js +6 -6
  389. package/clis/jike/auth.js +3 -3
  390. package/clis/jike/comment.js +1 -1
  391. package/clis/jike/create.js +6 -6
  392. package/clis/jike/feed.js +3 -3
  393. package/clis/jike/like.js +1 -1
  394. package/clis/jike/notifications.js +3 -3
  395. package/clis/jike/repost.js +13 -13
  396. package/clis/jike/search.js +3 -3
  397. package/clis/jimeng/auth.js +2 -2
  398. package/clis/jimeng/generate.js +10 -10
  399. package/clis/jira/shared.js +1 -1
  400. package/clis/juejin/article.js +4 -4
  401. package/clis/juejin/categories.js +1 -1
  402. package/clis/juejin/hot.js +1 -1
  403. package/clis/juejin/login.js +1 -1
  404. package/clis/juejin/recommend.js +1 -1
  405. package/clis/juejin/stats.js +3 -3
  406. package/clis/juejin/tags.js +1 -1
  407. package/clis/juejin/utils.js +1 -1
  408. package/clis/juejin/whoami.js +1 -1
  409. package/clis/ke/utils.js +4 -4
  410. package/clis/kimi/_utils.js +7 -7
  411. package/clis/kimi/audit-extras.js +14 -14
  412. package/clis/kimi/auth.js +3 -3
  413. package/clis/kimi/chat.js +15 -15
  414. package/clis/kimi/storage.js +5 -5
  415. package/clis/kimi/ui.js +13 -13
  416. package/clis/kimi/usage.js +3 -3
  417. package/clis/kuaishou/auth.js +1 -1
  418. package/clis/kuaishou/publish.js +6 -6
  419. package/clis/lesswrong/_helpers.js +5 -5
  420. package/clis/lesswrong/comments.js +5 -5
  421. package/clis/lichess/top.js +1 -1
  422. package/clis/lichess/user.js +1 -1
  423. package/clis/lichess/utils.js +1 -1
  424. package/clis/linkedin/comment.js +1 -1
  425. package/clis/linkedin/connect.js +11 -11
  426. package/clis/linkedin/create.js +5 -5
  427. package/clis/linkedin/follow.js +1 -1
  428. package/clis/linkedin/inbox.js +1 -1
  429. package/clis/linkedin/job-detail.js +3 -3
  430. package/clis/linkedin/like.js +1 -1
  431. package/clis/linkedin/notifications.js +1 -1
  432. package/clis/linkedin/people-search.js +1 -1
  433. package/clis/linkedin/post-comments.js +1 -1
  434. package/clis/linkedin/post-detail.js +1 -1
  435. package/clis/linkedin/profile-analytics.js +1 -1
  436. package/clis/linkedin/profile-api.js +13 -13
  437. package/clis/linkedin/profile-experience.js +1 -1
  438. package/clis/linkedin/profile-projects.js +1 -1
  439. package/clis/linkedin/profile-read.js +3 -3
  440. package/clis/linkedin/reply.js +1 -1
  441. package/clis/linkedin/repost.js +1 -1
  442. package/clis/linkedin/safe-send.js +8 -8
  443. package/clis/linkedin/salesnav-inbox.js +5 -5
  444. package/clis/linkedin/salesnav-message.js +3 -3
  445. package/clis/linkedin/salesnav-search.js +4 -4
  446. package/clis/linkedin/salesnav-thread.js +1 -1
  447. package/clis/linkedin/search-api.js +3 -3
  448. package/clis/linkedin/search.js +8 -8
  449. package/clis/linkedin/social-api.js +31 -31
  450. package/clis/linkedin/timeline.js +3 -3
  451. package/clis/linkedin-learning/search.js +2 -2
  452. package/clis/linkedin-learning/trending.js +2 -2
  453. package/clis/linux-do/feed.js +4 -4
  454. package/clis/linux-do/topic-content.js +8 -8
  455. package/clis/lobsters/domain.js +1 -1
  456. package/clis/lobsters/read.js +7 -7
  457. package/clis/maimai/auth.js +2 -2
  458. package/clis/maimai/search-talents.js +4 -4
  459. package/clis/manus/_utils.js +1 -1
  460. package/clis/mastodon/login.js +1 -1
  461. package/clis/mastodon/post.js +1 -1
  462. package/clis/mastodon/shared.js +12 -12
  463. package/clis/mastodon/whoami.js +1 -1
  464. package/clis/maven/artifact.js +1 -1
  465. package/clis/maven/search.js +1 -1
  466. package/clis/maven/utils.js +1 -1
  467. package/clis/mdn/search.js +1 -1
  468. package/clis/medium/article.js +5 -5
  469. package/clis/medium/login.js +1 -1
  470. package/clis/medium/stats.js +3 -3
  471. package/clis/medium/tag.js +1 -1
  472. package/clis/medium/whoami.js +1 -1
  473. package/clis/mubu/notes.js +1 -1
  474. package/clis/mubu/utils.js +15 -15
  475. package/clis/note/auth.js +2 -2
  476. package/clis/note/note-utils.js +2 -2
  477. package/clis/note/publish.js +8 -8
  478. package/clis/notebooklm/add-source.js +8 -8
  479. package/clis/notebooklm/create.js +1 -1
  480. package/clis/notebooklm/generate-slides.js +1 -1
  481. package/clis/notebooklm/notes-get.js +1 -1
  482. package/clis/notebooklm/rpc.js +8 -8
  483. package/clis/notebooklm/source-fulltext.js +1 -1
  484. package/clis/notebooklm/utils.js +10 -10
  485. package/clis/nowcoder/auth.js +2 -2
  486. package/clis/nowcoder/comment.js +1 -1
  487. package/clis/nowcoder/comments.js +1 -1
  488. package/clis/nowcoder/dm-list.js +1 -1
  489. package/clis/nowcoder/dm-read.js +1 -1
  490. package/clis/nowcoder/dm-send.js +1 -1
  491. package/clis/nowcoder/drafts.js +1 -1
  492. package/clis/nowcoder/inbox.js +1 -1
  493. package/clis/nowcoder/like.js +1 -1
  494. package/clis/nowcoder/my.js +1 -1
  495. package/clis/nowcoder/post.js +1 -1
  496. package/clis/nowcoder/utils.js +13 -13
  497. package/clis/npm/downloads.js +1 -1
  498. package/clis/npm/package.js +1 -1
  499. package/clis/npm/search.js +1 -1
  500. package/clis/npm/utils.js +1 -1
  501. package/clis/nuget/package.js +1 -1
  502. package/clis/nuget/search.js +1 -1
  503. package/clis/nuget/utils.js +1 -1
  504. package/clis/nvd/cve.js +1 -1
  505. package/clis/oeis/search.js +1 -1
  506. package/clis/oeis/sequence.js +1 -1
  507. package/clis/oeis/utils.js +1 -1
  508. package/clis/ones/common.js +6 -6
  509. package/clis/ones/enrich-tasks.js +1 -1
  510. package/clis/ones/my-tasks.js +1 -1
  511. package/clis/ones/resolve-labels.js +1 -1
  512. package/clis/ones/task-helpers.js +1 -1
  513. package/clis/ones/task.js +1 -1
  514. package/clis/ones/worklog.js +7 -7
  515. package/clis/openalex/search.js +1 -1
  516. package/clis/openalex/utils.js +1 -1
  517. package/clis/openalex/work.js +1 -1
  518. package/clis/openfda/drug-label.js +1 -1
  519. package/clis/openfda/food-recall.js +1 -1
  520. package/clis/openfda/utils.js +1 -1
  521. package/clis/openreview/author.js +1 -1
  522. package/clis/openreview/paper.js +1 -1
  523. package/clis/openreview/reviews.js +3 -3
  524. package/clis/openreview/search.js +1 -1
  525. package/clis/openreview/utils.js +1 -1
  526. package/clis/openreview/venue.js +1 -1
  527. package/clis/oschina/article.js +1 -1
  528. package/clis/oschina/catalogs.js +1 -1
  529. package/clis/oschina/login.js +1 -1
  530. package/clis/oschina/stats.js +6 -6
  531. package/clis/oschina/whoami.js +1 -1
  532. package/clis/osv/query.js +1 -1
  533. package/clis/osv/utils.js +1 -1
  534. package/clis/osv/vulnerability.js +1 -1
  535. package/clis/packagist/package.js +1 -1
  536. package/clis/packagist/search.js +1 -1
  537. package/clis/packagist/utils.js +1 -1
  538. package/clis/pinterest/api.js +1 -1
  539. package/clis/pinterest/auth.js +1 -1
  540. package/clis/pinterest/boards.js +1 -1
  541. package/clis/pinterest/client.js +6 -6
  542. package/clis/pinterest/publish.js +5 -5
  543. package/clis/pixiv/auth.js +2 -2
  544. package/clis/pixiv/download.js +1 -1
  545. package/clis/pixiv/illusts.js +1 -1
  546. package/clis/pixiv/search.js +1 -1
  547. package/clis/pixiv/utils.js +3 -3
  548. package/clis/powerchina/auth.js +2 -2
  549. package/clis/powerchina/search.js +1 -1
  550. package/clis/producthunt/browse.js +2 -2
  551. package/clis/producthunt/hot.js +2 -2
  552. package/clis/producthunt/posts.js +1 -1
  553. package/clis/producthunt/today.js +1 -1
  554. package/clis/producthunt/utils.js +1 -1
  555. package/clis/pypi/downloads.js +1 -1
  556. package/clis/pypi/package.js +1 -1
  557. package/clis/pypi/utils.js +1 -1
  558. package/clis/qiita/auth.js +1 -1
  559. package/clis/qiita/gql.js +6 -6
  560. package/clis/qiita/publish.js +4 -4
  561. package/clis/qiita/stats.js +4 -4
  562. package/clis/qoder/_utils.js +16 -16
  563. package/clis/qoder/composer.js +1 -1
  564. package/clis/qoder/quest.js +1 -1
  565. package/clis/qoder/ui.js +14 -14
  566. package/clis/quark/auth.js +2 -2
  567. package/clis/quark/utils.js +2 -2
  568. package/clis/quora/answer-comments.js +3 -3
  569. package/clis/quora/create-answer.js +4 -4
  570. package/clis/quora/create-question.js +3 -3
  571. package/clis/quora/shared.js +17 -17
  572. package/clis/quora/whoami.js +1 -1
  573. package/clis/qwen/ask.js +1 -1
  574. package/clis/qwen/auth.js +3 -3
  575. package/clis/qwen/detail.js +1 -1
  576. package/clis/qwen/image.js +5 -5
  577. package/clis/qwen/status.js +1 -1
  578. package/clis/qwen/utils.js +16 -16
  579. package/clis/reddit/home.js +3 -3
  580. package/clis/reddit/read.js +10 -10
  581. package/clis/reddit/reply.js +4 -4
  582. package/clis/reddit/submit.js +11 -11
  583. package/clis/reddit/subreddit-info.js +3 -3
  584. package/clis/reddit/subscribed.js +4 -4
  585. package/clis/reddit/whoami.js +2 -2
  586. package/clis/rednote/comments.js +1 -1
  587. package/clis/rednote/download.js +1 -1
  588. package/clis/rednote/feed.js +1 -1
  589. package/clis/rednote/note.js +1 -1
  590. package/clis/rednote/notifications.js +2 -2
  591. package/clis/rednote/search.js +2 -2
  592. package/clis/rest-countries/country.js +1 -1
  593. package/clis/rest-countries/region.js +1 -1
  594. package/clis/rest-countries/utils.js +1 -1
  595. package/clis/reuters/article-detail.js +1 -1
  596. package/clis/reuters/auth.js +3 -3
  597. package/clis/reuters/search.js +1 -1
  598. package/clis/reuters/utils.js +4 -4
  599. package/clis/rfc/rfc.js +1 -1
  600. package/clis/rfc/utils.js +1 -1
  601. package/clis/rubygems/gem.js +1 -1
  602. package/clis/rubygems/search.js +1 -1
  603. package/clis/rubygems/utils.js +1 -1
  604. package/clis/segmentfault/article.js +3 -3
  605. package/clis/segmentfault/channels.js +1 -1
  606. package/clis/segmentfault/login.js +1 -1
  607. package/clis/segmentfault/stats.js +4 -4
  608. package/clis/segmentfault/tags.js +1 -1
  609. package/clis/segmentfault/whoami.js +1 -1
  610. package/clis/semanticscholar/citations.js +1 -1
  611. package/clis/semanticscholar/paper.js +1 -1
  612. package/clis/semanticscholar/recommendations.js +1 -1
  613. package/clis/semanticscholar/search.js +1 -1
  614. package/clis/semanticscholar/utils.js +1 -1
  615. package/clis/sinafinance/news.js +1 -1
  616. package/clis/sinafinance/stock-rank.js +1 -1
  617. package/clis/sinafinance/stock.js +1 -1
  618. package/clis/slock/attachment-download.js +1 -1
  619. package/clis/slock/attachment-upload.js +7 -7
  620. package/clis/slock/attachment-url.js +1 -1
  621. package/clis/slock/auth-verify.js +1 -1
  622. package/clis/slock/bookmark-add.js +1 -1
  623. package/clis/slock/bookmark-remove.js +1 -1
  624. package/clis/slock/channel-action.js +1 -1
  625. package/clis/slock/channel-create.js +1 -1
  626. package/clis/slock/channel-files.js +1 -1
  627. package/clis/slock/channel-list.js +1 -1
  628. package/clis/slock/channel-mark.js +1 -1
  629. package/clis/slock/channel-members.js +1 -1
  630. package/clis/slock/channel-unarchive.js +1 -1
  631. package/clis/slock/dm-list.js +1 -1
  632. package/clis/slock/errors.js +1 -1
  633. package/clis/slock/in-page.js +28 -28
  634. package/clis/slock/inbox-done.js +5 -5
  635. package/clis/slock/inbox.js +1 -1
  636. package/clis/slock/message-read.js +22 -22
  637. package/clis/slock/message-search.js +10 -10
  638. package/clis/slock/message-send.js +25 -25
  639. package/clis/slock/reaction-add.js +1 -1
  640. package/clis/slock/reaction-remove.js +1 -1
  641. package/clis/slock/resolve.js +1 -1
  642. package/clis/slock/server-list.js +2 -2
  643. package/clis/slock/task-claim.js +4 -4
  644. package/clis/slock/task-convert.js +13 -13
  645. package/clis/slock/task-create.js +1 -1
  646. package/clis/slock/task-delete.js +5 -5
  647. package/clis/slock/task-get.js +5 -5
  648. package/clis/slock/task-list-server.js +4 -4
  649. package/clis/slock/task-list.js +5 -5
  650. package/clis/slock/task-status.js +3 -3
  651. package/clis/slock/task-unclaim.js +3 -3
  652. package/clis/slock/thread-follow.js +1 -1
  653. package/clis/slock/thread-state.js +1 -1
  654. package/clis/slock/unread-summary.js +5 -5
  655. package/clis/slock/whoami.js +1 -1
  656. package/clis/smzdm/search.js +3 -3
  657. package/clis/sohu/article.js +1 -1
  658. package/clis/sohu/attributes.js +1 -1
  659. package/clis/sohu/categories.js +1 -1
  660. package/clis/sohu/channels.js +1 -1
  661. package/clis/sohu/columns.js +1 -1
  662. package/clis/sohu/login.js +1 -1
  663. package/clis/sohu/stats.js +3 -3
  664. package/clis/sohu/topics.js +1 -1
  665. package/clis/sohu/whoami.js +1 -1
  666. package/clis/spotify/spotify.js +1 -1
  667. package/clis/stackoverflow/read.js +6 -6
  668. package/clis/stackoverflow/related.js +1 -1
  669. package/clis/stackoverflow/tag.js +1 -1
  670. package/clis/stackoverflow/user.js +1 -1
  671. package/clis/stackoverflow/utils.js +1 -1
  672. package/clis/steam/app.js +1 -1
  673. package/clis/steam/search.js +1 -1
  674. package/clis/steam/utils.js +1 -1
  675. package/clis/substack/auth.js +2 -2
  676. package/clis/substack/post-utils.js +2 -2
  677. package/clis/substack/publish.js +5 -5
  678. package/clis/suno/auth.js +2 -2
  679. package/clis/suno/download.js +5 -5
  680. package/clis/suno/generate.js +3 -3
  681. package/clis/suno/list.js +1 -1
  682. package/clis/suno/status.js +1 -1
  683. package/clis/suno/utils.js +29 -29
  684. package/clis/telegram/send.js +7 -7
  685. package/clis/telegram/shared.js +2 -2
  686. package/clis/telegram/whoami.js +1 -1
  687. package/clis/test-utils.js +1 -1
  688. package/clis/threads/post.js +23 -23
  689. package/clis/threads/shared.js +3 -3
  690. package/clis/threads/whoami.js +1 -1
  691. package/clis/tieba/hot.js +3 -3
  692. package/clis/tieba/posts.js +1 -1
  693. package/clis/tieba/read.js +2 -2
  694. package/clis/tieba/search.js +3 -3
  695. package/clis/tieba/utils.js +1 -1
  696. package/clis/tiktok/comment.js +2 -2
  697. package/clis/tiktok/creator-videos.js +3 -3
  698. package/clis/tiktok/explore.js +3 -3
  699. package/clis/tiktok/follow.js +8 -8
  700. package/clis/tiktok/following.js +4 -4
  701. package/clis/tiktok/friends.js +7 -7
  702. package/clis/tiktok/live.js +3 -3
  703. package/clis/tiktok/notifications.js +6 -6
  704. package/clis/tiktok/publish.js +17 -17
  705. package/clis/tiktok/unfollow.js +4 -4
  706. package/clis/tiktok/user.js +2 -2
  707. package/clis/tiktok/utils.js +1 -1
  708. package/clis/toutiao/article.js +1 -1
  709. package/clis/toutiao/articles.js +2 -2
  710. package/clis/toutiao/auth.js +2 -2
  711. package/clis/toutiao/hot.js +1 -1
  712. package/clis/toutiao/recommend.js +1 -1
  713. package/clis/toutiao/utils.js +2 -2
  714. package/clis/trae-solo/_actions.js +4 -4
  715. package/clis/trae-solo/_fs.js +5 -5
  716. package/clis/trae-solo/_state.js +2 -2
  717. package/clis/trae-solo/automation.js +4 -4
  718. package/clis/trae-solo/mode.js +4 -4
  719. package/clis/trae-solo/model.js +4 -4
  720. package/clis/trae-solo/renderer-storage.js +4 -4
  721. package/clis/trae-solo/settings.js +1 -1
  722. package/clis/trae-solo/skill-fs.js +1 -1
  723. package/clis/trae-solo/skill.js +12 -12
  724. package/clis/trae-solo/state-fs.js +2 -2
  725. package/clis/trae-solo/task-fs.js +5 -5
  726. package/clis/trae-solo/workspaces-fs.js +1 -1
  727. package/clis/tumblr/auth.js +2 -2
  728. package/clis/tumblr/publish.js +3 -3
  729. package/clis/tvmaze/search.js +1 -1
  730. package/clis/tvmaze/show.js +1 -1
  731. package/clis/tvmaze/utils.js +1 -1
  732. package/clis/twitter/accept.js +7 -7
  733. package/clis/twitter/article.js +7 -7
  734. package/clis/twitter/bookmark-folder.js +3 -3
  735. package/clis/twitter/bookmark-folders.js +3 -3
  736. package/clis/twitter/bookmarks.js +4 -4
  737. package/clis/twitter/delete.js +5 -5
  738. package/clis/twitter/device-follow.js +3 -3
  739. package/clis/twitter/dm-list.js +1 -1
  740. package/clis/twitter/dm-read.js +1 -1
  741. package/clis/twitter/dm-send.js +5 -5
  742. package/clis/twitter/dm-shared.js +3 -3
  743. package/clis/twitter/download.js +6 -6
  744. package/clis/twitter/followers.js +5 -5
  745. package/clis/twitter/following.js +2 -2
  746. package/clis/twitter/like.js +3 -3
  747. package/clis/twitter/likes.js +8 -8
  748. package/clis/twitter/list-add-core.js +9 -9
  749. package/clis/twitter/list-create.js +5 -5
  750. package/clis/twitter/list-remove-core.js +9 -9
  751. package/clis/twitter/list-tweets.js +7 -7
  752. package/clis/twitter/lists.js +6 -6
  753. package/clis/twitter/notifications.js +2 -2
  754. package/clis/twitter/post.js +24 -24
  755. package/clis/twitter/profile.js +7 -7
  756. package/clis/twitter/quote.js +7 -7
  757. package/clis/twitter/reply-dm.js +7 -7
  758. package/clis/twitter/reply.js +12 -12
  759. package/clis/twitter/retweet.js +3 -3
  760. package/clis/twitter/scheduled-delete.js +3 -3
  761. package/clis/twitter/scheduled-list.js +2 -2
  762. package/clis/twitter/scheduled-utils.js +3 -3
  763. package/clis/twitter/search.js +5 -5
  764. package/clis/twitter/shared.js +6 -6
  765. package/clis/twitter/thread.js +4 -4
  766. package/clis/twitter/timeline.js +3 -3
  767. package/clis/twitter/trending.js +2 -2
  768. package/clis/twitter/tweets.js +11 -11
  769. package/clis/twitter/unlike.js +3 -3
  770. package/clis/twitter/unretweet.js +3 -3
  771. package/clis/twitter/utils.js +6 -6
  772. package/clis/upwork/detail.js +2 -2
  773. package/clis/upwork/feed.js +2 -2
  774. package/clis/upwork/search.js +2 -2
  775. package/clis/upwork/utils.js +1 -1
  776. package/clis/v2ex/auth.js +2 -2
  777. package/clis/v2ex/daily.js +3 -3
  778. package/clis/v2ex/me.js +2 -2
  779. package/clis/v2ex/notifications.js +2 -2
  780. package/clis/vk/post.js +1 -1
  781. package/clis/vk/shared.js +2 -2
  782. package/clis/vk/whoami.js +1 -1
  783. package/clis/web/read.js +8 -8
  784. package/clis/wechat-channels/auth.js +2 -2
  785. package/clis/wechat-channels/publish.js +30 -30
  786. package/clis/weibo/article.js +1 -1
  787. package/clis/weibo/auth.js +3 -3
  788. package/clis/weibo/comments.js +1 -1
  789. package/clis/weibo/delete.js +2 -2
  790. package/clis/weibo/feed.js +3 -3
  791. package/clis/weibo/hot.js +1 -1
  792. package/clis/weibo/publish.js +10 -10
  793. package/clis/weibo/search.js +2 -2
  794. package/clis/weibo/user-posts.js +3 -3
  795. package/clis/weibo/user.js +2 -2
  796. package/clis/weibo/utils.js +3 -3
  797. package/clis/weixin/article.js +1 -1
  798. package/clis/weixin/create-draft.js +26 -26
  799. package/clis/weixin/download.js +2 -2
  800. package/clis/weixin/login.js +1 -1
  801. package/clis/weixin/search.js +2 -2
  802. package/clis/weixin/stats.js +3 -3
  803. package/clis/weixin/whoami.js +1 -1
  804. package/clis/weread/ai-outline.js +2 -2
  805. package/clis/weread/book-search.js +2 -2
  806. package/clis/weread/book.js +2 -2
  807. package/clis/weread/notebooks.js +1 -1
  808. package/clis/weread/search.js +1 -1
  809. package/clis/weread/shelf.js +1 -1
  810. package/clis/weread/utils.js +6 -6
  811. package/clis/weread-official/book.js +1 -1
  812. package/clis/weread-official/discover.js +1 -1
  813. package/clis/weread-official/list-apis.js +1 -1
  814. package/clis/weread-official/notes.js +1 -1
  815. package/clis/weread-official/readdata.js +1 -1
  816. package/clis/weread-official/review.js +1 -1
  817. package/clis/weread-official/search.js +1 -1
  818. package/clis/weread-official/shelf.js +1 -1
  819. package/clis/weread-official/utils.js +1 -1
  820. package/clis/wikidata/entity.js +1 -1
  821. package/clis/wikidata/search.js +1 -1
  822. package/clis/wikidata/utils.js +1 -1
  823. package/clis/wikipedia/page.js +1 -1
  824. package/clis/wikipedia/trending.js +1 -1
  825. package/clis/wikipedia/utils.js +1 -1
  826. package/clis/wordpress/login.js +1 -1
  827. package/clis/wordpress/publish.js +1 -1
  828. package/clis/wordpress/shared.js +3 -3
  829. package/clis/wordpress/whoami.js +1 -1
  830. package/clis/woshipm/article.js +1 -1
  831. package/clis/woshipm/login.js +1 -1
  832. package/clis/woshipm/stats.js +4 -4
  833. package/clis/woshipm/whoami.js +1 -1
  834. package/clis/wttr/current.js +1 -1
  835. package/clis/wttr/forecast.js +1 -1
  836. package/clis/wttr/utils.js +1 -1
  837. package/clis/xianyu/collect.js +1 -1
  838. package/clis/xianyu/inbox.js +1 -1
  839. package/clis/xianyu/item-stats.js +2 -2
  840. package/clis/xianyu/location.js +1 -1
  841. package/clis/xianyu/manage.js +1 -1
  842. package/clis/xianyu/messages.js +1 -1
  843. package/clis/xianyu/mtop.js +5 -5
  844. package/clis/xianyu/order.js +1 -1
  845. package/clis/xianyu/promote-stats.js +2 -2
  846. package/clis/xianyu/publish.js +17 -17
  847. package/clis/xianyu/rate.js +1 -1
  848. package/clis/xianyu/reply.js +1 -1
  849. package/clis/xianyu/search.js +8 -8
  850. package/clis/xianyu/seller-data.js +1 -1
  851. package/clis/xianyu/seller-list.js +1 -1
  852. package/clis/xianyu/user.js +1 -1
  853. package/clis/xiaoe/catalog.js +2 -2
  854. package/clis/xiaoe/content.js +2 -2
  855. package/clis/xiaoe/courses.js +2 -2
  856. package/clis/xiaohongshu/ask.js +6 -6
  857. package/clis/xiaohongshu/comments.js +6 -6
  858. package/clis/xiaohongshu/creator-note-detail.js +7 -7
  859. package/clis/xiaohongshu/creator-notes-summary.js +1 -1
  860. package/clis/xiaohongshu/creator-notes.js +13 -13
  861. package/clis/xiaohongshu/creator-profile.js +2 -2
  862. package/clis/xiaohongshu/creator-stats.js +2 -2
  863. package/clis/xiaohongshu/delete-note.js +7 -7
  864. package/clis/xiaohongshu/download.js +4 -4
  865. package/clis/xiaohongshu/feed.js +2 -2
  866. package/clis/xiaohongshu/follow.js +3 -3
  867. package/clis/xiaohongshu/human.js +3 -3
  868. package/clis/xiaohongshu/note-helpers.js +1 -1
  869. package/clis/xiaohongshu/note.js +2 -2
  870. package/clis/xiaohongshu/notifications.js +1 -1
  871. package/clis/xiaohongshu/publish-video.js +8 -8
  872. package/clis/xiaohongshu/publish.js +59 -59
  873. package/clis/xiaohongshu/search.js +8 -8
  874. package/clis/xiaohongshu/unfollow.js +4 -4
  875. package/clis/xiaohongshu/user-helpers.js +1 -1
  876. package/clis/xiaohongshu/user.js +2 -2
  877. package/clis/xiaoyuzhou/auth.js +3 -3
  878. package/clis/xiaoyuzhou/download.js +1 -1
  879. package/clis/xiaoyuzhou/utils.js +1 -1
  880. package/clis/xueqiu/article.js +4 -4
  881. package/clis/xueqiu/comments.js +4 -4
  882. package/clis/xueqiu/danjuan-utils.js +4 -4
  883. package/clis/xueqiu/earnings-date.js +1 -1
  884. package/clis/xueqiu/events.js +3 -3
  885. package/clis/xueqiu/hashtags.js +3 -3
  886. package/clis/xueqiu/stocks.js +3 -3
  887. package/clis/xueqiu/users.js +3 -3
  888. package/clis/xueqiu/utils.js +3 -3
  889. package/clis/yahoo-finance/quote.js +1 -1
  890. package/clis/yollomi/background.js +1 -1
  891. package/clis/yollomi/edit.js +1 -1
  892. package/clis/yollomi/face-swap.js +1 -1
  893. package/clis/yollomi/generate.js +1 -1
  894. package/clis/yollomi/object-remover.js +1 -1
  895. package/clis/yollomi/remove-bg.js +1 -1
  896. package/clis/yollomi/restore.js +1 -1
  897. package/clis/yollomi/try-on.js +1 -1
  898. package/clis/yollomi/upload.js +4 -4
  899. package/clis/yollomi/upscale.js +1 -1
  900. package/clis/yollomi/utils.js +4 -4
  901. package/clis/yollomi/video.js +1 -1
  902. package/clis/youtube/channel.js +4 -4
  903. package/clis/youtube/feed.js +2 -2
  904. package/clis/youtube/like.js +5 -5
  905. package/clis/youtube/playlist.js +7 -7
  906. package/clis/youtube/publish.js +11 -11
  907. package/clis/youtube/search.js +3 -3
  908. package/clis/youtube/subscribe.js +7 -7
  909. package/clis/youtube/subscriptions.js +1 -1
  910. package/clis/youtube/transcript-group.js +2 -2
  911. package/clis/youtube/transcript.js +13 -13
  912. package/clis/youtube/unlike.js +5 -5
  913. package/clis/youtube/unsubscribe.js +7 -7
  914. package/clis/youtube/utils.js +3 -3
  915. package/clis/youtube/video.js +4 -4
  916. package/clis/youtube/watch-later.js +2 -2
  917. package/clis/yuanbao/ask.js +18 -18
  918. package/clis/yuanbao/detail.js +1 -1
  919. package/clis/yuanbao/read.js +1 -1
  920. package/clis/yuanbao/shared.js +9 -9
  921. package/clis/yuanbao/status.js +1 -1
  922. package/clis/yuque/article.js +1 -1
  923. package/clis/yuque/whoami.js +1 -1
  924. package/clis/zhihu/answer-comments.js +2 -2
  925. package/clis/zhihu/answer-detail.js +3 -3
  926. package/clis/zhihu/answer.js +1 -1
  927. package/clis/zhihu/article-transform.js +1 -1
  928. package/clis/zhihu/article.js +1 -1
  929. package/clis/zhihu/auth.js +2 -2
  930. package/clis/zhihu/collection.js +3 -3
  931. package/clis/zhihu/collections.js +3 -3
  932. package/clis/zhihu/download.js +2 -2
  933. package/clis/zhihu/image-profile.js +6 -6
  934. package/clis/zhihu/paginate.js +3 -3
  935. package/clis/zhihu/pin.js +4 -4
  936. package/clis/zhihu/question.js +3 -3
  937. package/clis/zhihu/recommend.js +3 -3
  938. package/clis/zhihu/search.js +3 -3
  939. package/clis/zhihu/stats.js +3 -3
  940. package/clis/zhihu/user-arg.js +1 -1
  941. package/clis/zhihu/write-shared.js +7 -7
  942. package/clis/zlibrary/utils.js +6 -6
  943. package/clis/zsxq/auth.js +3 -3
  944. package/clis/zsxq/search.js +1 -1
  945. package/clis/zsxq/topic.js +1 -1
  946. package/clis/zsxq/utils.js +5 -5
  947. package/dist/src/adapter-source.js +1 -1
  948. package/dist/src/browser/analyze.js +1 -1
  949. package/dist/src/browser/article-extract.js +2 -2
  950. package/dist/src/browser/ax-snapshot.js +2 -2
  951. package/dist/src/browser/base-page.js +25 -25
  952. package/dist/src/browser/bridge.js +1 -1
  953. package/dist/src/browser/cdp.js +1 -1
  954. package/dist/src/browser/daemon-client.js +1 -1
  955. package/dist/src/browser/daemon-lifecycle.js +7 -7
  956. package/dist/src/browser/dom-helpers.js +35 -35
  957. package/dist/src/browser/dom-snapshot.js +19 -19
  958. package/dist/src/browser/errors.js +3 -3
  959. package/dist/src/browser/extract.js +5 -5
  960. package/dist/src/browser/find.js +13 -13
  961. package/dist/src/browser/fingerprint.js +2 -2
  962. package/dist/src/browser/html-tree.js +6 -6
  963. package/dist/src/browser/managed-chrome.js +1 -1
  964. package/dist/src/browser/network-cache.js +1 -1
  965. package/dist/src/browser/network-key.js +1 -1
  966. package/dist/src/browser/page.js +7 -7
  967. package/dist/src/browser/profile.js +2 -2
  968. package/dist/src/browser/proxy-test.js +8 -8
  969. package/dist/src/browser/shape-filter.js +1 -1
  970. package/dist/src/browser/shape.js +1 -1
  971. package/dist/src/browser/stealth.js +3 -3
  972. package/dist/src/browser/tabs.js +2 -2
  973. package/dist/src/browser/target-errors.js +1 -1
  974. package/dist/src/browser/target-resolver.js +23 -23
  975. package/dist/src/browser/trace.js +2 -2
  976. package/dist/src/browser/utils.js +1 -1
  977. package/dist/src/browser/verify-fixture.js +2 -2
  978. package/dist/src/browser-session-lock.js +1 -1
  979. package/dist/src/build-manifest.js +11 -11
  980. package/dist/src/capabilityRouting.js +1 -1
  981. package/dist/src/cli-argv-preprocess.js +1 -1
  982. package/dist/src/cli.js +32 -32
  983. package/dist/src/commanderAdapter.js +3 -3
  984. package/dist/src/commands/auth.js +3 -3
  985. package/dist/src/commands/daemon.js +1 -1
  986. package/dist/src/completion-fast.js +1 -1
  987. package/dist/src/completion.js +1 -1
  988. package/dist/src/constants.js +1 -1
  989. package/dist/src/convention-audit.js +2 -2
  990. package/dist/src/daemon-utils.js +1 -1
  991. package/dist/src/daemon.js +1 -1
  992. package/dist/src/discovery.js +2 -2
  993. package/dist/src/doctor.js +9 -9
  994. package/dist/src/download/article-download.js +15 -15
  995. package/dist/src/download/index.js +4 -4
  996. package/dist/src/download/media-download.js +1 -1
  997. package/dist/src/download/progress.js +3 -3
  998. package/dist/src/electron-apps.js +1 -1
  999. package/dist/src/errors.js +2 -2
  1000. package/dist/src/execution.js +2 -2
  1001. package/dist/src/external.js +1 -1
  1002. package/dist/src/help.js +8 -8
  1003. package/dist/src/hooks.js +1 -1
  1004. package/dist/src/interceptor.js +21 -21
  1005. package/dist/src/launcher.js +7 -7
  1006. package/dist/src/logger.js +8 -8
  1007. package/dist/src/main.js +7 -7
  1008. package/dist/src/observation/artifact.js +5 -5
  1009. package/dist/src/observation/retention.js +1 -1
  1010. package/dist/src/output.js +2 -2
  1011. package/dist/src/package-paths.js +1 -1
  1012. package/dist/src/pipeline/executor.js +1 -1
  1013. package/dist/src/pipeline/registry.js +1 -1
  1014. package/dist/src/pipeline/steps/browser.js +1 -1
  1015. package/dist/src/pipeline/steps/download.js +1 -1
  1016. package/dist/src/pipeline/steps/fetch.js +6 -6
  1017. package/dist/src/pipeline/steps/intercept.js +1 -1
  1018. package/dist/src/pipeline/steps/tap.js +17 -17
  1019. package/dist/src/pipeline/steps/transform.js +1 -1
  1020. package/dist/src/pipeline/template.js +1 -1
  1021. package/dist/src/plugin-manifest.js +1 -1
  1022. package/dist/src/plugin-scaffold.js +19 -19
  1023. package/dist/src/plugin.js +12 -12
  1024. package/dist/src/rate-limit.js +1 -1
  1025. package/dist/src/registry.js +1 -1
  1026. package/dist/src/runtime-detect.js +1 -1
  1027. package/dist/src/runtime.js +1 -1
  1028. package/dist/src/scripts/framework.js +1 -1
  1029. package/dist/src/scripts/interact.js +1 -1
  1030. package/dist/src/scripts/store.js +1 -1
  1031. package/dist/src/serialization.js +2 -2
  1032. package/dist/src/session-lease.js +1 -1
  1033. package/dist/src/snapshotFormatter.js +2 -2
  1034. package/dist/src/tui.js +12 -12
  1035. package/dist/src/user-runtime.js +1 -1
  1036. package/dist/src/utils.d.ts +12 -0
  1037. package/dist/src/utils.js +4 -4
  1038. package/dist/src/validate.js +2 -2
  1039. package/dist/src/verify.js +2 -2
  1040. package/dist/src/version.js +1 -1
  1041. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- import{ArgumentError as A}from"@jackwener/opencli/errors";const S=/^[A-Za-z0-9_-]+$/,E=/^[A-Za-z0-9_]{1,15}$/,z=/^\/(?:[^/]+|i)\/status\/(\d+)\/?$/,L=new Set(["x.com","twitter.com"]),U=new Set(["x.com","twitter.com","mobile.twitter.com"]),h=new Set(["compose","explore","help","home","i","intent","jobs","login","logout","messages","notifications","privacy","search","settings","signup","tos"]);function I(j){return L.has(j)||j.endsWith(".x.com")||j.endsWith(".twitter.com")}export function parseTweetUrl(j){const G=String(j??"").trim();if(!G)throw new A("twitter tweet URL cannot be empty","Example: ppcli twitter retweet https://x.com/jack/status/20");let V;try{V=new URL(G)}catch{throw new A(`Invalid tweet URL: ${G}`,"Use a full https://x.com/<user>/status/<id> URL")}const J=V.hostname.toLowerCase();if(V.protocol!=="https:"||!I(J))throw new A(`Invalid tweet URL host: ${G}`,"Use a full https://x.com/<user>/status/<id> URL");const X=V.pathname.match(z);if(!X?.[1])throw new A(`Could not extract tweet ID from URL: ${G}`,"Use a full https://x.com/<user>/status/<id> URL");return{id:X[1],url:V.toString()}}export function buildTwitterArticleScopeSource(j){return`
2
- const tweetId = ${JSON.stringify(j)};
1
+ import{ArgumentError as w}from"@jackwener/opencli/errors";const A=/^[A-Za-z0-9_-]+$/,x=/^[A-Za-z0-9_]{1,15}$/,q=/^\/(?:[^/]+|i)\/status\/(\d+)\/?$/,E=new Set(["x.com","twitter.com"]),S=new Set(["x.com","twitter.com","mobile.twitter.com"]),R=new Set(["compose","explore","help","home","i","intent","jobs","login","logout","messages","notifications","privacy","search","settings","signup","tos"]);function j(t){return E.has(t)||t.endsWith(".x.com")||t.endsWith(".twitter.com")}export function parseTweetUrl(t){const e=String(t??"").trim();if(!e)throw new w("twitter tweet URL cannot be empty","Example: ppcli twitter retweet https://x.com/jack/status/20");let n;try{n=new URL(e)}catch{throw new w(`Invalid tweet URL: ${e}`,"Use a full https://x.com/<user>/status/<id> URL")}const r=n.hostname.toLowerCase();if(n.protocol!=="https:"||!j(r))throw new w(`Invalid tweet URL host: ${e}`,"Use a full https://x.com/<user>/status/<id> URL");const o=n.pathname.match(q);if(!o?.[1])throw new w(`Could not extract tweet ID from URL: ${e}`,"Use a full https://x.com/<user>/status/<id> URL");return{id:o[1],url:n.toString()}}export function buildTwitterArticleScopeSource(t){return`
2
+ const tweetId = ${JSON.stringify(t)};
3
3
  const __twTweetPathRe = /^\\/(?:[^/]+|i)\\/status\\/(\\d+)\\/?$/;
4
4
  const __twIsTwitterHost = (hostname) => hostname === 'x.com'
5
5
  || hostname === 'twitter.com'
@@ -20,15 +20,15 @@ import{ArgumentError as A}from"@jackwener/opencli/errors";const S=/^[A-Za-z0-9_-
20
20
  .some((link) => __twGetStatusIdFromHref(link.href) === tweetId);
21
21
  const findTargetArticle = () => Array.from(document.querySelectorAll('article'))
22
22
  .find(__twHasLinkToTarget);
23
- `}export function sanitizeQueryId(j,G){return typeof j==="string"&&S.test(j)?j:G}export function normalizeTwitterScreenName(j){const G=String(j??"").trim();if(!G)return"";let V="";try{const J=G.startsWith("/")?new URL(G,"https://x.com"):new URL(G);if(J.protocol!=="https:"||J.username||J.password||J.port||!U.has(J.hostname))return"";const X=J.pathname.split("/").filter(Boolean);if(X.length!==1)return"";V=X[0]}catch{if(G.includes("/")||G.includes("?")||G.includes("#"))return"";V=G.replace(/^@+/,"")}if(!E.test(V))return"";if(h.has(V.toLowerCase()))return"";return V}function N(j){if(!Array.isArray(j))return{};return Object.fromEntries(j.filter((G)=>typeof G==="string"&&G).map((G)=>[G,!0]))}export function normalizeTwitterOperationFlags(j){if(Array.isArray(j))return N(j);if(!j||typeof j!=="object")return{};return Object.fromEntries(Object.entries(j).filter(([G,V])=>typeof G==="string"&&G&&typeof V==="boolean"))}function b(j){if(typeof j==="string")return{queryId:j,features:{},fieldToggles:{}};return{queryId:j?.queryId||null,features:normalizeTwitterOperationFlags(j?.features),fieldToggles:normalizeTwitterOperationFlags(j?.fieldToggles)}}export function unwrapBrowserResult(j){if(j&&typeof j==="object"&&typeof j.session==="string"&&Object.prototype.hasOwnProperty.call(j,"data"))return j.data;return j}function F(j){return j&&typeof j==="object"&&!Array.isArray(j)&&Object.keys(j).length===0}export function looksLikePrivateTwitterTimeline(j){const G=j?.data?.user?.result;if(!G||typeof G!=="object")return!1;return Boolean(F(G.timeline)||F(G.timeline_v2?.timeline))}export function normalizeTwitterGraphqlPayload(j){const G=unwrapBrowserResult(j);if(G?.data&&typeof G.data==="object")return G;if(G&&typeof G==="object"&&(Object.prototype.hasOwnProperty.call(G,"user")||Object.prototype.hasOwnProperty.call(G,"search_by_raw_query")))return{data:G};return G}export function sanitizeTwitterOperationMetadata(j,G){const V=unwrapBrowserResult(j),J=b(G);return{queryId:sanitizeQueryId(V?.queryId,J.queryId),features:Object.keys(normalizeTwitterOperationFlags(V?.features)).length>0?normalizeTwitterOperationFlags(V.features):J.features,fieldToggles:Object.keys(normalizeTwitterOperationFlags(V?.fieldToggles)).length>0?normalizeTwitterOperationFlags(V.fieldToggles):J.fieldToggles}}export function parseOperationFromBundleText(j,G){if(!j||!G)return null;const V=G.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),J=new RegExp(`queryId:"([A-Za-z0-9_-]+)"[^}]{0,400}operationName:"${V}"`),X=new RegExp(`operationName:"${V}"[^}]{0,400}queryId:"([A-Za-z0-9_-]+)"`);let Y=null,W=-1;const $=j.match(J);if($&&typeof $.index==="number"){Y=$[1];W=$.index}else{const R=j.match(X);if(R&&typeof R.index==="number"){Y=R[1];W=R.index}}if(!Y)return null;const Q=Math.max(0,W-500),_=Math.min(j.length,W+1500),H=j.slice(Q,_),C=(R)=>R?Array.from(R.matchAll(/"([^"]+)"/g)).map((Z)=>Z[1]):[],M=(R)=>Object.fromEntries((R||[]).filter((Z)=>typeof Z==="string"&&Z).map((Z)=>[Z,!0]));return{queryId:Y,features:M(C(H.match(/featureSwitches:\[([^\]]*)\]/)?.[1])),fieldToggles:M(C(H.match(/fieldToggles:\[([^\]]*)\]/)?.[1]))}}export async function resolveTwitterOperationMetadata(j,G,V){const J=parseOperationFromBundleText.toString(),X=await j.evaluate(`async () => {
24
- const operationName = ${JSON.stringify(G)};
23
+ `}export function sanitizeQueryId(t,e){return typeof t==="string"&&A.test(t)?t:e}export function normalizeTwitterScreenName(t){const e=String(t??"").trim();if(!e)return"";let n="";try{const r=e.startsWith("/")?new URL(e,"https://x.com"):new URL(e);if(r.protocol!=="https:"||r.username||r.password||r.port||!S.has(r.hostname))return"";const o=r.pathname.split("/").filter(Boolean);if(o.length!==1)return"";n=o[0]}catch{if(e.includes("/")||e.includes("?")||e.includes("#"))return"";n=e.replace(/^@+/,"")}if(!x.test(n))return"";if(R.has(n.toLowerCase()))return"";return n}function O(t){if(!Array.isArray(t))return{};return Object.fromEntries(t.filter((e)=>typeof e==="string"&&e).map((e)=>[e,!0]))}export function normalizeTwitterOperationFlags(t){if(Array.isArray(t))return O(t);if(!t||typeof t!=="object")return{};return Object.fromEntries(Object.entries(t).filter(([e,n])=>typeof e==="string"&&e&&typeof n==="boolean"))}function I(t){if(typeof t==="string")return{queryId:t,features:{},fieldToggles:{}};return{queryId:t?.queryId||null,features:normalizeTwitterOperationFlags(t?.features),fieldToggles:normalizeTwitterOperationFlags(t?.fieldToggles)}}export function unwrapBrowserResult(t){if(t&&typeof t==="object"&&typeof t.session==="string"&&Object.prototype.hasOwnProperty.call(t,"data"))return t.data;return t}function b(t){return t&&typeof t==="object"&&!Array.isArray(t)&&Object.keys(t).length===0}export function looksLikePrivateTwitterTimeline(t){const e=t?.data?.user?.result;if(!e||typeof e!=="object")return!1;return Boolean(b(e.timeline)||b(e.timeline_v2?.timeline))}export function normalizeTwitterGraphqlPayload(t){const e=unwrapBrowserResult(t);if(e?.data&&typeof e.data==="object")return e;if(e&&typeof e==="object"&&(Object.prototype.hasOwnProperty.call(e,"user")||Object.prototype.hasOwnProperty.call(e,"search_by_raw_query")))return{data:e};return e}export function sanitizeTwitterOperationMetadata(t,e){const n=unwrapBrowserResult(t),r=I(e);return{queryId:sanitizeQueryId(n?.queryId,r.queryId),features:Object.keys(normalizeTwitterOperationFlags(n?.features)).length>0?normalizeTwitterOperationFlags(n.features):r.features,fieldToggles:Object.keys(normalizeTwitterOperationFlags(n?.fieldToggles)).length>0?normalizeTwitterOperationFlags(n.fieldToggles):r.fieldToggles}}export function parseOperationFromBundleText(t,e){if(!t||!e)return null;const n=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),r=new RegExp(`queryId:"([A-Za-z0-9_-]+)"[^}]{0,400}operationName:"${n}"`),o=new RegExp(`operationName:"${n}"[^}]{0,400}queryId:"([A-Za-z0-9_-]+)"`);let s=null,l=-1;const a=t.match(r);if(a&&typeof a.index==="number"){s=a[1];l=a.index}else{const c=t.match(o);if(c&&typeof c.index==="number"){s=c[1];l=c.index}}if(!s)return null;const f=Math.max(0,l-500),p=Math.min(t.length,l+1500),m=t.slice(f,p),h=(c)=>c?Array.from(c.matchAll(/"([^"]+)"/g)).map((i)=>i[1]):[],g=(c)=>Object.fromEntries((c||[]).filter((i)=>typeof i==="string"&&i).map((i)=>[i,!0]));return{queryId:s,features:g(h(m.match(/featureSwitches:\[([^\]]*)\]/)?.[1])),fieldToggles:g(h(m.match(/fieldToggles:\[([^\]]*)\]/)?.[1]))}}export async function resolveTwitterOperationMetadata(t,e,n){const r=parseOperationFromBundleText.toString(),o=await t.evaluate(`async () => {
24
+ const operationName = ${JSON.stringify(e)};
25
25
  const keysToFlags = (keys) => Object.fromEntries((keys || []).filter((k) => typeof k === 'string' && k).map((key) => [key, true]));
26
26
  const normalizeFlags = (value) => {
27
27
  if (Array.isArray(value)) return keysToFlags(value);
28
28
  if (!value || typeof value !== 'object') return {};
29
29
  return Object.fromEntries(Object.entries(value).filter(([key, flag]) => typeof key === 'string' && key && typeof flag === 'boolean'));
30
30
  };
31
- const parseOperationFromBundleText = ${J};
31
+ const parseOperationFromBundleText = ${r};
32
32
 
33
33
  try {
34
34
  const controller = new AbortController();
@@ -73,4 +73,4 @@ import{ArgumentError as A}from"@jackwener/opencli/errors";const S=/^[A-Za-z0-9_-
73
73
  } catch {}
74
74
 
75
75
  return null;
76
- }`);return sanitizeTwitterOperationMetadata(X,V)}export async function resolveTwitterQueryId(j,G,V){return(await resolveTwitterOperationMetadata(j,G,V)).queryId}export function extractMedia(j){const G=j?.extended_entities?.media||j?.entities?.media;if(!Array.isArray(G)||G.length===0)return{has_media:!1,media_urls:[],media_posters:[]};const V=[],J=[];for(const X of G){if(!X)continue;if(X.type==="video"||X.type==="animated_gif"){const $=(X.video_info?.variants||[]).find((Q)=>Q?.content_type==="video/mp4")?.url||X.media_url_https;if($){V.push($);J.push(X.media_url_https||$)}}else if(X.media_url_https){V.push(X.media_url_https);J.push(X.media_url_https)}}return{has_media:V.length>0,media_urls:V,media_posters:J}}export function extractCard(j){const G=j?.card?.legacy;if(!G)return null;const V=Array.isArray(G.binding_values)?G.binding_values:[],J=new Map;for(const P of V)if(P&&typeof P.key==="string")J.set(P.key,P.value);const X=(P)=>{const O=J.get(P);return typeof O?.string_value==="string"&&O.string_value.length>0?O.string_value:void 0},Y=(P)=>{const B=J.get(P)?.image_value?.url;return typeof B==="string"&&B.length>0?B:void 0},W=X("title"),$=X("description"),Q=X("domain"),_=X("card_url"),H=Y("thumbnail_image_large")||Y("photo_image_full_size_large")||Y("summary_photo_image_large"),C=Array.isArray(j?.legacy?.entities?.urls)?j.legacy.entities.urls:[],R=(_?C.find((P)=>P?.url===_||P?.expanded_url===_):void 0)?.expanded_url,Z=typeof R==="string"&&R.length>0?R:_;let K=Q;if(!K&&Z)try{K=new URL(Z).hostname}catch{}if(!Z&&!W&&!$)return null;const D={name:G.name};if(W)D.title=W;if($)D.description=$;if(H)D.image_url=H;if(Z)D.url=Z;if(K)D.domain=K;return D}export function extractQuotedTweet(j){if(!j?.legacy?.is_quote_status)return null;const V=j?.quoted_status_result?.result??j?.legacy?.quoted_status_result?.result;if(!V)return null;const J=V.tweet||V;if(!J||typeof J!=="object")return null;const X=J.legacy&&typeof J.legacy==="object"?J.legacy:{};if(typeof J.rest_id!=="string"||!J.rest_id.trim())return null;const Y=J.core?.user_results?.result,W=Y?.legacy?.screen_name,$=Y?.core?.screen_name,Q=typeof W==="string"&&W.trim()?W.trim():typeof $==="string"&&$.trim()?$.trim():"";if(!E.test(Q))return null;const _=Y?.legacy?.name,H=Y?.core?.name,C=typeof _==="string"?_:typeof H==="string"?H:"",M=J.note_tweet?.note_tweet_results?.result?.text,R=typeof M==="string"&&M.length>0?M:typeof X.full_text==="string"?X.full_text:"",Z=extractMedia(X),K=extractCard(J);if(!R&&!Z.has_media&&!K)return null;const D={id:J.rest_id,author:Q,name:C,text:R,created_at:typeof X.created_at==="string"?X.created_at:"",url:`https://x.com/${Q}/status/${J.rest_id}`,has_media:Z.has_media,media_urls:Z.media_urls,media_posters:Z.media_posters};if(K)D.card=K;return D}export function describeTwitterApiError(j,G,V){const J=Number(G),X=`HTTP ${G}: ${j} fetch failed`;let Y;if(J===429)Y="rate-limited by Twitter (session quota); retry after cooldown (typically 15-30 min)";else if(J===401)Y="auth failed (cookie expired or invalidated); re-login required";else if(J===403)Y="forbidden (cookie lacks scope, or resource is private)";else if(J===404)Y="resource not found (deleted, suspended, or private)";else if(J>=500&&J<600)Y="Twitter server error; retry later";else Y="possibly queryId expired, schema change, or transient";if(V)Y=`${Y} (${V})`;return`${X} — ${Y}`}export const __test__={sanitizeQueryId,normalizeTwitterOperationFlags,sanitizeTwitterOperationMetadata,unwrapBrowserResult,normalizeTwitterGraphqlPayload,normalizeTwitterScreenName,extractMedia,extractCard,extractQuotedTweet,parseTweetUrl,buildTwitterArticleScopeSource,looksLikePrivateTwitterTimeline,parseOperationFromBundleText,describeTwitterApiError};
76
+ }`);return sanitizeTwitterOperationMetadata(o,n)}export async function resolveTwitterQueryId(t,e,n){return(await resolveTwitterOperationMetadata(t,e,n)).queryId}export function extractMedia(t){const e=t?.extended_entities?.media||t?.entities?.media;if(!Array.isArray(e)||e.length===0)return{has_media:!1,media_urls:[],media_posters:[]};const n=[],r=[];for(const o of e){if(!o)continue;if(o.type==="video"||o.type==="animated_gif"){const a=(o.video_info?.variants||[]).find((f)=>f?.content_type==="video/mp4")?.url||o.media_url_https;if(a){n.push(a);r.push(o.media_url_https||a)}}else if(o.media_url_https){n.push(o.media_url_https);r.push(o.media_url_https)}}return{has_media:n.length>0,media_urls:n,media_posters:r}}export function extractCard(t){const e=t?.card?.legacy;if(!e)return null;const n=Array.isArray(e.binding_values)?e.binding_values:[],r=new Map;for(const u of n)if(u&&typeof u.key==="string")r.set(u.key,u.value);const o=(u)=>{const _=r.get(u);return typeof _?.string_value==="string"&&_.string_value.length>0?_.string_value:void 0},s=(u)=>{const T=r.get(u)?.image_value?.url;return typeof T==="string"&&T.length>0?T:void 0},l=o("title"),a=o("description"),f=o("domain"),p=o("card_url"),m=s("thumbnail_image_large")||s("photo_image_full_size_large")||s("summary_photo_image_large"),h=Array.isArray(t?.legacy?.entities?.urls)?t.legacy.entities.urls:[],c=(p?h.find((u)=>u?.url===p||u?.expanded_url===p):void 0)?.expanded_url,i=typeof c==="string"&&c.length>0?c:p;let y=f;if(!y&&i)try{y=new URL(i).hostname}catch{}if(!i&&!l&&!a)return null;const d={name:e.name};if(l)d.title=l;if(a)d.description=a;if(m)d.image_url=m;if(i)d.url=i;if(y)d.domain=y;return d}export function extractQuotedTweet(t){if(!t?.legacy?.is_quote_status)return null;const n=t?.quoted_status_result?.result??t?.legacy?.quoted_status_result?.result;if(!n)return null;const r=n.tweet||n;if(!r||typeof r!=="object")return null;const o=r.legacy&&typeof r.legacy==="object"?r.legacy:{};if(typeof r.rest_id!=="string"||!r.rest_id.trim())return null;const s=r.core?.user_results?.result,l=s?.legacy?.screen_name,a=s?.core?.screen_name,f=typeof l==="string"&&l.trim()?l.trim():typeof a==="string"&&a.trim()?a.trim():"";if(!x.test(f))return null;const p=s?.legacy?.name,m=s?.core?.name,h=typeof p==="string"?p:typeof m==="string"?m:"",g=r.note_tweet?.note_tweet_results?.result?.text,c=typeof g==="string"&&g.length>0?g:typeof o.full_text==="string"?o.full_text:"",i=extractMedia(o),y=extractCard(r);if(!c&&!i.has_media&&!y)return null;const d={id:r.rest_id,author:f,name:h,text:c,created_at:typeof o.created_at==="string"?o.created_at:"",url:`https://x.com/${f}/status/${r.rest_id}`,has_media:i.has_media,media_urls:i.media_urls,media_posters:i.media_posters};if(y)d.card=y;return d}export function describeTwitterApiError(t,e,n){const r=Number(e),o=`HTTP ${e}: ${t} fetch failed`;let s;if(r===429)s="rate-limited by Twitter (session quota); retry after cooldown (typically 15-30 min)";else if(r===401)s="auth failed (cookie expired or invalidated); re-login required";else if(r===403)s="forbidden (cookie lacks scope, or resource is private)";else if(r===404)s="resource not found (deleted, suspended, or private)";else if(r>=500&&r<600)s="Twitter server error; retry later";else s="possibly queryId expired, schema change, or transient";if(n)s=`${s} (${n})`;return`${o} — ${s}`}export const __test__={sanitizeQueryId,normalizeTwitterOperationFlags,sanitizeTwitterOperationMetadata,unwrapBrowserResult,normalizeTwitterGraphqlPayload,normalizeTwitterScreenName,extractMedia,extractCard,extractQuotedTweet,parseTweetUrl,buildTwitterArticleScopeSource,looksLikePrivateTwitterTimeline,parseOperationFromBundleText,describeTwitterApiError};
@@ -1,4 +1,4 @@
1
- import{cli as L,Strategy as O}from"@jackwener/opencli/registry";import{AuthRequiredError as W,CommandExecutionError as j}from"@jackwener/opencli/errors";import{BROWSER_JSON_SNIFF_FN as A,throwIfLoginWall as F}from"@jackwener/opencli/utils";import{extractMedia as U,extractCard as h,extractQuotedTweet as D,describeTwitterApiError as N}from"./shared.js";import{TWITTER_BEARER_TOKEN as S,applyTopByEngagement as m}from"./utils.js";const R="nBS-WpgA6ZG0CyNHD517JQ",k={responsive_web_graphql_exclude_directive_enabled:!0,verified_phone_label_enabled:!1,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_timeline_navigation_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,longform_notetweets_consumption_enabled:!0,longform_notetweets_rich_text_read_enabled:!0,longform_notetweets_inline_media_enabled:!0,freedom_of_speech_not_reach_fetch_enabled:!0},y={withArticleRichContentState:!0,withArticlePlainText:!1};function C(X,V){const z={focalTweetId:X,referrer:"tweet",with_rux_injections:!1,includePromotedContent:!1,rankingMode:"Recency",withCommunity:!0,withQuickPromoteEligibilityTweetFields:!0,withBirdwatchNotes:!0,withVoice:!0};if(V)z.cursor=V;return`/i/api/graphql/${R}/TweetDetail?variables=${encodeURIComponent(JSON.stringify(z))}&features=${encodeURIComponent(JSON.stringify(k))}&fieldToggles=${encodeURIComponent(JSON.stringify(y))}`}function b(X,V){if(!X)return null;const z=X.tweet||X,P=z.legacy||{};if(!z.rest_id||V.has(z.rest_id))return null;V.add(z.rest_id);const $=z.core?.user_results?.result,q=z.note_tweet?.note_tweet_results?.result?.text,Z=$?.legacy?.screen_name||$?.core?.screen_name||"unknown",H=$?.legacy?.description||"";return{id:z.rest_id,author:Z,bio:H,text:q||P.full_text||"",likes:P.favorite_count||0,retweets:P.retweet_count||0,in_reply_to:P.in_reply_to_status_id_str||void 0,created_at:P.created_at,url:`https://x.com/${Z}/status/${z.rest_id}`,...U(P),card:h(z),quoted_tweet:D(z)}}function B(X,V){const z=[];let P=null;const $=X?.data?.threaded_conversation_with_injections_v2?.instructions||X?.data?.tweetResult?.result?.timeline?.instructions||[];for(const q of $)for(const Z of q.entries||[]){const H=Z.content;if(H?.entryType==="TimelineTimelineCursor"||H?.__typename==="TimelineTimelineCursor"){if(H.cursorType==="Bottom"||H.cursorType==="ShowMore")P=H.value;continue}if(Z.entryId?.startsWith("cursor-bottom-")||Z.entryId?.startsWith("cursor-showMore-")){P=H?.itemContent?.value||H?.value||P;continue}const K=b(H?.itemContent?.tweet_results?.result,V);if(K)z.push(K);for(const J of H?.items||[]){const Y=b(J.item?.itemContent?.tweet_results?.result,V);if(Y)z.push(Y)}}return{tweets:z,nextCursor:P}}export const __test__={parseTweetDetail:B};L({site:"twitter",name:"thread",access:"read",description:"Get a tweet thread (original + all replies)",domain:"x.com",strategy:O.COOKIE,browser:!0,args:[{name:"tweet-id",positional:!0,type:"string",required:!0,help:"Tweet numeric ID (e.g. 1234567890) or full status URL"},{name:"limit",type:"int",default:50},{name:"top-by-engagement",type:"int",default:0,help:"When set to N>0, re-rank the thread by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the conversation's structural ordering."}],columns:["id","author","bio","text","likes","retweets","url","has_media","media_urls","media_posters","card","quoted_tweet"],func:async(X,V)=>{let z=V["tweet-id"];const P=z.match(/\/status\/(\d+)/);if(P)z=P[1];const q=(await X.getCookies({url:"https://x.com"})).find((f)=>f.name==="ct0")?.value||null;if(!q)throw new W("x.com","Not logged into x.com (no ct0 cookie)");const Z=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(S)}`,"X-Csrf-Token":q,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),H=[],K=new Set;let J=null;for(let f=0;f<5;f++){const Q=C(z,J),v=F(await X.evaluate(`async () => {
2
- ${A}
3
- return await fetchJsonOrLoginWall("${Q}", { headers: ${Z}, credentials: 'include' });
4
- }`),{url:Q});if(v?.error){if(H.length===0)throw new j(N("TweetDetail",v.error));break}const{tweets:M,nextCursor:G}=B(v,K);H.push(...M);if(!G||G===J)break;J=G}const Y=H.slice(0,V.limit);return m(Y,V["top-by-engagement"])}});
1
+ import{cli as g,Strategy as y}from"@jackwener/opencli/registry";import{AuthRequiredError as T,CommandExecutionError as b}from"@jackwener/opencli/errors";import{BROWSER_JSON_SNIFF_FN as x,throwIfLoginWall as v}from"@jackwener/opencli/utils";import{extractMedia as C,extractCard as E,extractQuotedTweet as k,describeTwitterApiError as I}from"./shared.js";import{TWITTER_BEARER_TOKEN as R,applyTopByEngagement as S}from"./utils.js";const N="nBS-WpgA6ZG0CyNHD517JQ",A={responsive_web_graphql_exclude_directive_enabled:!0,verified_phone_label_enabled:!1,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_timeline_navigation_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,longform_notetweets_consumption_enabled:!0,longform_notetweets_rich_text_read_enabled:!0,longform_notetweets_inline_media_enabled:!0,freedom_of_speech_not_reach_fetch_enabled:!0},O={withArticleRichContentState:!0,withArticlePlainText:!1};function D(o,n){const e={focalTweetId:o,referrer:"tweet",with_rux_injections:!1,includePromotedContent:!1,rankingMode:"Recency",withCommunity:!0,withQuickPromoteEligibilityTweetFields:!0,withBirdwatchNotes:!0,withVoice:!0};if(n)e.cursor=n;return`/i/api/graphql/${N}/TweetDetail?variables=${encodeURIComponent(JSON.stringify(e))}&features=${encodeURIComponent(JSON.stringify(A))}&fieldToggles=${encodeURIComponent(JSON.stringify(O))}`}function f(o,n){if(!o)return null;const e=o.tweet||o,r=e.legacy||{};if(!e.rest_id||n.has(e.rest_id))return null;n.add(e.rest_id);const s=e.core?.user_results?.result,a=e.note_tweet?.note_tweet_results?.result?.text,i=s?.legacy?.screen_name||s?.core?.screen_name||"unknown",t=s?.legacy?.description||"";return{id:e.rest_id,author:i,bio:t,text:a||r.full_text||"",likes:r.favorite_count||0,retweets:r.retweet_count||0,in_reply_to:r.in_reply_to_status_id_str||void 0,created_at:r.created_at,url:`https://x.com/${i}/status/${e.rest_id}`,...C(r),card:E(e),quoted_tweet:k(e)}}function p(o,n){const e=[];let r=null;const s=o?.data?.threaded_conversation_with_injections_v2?.instructions||o?.data?.tweetResult?.result?.timeline?.instructions||[];for(const a of s)for(const i of a.entries||[]){const t=i.content;if(t?.entryType==="TimelineTimelineCursor"||t?.__typename==="TimelineTimelineCursor"){if(t.cursorType==="Bottom"||t.cursorType==="ShowMore")r=t.value;continue}if(i.entryId?.startsWith("cursor-bottom-")||i.entryId?.startsWith("cursor-showMore-")){r=t?.itemContent?.value||t?.value||r;continue}const c=f(t?.itemContent?.tweet_results?.result,n);if(c)e.push(c);for(const l of t?.items||[]){const u=f(l.item?.itemContent?.tweet_results?.result,n);if(u)e.push(u)}}return{tweets:e,nextCursor:r}}export const __test__={parseTweetDetail:p};g({site:"twitter",name:"thread",access:"read",description:"Get a tweet thread (original + all replies)",domain:"x.com",strategy:y.COOKIE,browser:!0,args:[{name:"tweet-id",positional:!0,type:"string",required:!0,help:"Tweet numeric ID (e.g. 1234567890) or full status URL"},{name:"limit",type:"int",default:50},{name:"top-by-engagement",type:"int",default:0,help:"When set to N>0, re-rank the thread by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the conversation's structural ordering."}],columns:["id","author","bio","text","likes","retweets","url","has_media","media_urls","media_posters","card","quoted_tweet"],func:async(o,n)=>{let e=n["tweet-id"];const r=e.match(/\/status\/(\d+)/);if(r)e=r[1];const a=(await o.getCookies({url:"https://x.com"})).find((_)=>_.name==="ct0")?.value||null;if(!a)throw new T("x.com","Not logged into x.com (no ct0 cookie)");const i=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(R)}`,"X-Csrf-Token":a,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),t=[],c=new Set;let l=null;for(let _=0;_<5;_++){const w=D(e,l),d=v(await o.evaluate(`async () => {
2
+ ${x}
3
+ return await fetchJsonOrLoginWall("${w}", { headers: ${i}, credentials: 'include' });
4
+ }`),{url:w});if(d?.error){if(t.length===0)throw new b(I("TweetDetail",d.error));break}const{tweets:h,nextCursor:m}=p(d,c);t.push(...h);if(!m||m===l)break;l=m}const u=t.slice(0,n.limit);return S(u,n["top-by-engagement"])}});
@@ -1,4 +1,4 @@
1
- import{AuthRequiredError as A,CommandExecutionError as R}from"@jackwener/opencli/errors";import{cli as b,Strategy as v}from"@jackwener/opencli/registry";import{resolveTwitterQueryId as C,extractMedia as N,extractCard as _,extractQuotedTweet as x,describeTwitterApiError as I}from"./shared.js";import{TWITTER_BEARER_TOKEN as g,applyTopByEngagement as y}from"./utils.js";const E="c-CzHF1LboFilMpsx4ZCrQ",m="BKB7oi212Fi7kQtCBGE4zA",p=100,d={"for-you":{endpoint:"HomeTimeline",method:"GET",fallbackQueryId:E},following:{endpoint:"HomeLatestTimeline",method:"POST",fallbackQueryId:m}},T={rweb_video_screen_enabled:!1,profile_label_improvements_pcf_label_in_post_enabled:!0,rweb_tipjar_consumption_enabled:!0,verified_phone_label_enabled:!1,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_timeline_navigation_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,premium_content_api_read_enabled:!1,communities_web_enable_tweet_community_results_fetch:!0,c9s_tweet_anatomy_moderator_badge_enabled:!0,responsive_web_grok_analyze_button_fetch_trends_enabled:!1,responsive_web_grok_analyze_post_followups_enabled:!0,responsive_web_jetfuel_frame:!1,responsive_web_grok_share_attachment_enabled:!0,articles_preview_enabled:!0,responsive_web_edit_tweet_api_enabled:!0,graphql_is_translatable_rweb_tweet_is_translatable_enabled:!0,view_counts_everywhere_api_enabled:!0,longform_notetweets_consumption_enabled:!0,responsive_web_twitter_article_tweet_consumption_enabled:!0,tweet_awards_web_tipping_enabled:!1,responsive_web_grok_show_grok_translated_post:!1,responsive_web_grok_analysis_button_from_backend:!1,creator_subscriptions_quote_tweet_preview_enabled:!1,freedom_of_speech_not_reach_fetch_enabled:!0,standardized_nudges_misinfo:!0,tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:!0,longform_notetweets_rich_text_read_enabled:!0,longform_notetweets_inline_media_enabled:!0,responsive_web_grok_image_annotation_enabled:!0,responsive_web_enhance_cards_enabled:!1};function L(Z,$,z){const J={count:$,includePromotedContent:!1,latestControlAvailable:!0,requestContext:"launch"};if(Z==="for-you")J.withCommunity=!0;if(Z==="following")J.seenTweetIds=[];if(z)J.cursor=z;return J}function f(Z,$,z){return`/i/api/graphql/${Z}/${$}?variables=${encodeURIComponent(JSON.stringify(z))}&features=${encodeURIComponent(JSON.stringify(T))}`}function O(Z,$){if(!Z)return null;const z=Z.tweet||Z,J=z.legacy||{};if(!z.rest_id||$.has(z.rest_id))return null;$.add(z.rest_id);const K=z.core?.user_results?.result,G=K?.legacy?.screen_name||K?.core?.screen_name||"unknown",V=K?.legacy?.description||"",F=z.note_tweet?.note_tweet_results?.result?.text,X=z.views?.count?parseInt(z.views.count,10):0;return{id:z.rest_id,author:G,bio:V,text:F||J.full_text||"",likes:J.favorite_count||0,retweets:J.retweet_count||0,replies:J.reply_count||0,views:X,created_at:J.created_at||"",url:`https://x.com/${G}/status/${z.rest_id}`,...N(J),card:_(z),quoted_tweet:x(z)}}function Q(Z,$){const z=[];let J=null;const K=Z?.data?.home?.home_timeline_urt?.instructions||[];for(const G of K)for(const V of G.entries||[]){const F=V.content;if(F?.entryType==="TimelineTimelineCursor"||F?.__typename==="TimelineTimelineCursor"){if(F.cursorType==="Bottom")J=F.value;continue}if(V.entryId?.startsWith("cursor-bottom-")){J=F?.value||J;continue}const X=F?.itemContent?.tweet_results?.result;if(X){if(F?.itemContent?.promotedMetadata)continue;const B=O(X,$);if(B)z.push(B);continue}for(const B of F?.items||[]){const Y=B.item?.itemContent?.tweet_results?.result;if(Y){if(B.item?.itemContent?.promotedMetadata)continue;const W=O(Y,$);if(W)z.push(W)}}}return{tweets:z,nextCursor:J}}b({site:"twitter",name:"timeline",access:"read",description:"Fetch the logged-in user's home timeline (for-you algorithmic feed by default; pass --type following for the chronological feed of accounts you follow)",domain:"x.com",strategy:v.COOKIE,browser:!0,args:[{name:"type",default:"for-you",choices:["for-you","following"],help:"Which home-timeline feed to read. Default for-you (algorithmic). Use following for the chronological feed of accounts you follow."},{name:"limit",type:"int",default:20,help:"Maximum number of tweets to return (default 20)."},{name:"top-by-engagement",type:"int",default:0,help:"When set to N>0, re-rank the timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering."}],columns:["id","author","bio","text","likes","retweets","replies","views","created_at","url","has_media","media_urls","media_posters","card","quoted_tweet"],func:async(Z,$)=>{const z=$.limit||20,J=$.type==="following"?"following":"for-you",{endpoint:K,method:G,fallbackQueryId:V}=d[J],X=(await Z.getCookies({url:"https://x.com"})).find((D)=>D.name==="ct0")?.value||null;if(!X)throw new A("x.com","Not logged into x.com (no ct0 cookie)");const B=await C(Z,K,V),Y=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(g)}`,"X-Csrf-Token":X,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),W=[],S=new Set;let P=null;for(let D=0;D<p&&W.length<z;D++){const U=Math.min(40,z-W.length+5),h=L(J,U,P),M=f(B,K,h),j=await Z.evaluate(`async () => {
2
- const r = await fetch("${M}", { method: "${G}", headers: ${Y}, credentials: 'include' });
1
+ import{AuthRequiredError as I,CommandExecutionError as x}from"@jackwener/opencli/errors";import{cli as A,Strategy as N}from"@jackwener/opencli/registry";import{resolveTwitterQueryId as M,extractMedia as O,extractCard as q,extractQuotedTweet as R,describeTwitterApiError as S}from"./shared.js";import{TWITTER_BEARER_TOKEN as U,applyTopByEngagement as Q}from"./utils.js";const B="c-CzHF1LboFilMpsx4ZCrQ",H="BKB7oi212Fi7kQtCBGE4zA",z=100,L={"for-you":{endpoint:"HomeTimeline",method:"GET",fallbackQueryId:B},following:{endpoint:"HomeLatestTimeline",method:"POST",fallbackQueryId:H}},D={rweb_video_screen_enabled:!1,profile_label_improvements_pcf_label_in_post_enabled:!0,rweb_tipjar_consumption_enabled:!0,verified_phone_label_enabled:!1,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_timeline_navigation_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,premium_content_api_read_enabled:!1,communities_web_enable_tweet_community_results_fetch:!0,c9s_tweet_anatomy_moderator_badge_enabled:!0,responsive_web_grok_analyze_button_fetch_trends_enabled:!1,responsive_web_grok_analyze_post_followups_enabled:!0,responsive_web_jetfuel_frame:!1,responsive_web_grok_share_attachment_enabled:!0,articles_preview_enabled:!0,responsive_web_edit_tweet_api_enabled:!0,graphql_is_translatable_rweb_tweet_is_translatable_enabled:!0,view_counts_everywhere_api_enabled:!0,longform_notetweets_consumption_enabled:!0,responsive_web_twitter_article_tweet_consumption_enabled:!0,tweet_awards_web_tipping_enabled:!1,responsive_web_grok_show_grok_translated_post:!1,responsive_web_grok_analysis_button_from_backend:!1,creator_subscriptions_quote_tweet_preview_enabled:!1,freedom_of_speech_not_reach_fetch_enabled:!0,standardized_nudges_misinfo:!0,tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:!0,longform_notetweets_rich_text_read_enabled:!0,longform_notetweets_inline_media_enabled:!0,responsive_web_grok_image_annotation_enabled:!0,responsive_web_enhance_cards_enabled:!1};function w(o,n,e){const t={count:n,includePromotedContent:!1,latestControlAvailable:!0,requestContext:"launch"};if(o==="for-you")t.withCommunity=!0;if(o==="following")t.seenTweetIds=[];if(e)t.cursor=e;return t}function b(o,n,e){return`/i/api/graphql/${o}/${n}?variables=${encodeURIComponent(JSON.stringify(e))}&features=${encodeURIComponent(JSON.stringify(D))}`}function h(o,n){if(!o)return null;const e=o.tweet||o,t=e.legacy||{};if(!e.rest_id||n.has(e.rest_id))return null;n.add(e.rest_id);const i=e.core?.user_results?.result,_=i?.legacy?.screen_name||i?.core?.screen_name||"unknown",c=i?.legacy?.description||"",r=e.note_tweet?.note_tweet_results?.result?.text,a=e.views?.count?parseInt(e.views.count,10):0;return{id:e.rest_id,author:_,bio:c,text:r||t.full_text||"",likes:t.favorite_count||0,retweets:t.retweet_count||0,replies:t.reply_count||0,views:a,created_at:t.created_at||"",url:`https://x.com/${_}/status/${e.rest_id}`,...O(t),card:q(e),quoted_tweet:R(e)}}function g(o,n){const e=[];let t=null;const i=o?.data?.home?.home_timeline_urt?.instructions||[];for(const _ of i)for(const c of _.entries||[]){const r=c.content;if(r?.entryType==="TimelineTimelineCursor"||r?.__typename==="TimelineTimelineCursor"){if(r.cursorType==="Bottom")t=r.value;continue}if(c.entryId?.startsWith("cursor-bottom-")){t=r?.value||t;continue}const a=r?.itemContent?.tweet_results?.result;if(a){if(r?.itemContent?.promotedMetadata)continue;const l=h(a,n);if(l)e.push(l);continue}for(const l of r?.items||[]){const u=l.item?.itemContent?.tweet_results?.result;if(u){if(l.item?.itemContent?.promotedMetadata)continue;const s=h(u,n);if(s)e.push(s)}}}return{tweets:e,nextCursor:t}}A({site:"twitter",name:"timeline",access:"read",description:"Fetch the logged-in user's home timeline (for-you algorithmic feed by default; pass --type following for the chronological feed of accounts you follow)",domain:"x.com",strategy:N.COOKIE,browser:!0,args:[{name:"type",default:"for-you",choices:["for-you","following"],help:"Which home-timeline feed to read. Default for-you (algorithmic). Use following for the chronological feed of accounts you follow."},{name:"limit",type:"int",default:20,help:"Maximum number of tweets to return (default 20)."},{name:"top-by-engagement",type:"int",default:0,help:"When set to N>0, re-rank the timeline by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps X's native ordering."}],columns:["id","author","bio","text","likes","retweets","replies","views","created_at","url","has_media","media_urls","media_posters","card","quoted_tweet"],func:async(o,n)=>{const e=n.limit||20,t=n.type==="following"?"following":"for-you",{endpoint:i,method:_,fallbackQueryId:c}=L[t],a=(await o.getCookies({url:"https://x.com"})).find((d)=>d.name==="ct0")?.value||null;if(!a)throw new I("x.com","Not logged into x.com (no ct0 cookie)");const l=await M(o,i,c),u=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(U)}`,"X-Csrf-Token":a,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),s=[],y=new Set;let m=null;for(let d=0;d<z&&s.length<e;d++){const T=Math.min(40,e-s.length+5),k=w(t,T,m),E=b(l,i,k),f=await o.evaluate(`async () => {
2
+ const r = await fetch("${E}", { method: "${_}", headers: ${u}, credentials: 'include' });
3
3
  return r.ok ? await r.json() : { error: r.status };
4
- }`);if(j?.error){if(W.length===0)throw new R(I(K,j.error));break}const{tweets:k,nextCursor:q}=Q(j,S);W.push(...k);if(!q||q===P)break;P=q}const H=W.slice(0,z);return y(H,$["top-by-engagement"])}});export const __test__={buildTimelineVariables:L,buildHomeTimelineUrl:f,parseHomeTimeline:Q};
4
+ }`);if(f?.error){if(s.length===0)throw new x(S(i,f.error));break}const{tweets:C,nextCursor:p}=g(f,y);s.push(...C);if(!p||p===m)break;m=p}const v=s.slice(0,e);return Q(v,n["top-by-engagement"])}});export const __test__={buildTimelineVariables:w,buildHomeTimelineUrl:b,parseHomeTimeline:g};
@@ -1,4 +1,4 @@
1
- import{cli as H,Strategy as I}from"@jackwener/opencli/registry";import{AuthRequiredError as J,EmptyResultError as K}from"@jackwener/opencli/errors";H({site:"twitter",name:"trending",access:"read",description:"Twitter/X trending topics",domain:"x.com",strategy:I.COOKIE,browser:!0,args:[{name:"limit",type:"int",default:20,help:"Number of trends to show"}],columns:["rank","topic","category"],func:async(b,z)=>{const B=z.limit||20;await b.goto("https://x.com/explore/tabs/trending");await b.wait(3);if(!(await b.getCookies({url:"https://x.com"})).find((G)=>G.name==="ct0")?.value)throw new J("x.com","Not logged into x.com (no ct0 cookie)");await b.wait(2);const x=await b.evaluate(`(() => {
1
+ import{cli as i,Strategy as c}from"@jackwener/opencli/registry";import{AuthRequiredError as s,EmptyResultError as a}from"@jackwener/opencli/errors";i({site:"twitter",name:"trending",access:"read",description:"Twitter/X trending topics",domain:"x.com",strategy:c.COOKIE,browser:!0,args:[{name:"limit",type:"int",default:20,help:"Number of trends to show"}],columns:["rank","topic","category"],func:async(t,r)=>{const o=r.limit||20;await t.goto("https://x.com/explore/tabs/trending");await t.wait(3);if(!(await t.getCookies({url:"https://x.com"})).find((n)=>n.name==="ct0")?.value)throw new s("x.com","Not logged into x.com (no ct0 cookie)");await t.wait(2);const e=await t.evaluate(`(() => {
2
2
  const items = [];
3
3
  const cells = document.querySelectorAll('[data-testid="trend"]');
4
4
  cells.forEach((cell) => {
@@ -15,4 +15,4 @@ import{cli as H,Strategy as I}from"@jackwener/opencli/registry";import{AuthRequi
15
15
  items.push({ rank: items.length + 1, topic, category });
16
16
  });
17
17
  return items;
18
- })()`);if(!Array.isArray(x)||x.length===0)throw new K("twitter trending","No trends found. The page structure may have changed.");return x.slice(0,B)}});
18
+ })()`);if(!Array.isArray(e)||e.length===0)throw new a("twitter trending","No trends found. The page structure may have changed.");return e.slice(0,o)}});
@@ -1,23 +1,23 @@
1
- import{cli as r,Strategy as a}from"@jackwener/opencli/registry";import{ArgumentError as C,AuthRequiredError as I,CommandExecutionError as h,EmptyResultError as e}from"@jackwener/opencli/errors";import{resolveTwitterOperationMetadata as A,sanitizeQueryId as jj,extractMedia as qj,extractQuotedTweet as Fj,normalizeTwitterGraphqlPayload as y,unwrapBrowserResult as D,normalizeTwitterScreenName as R,describeTwitterApiError as v}from"./shared.js";import{TWITTER_BEARER_TOKEN as w,applyTopByEngagement as Gj}from"./utils.js";const c="lrMzG9qPQHpqJdP3AbM-bQ",d="IGgvgiOx4QZndDHuD3x9TQ",m=100,g=100,B=m*g,p=2,_={rweb_video_screen_enabled:!0,rweb_cashtags_enabled:!0,payments_enabled:!1,profile_label_improvements_pcf_label_in_post_enabled:!0,responsive_web_profile_redirect_enabled:!0,rweb_tipjar_consumption_enabled:!0,verified_phone_label_enabled:!1,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_timeline_navigation_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,premium_content_api_read_enabled:!1,communities_web_enable_tweet_community_results_fetch:!0,c9s_tweet_anatomy_moderator_badge_enabled:!0,responsive_web_grok_analyze_button_fetch_trends_enabled:!1,responsive_web_grok_analyze_post_followups_enabled:!0,rweb_cashtags_composer_attachment_enabled:!0,responsive_web_jetfuel_frame:!0,responsive_web_grok_share_attachment_enabled:!0,responsive_web_grok_annotations_enabled:!0,articles_preview_enabled:!0,responsive_web_edit_tweet_api_enabled:!0,graphql_is_translatable_rweb_tweet_is_translatable_enabled:!0,view_counts_everywhere_api_enabled:!0,longform_notetweets_consumption_enabled:!0,responsive_web_twitter_article_tweet_consumption_enabled:!0,tweet_awards_web_tipping_enabled:!1,content_disclosure_indicator_enabled:!0,content_disclosure_ai_generated_indicator_enabled:!0,responsive_web_grok_show_grok_translated_post:!1,responsive_web_grok_analysis_button_from_backend:!0,post_ctas_fetch_enabled:!1,freedom_of_speech_not_reach_fetch_enabled:!0,standardized_nudges_misinfo:!0,tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:!0,longform_notetweets_rich_text_read_enabled:!0,longform_notetweets_inline_media_enabled:!0,responsive_web_grok_image_annotation_enabled:!0,responsive_web_grok_imagine_annotation_enabled:!0,responsive_web_grok_community_note_auto_translation_is_enabled:!1,responsive_web_enhance_cards_enabled:!1},O={withPayments:!0,withAuxiliaryUserLabels:!0,withArticleRichContentState:!0,withArticlePlainText:!0,withArticleSummaryText:!0,withArticleVoiceOver:!0,withGrokAnalyze:!0,withDisallowedReplyControls:!0},S={hidden_profile_subscriptions_enabled:!0,profile_label_improvements_pcf_label_in_post_enabled:!0,responsive_web_profile_redirect_enabled:!0,rweb_tipjar_consumption_enabled:!0,responsive_web_graphql_exclude_directive_enabled:!0,verified_phone_label_enabled:!1,subscriptions_verification_info_is_identity_verified_enabled:!0,subscriptions_verification_info_verified_since_enabled:!0,highlights_tweets_tab_ui_enabled:!0,responsive_web_twitter_article_notes_tab_enabled:!0,subscriptions_feature_can_gift_premium:!0,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,responsive_web_graphql_timeline_navigation_enabled:!0},T={withPayments:!0,withAuxiliaryUserLabels:!0},i={queryId:c,features:_,fieldToggles:O},n={queryId:d,features:S,fieldToggles:T};function Hj(j){if(typeof j==="string")return{queryId:j,features:_,fieldToggles:O};return{queryId:j?.queryId||c,features:j?.features||_,fieldToggles:j?.fieldToggles||O}}function Jj(j){if(typeof j==="string")return{queryId:j,features:S,fieldToggles:T};return{queryId:j?.queryId||d,features:j?.features||S,fieldToggles:j?.fieldToggles||T}}function u(j,q,F){const G=F.fieldToggles||{},H=[`variables=${encodeURIComponent(JSON.stringify(q))}`,`features=${encodeURIComponent(JSON.stringify(F.features||{}))}`];if(Object.keys(G).length>0)H.push(`fieldToggles=${encodeURIComponent(JSON.stringify(G))}`);return`${j}?${H.join("&")}`}function x(j,q,F,G){const H=Hj(j),J={userId:q,count:F,includePromotedContent:!1,withQuickPromoteEligibilityTweetFields:!0,withVoice:!0};if(G)J.cursor=G;return u(`/i/api/graphql/${H.queryId}/UserTweets`,J,H)}function E(j,q){const F=Jj(j),G={screen_name:q,withSafetyModeUserFields:!0};return u(`/i/api/graphql/${F.queryId}/UserByScreenName`,G,F)}function o(j,q){if(!j)return null;const F=j.__typename==="TweetWithVisibilityResults"&&j.tweet?j.tweet:j.tweet||j,G=F.legacy||{};if(!F.rest_id||q.has(F.rest_id))return null;q.add(F.rest_id);const H=F.core?.user_results?.result,J=H?.legacy?.screen_name||H?.core?.screen_name||"unknown",Q=H?.legacy?.name||H?.core?.name||"",X=F.note_tweet?.note_tweet_results?.result?.text,K=Boolean(G.retweeted_status_result||G.full_text?.startsWith("RT @"));return{id:F.rest_id,author:J,name:Q,text:X||G.full_text||"",likes:G.favorite_count||0,retweets:G.retweet_count||0,replies:G.reply_count||0,views:Number(F.views?.count)||0,is_retweet:K,created_at:G.created_at||"",url:`https://x.com/${J}/status/${F.rest_id}`,...qj(G),quoted_tweet:Fj(F)}}function N(j,q){const F=[];let G=null;const H=j?.data?.user?.result||{},Q=[H.timeline_v2?.timeline?.instructions,H.timeline?.timeline?.instructions].filter(Array.isArray).flat(),X=(K)=>{if(!K||typeof K!=="object")return;if(K.type==="TimelinePinEntry")return;if(K.tweet_results?.result){const V=o(K.tweet_results.result,q);if(V)F.push(V)}if((K.entryType==="TimelineTimelineCursor"||K.__typename==="TimelineTimelineCursor")&&(K.cursorType==="Bottom"||K.cursorType==="ShowMore")&&K.value)G=K.value;if(Array.isArray(K)){for(const V of K)X(V);return}for(const V of Object.values(K))if(V&&typeof V==="object")X(V)};X(Q);return{tweets:F,nextCursor:G}}function l(j){const q=j??20;if(!Number.isInteger(q)||q<1||q>B)throw new C(`twitter tweets --limit must be an integer between 1 and ${B}`,"Example: ppcli twitter tweets @jack --limit 250");return q}function s(j){const q=j??p;if(!Number.isInteger(q)||q<0||q>60)throw new C("twitter tweets --page-delay must be an integer between 0 and 60 seconds","Example: ppcli twitter tweets @jack --limit 250 --page-delay 2");return q}export async function confirmTweetByReadback(j,q,F={}){const G=Math.max(1,Math.min(40,Number(F.limit)||20)),H=(Q)=>String(Q||"").replace(/ /g," ").replace(/\s+/g," ").trim(),J=H(q);if(!J)return{found:!1,checked:0,error:"empty expected text"};try{await j.goto("https://x.com/home");await j.wait({selector:'[data-testid="primaryColumn"]'});const Q=D(await j.evaluate(`() => {
1
+ import{cli as K,Strategy as Z}from"@jackwener/opencli/registry";import{ArgumentError as v,AuthRequiredError as A,CommandExecutionError as B,EmptyResultError as ee}from"@jackwener/opencli/errors";import{resolveTwitterOperationMetadata as h,sanitizeQueryId as te,extractMedia as re,extractQuotedTweet as ne,normalizeTwitterGraphqlPayload as P,unwrapBrowserResult as g,normalizeTwitterScreenName as x,describeTwitterApiError as z}from"./shared.js";import{TWITTER_BEARER_TOKEN as M,applyTopByEngagement as se}from"./utils.js";const j="lrMzG9qPQHpqJdP3AbM-bQ",D="IGgvgiOx4QZndDHuD3x9TQ",G=100,L=100,y=G*L,W=2,U={rweb_video_screen_enabled:!0,rweb_cashtags_enabled:!0,payments_enabled:!1,profile_label_improvements_pcf_label_in_post_enabled:!0,responsive_web_profile_redirect_enabled:!0,rweb_tipjar_consumption_enabled:!0,verified_phone_label_enabled:!1,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_timeline_navigation_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,premium_content_api_read_enabled:!1,communities_web_enable_tweet_community_results_fetch:!0,c9s_tweet_anatomy_moderator_badge_enabled:!0,responsive_web_grok_analyze_button_fetch_trends_enabled:!1,responsive_web_grok_analyze_post_followups_enabled:!0,rweb_cashtags_composer_attachment_enabled:!0,responsive_web_jetfuel_frame:!0,responsive_web_grok_share_attachment_enabled:!0,responsive_web_grok_annotations_enabled:!0,articles_preview_enabled:!0,responsive_web_edit_tweet_api_enabled:!0,graphql_is_translatable_rweb_tweet_is_translatable_enabled:!0,view_counts_everywhere_api_enabled:!0,longform_notetweets_consumption_enabled:!0,responsive_web_twitter_article_tweet_consumption_enabled:!0,tweet_awards_web_tipping_enabled:!1,content_disclosure_indicator_enabled:!0,content_disclosure_ai_generated_indicator_enabled:!0,responsive_web_grok_show_grok_translated_post:!1,responsive_web_grok_analysis_button_from_backend:!0,post_ctas_fetch_enabled:!1,freedom_of_speech_not_reach_fetch_enabled:!0,standardized_nudges_misinfo:!0,tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:!0,longform_notetweets_rich_text_read_enabled:!0,longform_notetweets_inline_media_enabled:!0,responsive_web_grok_image_annotation_enabled:!0,responsive_web_grok_imagine_annotation_enabled:!0,responsive_web_grok_community_note_auto_translation_is_enabled:!1,responsive_web_enhance_cards_enabled:!1},N={withPayments:!0,withAuxiliaryUserLabels:!0,withArticleRichContentState:!0,withArticlePlainText:!0,withArticleSummaryText:!0,withArticleVoiceOver:!0,withGrokAnalyze:!0,withDisallowedReplyControls:!0},R={hidden_profile_subscriptions_enabled:!0,profile_label_improvements_pcf_label_in_post_enabled:!0,responsive_web_profile_redirect_enabled:!0,rweb_tipjar_consumption_enabled:!0,responsive_web_graphql_exclude_directive_enabled:!0,verified_phone_label_enabled:!1,subscriptions_verification_info_is_identity_verified_enabled:!0,subscriptions_verification_info_verified_since_enabled:!0,highlights_tweets_tab_ui_enabled:!0,responsive_web_twitter_article_notes_tab_enabled:!0,subscriptions_feature_can_gift_premium:!0,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,responsive_web_graphql_timeline_navigation_enabled:!0},I={withPayments:!0,withAuxiliaryUserLabels:!0},Q={queryId:j,features:U,fieldToggles:N},X={queryId:D,features:R,fieldToggles:I};function ie(e){if(typeof e==="string")return{queryId:e,features:U,fieldToggles:N};return{queryId:e?.queryId||j,features:e?.features||U,fieldToggles:e?.fieldToggles||N}}function oe(e){if(typeof e==="string")return{queryId:e,features:R,fieldToggles:I};return{queryId:e?.queryId||D,features:e?.features||R,fieldToggles:e?.fieldToggles||I}}function F(e,t,r){const n=r.fieldToggles||{},s=[`variables=${encodeURIComponent(JSON.stringify(t))}`,`features=${encodeURIComponent(JSON.stringify(r.features||{}))}`];if(Object.keys(n).length>0)s.push(`fieldToggles=${encodeURIComponent(JSON.stringify(n))}`);return`${e}?${s.join("&")}`}function q(e,t,r,n){const s=ie(e),i={userId:t,count:r,includePromotedContent:!1,withQuickPromoteEligibilityTweetFields:!0,withVoice:!0};if(n)i.cursor=n;return F(`/i/api/graphql/${s.queryId}/UserTweets`,i,s)}function C(e,t){const r=oe(e),n={screen_name:t,withSafetyModeUserFields:!0};return F(`/i/api/graphql/${r.queryId}/UserByScreenName`,n,r)}function Y(e,t){if(!e)return null;const r=e.__typename==="TweetWithVisibilityResults"&&e.tweet?e.tweet:e.tweet||e,n=r.legacy||{};if(!r.rest_id||t.has(r.rest_id))return null;t.add(r.rest_id);const s=r.core?.user_results?.result,i=s?.legacy?.screen_name||s?.core?.screen_name||"unknown",a=s?.legacy?.name||s?.core?.name||"",c=r.note_tweet?.note_tweet_results?.result?.text,o=Boolean(n.retweeted_status_result||n.full_text?.startsWith("RT @"));return{id:r.rest_id,author:i,name:a,text:c||n.full_text||"",likes:n.favorite_count||0,retweets:n.retweet_count||0,replies:n.reply_count||0,views:Number(r.views?.count)||0,is_retweet:o,created_at:n.created_at||"",url:`https://x.com/${i}/status/${r.rest_id}`,...re(n),quoted_tweet:ne(r)}}function O(e,t){const r=[];let n=null;const s=e?.data?.user?.result||{},a=[s.timeline_v2?.timeline?.instructions,s.timeline?.timeline?.instructions].filter(Array.isArray).flat(),c=(o)=>{if(!o||typeof o!=="object")return;if(o.type==="TimelinePinEntry")return;if(o.tweet_results?.result){const l=Y(o.tweet_results.result,t);if(l)r.push(l)}if((o.entryType==="TimelineTimelineCursor"||o.__typename==="TimelineTimelineCursor")&&(o.cursorType==="Bottom"||o.cursorType==="ShowMore")&&o.value)n=o.value;if(Array.isArray(o)){for(const l of o)c(l);return}for(const l of Object.values(o))if(l&&typeof l==="object")c(l)};c(a);return{tweets:r,nextCursor:n}}function J(e){const t=e??20;if(!Number.isInteger(t)||t<1||t>y)throw new v(`twitter tweets --limit must be an integer between 1 and ${y}`,"Example: ppcli twitter tweets @jack --limit 250");return t}function V(e){const t=e??W;if(!Number.isInteger(t)||t<0||t>60)throw new v("twitter tweets --page-delay must be an integer between 0 and 60 seconds","Example: ppcli twitter tweets @jack --limit 250 --page-delay 2");return t}export async function confirmTweetByReadback(e,t,r={}){const n=Math.max(1,Math.min(40,Number(r.limit)||20)),s=(a)=>String(a||"").replace(/ /g," ").replace(/\s+/g," ").trim(),i=s(t);if(!i)return{found:!1,checked:0,error:"empty expected text"};try{await e.goto("https://x.com/home");await e.wait({selector:'[data-testid="primaryColumn"]'});const a=g(await e.evaluate(`() => {
2
2
  const link = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]');
3
3
  return link ? link.getAttribute('href') : null;
4
- }`)),X=R(typeof Q==="string"?Q:"");if(!X)return{found:!1,checked:0,error:"could not detect logged-in user"};const V=(await j.getCookies({url:"https://x.com"})).find(($)=>$.name==="ct0")?.value||null;if(!V)return{found:!1,checked:0,error:"no ct0 cookie"};const b=await A(j,"UserTweets",i),z=await A(j,"UserByScreenName",n),M=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(w)}`,"X-Csrf-Token":V,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),U=E(z,X),Z=D(await j.evaluate(`async () => {
5
- const resp = await fetch("${U}", { headers: ${M}, credentials: 'include' });
4
+ }`)),c=x(typeof a==="string"?a:"");if(!c)return{found:!1,checked:0,error:"could not detect logged-in user"};const l=(await e.getCookies({url:"https://x.com"})).find((d)=>d.name==="ct0")?.value||null;if(!l)return{found:!1,checked:0,error:"no ct0 cookie"};const b=await h(e,"UserTweets",Q),T=await h(e,"UserByScreenName",X),m=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(M)}`,"X-Csrf-Token":l,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),E=C(T,c),u=g(await e.evaluate(`async () => {
5
+ const resp = await fetch("${E}", { headers: ${m}, credentials: 'include' });
6
6
  if (!resp.ok) return null;
7
7
  const d = await resp.json();
8
8
  return d?.data?.user?.result?.rest_id || null;
9
- }`));if(!Z)return{found:!1,checked:0,error:`could not resolve @${X}`};const P=x(b,Z,G,null),Y=y(await j.evaluate(`async () => {
10
- const r = await fetch("${P}", { headers: ${M}, credentials: 'include' });
9
+ }`));if(!u)return{found:!1,checked:0,error:`could not resolve @${c}`};const f=q(b,u,n,null),p=P(await e.evaluate(`async () => {
10
+ const r = await fetch("${f}", { headers: ${m}, credentials: 'include' });
11
11
  return r.ok ? await r.json() : { error: r.status };
12
- }`));if(Y?.error)return{found:!1,checked:0,error:v("UserTweets",Y.error)};const{tweets:W}=N(Y,new Set);for(const $ of W){const L=H($?.text);if(!L)continue;if(L===J||L.includes(J)||J.includes(L))return{found:!0,id:$.id,url:$.url,checked:W.length}}return{found:!1,checked:W.length}}catch(Q){return{found:!1,checked:0,error:String(Q&&Q.message||Q)}}}r({site:"twitter",name:"tweets",access:"read",description:"Fetch a Twitter user's most recent tweets (chronological, excludes pinned; defaults to the logged-in user when no username is given)",domain:"x.com",strategy:a.COOKIE,browser:!0,args:[{name:"username",type:"string",positional:!0,help:"Twitter screen name (with or without @). Defaults to the logged-in user when omitted."},{name:"limit",type:"int",default:20,help:`Max tweets to return (1-${B}; fetched across cursor pages)`},{name:"page-delay",type:"int",default:p,help:"Seconds to wait between paginated timeline requests to reduce rate-limit risk. Use 0 to disable."},{name:"top-by-engagement",type:"int",default:0,help:"When set to N>0, re-rank the tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the chronological ordering."}],columns:["id","author","created_at","is_retweet","text","likes","retweets","replies","views","url","has_media","media_urls","media_posters","quoted_tweet"],func:async(j,q)=>{const F=l(q.limit),G=s(q["page-delay"]),H=String(q.username??"").trim();let J=R(H);if(H&&!J)throw new C("twitter tweets username must be a valid Twitter/X handle","Example: ppcli twitter tweets @jack --limit 20");if(!J){await j.goto("https://x.com/home");await j.wait({selector:'[data-testid="primaryColumn"]'});const W=D(await j.evaluate(`() => {
12
+ }`));if(p?.error)return{found:!1,checked:0,error:z("UserTweets",p.error)};const{tweets:_}=O(p,new Set);for(const d of _){const w=s(d?.text);if(!w)continue;if(w===i||w.includes(i)||i.includes(w))return{found:!0,id:d.id,url:d.url,checked:_.length}}return{found:!1,checked:_.length}}catch(a){return{found:!1,checked:0,error:String(a&&a.message||a)}}}K({site:"twitter",name:"tweets",access:"read",description:"Fetch a Twitter user's most recent tweets (chronological, excludes pinned; defaults to the logged-in user when no username is given)",domain:"x.com",strategy:Z.COOKIE,browser:!0,args:[{name:"username",type:"string",positional:!0,help:"Twitter screen name (with or without @). Defaults to the logged-in user when omitted."},{name:"limit",type:"int",default:20,help:`Max tweets to return (1-${y}; fetched across cursor pages)`},{name:"page-delay",type:"int",default:W,help:"Seconds to wait between paginated timeline requests to reduce rate-limit risk. Use 0 to disable."},{name:"top-by-engagement",type:"int",default:0,help:"When set to N>0, re-rank the tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the chronological ordering."}],columns:["id","author","created_at","is_retweet","text","likes","retweets","replies","views","url","has_media","media_urls","media_posters","quoted_tweet"],func:async(e,t)=>{const r=J(t.limit),n=V(t["page-delay"]),s=String(t.username??"").trim();let i=x(s);if(s&&!i)throw new v("twitter tweets username must be a valid Twitter/X handle","Example: ppcli twitter tweets @jack --limit 20");if(!i){await e.goto("https://x.com/home");await e.wait({selector:'[data-testid="primaryColumn"]'});const _=g(await e.evaluate(`() => {
13
13
  const link = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]');
14
14
  return link ? link.getAttribute('href') : null;
15
- }`));if(!W||typeof W!=="string")throw new I("x.com","Could not detect logged-in user. Are you logged in?");J=R(W);if(!J)throw new I("x.com","Could not detect logged-in user. Are you logged in?")}const X=(await j.getCookies({url:"https://x.com"})).find((W)=>W.name==="ct0")?.value||null;if(!X)throw new I("x.com","Not logged into x.com (no ct0 cookie)");const K=await A(j,"UserTweets",i),V=await A(j,"UserByScreenName",n),b=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(w)}`,"X-Csrf-Token":X,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),z=E(V,J),M=D(await j.evaluate(`async () => {
16
- const resp = await fetch("${z}", { headers: ${b}, credentials: 'include' });
15
+ }`));if(!_||typeof _!=="string")throw new A("x.com","Could not detect logged-in user. Are you logged in?");i=x(_);if(!i)throw new A("x.com","Could not detect logged-in user. Are you logged in?")}const c=(await e.getCookies({url:"https://x.com"})).find((_)=>_.name==="ct0")?.value||null;if(!c)throw new A("x.com","Not logged into x.com (no ct0 cookie)");const o=await h(e,"UserTweets",Q),l=await h(e,"UserByScreenName",X),b=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(M)}`,"X-Csrf-Token":c,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),T=C(l,i),m=g(await e.evaluate(`async () => {
16
+ const resp = await fetch("${T}", { headers: ${b}, credentials: 'include' });
17
17
  if (!resp.ok) return null;
18
18
  const d = await resp.json();
19
19
  return d?.data?.user?.result?.rest_id || null;
20
- }`));if(!M)throw new h(`Could not resolve @${J}`);const U=new Set,Z=[];let P=null;for(let W=0;W<m&&Z.length<F;W++){if(W>0&&G>0)await j.wait(G);const $=Math.min(g,F-Z.length+10),L=x(K,M,$,P),f=y(await j.evaluate(`async () => {
21
- const r = await fetch("${L}", { headers: ${b}, credentials: 'include' });
20
+ }`));if(!m)throw new B(`Could not resolve @${i}`);const E=new Set,u=[];let f=null;for(let _=0;_<G&&u.length<r;_++){if(_>0&&n>0)await e.wait(n);const d=Math.min(L,r-u.length+10),w=q(o,m,d,f),S=P(await e.evaluate(`async () => {
21
+ const r = await fetch("${w}", { headers: ${b}, credentials: 'include' });
22
22
  return r.ok ? await r.json() : { error: r.status };
23
- }`));if(f?.error){if(Z.length===0)throw new h(v("UserTweets",f.error));break}const{tweets:t,nextCursor:k}=N(f,U);Z.push(...t);if(!k||k===P)break;P=k}if(Z.length===0)throw new e(`@${J} has no recent tweets`,"Account may be private or suspended");const Y=Z.slice(0,F);return Gj(Y,q["top-by-engagement"])}});export const __test__={MAX_TWEETS_LIMIT:B,sanitizeQueryId:jj,buildUserTweetsUrl:x,buildUserByScreenNameUrl:E,extractTweet:o,parseUserTweets:N,normalizeLimit:l,normalizePageDelaySeconds:s};
23
+ }`));if(S?.error){if(u.length===0)throw new B(z("UserTweets",S.error));break}const{tweets:H,nextCursor:k}=O(S,E);u.push(...H);if(!k||k===f)break;f=k}if(u.length===0)throw new ee(`@${i} has no recent tweets`,"Account may be private or suspended");const p=u.slice(0,r);return se(p,t["top-by-engagement"])}});export const __test__={MAX_TWEETS_LIMIT:y,sanitizeQueryId:te,buildUserTweetsUrl:q,buildUserByScreenNameUrl:C,extractTweet:Y,parseUserTweets:O,normalizeLimit:J,normalizePageDelaySeconds:V};
@@ -1,6 +1,6 @@
1
- import{CommandExecutionError as F}from"@jackwener/opencli/errors";import{cli as G,Strategy as H}from"@jackwener/opencli/registry";import{parseTweetUrl as I,buildTwitterArticleScopeSource as J}from"./shared.js";G({site:"twitter",name:"unlike",access:"write",description:"Remove a like from a specific tweet",domain:"x.com",strategy:H.UI,browser:!0,args:[{name:"url",type:"string",required:!0,positional:!0,help:"The URL of the tweet to unlike"}],columns:["status","message"],func:async(v,D)=>{if(!v)throw new F("Browser session required for twitter unlike");const B=I(D.url);await v.goto(B.url);await v.wait({selector:'[data-testid="primaryColumn"]'});const z=await v.evaluate(`(async () => {
1
+ import{CommandExecutionError as a}from"@jackwener/opencli/errors";import{cli as l,Strategy as n}from"@jackwener/opencli/registry";import{parseTweetUrl as s,buildTwitterArticleScopeSource as o}from"./shared.js";l({site:"twitter",name:"unlike",access:"write",description:"Remove a like from a specific tweet",domain:"x.com",strategy:n.UI,browser:!0,args:[{name:"url",type:"string",required:!0,positional:!0,help:"The URL of the tweet to unlike"}],columns:["status","message"],func:async(e,r)=>{if(!e)throw new a("Browser session required for twitter unlike");const i=s(r.url);await e.goto(i.url);await e.wait({selector:'[data-testid="primaryColumn"]'});const t=await e.evaluate(`(async () => {
2
2
  try {
3
- ${J(B.id)}
3
+ ${o(i.id)}
4
4
  // Poll for the tweet to render. State probes scoped to the article
5
5
  // matching the requested status id — bare querySelector on a
6
6
  // conversation page would silently grab the first article (e.g.
@@ -45,4 +45,4 @@ import{CommandExecutionError as F}from"@jackwener/opencli/errors";import{cli as
45
45
  } catch (e) {
46
46
  return { ok: false, message: e.toString() };
47
47
  }
48
- })()`);if(z.ok)await v.wait(2);return[{status:z.ok?"success":"failed",message:z.message}]}});
48
+ })()`);if(t.ok)await e.wait(2);return[{status:t.ok?"success":"failed",message:t.message}]}});
@@ -1,6 +1,6 @@
1
- import{CommandExecutionError as F}from"@jackwener/opencli/errors";import{cli as G,Strategy as H}from"@jackwener/opencli/registry";import{parseTweetUrl as I,buildTwitterArticleScopeSource as J}from"./shared.js";G({site:"twitter",name:"unretweet",access:"write",description:"Undo a retweet on a specific tweet",domain:"x.com",strategy:H.UI,browser:!0,args:[{name:"url",type:"string",required:!0,positional:!0,help:"The URL of the tweet to unretweet"}],columns:["status","message"],func:async(v,D)=>{if(!v)throw new F("Browser session required for twitter unretweet");const B=I(D.url);await v.goto(B.url);await v.wait({selector:'[data-testid="primaryColumn"]'});const z=await v.evaluate(`(async () => {
1
+ import{CommandExecutionError as i}from"@jackwener/opencli/errors";import{cli as o,Strategy as a}from"@jackwener/opencli/registry";import{parseTweetUrl as s,buildTwitterArticleScopeSource as u}from"./shared.js";o({site:"twitter",name:"unretweet",access:"write",description:"Undo a retweet on a specific tweet",domain:"x.com",strategy:a.UI,browser:!0,args:[{name:"url",type:"string",required:!0,positional:!0,help:"The URL of the tweet to unretweet"}],columns:["status","message"],func:async(e,n)=>{if(!e)throw new i("Browser session required for twitter unretweet");const r=s(n.url);await e.goto(r.url);await e.wait({selector:'[data-testid="primaryColumn"]'});const t=await e.evaluate(`(async () => {
2
2
  try {
3
- ${J(B.id)}
3
+ ${u(r.id)}
4
4
  // Poll for the tweet to render. State probes scoped to the article
5
5
  // matching the requested status id — bare querySelector on a
6
6
  // conversation page would silently grab the first article (e.g.
@@ -59,4 +59,4 @@ import{CommandExecutionError as F}from"@jackwener/opencli/errors";import{cli as
59
59
  } catch (e) {
60
60
  return { ok: false, message: e.toString() };
61
61
  }
62
- })()`);if(z.ok)await v.wait(2);return[{status:z.ok?"success":"failed",message:z.message}]}});
62
+ })()`);if(t.ok)await e.wait(2);return[{status:t.ok?"success":"failed",message:t.message}]}});
@@ -1,8 +1,8 @@
1
- import*as z from"node:fs";import*as Y from"node:os";import*as F from"node:path";import{ArgumentError as V}from"@jackwener/opencli/errors";import{setInputFiles as R}from"../_shared/file-inject.js";export const TWITTER_BEARER_TOKEN="AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA",COMPOSER_FILE_INPUT_SELECTOR='input[type="file"][data-testid="fileInput"]',SUPPORTED_IMAGE_EXTENSIONS=new Set([".jpg",".jpeg",".png",".gif",".webp"]),MAX_IMAGE_SIZE_BYTES=20971520;const Z=500,B=30000,W={"image/jpeg":".jpg","image/jpg":".jpg","image/png":".png","image/gif":".gif","image/webp":".webp"};export function resolveImagePath(q){const J=F.resolve(q);if(!z.existsSync(J))throw new V(`Image file not found: ${J}`);const K=F.extname(J).toLowerCase();if(!SUPPORTED_IMAGE_EXTENSIONS.has(K))throw new V(`Unsupported image format "${K}". Supported: jpg, jpeg, png, gif, webp`);const Q=z.statSync(J);if(Q.size>MAX_IMAGE_SIZE_BYTES)throw new V(`Image too large: ${(Q.size/1024/1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES/1024/1024} MB)`);return J}export function resolveImageExtension(q,J){const K=(J||"").split(";")[0].trim().toLowerCase();if(K&&W[K])return W[K];try{const Q=new URL(q).pathname,$=F.extname(Q).toLowerCase();if(SUPPORTED_IMAGE_EXTENSIONS.has($))return $}catch{}throw new V(`Unsupported remote image format "${K||"unknown"}". Supported: jpg, jpeg, png, gif, webp`)}export async function downloadRemoteImage(q){let J;try{J=new URL(q)}catch{throw new V(`Invalid image URL: ${q}`)}if(!/^https?:$/.test(J.protocol))throw new V(`Unsupported image URL protocol: ${J.protocol}`);const K=await fetch(q);if(!K.ok)throw new V(`Image download failed: HTTP ${K.status}`);const Q=Number(K.headers.get("content-length")||"0");if(Q>MAX_IMAGE_SIZE_BYTES)throw new V(`Image too large: ${(Q/1024/1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES/1024/1024} MB)`);const $=resolveImageExtension(q,K.headers.get("content-type")),j=z.mkdtempSync(F.join(Y.tmpdir(),"opencli-twitter-")),H=F.join(j,`image${$}`),k=Buffer.from(await K.arrayBuffer());if(k.byteLength>MAX_IMAGE_SIZE_BYTES){z.rmSync(j,{recursive:!0,force:!0});throw new V(`Image too large: ${(k.byteLength/1024/1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES/1024/1024} MB)`)}z.writeFileSync(H,k);return{absPath:H,cleanupDir:j}}export async function attachComposerImage(q,J,K=COMPOSER_FILE_INPUT_SELECTOR){try{await R(q,K,[J])}catch($){throw Error(`Image upload failed: ${$ instanceof Error?$.message:String($)}`)}const Q=await waitForComposerMediaReady(q,1);if(!Q?.ok)throw Error(Q?.message??"Image upload failed: preview did not appear.")}export function isRecoverableFileInputError(q){const J=q instanceof Error?q.message:String(q);return/unknown action|not supported|not[-\s]?allowed|notallowederror/i.test(J)}export async function waitForComposerMediaReady(q,J=1){const K=Math.ceil(B/Z);return q.evaluate(`
1
+ import*as c from"node:fs";import*as f from"node:os";import*as p from"node:path";import{ArgumentError as a}from"@jackwener/opencli/errors";import{setInputFiles as w}from"../_shared/file-inject.js";export const TWITTER_BEARER_TOKEN="AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA",COMPOSER_FILE_INPUT_SELECTOR='input[type="file"][data-testid="fileInput"]',SUPPORTED_IMAGE_EXTENSIONS=new Set([".jpg",".jpeg",".png",".gif",".webp"]),MAX_IMAGE_SIZE_BYTES=20971520;const l=500,g=30000,d={"image/jpeg":".jpg","image/jpg":".jpg","image/png":".png","image/gif":".gif","image/webp":".webp"};export function resolveImagePath(e){const t=p.resolve(e);if(!c.existsSync(t))throw new a(`Image file not found: ${t}`);const o=p.extname(t).toLowerCase();if(!SUPPORTED_IMAGE_EXTENSIONS.has(o))throw new a(`Unsupported image format "${o}". Supported: jpg, jpeg, png, gif, webp`);const r=c.statSync(t);if(r.size>MAX_IMAGE_SIZE_BYTES)throw new a(`Image too large: ${(r.size/1024/1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES/1024/1024} MB)`);return t}export function resolveImageExtension(e,t){const o=(t||"").split(";")[0].trim().toLowerCase();if(o&&d[o])return d[o];try{const r=new URL(e).pathname,n=p.extname(r).toLowerCase();if(SUPPORTED_IMAGE_EXTENSIONS.has(n))return n}catch{}throw new a(`Unsupported remote image format "${o||"unknown"}". Supported: jpg, jpeg, png, gif, webp`)}export async function downloadRemoteImage(e){let t;try{t=new URL(e)}catch{throw new a(`Invalid image URL: ${e}`)}if(!/^https?:$/.test(t.protocol))throw new a(`Unsupported image URL protocol: ${t.protocol}`);const o=await fetch(e);if(!o.ok)throw new a(`Image download failed: HTTP ${o.status}`);const r=Number(o.headers.get("content-length")||"0");if(r>MAX_IMAGE_SIZE_BYTES)throw new a(`Image too large: ${(r/1024/1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES/1024/1024} MB)`);const n=resolveImageExtension(e,o.headers.get("content-type")),i=c.mkdtempSync(p.join(f.tmpdir(),"opencli-twitter-")),u=p.join(i,`image${n}`),m=Buffer.from(await o.arrayBuffer());if(m.byteLength>MAX_IMAGE_SIZE_BYTES){c.rmSync(i,{recursive:!0,force:!0});throw new a(`Image too large: ${(m.byteLength/1024/1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES/1024/1024} MB)`)}c.writeFileSync(u,m);return{absPath:u,cleanupDir:i}}export async function attachComposerImage(e,t,o=COMPOSER_FILE_INPUT_SELECTOR){try{await w(e,o,[t])}catch(n){throw Error(`Image upload failed: ${n instanceof Error?n.message:String(n)}`)}const r=await waitForComposerMediaReady(e,1);if(!r?.ok)throw Error(r?.message??"Image upload failed: preview did not appear.")}export function isRecoverableFileInputError(e){const t=e instanceof Error?e.message:String(e);return/unknown action|not supported|not[-\s]?allowed|notallowederror/i.test(t)}export async function waitForComposerMediaReady(e,t=1){const o=Math.ceil(g/l);return e.evaluate(`
2
2
  (async () => {
3
- const expected = ${JSON.stringify(J)};
4
- for (let i = 0; i < ${JSON.stringify(K)}; i++) {
5
- await new Promise(r => setTimeout(r, ${JSON.stringify(Z)}));
3
+ const expected = ${JSON.stringify(t)};
4
+ for (let i = 0; i < ${JSON.stringify(o)}; i++) {
5
+ await new Promise(r => setTimeout(r, ${JSON.stringify(l)}));
6
6
  const previewCount = document.querySelectorAll(
7
7
  '[data-testid="attachments"] img, [data-testid="attachments"] video, [data-testid="attachments"] [role="group"], [data-testid="tweetPhoto"]'
8
8
  ).length;
@@ -18,6 +18,6 @@ import*as z from"node:fs";import*as Y from"node:os";import*as F from"node:path";
18
18
  );
19
19
  if (explicitPreviewCount >= expected) return { ok: true, previewCount: explicitPreviewCount };
20
20
  }
21
- return { ok: false, message: 'Image upload timed out (${B/1000}s).' };
21
+ return { ok: false, message: 'Image upload timed out (${g/1000}s).' };
22
22
  })()
23
- `)}const v=Object.freeze({likes:1,retweets:3,replies:2,bookmarks:5,viewsLog:0.5});export function computeEngagementScore(q){if(!q||typeof q!=="object")return 0;const J=(Q)=>{const $=q[Q];if($===void 0||$===null)return 0;const j=Number($);return Number.isFinite(j)?Math.max(0,j):0},K=J("likes")*v.likes+J("retweets")*v.retweets+J("replies")*v.replies+J("bookmarks")*v.bookmarks+Math.log10(J("views")+1)*v.viewsLog;return Math.round(K*100)/100}export function applyTopByEngagement(q,J){if(!Array.isArray(q)||q.length===0)return q;const K=Number(J);if(!Number.isFinite(K)||K<=0)return q;return q.map((Q,$)=>({row:Q,idx:$,score:computeEngagementScore(Q)})).sort((Q,$)=>$.score-Q.score||Q.idx-$.idx).slice(0,Math.floor(K)).map((Q)=>Q.row)}export const __test__={resolveImagePath,resolveImageExtension,downloadRemoteImage,attachComposerImage,isRecoverableFileInputError,waitForComposerMediaReady,computeEngagementScore,applyTopByEngagement,ENGAGEMENT_WEIGHTS:v};
23
+ `)}const s=Object.freeze({likes:1,retweets:3,replies:2,bookmarks:5,viewsLog:0.5});export function computeEngagementScore(e){if(!e||typeof e!=="object")return 0;const t=(r)=>{const n=e[r];if(n===void 0||n===null)return 0;const i=Number(n);return Number.isFinite(i)?Math.max(0,i):0},o=t("likes")*s.likes+t("retweets")*s.retweets+t("replies")*s.replies+t("bookmarks")*s.bookmarks+Math.log10(t("views")+1)*s.viewsLog;return Math.round(o*100)/100}export function applyTopByEngagement(e,t){if(!Array.isArray(e)||e.length===0)return e;const o=Number(t);if(!Number.isFinite(o)||o<=0)return e;return e.map((r,n)=>({row:r,idx:n,score:computeEngagementScore(r)})).sort((r,n)=>n.score-r.score||r.idx-n.idx).slice(0,Math.floor(o)).map((r)=>r.row)}export const __test__={resolveImagePath,resolveImageExtension,downloadRemoteImage,attachComposerImage,isRecoverableFileInputError,waitForComposerMediaReady,computeEngagementScore,applyTopByEngagement,ENGAGEMENT_WEIGHTS:s};
@@ -1,4 +1,4 @@
1
- import{cli as f,Strategy as j}from"@jackwener/opencli/registry";import{CommandExecutionError as r,EmptyResultError as k,AuthRequiredError as x}from"@jackwener/opencli/errors";import{DETAIL_COLUMNS as $,buildJobUrl as N,decodeExperienceLevel as S,decodeWorkload as U,formatBudgetFromDetail as v,formatSkills as O,isPlainObject as b,jobType as C,requireCiphertext as L,stripHighlight as E,unwrapBrowserResult as J}from"./utils.js";f({site:"upwork",name:"detail",aliases:["job","view"],access:"read",description:"Read the full Upwork job posting by ciphertext id (e.g. ~022054964136512093518)",domain:"www.upwork.com",strategy:j.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"id",positional:!0,required:!0,help:"Job ciphertext id (~01… / ~02…) or full /jobs/~02… URL"}],columns:$,func:async(i,h)=>{const o=L(h.id),c=N(o);await i.goto(c);await i.wait(5);let t;try{t=J(await i.evaluate(`(async () => {
1
+ import{cli as f,Strategy as j}from"@jackwener/opencli/registry";import{CommandExecutionError as r,EmptyResultError as k,AuthRequiredError as x}from"@jackwener/opencli/errors";import{DETAIL_COLUMNS as S,buildJobUrl as N,decodeExperienceLevel as O,decodeWorkload as U,formatBudgetFromDetail as v,formatSkills as C,isPlainObject as b,jobType as L,requireCiphertext as E,stripHighlight as J,unwrapBrowserResult as F}from"./utils.js";f({site:"upwork",name:"detail",aliases:["job","view"],access:"read",description:"Read the full Upwork job posting by ciphertext id (e.g. ~022054964136512093518)",domain:"www.upwork.com",strategy:j.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"id",positional:!0,required:!0,help:"Job ciphertext id (~01… / ~02…) or full /jobs/~02… URL"}],columns:S,func:async(n,h)=>{const o=E(h.id),c=N(o);await n.goto(c);await n.wait(5);let t;try{t=F(await n.evaluate(`(async () => {
2
2
  const haveStore = () => !!(window.$nuxt && window.$nuxt.$store && window.$nuxt.$store.state && window.$nuxt.$store.state.jobDetails && window.$nuxt.$store.state.jobDetails.job);
3
3
  let ready = haveStore();
4
4
  for (let i = 0; i < 30; i++) {
@@ -19,4 +19,4 @@ import{cli as f,Strategy as j}from"@jackwener/opencli/registry";import{CommandEx
19
19
  job: s.job ? JSON.parse(JSON.stringify(s.job)) : null,
20
20
  buyer: s.buyer ? JSON.parse(JSON.stringify(s.buyer)) : null,
21
21
  };
22
- })()`))}catch(w){throw new r(`Failed to read Upwork job-detail store: ${w?.message??w}`,"The Vuex store was not reachable; try again after opening Upwork in the connected browser.")}if(t?.onLogin)throw new x("upwork.com","Upwork redirected to login. Open https://www.upwork.com in the connected browser and sign in, then retry.");if(t?.challenge)throw new r("Upwork served a Cloudflare challenge page","Open https://www.upwork.com in the connected browser and clear the challenge, then retry.");if(!b(t))throw new r("Upwork detail returned an unexpected Browser Bridge payload shape");if(!t?.ready||!t.job)throw new k("upwork detail",`No Upwork job posting found for id "${o}" (may be closed, expired, or private)`);if(!b(t.job))throw new r("Upwork job-detail store had an unexpected job shape; expected an object.");const e=t.job,n=String(e?.ciphertext??"").trim();if(n&&n!==o)throw new r(`Upwork job-detail store returned ciphertext "${n}" while reading "${o}".`);const d=t.buyer||{},a=d?.stats||{},m=d?.location||{},g=e?.category?.name||"",y=O(e),s=Number(a?.totalCharges?.amount),u=Number(a?.totalJobsWithHires),l=Number(a?.score),p=Number(e?.clientActivity?.totalApplicants);return[{id:o,title:E(e?.title),type:C(e?.type),budget:v(e),experienceLevel:S(e?.contractorTier),workload:U(e?.workload),category:g,skills:y,description:String(e?.description??"").trim(),clientCountry:m?.country||"",clientSpent:Number.isFinite(s)&&s>0?s:null,clientHires:Number.isFinite(u)?u:null,clientRating:Number.isFinite(l)&&l>0?l:null,proposalsCount:Number.isFinite(p)?p:null,publishedOn:e?.publishTime||e?.postedOn||e?.createdOn||"",url:c}]}});
22
+ })()`))}catch(p){throw new r(`Failed to read Upwork job-detail store: ${p?.message??p}`,"The Vuex store was not reachable; try again after opening Upwork in the connected browser.")}if(t?.onLogin)throw new x("upwork.com","Upwork redirected to login. Open https://www.upwork.com in the connected browser and sign in, then retry.");if(t?.challenge)throw new r("Upwork served a Cloudflare challenge page","Open https://www.upwork.com in the connected browser and clear the challenge, then retry.");if(!b(t))throw new r("Upwork detail returned an unexpected Browser Bridge payload shape");if(!t?.ready||!t.job)throw new k("upwork detail",`No Upwork job posting found for id "${o}" (may be closed, expired, or private)`);if(!b(t.job))throw new r("Upwork job-detail store had an unexpected job shape; expected an object.");const e=t.job,i=String(e?.ciphertext??"").trim();if(i&&i!==o)throw new r(`Upwork job-detail store returned ciphertext "${i}" while reading "${o}".`);const u=t.buyer||{},a=u?.stats||{},m=u?.location||{},g=e?.category?.name||"",y=C(e),s=Number(a?.totalCharges?.amount),d=Number(a?.totalJobsWithHires),l=Number(a?.score),w=Number(e?.clientActivity?.totalApplicants);return[{id:o,title:J(e?.title),type:L(e?.type),budget:v(e),experienceLevel:O(e?.contractorTier),workload:U(e?.workload),category:g,skills:y,description:String(e?.description??"").trim(),clientCountry:m?.country||"",clientSpent:Number.isFinite(s)&&s>0?s:null,clientHires:Number.isFinite(d)?d:null,clientRating:Number.isFinite(l)&&l>0?l:null,proposalsCount:Number.isFinite(w)?w:null,publishedOn:e?.publishTime||e?.postedOn||e?.createdOn||"",url:c}]}});
@@ -1,4 +1,4 @@
1
- import{cli as l,Strategy as h}from"@jackwener/opencli/registry";import{CommandExecutionError as t,EmptyResultError as p,AuthRequiredError as u}from"@jackwener/opencli/errors";import{buildFeedUrl as f,feedStateKey as g,isPlainObject as m,jobsToListRows as b,LIST_COLUMNS as _,requireBoundedInt as y,requireFeedTab as U,unwrapBrowserResult as k}from"./utils.js";l({site:"upwork",name:"feed",aliases:["best-matches"],access:"read",description:"Upwork personalized jobs feed (best-matches | most-recent) — requires login",domain:"www.upwork.com",strategy:h.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"tab",positional:!0,required:!1,default:"best-matches",help:"Feed tab: best-matches | most-recent"},{name:"limit",type:"int",default:20,help:"Max rows to return (1-50, capped at one page)"}],columns:_,func:async(a,n)=>{const o=U(n.tab),w=y(n.limit,20,1,50,"limit"),r=g(o),c=f(o);await a.goto(c);await a.wait(5);let e;try{e=k(await a.evaluate(`(async () => {
1
+ import{cli as l,Strategy as p}from"@jackwener/opencli/registry";import{CommandExecutionError as t,EmptyResultError as h,AuthRequiredError as u}from"@jackwener/opencli/errors";import{buildFeedUrl as m,feedStateKey as b,isPlainObject as f,jobsToListRows as y,LIST_COLUMNS as g,requireBoundedInt as _,requireFeedTab as U,unwrapBrowserResult as k}from"./utils.js";l({site:"upwork",name:"feed",aliases:["best-matches"],access:"read",description:"Upwork personalized jobs feed (best-matches | most-recent) — requires login",domain:"www.upwork.com",strategy:p.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"tab",positional:!0,required:!1,default:"best-matches",help:"Feed tab: best-matches | most-recent"},{name:"limit",type:"int",default:20,help:"Max rows to return (1-50, capped at one page)"}],columns:g,func:async(o,n)=>{const a=U(n.tab),d=_(n.limit,20,1,50,"limit"),r=b(a),c=m(a);await o.goto(c);await o.wait(5);let e;try{e=k(await o.evaluate(`(async () => {
2
2
  const key = ${JSON.stringify(r)};
3
3
  const haveState = () => !!(window.__NUXT__ && window.__NUXT__.state && window.__NUXT__.state[key]);
4
4
  let ready = haveState();
@@ -18,4 +18,4 @@ import{cli as l,Strategy as h}from"@jackwener/opencli/registry";import{CommandEx
18
18
  jobs: state ? state.jobs : undefined,
19
19
  paging: state && state.paging ? state.paging : null,
20
20
  };
21
- })()`))}catch(d){throw new t(`Failed to read Upwork feed state: ${d?.message??d}`,"The Nuxt state global was not reachable; try again after opening Upwork in the connected browser.")}if(e?.onLogin)throw new u("upwork.com","Upwork redirected to login. Open https://www.upwork.com in the connected browser and sign in, then retry.");if(e?.challenge)throw new t("Upwork served a Cloudflare challenge page","Open https://www.upwork.com in the connected browser and clear the challenge, then retry.");if(!e?.ready)throw new t(`Upwork feed state (window.__NUXT__.state.${r}) was not present within 15s`,"The page may not have finished hydrating, or the SSR state shape may have changed.");if(!m(e))throw new t("Upwork feed returned an unexpected Browser Bridge payload shape");if(!e.jobsPresent||!Array.isArray(e.jobs))throw new t(`Upwork feed state had an unexpected jobs shape; expected window.__NUXT__.state.${r}.jobs to be an array.`);const s=e.jobs;if(s.length===0)throw new p(`upwork feed ${o}`,`Upwork ${o} feed is empty for the current account`);const i=b(s,{limit:w});if(i.length===0)throw new t("Upwork feed results did not include any job with a valid ciphertext id; cannot produce round-trippable detail rows.");return i}});
21
+ })()`))}catch(w){throw new t(`Failed to read Upwork feed state: ${w?.message??w}`,"The Nuxt state global was not reachable; try again after opening Upwork in the connected browser.")}if(e?.onLogin)throw new u("upwork.com","Upwork redirected to login. Open https://www.upwork.com in the connected browser and sign in, then retry.");if(e?.challenge)throw new t("Upwork served a Cloudflare challenge page","Open https://www.upwork.com in the connected browser and clear the challenge, then retry.");if(!e?.ready)throw new t(`Upwork feed state (window.__NUXT__.state.${r}) was not present within 15s`,"The page may not have finished hydrating, or the SSR state shape may have changed.");if(!f(e))throw new t("Upwork feed returned an unexpected Browser Bridge payload shape");if(!e.jobsPresent||!Array.isArray(e.jobs))throw new t(`Upwork feed state had an unexpected jobs shape; expected window.__NUXT__.state.${r}.jobs to be an array.`);const s=e.jobs;if(s.length===0)throw new h(`upwork feed ${a}`,`Upwork ${a} feed is empty for the current account`);const i=y(s,{limit:d});if(i.length===0)throw new t("Upwork feed results did not include any job with a valid ciphertext id; cannot produce round-trippable detail rows.");return i}});
@@ -1,4 +1,4 @@
1
- import{cli as g,Strategy as y}from"@jackwener/opencli/registry";import{CommandExecutionError as a,EmptyResultError as b,AuthRequiredError as _}from"@jackwener/opencli/errors";import{buildSearchUrl as m,isPlainObject as f,jobsToListRows as j,LIST_COLUMNS as U,requireBoundedInt as S,requirePositiveInt as k,requireQuery as T,requireSort as v,unwrapBrowserResult as N}from"./utils.js";g({site:"upwork",name:"search",access:"read",description:"Upwork keyword job search (logged-in browser session, US site)",domain:"www.upwork.com",strategy:y.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"query",positional:!0,required:!0,help:"Job keyword (skill / title / company)"},{name:"location",type:"string",default:"",help:'Country/city filter (e.g. "United States", "Remote")'},{name:"category",type:"string",default:"",help:"Category uid filter (advanced; from job detail `category` slug)"},{name:"sort",type:"string",default:"recency",help:"Sort: recency | relevance | client_total_charge | client_total_reviews"},{name:"page",type:"int",default:1,help:"Page number (1-based)"},{name:"per_page",type:"int",default:10,help:"Rows per page (10-50, capped at one page)"}],columns:U,func:async(r,t)=>{const s=T(t.query),o=String(t.location??"").trim(),p=String(t.category??"").trim(),h=v(t.sort),i=k(t.page,1,"page"),n=S(t.per_page,10,10,50,"per_page"),d=m({query:s,location:o,category:p,sort:h,page:i,perPage:n});await r.goto(d);await r.wait(4);let e;try{e=N(await r.evaluate(`(async () => {
1
+ import{cli as g,Strategy as y}from"@jackwener/opencli/registry";import{CommandExecutionError as r,EmptyResultError as m,AuthRequiredError as _}from"@jackwener/opencli/errors";import{buildSearchUrl as b,isPlainObject as f,jobsToListRows as j,LIST_COLUMNS as U,requireBoundedInt as S,requirePositiveInt as k,requireQuery as v,requireSort as T,unwrapBrowserResult as N}from"./utils.js";g({site:"upwork",name:"search",access:"read",description:"Upwork keyword job search (logged-in browser session, US site)",domain:"www.upwork.com",strategy:y.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"query",positional:!0,required:!0,help:"Job keyword (skill / title / company)"},{name:"location",type:"string",default:"",help:'Country/city filter (e.g. "United States", "Remote")'},{name:"category",type:"string",default:"",help:"Category uid filter (advanced; from job detail `category` slug)"},{name:"sort",type:"string",default:"recency",help:"Sort: recency | relevance | client_total_charge | client_total_reviews"},{name:"page",type:"int",default:1,help:"Page number (1-based)"},{name:"per_page",type:"int",default:10,help:"Rows per page (10-50, capped at one page)"}],columns:U,func:async(a,t)=>{const s=v(t.query),o=String(t.location??"").trim(),p=String(t.category??"").trim(),d=T(t.sort),i=k(t.page,1,"page"),n=S(t.per_page,10,10,50,"per_page"),h=b({query:s,location:o,category:p,sort:d,page:i,perPage:n});await a.goto(h);await a.wait(4);let e;try{e=N(await a.evaluate(`(async () => {
2
2
  const haveState = () => !!(window.__NUXT__ && window.__NUXT__.state && window.__NUXT__.state.jobsSearch);
3
3
  let ready = haveState();
4
4
  for (let i = 0; i < 30; i++) {
@@ -18,4 +18,4 @@ import{cli as g,Strategy as y}from"@jackwener/opencli/registry";import{CommandEx
18
18
  paging: state && state.paging ? state.paging : null,
19
19
  status: state ? state.status : null,
20
20
  };
21
- })()`))}catch(w){throw new a(`Failed to read Upwork search state: ${w?.message??w}`,"The Nuxt state global was not reachable; try again after opening Upwork in the connected browser.")}if(e?.onLogin)throw new _("upwork.com","Upwork redirected to login. Open https://www.upwork.com in the connected browser and sign in, then retry.");if(e?.challenge)throw new a("Upwork served a Cloudflare challenge page","Open https://www.upwork.com in the connected browser and clear the challenge, then retry.");if(!e?.ready)throw new a("Upwork search state (window.__NUXT__.state.jobsSearch) was not present within 15s","The page may not have finished hydrating, or the SSR state shape may have changed.");if(!f(e))throw new a("Upwork search returned an unexpected Browser Bridge payload shape");if(!e.jobsPresent||!Array.isArray(e.jobs))throw new a("Upwork search state had an unexpected jobs shape; expected window.__NUXT__.state.jobsSearch.jobs to be an array.");const c=e.jobs;if(c.length===0)throw new b("upwork search",`No Upwork jobs matched "${s}"${o?` in ${o}`:""}`);const u=(i-1)*n,l=j(c,{offset:u,limit:n});if(l.length===0)throw new a("Upwork search results did not include any job with a valid ciphertext id; cannot produce round-trippable detail rows.");return l}});
21
+ })()`))}catch(w){throw new r(`Failed to read Upwork search state: ${w?.message??w}`,"The Nuxt state global was not reachable; try again after opening Upwork in the connected browser.")}if(e?.onLogin)throw new _("upwork.com","Upwork redirected to login. Open https://www.upwork.com in the connected browser and sign in, then retry.");if(e?.challenge)throw new r("Upwork served a Cloudflare challenge page","Open https://www.upwork.com in the connected browser and clear the challenge, then retry.");if(!e?.ready)throw new r("Upwork search state (window.__NUXT__.state.jobsSearch) was not present within 15s","The page may not have finished hydrating, or the SSR state shape may have changed.");if(!f(e))throw new r("Upwork search returned an unexpected Browser Bridge payload shape");if(!e.jobsPresent||!Array.isArray(e.jobs))throw new r("Upwork search state had an unexpected jobs shape; expected window.__NUXT__.state.jobsSearch.jobs to be an array.");const c=e.jobs;if(c.length===0)throw new m("upwork search",`No Upwork jobs matched "${s}"${o?` in ${o}`:""}`);const u=(i-1)*n,l=j(c,{offset:u,limit:n});if(l.length===0)throw new r("Upwork search results did not include any job with a valid ciphertext id; cannot produce round-trippable detail rows.");return l}});
@@ -1 +1 @@
1
- import{ArgumentError as X,CommandExecutionError as F}from"@jackwener/opencli/errors";export const UPWORK_ORIGIN="https://www.upwork.com";const W=/^~0[12]\d{15,21}$/,H={"best-matches":{path:"/nx/find-work/best-matches",state:"feedBestMatch"},"most-recent":{path:"/nx/find-work/most-recent",state:"feedMostRecent"}},D=new Set(["recency","relevance","client_total_charge","client_total_reviews"]);export function unwrapBrowserResult(Q){if(Q&&typeof Q==="object"&&!Array.isArray(Q)&&"session"in Q&&"data"in Q)return Q.data;return Q}export function isPlainObject(Q){return Q!==null&&typeof Q==="object"&&!Array.isArray(Q)}function q(Q){if(Q===void 0||Q===null||Q==="")return NaN;const Y=typeof Q==="number"?Q:Number(Q);return Number.isFinite(Y)&&Number.isInteger(Y)?Y:NaN}export function requireQuery(Q,Y="query"){const Z=String(Q??"").trim();if(!Z)throw new X(`upwork ${Y} cannot be empty`);return Z}export function requirePositiveInt(Q,Y,Z){const z=q(Q??Y);if(!Number.isInteger(z)||z<=0)throw new X(`upwork ${Z} must be a positive integer`);return z}export function requireBoundedInt(Q,Y,Z,$,z){const J=requirePositiveInt(Q,Y,z);if(J<Z)throw new X(`upwork ${z} must be >= ${Z}`);if(J>$)throw new X(`upwork ${z} must be <= ${$}`);return J}export function requireCiphertext(Q){let Y=String(Q??"").trim();if(!Y)throw new X("upwork job id is required");const Z=Y.match(/~0[12]\d+/);if(Z)Y=Z[0];if(!W.test(Y))throw new X(`upwork job id "${Q}" is not a valid ciphertext (expected ~01… or ~02… followed by digits)`);return Y}export function requireFeedTab(Q,Y="best-matches"){const Z=String(Q??Y).trim().toLowerCase();if(!H[Z])throw new X(`upwork tab must be one of ${Object.keys(H).join(" / ")}, got "${Q}"`);return Z}export function requireSort(Q,Y="recency"){const Z=String(Q??Y).trim().toLowerCase();if(!D.has(Z))throw new X(`upwork sort must be one of ${Array.from(D).join(" / ")}, got "${Q}"`);return Z}export function buildSearchUrl({query:Q,location:Y,category:Z,sort:$,page:z,perPage:J}){const G=new URLSearchParams;G.set("q",Q);if(Y)G.set("location",Y);if(Z)G.set("category2_uid",Z);if($&&$!=="recency")G.set("sort",$);if(J&&J!==10)G.set("per_page",String(J));if(z&&z>1)G.set("page",String(z));return`${UPWORK_ORIGIN}/nx/search/jobs/?${G.toString()}`}export function buildFeedUrl(Q){const Y=H[Q];if(!Y)throw new X(`unknown feed tab "${Q}"`);return`${UPWORK_ORIGIN}${Y.path}`}export function feedStateKey(Q){const Y=H[Q];if(!Y)throw new X(`unknown feed tab "${Q}"`);return Y.state}export function buildJobUrl(Q){return`${UPWORK_ORIGIN}/jobs/${Q}`}export function isValidCiphertext(Q){return W.test(String(Q??"").trim())}export function stripHighlight(Q){if(Q==null)return"";return String(Q).replace(/<span class="highlight">/g,"").replace(/<\/span>/g,"").replace(/\s+/g," ").trim()}export function decodeExperienceLevel(Q){if(Q==null||Q==="")return"";if(typeof Q==="number"){if(Q===1)return"entry";if(Q===2)return"intermediate";if(Q===3)return"expert";return""}const Y=String(Q).toLowerCase();if(Y.includes("entry"))return"entry";if(Y.includes("intermediate"))return"intermediate";if(Y.includes("expert"))return"expert";return""}export function decodeWorkload(Q){if(Q==null||Q==="")return"";const Y=String(Q),Z=Y.includes(".")?Y.split(".").pop():Y,$=Z.toLowerCase();if($==="fulltime"||$.includes("full"))return"full-time";if($==="parttime"||$.includes("part"))return"part-time";if($.includes("hrs/week")||$.includes("hours"))return Z.trim();return""}export function decodeProposalsTier(Q){if(Q==null||Q==="")return"";const Y=String(Q),$=(Y.includes(".")?Y.split(".").pop():Y).trim();if(/^lessThan(\d+)$/i.test($))return`<${$.match(/\d+/)[0]}`;if(/^(\d+)plus$/i.test($))return`${$.match(/\d+/)[0]}+`;const z=$.match(/^(\d+)\s*(?:to|-|–)\s*(\d+)$/i);if(z)return`${z[1]}-${z[2]}`;return $}export function formatBudget(Q){const Y=Q?.type,Z=Number(Q?.hourlyBudget?.min)||0,$=Number(Q?.hourlyBudget?.max)||0,z=Number(Q?.amount?.amount)||0;if(Y===2){if(Z>0&&$>0&&$!==Z)return`$${Z}-$${$}/hr`;if($>0)return`$${$}/hr`;if(Z>0)return`$${Z}/hr`;return""}if(Y===1)return z>0?`$${z}`:"";return""}export function formatBudgetFromDetail(Q){const Y=Q?.type,Z=Number(Q?.extendedBudgetInfo?.hourlyBudgetMin)||0,$=Number(Q?.extendedBudgetInfo?.hourlyBudgetMax)||0,z=Number(Q?.budget?.amount)||0;if(Y===2){if(Z>0&&$>0&&$!==Z)return`$${Z}-$${$}/hr`;if($>0)return`$${$}/hr`;if(Z>0)return`$${Z}/hr`;return""}if(Y===1)return z>0?`$${z}`:"";return""}export function jobType(Q){if(Q===1)return"fixed";if(Q===2)return"hourly";return""}export function formatSkills(Q){const Y=[],Z=[Q?.attrs,Q?.skills,Q?.ontologySkills];for(const $ of Z){if(!Array.isArray($))continue;for(const z of $){const J=(z?.prettyName??z?.prefLabel??z?.name??"").trim();if(J&&!Y.includes(J))Y.push(J)}}return Y.join(", ")}export function jobToListRow(Q,Y){const Z=String(Q?.ciphertext??"").trim();if(!isValidCiphertext(Z))return null;const $=Q?.client||{},z=$?.location?.country||"",J=Number($?.totalFeedback);return{rank:Y,id:Z,title:stripHighlight(Q?.title),type:jobType(Q?.type),budget:formatBudget(Q),experienceLevel:decodeExperienceLevel(Q?.tierText??Q?.tier),proposalsTier:decodeProposalsTier(Q?.proposalsTier),skills:formatSkills(Q),clientCountry:z,clientRating:Number.isFinite(J)&&J>0?J:null,publishedOn:Q?.publishedOn||Q?.createdOn||"",url:buildJobUrl(Z)}}export function jobsToListRows(Q,{offset:Y=0,limit:Z}={}){const $=[],z=Z?Q.slice(0,Z):Q;for(const[J,G]of z.entries()){const K=Y+J+1,M=jobToListRow(G,K);if(!M)throw new F(`Upwork result at rank ${K} did not include a valid ciphertext id; cannot produce round-trippable detail rows.`);$.push(M)}return $}export const LIST_COLUMNS=["rank","id","title","type","budget","experienceLevel","proposalsTier","skills","clientCountry","clientRating","publishedOn","url"],DETAIL_COLUMNS=["id","title","type","budget","experienceLevel","workload","category","skills","description","clientCountry","clientSpent","clientHires","clientRating","proposalsCount","publishedOn","url"];
1
+ import{ArgumentError as s,CommandExecutionError as a}from"@jackwener/opencli/errors";export const UPWORK_ORIGIN="https://www.upwork.com";const d=/^~0[12]\d{15,21}$/,c={"best-matches":{path:"/nx/find-work/best-matches",state:"feedBestMatch"},"most-recent":{path:"/nx/find-work/most-recent",state:"feedMostRecent"}},l=new Set(["recency","relevance","client_total_charge","client_total_reviews"]);export function unwrapBrowserResult(t){if(t&&typeof t==="object"&&!Array.isArray(t)&&"session"in t&&"data"in t)return t.data;return t}export function isPlainObject(t){return t!==null&&typeof t==="object"&&!Array.isArray(t)}function m(t){if(t===void 0||t===null||t==="")return NaN;const e=typeof t==="number"?t:Number(t);return Number.isFinite(e)&&Number.isInteger(e)?e:NaN}export function requireQuery(t,e="query"){const r=String(t??"").trim();if(!r)throw new s(`upwork ${e} cannot be empty`);return r}export function requirePositiveInt(t,e,r){const i=m(t??e);if(!Number.isInteger(i)||i<=0)throw new s(`upwork ${r} must be a positive integer`);return i}export function requireBoundedInt(t,e,r,n,i){const o=requirePositiveInt(t,e,i);if(o<r)throw new s(`upwork ${i} must be >= ${r}`);if(o>n)throw new s(`upwork ${i} must be <= ${n}`);return o}export function requireCiphertext(t){let e=String(t??"").trim();if(!e)throw new s("upwork job id is required");const r=e.match(/~0[12]\d+/);if(r)e=r[0];if(!d.test(e))throw new s(`upwork job id "${t}" is not a valid ciphertext (expected ~01… or ~02… followed by digits)`);return e}export function requireFeedTab(t,e="best-matches"){const r=String(t??e).trim().toLowerCase();if(!c[r])throw new s(`upwork tab must be one of ${Object.keys(c).join(" / ")}, got "${t}"`);return r}export function requireSort(t,e="recency"){const r=String(t??e).trim().toLowerCase();if(!l.has(r))throw new s(`upwork sort must be one of ${Array.from(l).join(" / ")}, got "${t}"`);return r}export function buildSearchUrl({query:t,location:e,category:r,sort:n,page:i,perPage:o}){const u=new URLSearchParams;u.set("q",t);if(e)u.set("location",e);if(r)u.set("category2_uid",r);if(n&&n!=="recency")u.set("sort",n);if(o&&o!==10)u.set("per_page",String(o));if(i&&i>1)u.set("page",String(i));return`${UPWORK_ORIGIN}/nx/search/jobs/?${u.toString()}`}export function buildFeedUrl(t){const e=c[t];if(!e)throw new s(`unknown feed tab "${t}"`);return`${UPWORK_ORIGIN}${e.path}`}export function feedStateKey(t){const e=c[t];if(!e)throw new s(`unknown feed tab "${t}"`);return e.state}export function buildJobUrl(t){return`${UPWORK_ORIGIN}/jobs/${t}`}export function isValidCiphertext(t){return d.test(String(t??"").trim())}export function stripHighlight(t){if(t==null)return"";return String(t).replace(/<span class="highlight">/g,"").replace(/<\/span>/g,"").replace(/\s+/g," ").trim()}export function decodeExperienceLevel(t){if(t==null||t==="")return"";if(typeof t==="number"){if(t===1)return"entry";if(t===2)return"intermediate";if(t===3)return"expert";return""}const e=String(t).toLowerCase();if(e.includes("entry"))return"entry";if(e.includes("intermediate"))return"intermediate";if(e.includes("expert"))return"expert";return""}export function decodeWorkload(t){if(t==null||t==="")return"";const e=String(t),r=e.includes(".")?e.split(".").pop():e,n=r.toLowerCase();if(n==="fulltime"||n.includes("full"))return"full-time";if(n==="parttime"||n.includes("part"))return"part-time";if(n.includes("hrs/week")||n.includes("hours"))return r.trim();return""}export function decodeProposalsTier(t){if(t==null||t==="")return"";const e=String(t),n=(e.includes(".")?e.split(".").pop():e).trim();if(/^lessThan(\d+)$/i.test(n))return`<${n.match(/\d+/)[0]}`;if(/^(\d+)plus$/i.test(n))return`${n.match(/\d+/)[0]}+`;const i=n.match(/^(\d+)\s*(?:to|-|–)\s*(\d+)$/i);if(i)return`${i[1]}-${i[2]}`;return n}export function formatBudget(t){const e=t?.type,r=Number(t?.hourlyBudget?.min)||0,n=Number(t?.hourlyBudget?.max)||0,i=Number(t?.amount?.amount)||0;if(e===2){if(r>0&&n>0&&n!==r)return`$${r}-$${n}/hr`;if(n>0)return`$${n}/hr`;if(r>0)return`$${r}/hr`;return""}if(e===1)return i>0?`$${i}`:"";return""}export function formatBudgetFromDetail(t){const e=t?.type,r=Number(t?.extendedBudgetInfo?.hourlyBudgetMin)||0,n=Number(t?.extendedBudgetInfo?.hourlyBudgetMax)||0,i=Number(t?.budget?.amount)||0;if(e===2){if(r>0&&n>0&&n!==r)return`$${r}-$${n}/hr`;if(n>0)return`$${n}/hr`;if(r>0)return`$${r}/hr`;return""}if(e===1)return i>0?`$${i}`:"";return""}export function jobType(t){if(t===1)return"fixed";if(t===2)return"hourly";return""}export function formatSkills(t){const e=[],r=[t?.attrs,t?.skills,t?.ontologySkills];for(const n of r){if(!Array.isArray(n))continue;for(const i of n){const o=(i?.prettyName??i?.prefLabel??i?.name??"").trim();if(o&&!e.includes(o))e.push(o)}}return e.join(", ")}export function jobToListRow(t,e){const r=String(t?.ciphertext??"").trim();if(!isValidCiphertext(r))return null;const n=t?.client||{},i=n?.location?.country||"",o=Number(n?.totalFeedback);return{rank:e,id:r,title:stripHighlight(t?.title),type:jobType(t?.type),budget:formatBudget(t),experienceLevel:decodeExperienceLevel(t?.tierText??t?.tier),proposalsTier:decodeProposalsTier(t?.proposalsTier),skills:formatSkills(t),clientCountry:i,clientRating:Number.isFinite(o)&&o>0?o:null,publishedOn:t?.publishedOn||t?.createdOn||"",url:buildJobUrl(r)}}export function jobsToListRows(t,{offset:e=0,limit:r}={}){const n=[],i=r?t.slice(0,r):t;for(const[o,u]of i.entries()){const f=e+o+1,p=jobToListRow(u,f);if(!p)throw new a(`Upwork result at rank ${f} did not include a valid ciphertext id; cannot produce round-trippable detail rows.`);n.push(p)}return n}export const LIST_COLUMNS=["rank","id","title","type","budget","experienceLevel","proposalsTier","skills","clientCountry","clientRating","publishedOn","url"],DETAIL_COLUMNS=["id","title","type","budget","experienceLevel","workload","category","skills","description","clientCountry","clientSpent","clientHires","clientRating","proposalsCount","publishedOn","url"];
package/clis/v2ex/auth.js CHANGED
@@ -1,4 +1,4 @@
1
- import{AuthRequiredError as D,CommandExecutionError as J}from"@jackwener/opencli/errors";import{registerSiteAuthCommands as K}from"../_shared/site-auth.js";async function F(z){return(await z.getCookies({url:"https://www.v2ex.com"})).some((G)=>G.name==="A2"&&G.value)}async function H(z){if(!await F(z))throw new D("v2ex.com","V2EX A2 session cookie missing — anonymous");await z.goto("https://www.v2ex.com/");await z.wait(1);const B=await z.evaluate(`
1
+ import{AuthRequiredError as n,CommandExecutionError as s}from"@jackwener/opencli/errors";import{registerSiteAuthCommands as a}from"../_shared/site-auth.js";async function o(e){return(await e.getCookies({url:"https://www.v2ex.com"})).some((r)=>r.name==="A2"&&r.value)}async function i(e){if(!await o(e))throw new n("v2ex.com","V2EX A2 session cookie missing — anonymous");await e.goto("https://www.v2ex.com/");await e.wait(1);const t=await e.evaluate(`
2
2
  (() => {
3
3
  const link = document.querySelector('#Top a[href^="/member/"]');
4
4
  if (!link) return { kind: 'auth', detail: 'V2EX top bar has no member link — anonymous session' };
@@ -7,4 +7,4 @@ import{AuthRequiredError as D,CommandExecutionError as J}from"@jackwener/opencli
7
7
  if (!username) return { kind: 'auth', detail: 'V2EX member link present but username empty' };
8
8
  return { ok: true, username };
9
9
  })()
10
- `);if(B?.kind==="auth")throw new D("v2ex.com",B.detail);if(!B?.ok)throw new J(`Unexpected V2EX probe: ${JSON.stringify(B)}`);return{username:B.username}}K({site:"v2ex",domain:"v2ex.com",loginUrl:"https://www.v2ex.com/signin",columns:["username"],quickCheck:F,verify:H,poll:async(z)=>{if(!await F(z))throw new D("v2ex.com","Waiting for V2EX A2 session cookie");return H(z)}});
10
+ `);if(t?.kind==="auth")throw new n("v2ex.com",t.detail);if(!t?.ok)throw new s(`Unexpected V2EX probe: ${JSON.stringify(t)}`);return{username:t.username}}a({site:"v2ex",domain:"v2ex.com",loginUrl:"https://www.v2ex.com/signin",columns:["username"],quickCheck:o,verify:i,poll:async(e)=>{if(!await o(e))throw new n("v2ex.com","Waiting for V2EX A2 session cookie");return i(e)}});
@@ -1,4 +1,4 @@
1
- import{CommandExecutionError as B}from"@jackwener/opencli/errors";import{cli as G,Strategy as H}from"@jackwener/opencli/registry";G({site:"v2ex",name:"daily",access:"write",description:"V2EX 每日签到并领取铜币",domain:"www.v2ex.com",strategy:H.COOKIE,browser:!0,args:[],columns:["status","message"],func:async(q)=>{if(!q)throw new B("Browser page required");if(process.env.OPENCLI_VERBOSE)console.error("[ppcli:v2ex] Navigating to /mission/daily");await q.goto("https://www.v2ex.com/mission/daily");for(let z=0;z<5;z++){await new Promise((F)=>setTimeout(F,1500));if(!(await q.evaluate("() => document.title"))?.includes("Just a moment"))break;if(process.env.OPENCLI_VERBOSE)console.error("[ppcli:v2ex] Waiting for Cloudflare...")}const b=await q.evaluate(`
1
+ import{CommandExecutionError as r}from"@jackwener/opencli/errors";import{cli as s,Strategy as i}from"@jackwener/opencli/registry";s({site:"v2ex",name:"daily",access:"write",description:"V2EX 每日签到并领取铜币",domain:"www.v2ex.com",strategy:i.COOKIE,browser:!0,args:[],columns:["status","message"],func:async(t)=>{if(!t)throw new r("Browser page required");if(process.env.OPENCLI_VERBOSE)console.error("[ppcli:v2ex] Navigating to /mission/daily");await t.goto("https://www.v2ex.com/mission/daily");for(let n=0;n<5;n++){await new Promise((c)=>setTimeout(c,1500));if(!(await t.evaluate("() => document.title"))?.includes("Just a moment"))break;if(process.env.OPENCLI_VERBOSE)console.error("[ppcli:v2ex] Waiting for Cloudflare...")}const e=await t.evaluate(`
2
2
  async () => {
3
3
  const btn = document.querySelector('input.super.normal.button');
4
4
  if (!btn || !btn.value.includes('领取')) {
@@ -20,7 +20,7 @@ import{CommandExecutionError as B}from"@jackwener/opencli/errors";import{cli as
20
20
  debug_body: document.body.innerText.substring(0, 200).replace(/\\n/g, ' ')
21
21
  };
22
22
  }
23
- `);if(b.error){if(process.env.OPENCLI_VERBOSE){console.error(`[ppcli:v2ex:debug] Page Title: ${b.debug_title}`);console.error(`[ppcli:v2ex:debug] Page Body: ${b.debug_body}`)}throw new B(b.error)}if(b.claimed)return[{status:"✅ 已签到",message:b.message}];if(process.env.OPENCLI_VERBOSE)console.error(`[ppcli:v2ex] Found check-in token: once=${b.once}. Checking in...`);await q.goto(`https://www.v2ex.com/mission/daily/redeem?once=${b.once}`);await new Promise((z)=>setTimeout(z,3000));const A=await q.evaluate(`
23
+ `);if(e.error){if(process.env.OPENCLI_VERBOSE){console.error(`[ppcli:v2ex:debug] Page Title: ${e.debug_title}`);console.error(`[ppcli:v2ex:debug] Page Body: ${e.debug_body}`)}throw new r(e.error)}if(e.claimed)return[{status:"✅ 已签到",message:e.message}];if(process.env.OPENCLI_VERBOSE)console.error(`[ppcli:v2ex] Found check-in token: once=${e.once}. Checking in...`);await t.goto(`https://www.v2ex.com/mission/daily/redeem?once=${e.once}`);await new Promise((n)=>setTimeout(n,3000));const o=await t.evaluate(`
24
24
  async () => {
25
25
  const btn = document.querySelector('input.super.normal.button');
26
26
  if (!btn || !btn.value.includes('领取')) {
@@ -38,4 +38,4 @@ import{CommandExecutionError as B}from"@jackwener/opencli/errors";import{cli as
38
38
  }
39
39
  return { success: false };
40
40
  }
41
- `);if(A.success)return[{status:"\uD83C\uDF89 签到成功",message:`当前余额: ${A.balance||"未知"}`}];else return[{status:"❌ 签到失败",message:"未能确认签到结果,请手动检查"}]}});
41
+ `);if(o.success)return[{status:"\uD83C\uDF89 签到成功",message:`当前余额: ${o.balance||"未知"}`}];else return[{status:"❌ 签到失败",message:"未能确认签到结果,请手动检查"}]}});