publishport-opencli 1.0.20 → 1.0.22

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 (1045) 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 +75 -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.d.ts +14 -1
  952. package/dist/src/browser/base-page.js +25 -25
  953. package/dist/src/browser/bridge.js +1 -1
  954. package/dist/src/browser/cdp.js +1 -1
  955. package/dist/src/browser/daemon-client.js +1 -1
  956. package/dist/src/browser/daemon-lifecycle.js +7 -7
  957. package/dist/src/browser/dom-helpers.d.ts +24 -0
  958. package/dist/src/browser/dom-helpers.js +36 -36
  959. package/dist/src/browser/dom-snapshot.js +19 -19
  960. package/dist/src/browser/errors.js +3 -3
  961. package/dist/src/browser/extract.js +5 -5
  962. package/dist/src/browser/find.js +13 -13
  963. package/dist/src/browser/fingerprint.js +2 -2
  964. package/dist/src/browser/html-tree.js +6 -6
  965. package/dist/src/browser/managed-chrome.js +1 -1
  966. package/dist/src/browser/network-cache.js +1 -1
  967. package/dist/src/browser/network-key.js +1 -1
  968. package/dist/src/browser/page.js +7 -7
  969. package/dist/src/browser/profile.js +2 -2
  970. package/dist/src/browser/proxy-test.js +8 -8
  971. package/dist/src/browser/shape-filter.js +1 -1
  972. package/dist/src/browser/shape.js +1 -1
  973. package/dist/src/browser/stealth.js +3 -3
  974. package/dist/src/browser/tabs.js +2 -2
  975. package/dist/src/browser/target-errors.js +1 -1
  976. package/dist/src/browser/target-resolver.js +23 -23
  977. package/dist/src/browser/trace.js +2 -2
  978. package/dist/src/browser/utils.js +1 -1
  979. package/dist/src/browser/verify-fixture.js +2 -2
  980. package/dist/src/browser-session-lock.js +1 -1
  981. package/dist/src/build-manifest.js +11 -11
  982. package/dist/src/capabilityRouting.js +1 -1
  983. package/dist/src/cli-argv-preprocess.js +1 -1
  984. package/dist/src/cli.js +32 -32
  985. package/dist/src/commanderAdapter.js +3 -3
  986. package/dist/src/commands/auth.js +3 -3
  987. package/dist/src/commands/daemon.d.ts +3 -1
  988. package/dist/src/commands/daemon.js +1 -1
  989. package/dist/src/completion-fast.js +1 -1
  990. package/dist/src/completion.js +1 -1
  991. package/dist/src/constants.js +1 -1
  992. package/dist/src/convention-audit.js +2 -2
  993. package/dist/src/daemon-utils.js +1 -1
  994. package/dist/src/daemon.js +1 -1
  995. package/dist/src/discovery.js +2 -2
  996. package/dist/src/doctor.js +9 -9
  997. package/dist/src/download/article-download.js +15 -15
  998. package/dist/src/download/index.js +4 -4
  999. package/dist/src/download/media-download.js +1 -1
  1000. package/dist/src/download/progress.js +3 -3
  1001. package/dist/src/electron-apps.js +1 -1
  1002. package/dist/src/errors.js +2 -2
  1003. package/dist/src/execution.js +2 -2
  1004. package/dist/src/external.js +1 -1
  1005. package/dist/src/help.js +8 -8
  1006. package/dist/src/hooks.js +1 -1
  1007. package/dist/src/interceptor.js +21 -21
  1008. package/dist/src/launcher.js +7 -7
  1009. package/dist/src/logger.js +8 -8
  1010. package/dist/src/main.js +7 -7
  1011. package/dist/src/observation/artifact.js +5 -5
  1012. package/dist/src/observation/retention.js +1 -1
  1013. package/dist/src/output.js +2 -2
  1014. package/dist/src/package-paths.js +1 -1
  1015. package/dist/src/pipeline/executor.js +1 -1
  1016. package/dist/src/pipeline/registry.js +1 -1
  1017. package/dist/src/pipeline/steps/browser.js +1 -1
  1018. package/dist/src/pipeline/steps/download.js +1 -1
  1019. package/dist/src/pipeline/steps/fetch.js +6 -6
  1020. package/dist/src/pipeline/steps/intercept.js +1 -1
  1021. package/dist/src/pipeline/steps/tap.js +17 -17
  1022. package/dist/src/pipeline/steps/transform.js +1 -1
  1023. package/dist/src/pipeline/template.js +1 -1
  1024. package/dist/src/plugin-manifest.js +1 -1
  1025. package/dist/src/plugin-scaffold.js +19 -19
  1026. package/dist/src/plugin.js +12 -12
  1027. package/dist/src/rate-limit.js +1 -1
  1028. package/dist/src/registry.js +1 -1
  1029. package/dist/src/runtime-detect.js +1 -1
  1030. package/dist/src/runtime.js +1 -1
  1031. package/dist/src/scripts/framework.js +1 -1
  1032. package/dist/src/scripts/interact.js +1 -1
  1033. package/dist/src/scripts/store.js +1 -1
  1034. package/dist/src/serialization.js +2 -2
  1035. package/dist/src/session-lease.js +1 -1
  1036. package/dist/src/snapshotFormatter.js +2 -2
  1037. package/dist/src/tui.js +12 -12
  1038. package/dist/src/types.d.ts +2 -1
  1039. package/dist/src/user-runtime.js +1 -1
  1040. package/dist/src/utils.d.ts +12 -0
  1041. package/dist/src/utils.js +4 -4
  1042. package/dist/src/validate.js +2 -2
  1043. package/dist/src/verify.js +2 -2
  1044. package/dist/src/version.js +1 -1
  1045. package/package.json +1 -1
@@ -1,2 +1,2 @@
1
- import*as D from"node:fs";import*as B from"node:os";import*as T from"node:path";import{ArgumentError as K,CommandExecutionError as J}from"@jackwener/opencli/errors";import{Strategy as M}from"@jackwener/opencli/registry";const _=1,A=1e4;function k(q){return T.join(B.homedir(),".ppcli","sites",q,"connections.json")}function C(q){return T.join(B.homedir(),".opencli","sites",q,"connections.json")}function O(q){const Q=String(q??"").trim();if(!Q)throw new K("站点名称不能为空");if(Q.length>64)throw new K("站点名称不能超过 64 个字符");if(/[\u0000-\u001f\u007f]/.test(Q))throw new K("站点名称不能包含控制字符");return Q}export function normalizeSiteUrl(q,{originOnly:Q=!1,label:H="站点网址"}={}){const X=String(q??"").trim();if(!X)throw new K(`${H}不能为空`);let Z;try{Z=new URL(X.includes("://")?X:`https://${X}`)}catch{throw new K(`${H}不是合法网址: ${X}`)}if(Z.protocol!=="http:"&&Z.protocol!=="https:")throw new K(`${H}必须是 http(s) 网址: ${X}`);if(Z.username||Z.password||Z.search||Z.hash)throw new K(`${H}只应包含站点根地址,不要带账号、查询参数或锚点`);if(Q)return Z.origin;const $=Z.pathname.replace(/\/+$/,"");return`${Z.origin}${$==="/"?"":$}`}function b(){return{version:_,default:"",sites:[]}}function I(q,Q){if(!Q||Q.version!==_||!Array.isArray(Q.sites))throw new J(`${q} 站点连接文件格式不受支持`,"请删除损坏的 connections.json 后重新添加站点。");const H=Q.sites.filter(($)=>$&&typeof $.name==="string"&&typeof $.url==="string").map(($)=>({name:$.name,url:$.url})),X=H[0]?.name??"",Z=typeof Q.default==="string"?Q.default:"";return{version:_,default:H.some(($)=>$.name===Z)?Z:X,sites:H}}export function loadSiteConnections(q){const Q=k(q);try{return I(q,JSON.parse(D.readFileSync(Q,"utf8")))}catch(H){if(H?.code==="ENOENT"){const X=C(q);try{const Z=I(q,JSON.parse(D.readFileSync(X,"utf8")));V(q,Z);return Z}catch(Z){if(Z?.code==="ENOENT")return b();if(Z instanceof J)throw Z;throw new J(`${q} 旧版站点连接读取失败: ${Z?.message??Z}`)}}if(H instanceof J)throw H;throw new J(`${q} 站点连接读取失败: ${H?.message??H}`)}}function V(q,Q){const H=k(q),X=T.dirname(H),Z=`${H}.${process.pid}.tmp`;try{D.mkdirSync(X,{recursive:!0,mode:448});D.writeFileSync(Z,`${JSON.stringify(Q,null,2)}
2
- `,{mode:384});D.renameSync(Z,H);try{D.chmodSync(H,384)}catch{}}catch($){try{D.rmSync(Z,{force:!0})}catch{}throw new J(`${q} 站点连接保存失败: ${$?.message??$}`)}}function L(q,Q){const H=String(Q??"").trim();return q.sites.find((X)=>X.name===H)??q.sites.find((X)=>X.name.toLocaleLowerCase()===H.toLocaleLowerCase())}export function addSiteConnection(q,Q,H,X={}){const Z=O(Q),$=normalizeSiteUrl(H,X),G=loadSiteConnections(q),W=L(G,Z);if(W){if(W.url===$)return{status:"unchanged",name:W.name,url:$,default:G.default===W.name};throw new K(`站点名称“${W.name}”已指向 ${W.url}`,"请换一个名称,或先删除原连接。")}const F=G.sites.find((j)=>j.url===$);if(F)throw new K(`${$} 已保存为“${F.name}”`);G.sites.push({name:Z,url:$});if(!G.default)G.default=Z;V(q,G);return{status:"added",name:Z,url:$,default:G.default===Z}}export function removeSiteConnection(q,Q){const H=loadSiteConnections(q),X=L(H,Q);if(!X)throw new K(`未找到站点连接: ${String(Q??"").trim()}`);H.sites=H.sites.filter((Z)=>Z.name!==X.name);if(H.default===X.name)H.default=H.sites[0]?.name??"";V(q,H);return{status:"removed",name:X.name,url:X.url}}export function useSiteConnection(q,Q){const H=loadSiteConnections(q),X=L(H,Q);if(!X)throw new K(`未找到站点连接: ${String(Q??"").trim()}`);H.default=X.name;V(q,H);return{status:"default",name:X.name,url:X.url,default:!0}}export function listSiteConnectionRows(q){const Q=loadSiteConnections(q);return Q.sites.map((H)=>({...H,default:H.name===Q.default}))}function N(q,Q){const H=Q.replace(/\/+$/,"");if(q==="mastodon")return`${H}/api/v2/instance`;if(q==="wordpress")return`${H}/wp-json/`;if(q==="ghost")return`${H}/ghost/api/admin/site/`;throw new K(`不支持校验站点类型: ${q}`)}async function P(q){const Q=new AbortController,H=setTimeout(()=>Q.abort(),A);try{return await fetch(q,{method:"GET",headers:{accept:"application/json"},redirect:"follow",signal:Q.signal})}finally{clearTimeout(H)}}async function x(q){const Q=q.headers?.get?.("content-type")??"";if(!/json/i.test(Q))return null;try{return await q.json()}catch{return null}}function R(q){if(!q||typeof q!=="object"||Array.isArray(q))return!1;const Q=q.domain??q.uri;return typeof Q==="string"&&Q.length>0&&(typeof q.version==="string"||q.title&&typeof q.title==="string")}function U(q){if(!q||typeof q!=="object"||Array.isArray(q))return!1;if((Array.isArray(q.namespaces)?q.namespaces:[]).some((X)=>X==="wp/v2"||String(X).startsWith("wp/v2/")))return!0;const H=q.routes;return!!H&&typeof H==="object"&&Object.keys(H).some((X)=>X.startsWith("/wp/v2/"))}function E(q,Q){if(!q||typeof q!=="object"||Array.isArray(q))return!1;if(q.site&&typeof q.site==="object")return!0;if(Q!==401&&Q!==403)return!1;return(Array.isArray(q.errors)?q.errors:[]).some((X)=>{if(!X||typeof X!=="object")return!1;const Z=String(X.type??""),$=String(X.context??"");return/GhostError|NoPermissionError|UnauthorizedError|NotFoundError/i.test(Z)||/ghost admin api/i.test($)})}export async function verifySiteKind(q,Q,{originOnly:H=!1}={}){const X=normalizeSiteUrl(Q,{originOnly:H}),Z=N(q,X);let $;try{$=await P(Z)}catch(F){const j=F?.name==="AbortError"?"连接超时":F?.message??String(F);throw new J(`连接不到 ${q} 站点 ${X}: ${j}`,"请检查站点地址、网络和 HTTPS 证书后重试。")}const G=await x($),W=q==="mastodon"?$.ok&&R(G):q==="wordpress"?$.ok&&U(G):E(G,$.status);if(!W&&q==="mastodon"&&$.status===404)try{const F=await P(`${X.replace(/\/+$/,"")}/api/v1/instance`),j=await x(F);if(F.ok&&R(j))return{platform:q,url:X,verified:!0}}catch{}if(!W){const F=q==="mastodon"?"Mastodon":q==="wordpress"?"WordPress":"Ghost";throw new K(`${X} 不是可识别的 ${F} 站点`,`请填写 ${F} 站点根地址,当前检测地址: ${Z}`)}return{platform:q,url:X,verified:!0}}export function resolveSiteConnection(q,Q={},{selector:H="site",legacySelectors:X=[],envUrl:Z,originOnly:$=!1}={}){const G=X.map((Y)=>Q[Y]).find((Y)=>Y!=null&&String(Y).trim());if(G!=null)return{name:"",url:normalizeSiteUrl(G,{originOnly:$}),saved:!1};const W=Q[H];if(W!=null&&String(W).trim()){const Y=String(W).trim(),h=loadSiteConnections(q),z=L(h,Y);if(z)return{...z,saved:!0};if(Y.includes("://")||Y.includes(".")||/^localhost(?::\d+)?(?:\/|$)/i.test(Y)||/^\[[0-9a-f:]+\](?::\d+)?(?:\/|$)/i.test(Y))return{name:"",url:normalizeSiteUrl(Y,{originOnly:$}),saved:!1};throw new K(`未找到 ${q} 站点连接“${Y}”`,`运行 ppcli ${q} sites 查看已保存站点。`)}const F=loadSiteConnections(q),j=L(F,F.default)??F.sites[0];if(j)return{...j,saved:!0};if(Z&&String(Z).trim())return{name:"",url:normalizeSiteUrl(Z,{originOnly:$}),saved:!1};throw new K(`${q} 尚未添加站点`,`先运行 ppcli ${q} add-site --name <名称> --url <网址>,或在本次命令传 --${H} <网址>。`)}export function connectionCommandDefinitions(q,{originOnly:Q=!1}={}){return[{site:q,name:"sites",access:"read",description:`列出已保存的 ${q} 自部署站点连接。`,strategy:M.LOCAL,browser:!1,args:[],columns:["name","url","default"],func:async()=>listSiteConnectionRows(q)},{site:q,name:"add-site",access:"write",description:`校验并保存一个命名的 ${q} 自部署站点连接(只保存名称和网址)。`,strategy:M.LOCAL,browser:!1,args:[{name:"name",required:!0,help:"连接名称,例如“公司博客”"},{name:"url",required:!0,help:"站点根网址,例如 https://blog.example.com"}],columns:["status","name","url","default"],func:async(H)=>{await verifySiteKind(q,H.url,{originOnly:Q});return[addSiteConnection(q,H.name,H.url,{originOnly:Q})]}},{site:q,name:"remove-site",access:"write",description:`删除一个 ${q} 站点连接;不会清除浏览器里的站点 cookie。`,strategy:M.LOCAL,browser:!1,args:[{name:"site",required:!0,help:"要删除的连接名称"}],columns:["status","name","url"],func:async(H)=>[removeSiteConnection(q,H.site)]},{site:q,name:"use-site",access:"write",description:`设置 ${q} 默认站点;之后省略 --site 时使用它。`,strategy:M.LOCAL,browser:!1,args:[{name:"site",required:!0,help:"要设为默认的连接名称"}],columns:["status","name","url","default"],func:async(H)=>[useSiteConnection(q,H.site)]}]}
1
+ import*as m from"node:fs";import*as b from"node:os";import*as p from"node:path";import{ArgumentError as a,CommandExecutionError as l}from"@jackwener/opencli/errors";import{Strategy as y}from"@jackwener/opencli/registry";const g=1,k=1e4;function A(e){return p.join(b.homedir(),".ppcli","sites",e,"connections.json")}function N(e){return p.join(b.homedir(),".opencli","sites",e,"connections.json")}function O(e){const n=String(e??"").trim();if(!n)throw new a("站点名称不能为空");if(n.length>64)throw new a("站点名称不能超过 64 个字符");if(/[\u0000-\u001f\u007f]/.test(n))throw new a("站点名称不能包含控制字符");return n}export function normalizeSiteUrl(e,{originOnly:n=!1,label:t="站点网址"}={}){const r=String(e??"").trim();if(!r)throw new a(`${t}不能为空`);let s;try{s=new URL(r.includes("://")?r:`https://${r}`)}catch{throw new a(`${t}不是合法网址: ${r}`)}if(s.protocol!=="http:"&&s.protocol!=="https:")throw new a(`${t}必须是 http(s) 网址: ${r}`);if(s.username||s.password||s.search||s.hash)throw new a(`${t}只应包含站点根地址,不要带账号、查询参数或锚点`);if(n)return s.origin;const o=s.pathname.replace(/\/+$/,"");return`${s.origin}${o==="/"?"":o}`}function T(){return{version:g,default:"",sites:[]}}function E(e,n){if(!n||n.version!==g||!Array.isArray(n.sites))throw new l(`${e} 站点连接文件格式不受支持`,"请删除损坏的 connections.json 后重新添加站点。");const t=n.sites.filter((o)=>o&&typeof o.name==="string"&&typeof o.url==="string").map((o)=>({name:o.name,url:o.url})),r=t[0]?.name??"",s=typeof n.default==="string"?n.default:"";return{version:g,default:t.some((o)=>o.name===s)?s:r,sites:t}}export function loadSiteConnections(e){const n=A(e);try{return E(e,JSON.parse(m.readFileSync(n,"utf8")))}catch(t){if(t?.code==="ENOENT"){const r=N(e);try{const s=E(e,JSON.parse(m.readFileSync(r,"utf8")));w(e,s);return s}catch(s){if(s?.code==="ENOENT")return T();if(s instanceof l)throw s;throw new l(`${e} 旧版站点连接读取失败: ${s?.message??s}`)}}if(t instanceof l)throw t;throw new l(`${e} 站点连接读取失败: ${t?.message??t}`)}}function w(e,n){const t=A(e),r=p.dirname(t),s=`${t}.${process.pid}.tmp`;try{m.mkdirSync(r,{recursive:!0,mode:448});m.writeFileSync(s,`${JSON.stringify(n,null,2)}
2
+ `,{mode:384});m.renameSync(s,t);try{m.chmodSync(t,384)}catch{}}catch(o){try{m.rmSync(s,{force:!0})}catch{}throw new l(`${e} 站点连接保存失败: ${o?.message??o}`)}}function h(e,n){const t=String(n??"").trim();return e.sites.find((r)=>r.name===t)??e.sites.find((r)=>r.name.toLocaleLowerCase()===t.toLocaleLowerCase())}export function addSiteConnection(e,n,t,r={}){const s=O(n),o=normalizeSiteUrl(t,r),c=loadSiteConnections(e),u=h(c,s);if(u){if(u.url===o)return{status:"unchanged",name:u.name,url:o,default:c.default===u.name};throw new a(`站点名称“${u.name}”已指向 ${u.url}`,"请换一个名称,或先删除原连接。")}const i=c.sites.find((d)=>d.url===o);if(i)throw new a(`${o} 已保存为“${i.name}”`);c.sites.push({name:s,url:o});if(!c.default)c.default=s;w(e,c);return{status:"added",name:s,url:o,default:c.default===s}}export function removeSiteConnection(e,n){const t=loadSiteConnections(e),r=h(t,n);if(!r)throw new a(`未找到站点连接: ${String(n??"").trim()}`);t.sites=t.sites.filter((s)=>s.name!==r.name);if(t.default===r.name)t.default=t.sites[0]?.name??"";w(e,t);return{status:"removed",name:r.name,url:r.url}}export function useSiteConnection(e,n){const t=loadSiteConnections(e),r=h(t,n);if(!r)throw new a(`未找到站点连接: ${String(n??"").trim()}`);t.default=r.name;w(e,t);return{status:"default",name:r.name,url:r.url,default:!0}}export function listSiteConnectionRows(e){const n=loadSiteConnections(e);return n.sites.map((t)=>({...t,default:t.name===n.default}))}function P(e,n){const t=n.replace(/\/+$/,"");if(e==="mastodon")return`${t}/api/v2/instance`;if(e==="wordpress")return`${t}/wp-json/`;if(e==="ghost")return`${t}/ghost/api/admin/site/`;throw new a(`不支持校验站点类型: ${e}`)}async function j(e){const n=new AbortController,t=setTimeout(()=>n.abort(),k);try{return await fetch(e,{method:"GET",headers:{accept:"application/json"},redirect:"follow",signal:n.signal})}finally{clearTimeout(t)}}async function C(e){const n=e.headers?.get?.("content-type")??"";if(!/json/i.test(n))return null;try{return await e.json()}catch{return null}}function L(e){if(!e||typeof e!=="object"||Array.isArray(e))return!1;const n=e.domain??e.uri;return typeof n==="string"&&n.length>0&&(typeof e.version==="string"||e.title&&typeof e.title==="string")}function U(e){if(!e||typeof e!=="object"||Array.isArray(e))return!1;if((Array.isArray(e.namespaces)?e.namespaces:[]).some((r)=>r==="wp/v2"||String(r).startsWith("wp/v2/")))return!0;const t=e.routes;return!!t&&typeof t==="object"&&Object.keys(t).some((r)=>r.startsWith("/wp/v2/"))}function R(e,n){if(!e||typeof e!=="object"||Array.isArray(e))return!1;if(e.site&&typeof e.site==="object")return!0;if(n!==401&&n!==403)return!1;return(Array.isArray(e.errors)?e.errors:[]).some((r)=>{if(!r||typeof r!=="object")return!1;const s=String(r.type??""),o=String(r.context??"");return/GhostError|NoPermissionError|UnauthorizedError|NotFoundError/i.test(s)||/ghost admin api/i.test(o)})}export async function verifySiteKind(e,n,{originOnly:t=!1}={}){const r=normalizeSiteUrl(n,{originOnly:t}),s=P(e,r);let o;try{o=await j(s)}catch(i){const d=i?.name==="AbortError"?"连接超时":i?.message??String(i);throw new l(`连接不到 ${e} 站点 ${r}: ${d}`,"请检查站点地址、网络和 HTTPS 证书后重试。")}const c=await C(o),u=e==="mastodon"?o.ok&&L(c):e==="wordpress"?o.ok&&U(c):R(c,o.status);if(!u&&e==="mastodon"&&o.status===404)try{const i=await j(`${r.replace(/\/+$/,"")}/api/v1/instance`),d=await C(i);if(i.ok&&L(d))return{platform:e,url:r,verified:!0}}catch{}if(!u){const i=e==="mastodon"?"Mastodon":e==="wordpress"?"WordPress":"Ghost";throw new a(`${r} 不是可识别的 ${i} 站点`,`请填写 ${i} 站点根地址,当前检测地址: ${s}`)}return{platform:e,url:r,verified:!0}}export function resolveSiteConnection(e,n={},{selector:t="site",legacySelectors:r=[],envUrl:s,originOnly:o=!1}={}){const c=r.map((f)=>n[f]).find((f)=>f!=null&&String(f).trim());if(c!=null)return{name:"",url:normalizeSiteUrl(c,{originOnly:o}),saved:!1};const u=n[t];if(u!=null&&String(u).trim()){const f=String(u).trim(),x=loadSiteConnections(e),S=h(x,f);if(S)return{...S,saved:!0};if(f.includes("://")||f.includes(".")||/^localhost(?::\d+)?(?:\/|$)/i.test(f)||/^\[[0-9a-f:]+\](?::\d+)?(?:\/|$)/i.test(f))return{name:"",url:normalizeSiteUrl(f,{originOnly:o}),saved:!1};throw new a(`未找到 ${e} 站点连接“${f}”`,`运行 ppcli ${e} sites 查看已保存站点。`)}const i=loadSiteConnections(e),d=h(i,i.default)??i.sites[0];if(d)return{...d,saved:!0};if(s&&String(s).trim())return{name:"",url:normalizeSiteUrl(s,{originOnly:o}),saved:!1};throw new a(`${e} 尚未添加站点`,`先运行 ppcli ${e} add-site --name <名称> --url <网址>,或在本次命令传 --${t} <网址>。`)}export function connectionCommandDefinitions(e,{originOnly:n=!1}={}){return[{site:e,name:"sites",access:"read",description:`列出已保存的 ${e} 自部署站点连接。`,strategy:y.LOCAL,browser:!1,args:[],columns:["name","url","default"],func:async()=>listSiteConnectionRows(e)},{site:e,name:"add-site",access:"write",description:`校验并保存一个命名的 ${e} 自部署站点连接(只保存名称和网址)。`,strategy:y.LOCAL,browser:!1,args:[{name:"name",required:!0,help:"连接名称,例如“公司博客”"},{name:"url",required:!0,help:"站点根网址,例如 https://blog.example.com"}],columns:["status","name","url","default"],func:async(t)=>{await verifySiteKind(e,t.url,{originOnly:n});return[addSiteConnection(e,t.name,t.url,{originOnly:n})]}},{site:e,name:"remove-site",access:"write",description:`删除一个 ${e} 站点连接;不会清除浏览器里的站点 cookie。`,strategy:y.LOCAL,browser:!1,args:[{name:"site",required:!0,help:"要删除的连接名称"}],columns:["status","name","url"],func:async(t)=>[removeSiteConnection(e,t.site)]},{site:e,name:"use-site",access:"write",description:`设置 ${e} 默认站点;之后省略 --site 时使用它。`,strategy:y.LOCAL,browser:!1,args:[{name:"site",required:!0,help:"要设为默认的连接名称"}],columns:["status","name","url","default"],func:async(t)=>[useSiteConnection(e,t.site)]}]}
@@ -1 +1 @@
1
- import{AuthRequiredError as q,TimeoutError as z,getErrorMessage as N}from"@jackwener/opencli/errors";import{cli as $,Strategy as Q}from"@jackwener/opencli/registry";const V=300,b=2000;function D(v,x){const G=x&&typeof x==="object"&&!Array.isArray(x)?x:{};return{logged_in:!0,site:v,...G}}function j(v){return v instanceof q}async function Y(v,x,G){const K=G==="poll"&&v.poll?v.poll:v.verify;return D(v.site,await K(x,{phase:G}))}async function J(v){if(typeof v?.closeWindow==="function")try{await v.closeWindow()}catch{}}function F(v){return`Run \`ppcli ${v.site} login\` to open the login page, then retry.`}function H(v){return["logged_in","site",...v.columns??["id","username","name"]]}function I(v){if(typeof v==="boolean")return{logged_in:v};if(v&&typeof v==="object"&&!Array.isArray(v))return{logged_in:!!v.logged_in,...v};return{logged_in:!1}}function P(v){if(v&&typeof v==="object"&&!Array.isArray(v))return v;return{touched:!0}}export function registerSiteAuthCommands(v){if(!v?.site||!v?.domain||!v?.loginUrl||typeof v.verify!=="function")throw Error("registerSiteAuthCommands requires site, domain, loginUrl, and verify(page)");$({site:v.site,name:"whoami",access:"read",description:v.whoamiDescription??`Show the current logged-in ${v.site} account`,domain:v.domain,strategy:Q.COOKIE,browser:!0,navigateBefore:!1,siteSession:"persistent",args:[],columns:H(v),authStatus:{...typeof v.quickCheck==="function"?{quickCheck:async(x)=>I(await v.quickCheck(x))}:{},...v.quickCheckRequiresNavigation===!0?{quickCheckRequiresNavigation:!0}:{},...typeof v.refresh==="function"?{refresh:async(x,G)=>P(await v.refresh(x,G))}:{}},func:async(x)=>Y(v,x,"identity")});$({site:v.site,name:"login",access:"write",description:v.loginDescription??`Open ${v.site} login and wait until the browser session is authenticated`,domain:v.domain,strategy:Q.COOKIE,browser:!0,navigateBefore:!1,defaultWindowMode:"foreground",siteSession:"persistent",args:[{name:"timeout",type:"int",default:V,help:"Maximum seconds to wait for the user to finish login"}],columns:["status",...H(v)],func:async(x,G)=>{try{const B={status:"already_logged_in",...await Y(v,x,"identity")};await J(x);return B}catch(B){if(!j(B)){await J(x);throw B}}await x.goto(v.loginUrl);const K=Number(G.timeout??V),Z=Date.now()+K*1000;let X="";while(Date.now()<Z){await x.wait(Math.min(b/1000,Math.max(0.2,(Z-Date.now())/1000)));try{const B=await Y(v,x,"poll");await J(x);return{status:"login_complete",...B}}catch(B){if(!j(B)){await J(x);throw B}X=N(B)}}await J(x);throw new z(`${v.site} login`,K,X?`${F(v)} Last auth check: ${X}`:F(v))}})}
1
+ import{AuthRequiredError as y,TimeoutError as f,getErrorMessage as p}from"@jackwener/opencli/errors";import{cli as c,Strategy as l}from"@jackwener/opencli/registry";const d=300,C=2000;function k(t,e){const i=e&&typeof e==="object"&&!Array.isArray(e)?e:{};return{logged_in:!0,site:t,...i}}function m(t){return t instanceof y}async function s(t,e,i){const o=i==="poll"&&t.poll?t.poll:t.verify;return k(t.site,await o(e,{phase:i}))}async function n(t){if(typeof t?.closeWindow==="function")try{await t.closeWindow()}catch{}}function h(t){return`Run \`ppcli ${t.site} login\` to open the login page, then retry.`}function w(t){return["logged_in","site",...t.columns??["id","username","name"]]}function A(t){if(typeof t==="boolean")return{logged_in:t};if(t&&typeof t==="object"&&!Array.isArray(t))return{logged_in:!!t.logged_in,...t};return{logged_in:!1}}function _(t){if(t&&typeof t==="object"&&!Array.isArray(t))return t;return{touched:!0}}export function registerSiteAuthCommands(t){if(!t?.site||!t?.domain||!t?.loginUrl||typeof t.verify!=="function")throw Error("registerSiteAuthCommands requires site, domain, loginUrl, and verify(page)");c({site:t.site,name:"whoami",access:"read",description:t.whoamiDescription??`Show the current logged-in ${t.site} account`,domain:t.domain,strategy:l.COOKIE,browser:!0,navigateBefore:!1,siteSession:"persistent",args:[],columns:w(t),authStatus:{...typeof t.quickCheck==="function"?{quickCheck:async(e)=>A(await t.quickCheck(e))}:{},...t.quickCheckRequiresNavigation===!0?{quickCheckRequiresNavigation:!0}:{},...typeof t.refresh==="function"?{refresh:async(e,i)=>_(await t.refresh(e,i))}:{}},func:async(e)=>s(t,e,"identity")});c({site:t.site,name:"login",access:"write",description:t.loginDescription??`Open ${t.site} login and wait until the browser session is authenticated`,domain:t.domain,strategy:l.COOKIE,browser:!0,navigateBefore:!1,defaultWindowMode:"foreground",siteSession:"persistent",args:[{name:"timeout",type:"int",default:d,help:"Maximum seconds to wait for the user to finish login"}],columns:["status",...w(t)],func:async(e,i)=>{try{const r={status:"already_logged_in",...await s(t,e,"identity")};await n(e);return r}catch(r){if(!m(r)){await n(e);throw r}}await e.goto(t.loginUrl);const o=Number(i.timeout??d),u=Date.now()+o*1000;let a="";while(Date.now()<u){await e.wait(Math.min(C/1000,Math.max(0.2,(u-Date.now())/1000)));try{const r=await s(t,e,"poll");await n(e);return{status:"login_complete",...r}}catch(r){if(!m(r)){await n(e);throw r}a=p(r)}}await n(e);throw new f(`${t.site} login`,o,a?`${h(t)} Last auth check: ${a}`:h(t))}})}
@@ -1,2 +1,2 @@
1
- import*as D from"node:fs";import*as O from"node:os";import*as K from"node:path";import{cli as L,Strategy as M}from"@jackwener/opencli/registry";import{ArgumentError as Q,AuthRequiredError as T}from"@jackwener/opencli/errors";function I(b){return K.join(O.homedir(),".ppcli","sites",b,"credentials.json")}export function loadCredentials(b){try{return JSON.parse(D.readFileSync(I(b),"utf8"))}catch(x){if(x&&x.code==="ENOENT")return null;throw x}}export function saveCredentials(b,x){const z=I(b);D.mkdirSync(K.dirname(z),{recursive:!0});D.writeFileSync(z,`${JSON.stringify(x,null,2)}
2
- `,{mode:384});try{D.chmodSync(z,384)}catch{}}export function clearCredentials(b){try{D.rmSync(I(b));return!0}catch(x){if(x&&x.code==="ENOENT")return!1;throw x}}export function requireCredentials(b){const x=loadCredentials(b);if(!x)throw new T(b,`${b} not configured. Run \`ppcli ${b} login\` with your credentials first.`);return x}export function registerTokenAuth(b){if(!b?.site||!b?.domain||!Array.isArray(b.fields)||typeof b.validate!=="function")throw Error("registerTokenAuth requires site, domain, fields[], and validate(creds)");const x=b.identityColumns??["id","username","name"];L({site:b.site,name:"whoami",access:"read",description:b.whoamiDescription??`Show the configured ${b.site} account`,domain:b.domain,strategy:M.LOCAL,browser:!1,args:[],columns:["logged_in","site",...x],func:async()=>{const z=requireCredentials(b.site),F=await b.validate(z);return{logged_in:!0,site:b.site,...F}}});L({site:b.site,name:"login",access:"write",description:b.loginDescription??`Configure ${b.site} credentials (token/key based, no browser). Pass each field as a flag.`,domain:b.domain,strategy:M.LOCAL,browser:!1,args:b.fields.map((z)=>({name:z.name,type:"string",required:!1,help:z.help??`${b.site} ${z.name}`,...z.default!==void 0?{default:z.default}:{}})),columns:["status","logged_in","site",...x],func:async(z)=>{const F=loadCredentials(b.site)??{},G={};for(const B of b.fields){const H=z[B.name]??F[B.name]??B.default;if(H!==void 0&&H!==null&&H!=="")G[B.name]=H}const J=b.fields.filter((B)=>B.required!==!1&&G[B.name]===void 0);if(J.length)throw new Q(`Missing required credential(s): ${J.map((B)=>`--${B.name}`).join(", ")}`,`Run \`ppcli ${b.site} login -h\` to see all fields.`);const N=await b.validate(G);saveCredentials(b.site,G);return{status:Object.keys(F).length?"updated":"login_complete",logged_in:!0,site:b.site,...N}}})}
1
+ import*as i from"node:fs";import*as h from"node:os";import*as u from"node:path";import{cli as m,Strategy as c}from"@jackwener/opencli/registry";import{ArgumentError as y,AuthRequiredError as f}from"@jackwener/opencli/errors";function d(e){return u.join(h.homedir(),".ppcli","sites",e,"credentials.json")}export function loadCredentials(e){try{return JSON.parse(i.readFileSync(d(e),"utf8"))}catch(t){if(t&&t.code==="ENOENT")return null;throw t}}export function saveCredentials(e,t){const r=d(e);i.mkdirSync(u.dirname(r),{recursive:!0});i.writeFileSync(r,`${JSON.stringify(t,null,2)}
2
+ `,{mode:384});try{i.chmodSync(r,384)}catch{}}export function clearCredentials(e){try{i.rmSync(d(e));return!0}catch(t){if(t&&t.code==="ENOENT")return!1;throw t}}export function requireCredentials(e){const t=loadCredentials(e);if(!t)throw new f(e,`${e} not configured. Run \`ppcli ${e} login\` with your credentials first.`);return t}export function registerTokenAuth(e){if(!e?.site||!e?.domain||!Array.isArray(e.fields)||typeof e.validate!=="function")throw Error("registerTokenAuth requires site, domain, fields[], and validate(creds)");const t=e.identityColumns??["id","username","name"];m({site:e.site,name:"whoami",access:"read",description:e.whoamiDescription??`Show the configured ${e.site} account`,domain:e.domain,strategy:c.LOCAL,browser:!1,args:[],columns:["logged_in","site",...t],func:async()=>{const r=requireCredentials(e.site),n=await e.validate(r);return{logged_in:!0,site:e.site,...n}}});m({site:e.site,name:"login",access:"write",description:e.loginDescription??`Configure ${e.site} credentials (token/key based, no browser). Pass each field as a flag.`,domain:e.domain,strategy:c.LOCAL,browser:!1,args:e.fields.map((r)=>({name:r.name,type:"string",required:!1,help:r.help??`${e.site} ${r.name}`,...r.default!==void 0?{default:r.default}:{}})),columns:["status","logged_in","site",...t],func:async(r)=>{const n=loadCredentials(e.site)??{},a={};for(const s of e.fields){const o=r[s.name]??n[s.name]??s.default;if(o!==void 0&&o!==null&&o!=="")a[s.name]=o}const l=e.fields.filter((s)=>s.required!==!1&&a[s.name]===void 0);if(l.length)throw new y(`Missing required credential(s): ${l.map((s)=>`--${s.name}`).join(", ")}`,`Run \`ppcli ${e.site} login -h\` to see all fields.`);const p=await e.validate(a);saveCredentials(e.site,a);return{status:Object.keys(n).length?"updated":"login_complete",logged_in:!0,site:e.site,...p}}})}
@@ -1,4 +1,4 @@
1
- import*as Y from"node:fs";import*as L from"node:path";import{ArgumentError as H,CommandExecutionError as $}from"@jackwener/opencli/errors";import{isFileAccessDenied as k,setInputFiles as C}from"./file-inject.js";export const SUPPORTED_VIDEO_EXTENSIONS=new Set([".mp4",".mov",".avi",".mkv",".m4v",".webm",".flv",".wmv"]),SUPPORTED_IMAGE_EXTENSIONS=new Set([".jpg",".jpeg",".png",".webp",".bmp"]);export function parseScheduleDate(q){if(q==null||q==="")return null;const y=String(q),z=/^\d+$/.test(y)?new Date(Number(y)<1000000000000?Number(y)*1000:Number(y)):new Date(y);if(Number.isNaN(z.getTime()))throw new H(`无法解析定时时间: ${q}`);if(z.getTime()<=Date.now())throw new H("定时发布时间必须晚于当前时间");return z}export function unwrap(q){if(q&&typeof q==="object"&&"data"in q&&"session"in q)return q.data;return q}export async function evalPage(q,y){return unwrap(await q.evaluate(y))}export function sleep(q){return new Promise((y)=>setTimeout(y,q*1000))}export const videoTimeoutArg=(q=540)=>({name:"timeout",type:"int",default:q,help:`命令超时(秒,默认 ${q})。大视频/上行慢时调大;`+"超时不代表没发成功,先用回查命令确认再决定要不要重发。"});export function resolveVideoFile(q){const y=L.resolve(String(q));let z;try{z=Y.statSync(y)}catch{throw new H(`视频文件不存在: ${y}`)}if(!z.isFile())throw new H(`视频路径不是文件: ${y}`);const B=L.extname(y).toLowerCase();if(!SUPPORTED_VIDEO_EXTENSIONS.has(B))throw new H(`不支持的视频格式: ${B}(支持 ${[...SUPPORTED_VIDEO_EXTENSIONS].join("/")})`);return y}export function resolveImageFile(q){const y=L.resolve(String(q));let z;try{z=Y.statSync(y)}catch{throw new H(`图片文件不存在: ${y}`)}if(!z.isFile())throw new H(`图片路径不是文件: ${y}`);const B=L.extname(y).toLowerCase();if(!SUPPORTED_IMAGE_EXTENSIONS.has(B))throw new H(`不支持的图片格式: ${B}(支持 ${[...SUPPORTED_IMAGE_EXTENSIONS].join("/")})`);return y}export function throwIfFileAccessDenied(q){if(!k(q))return;throw new $("Chrome 拦截了扩展读取本地文件(DOM.setFileInputFiles → File access not granted):商店安装的扩展默认没有本地文件访问权限","请让用户在 Chrome 打开 chrome://extensions,找到「ppcli Browser Bridge」→「详情」→ 打开「允许访问文件网址」(Allow access to file URLs),开关即时生效,然后重试本命令")}export async function setVideoInput(q,y,z){return(await C(q,y,z)).selector}export async function currentUrl(q){try{if(q.getCurrentUrl){const y=await q.getCurrentUrl();if(y)return String(y)}}catch{}try{return String(await evalPage(q,"() => location.href")||"")}catch{return""}}export async function waitForUrlIncludes(q,y,z=60000,B=500){const J=Array.isArray(y)?y:[y],K=Date.now()+z;while(Date.now()<K){const Q=await currentUrl(q);if(Q&&J.some((Z)=>Q.includes(Z)))return!0;await q.wait({time:B/1000})}return!1}export async function countByText(q,y){return Number(unwrap(await q.evaluate(`
1
+ import*as l from"node:fs";import*as a from"node:path";import{ArgumentError as o,CommandExecutionError as f}from"@jackwener/opencli/errors";import{isFileAccessDenied as w,setInputFiles as m}from"./file-inject.js";export const SUPPORTED_VIDEO_EXTENSIONS=new Set([".mp4",".mov",".avi",".mkv",".m4v",".webm",".flv",".wmv"]),SUPPORTED_IMAGE_EXTENSIONS=new Set([".jpg",".jpeg",".png",".webp",".bmp"]);export function parseScheduleDate(e){if(e==null||e==="")return null;const t=String(e),n=/^\d+$/.test(t)?new Date(Number(t)<1000000000000?Number(t)*1000:Number(t)):new Date(t);if(Number.isNaN(n.getTime()))throw new o(`无法解析定时时间: ${e}`);if(n.getTime()<=Date.now())throw new o("定时发布时间必须晚于当前时间");return n}export function unwrap(e){if(e&&typeof e==="object"&&"data"in e&&"session"in e)return e.data;return e}export async function evalPage(e,t){return unwrap(await e.evaluate(t))}export function sleep(e){return new Promise((t)=>setTimeout(t,e*1000))}export const videoTimeoutArg=(e=540)=>({name:"timeout",type:"int",default:e,help:`命令超时(秒,默认 ${e})。大视频/上行慢时调大;`+"超时不代表没发成功,先用回查命令确认再决定要不要重发。"});export function resolveVideoFile(e){const t=a.resolve(String(e));let n;try{n=l.statSync(t)}catch{throw new o(`视频文件不存在: ${t}`)}if(!n.isFile())throw new o(`视频路径不是文件: ${t}`);const r=a.extname(t).toLowerCase();if(!SUPPORTED_VIDEO_EXTENSIONS.has(r))throw new o(`不支持的视频格式: ${r}(支持 ${[...SUPPORTED_VIDEO_EXTENSIONS].join("/")})`);return t}export function resolveImageFile(e){const t=a.resolve(String(e));let n;try{n=l.statSync(t)}catch{throw new o(`图片文件不存在: ${t}`)}if(!n.isFile())throw new o(`图片路径不是文件: ${t}`);const r=a.extname(t).toLowerCase();if(!SUPPORTED_IMAGE_EXTENSIONS.has(r))throw new o(`不支持的图片格式: ${r}(支持 ${[...SUPPORTED_IMAGE_EXTENSIONS].join("/")})`);return t}export function throwIfFileAccessDenied(e){if(!w(e))return;throw new f("Chrome 拦截了扩展读取本地文件(DOM.setFileInputFiles → File access not granted):商店安装的扩展默认没有本地文件访问权限","请让用户在 Chrome 打开 chrome://extensions,找到「ppcli Browser Bridge」→「详情」→ 打开「允许访问文件网址」(Allow access to file URLs),开关即时生效,然后重试本命令")}export async function setVideoInput(e,t,n){return(await m(e,t,n)).selector}export async function currentUrl(e){try{if(e.getCurrentUrl){const t=await e.getCurrentUrl();if(t)return String(t)}}catch{}try{return String(await evalPage(e,"() => location.href")||"")}catch{return""}}export async function waitForUrlIncludes(e,t,n=60000,r=500){const i=Array.isArray(t)?t:[t],s=Date.now()+n;while(Date.now()<s){const c=await currentUrl(e);if(c&&i.some((u)=>c.includes(u)))return!0;await e.wait({time:r/1000})}return!1}export async function countByText(e,t){return Number(unwrap(await e.evaluate(`
2
2
  ((want) => {
3
3
  let n = 0;
4
4
  const nodes = document.querySelectorAll('body *');
@@ -11,8 +11,8 @@ import*as Y from"node:fs";import*as L from"node:path";import{ArgumentError as H,
11
11
  }
12
12
  }
13
13
  return n;
14
- })(${JSON.stringify(y)})
15
- `)))||0}export async function clickByText(q,y,{exact:z=!1,timeoutMs:B=0,pollMs:J=500}={}){const K=Date.now()+Math.max(0,B);for(;;){if(Boolean(unwrap(await q.evaluate(`
14
+ })(${JSON.stringify(t)})
15
+ `)))||0}export async function clickByText(e,t,{exact:n=!1,timeoutMs:r=0,pollMs:i=500}={}){const s=Date.now()+Math.max(0,r);for(;;){if(Boolean(unwrap(await e.evaluate(`
16
16
  ((want, exact) => {
17
17
  const norm = (v) => (v || '').replace(/\\s+/g, ' ').trim();
18
18
  const isVisible = (el) => {
@@ -36,8 +36,8 @@ import*as Y from"node:fs";import*as L from"node:path";import{ArgumentError as H,
36
36
  return true;
37
37
  }
38
38
  return false;
39
- })(${JSON.stringify(y)}, ${z?"true":"false"})
40
- `))))return!0;if(Date.now()>=K)return!1;await q.wait({time:J/1000})}}export async function existsByText(q,y,{exact:z=!1}={}){return Boolean(unwrap(await q.evaluate(`
39
+ })(${JSON.stringify(t)}, ${n?"true":"false"})
40
+ `))))return!0;if(Date.now()>=s)return!1;await e.wait({time:i/1000})}}export async function existsByText(e,t,{exact:n=!1}={}){return Boolean(unwrap(await e.evaluate(`
41
41
  ((want, exact) => {
42
42
  const norm = (v) => (v || '').replace(/\\s+/g, ' ').trim();
43
43
  const isVisible = (el) => {
@@ -51,12 +51,12 @@ import*as Y from"node:fs";import*as L from"node:path";import{ArgumentError as H,
51
51
  if (exact ? t === want : (t && t.includes(want))) return true;
52
52
  }
53
53
  return false;
54
- })(${JSON.stringify(y)}, ${z?"true":"false"})
55
- `)))}export async function waitForSelector(q,y,z=30000,B=500){const J=Date.now()+z;while(Date.now()<J){if(Boolean(unwrap(await q.evaluate(`
54
+ })(${JSON.stringify(t)}, ${n?"true":"false"})
55
+ `)))}export async function waitForSelector(e,t,n=30000,r=500){const i=Date.now()+n;while(Date.now()<i){if(Boolean(unwrap(await e.evaluate(`
56
56
  ((sel) => {
57
57
  const el = document.querySelector(sel);
58
58
  if (!el) return false;
59
59
  const r = el.getBoundingClientRect();
60
60
  return r.width > 0 && r.height > 0;
61
- })(${JSON.stringify(y)})
62
- `))))return!0;await q.wait({time:B/1000})}return!1}
61
+ })(${JSON.stringify(t)})
62
+ `))))return!0;await e.wait({time:r/1000})}return!1}
@@ -1,9 +1,9 @@
1
- import{CommandExecutionError as $}from"@jackwener/opencli/errors";import{cli as L,Strategy as _}from"@jackwener/opencli/registry";import{buildProductUrl as Y,buildProvenance as j,cleanText as K,extractAsin as U,isAmazonEntity as M,normalizeProductUrl as C,PRIMARY_PRICE_SELECTORS as I,parsePriceText as k,assertUsableState as B,gotoAndReadState as O}from"./shared.js";const R=["#sellerProfileTriggerId","#shipsFromSoldByInsideBuyBox_feature_div","#fulfillerInfoFeature_feature_div","#merchantInfoFeature_feature_div","#tabular-buybox-container","#merchant-info"].join(", ");function W(q){const H=K(q).split(" ").filter(Boolean),G=[];for(const J of H){if(G[G.length-1]===J)continue;G.push(J)}return G.join(" ")}function Q(q){const G=K(q).match(/Ships from\s+(.+?)(?=Sold by|and Fulfilled by|$)/i);return G?W(G[1].replace(/Ships from/ig,"")):null}function V(q){const G=K(q).match(/Sold by\s+(.+?)(?=and Fulfilled by|Ships from|$)/i);return G?W(G[1]):null}function D(q){const H=K(q).toLowerCase();return H.includes("cannot be shipped to your selected delivery location")||H.includes("similar items shipping to")||H.includes("deliver to hong kong")}function v(q){const H=U(q.href??"")??null,G=K(q.href)||Y(q.href??""),J=k(q.price_text),N=K(q.merchant_info)||null,X=K(q.sold_by)||V(q.ships_from_text??"")||V(N??"")||null,Z=Q(q.ships_from_text??"")||Q(N??"")||K(q.ships_from_text)||null,w=j(G);return{asin:H,product_url:C(q.href),...w,price_text:J.price_text,price_value:J.price_value,currency:J.currency,merchant_info_text:N,sold_by:X,ships_from:Z,offer_listing_url:K(q.offer_link)||null,review_url:K(q.review_url)||null,qa_url:K(q.qa_url)||null,is_amazon_sold:M(X),is_amazon_fulfilled:M(Z)||/fulfilled by amazon/i.test(N??"")}}async function b(q,H){const G=Y(H),J=await O(q,G,2500,"offer");B(J,"offer");await q.wait({selector:R,timeout:6}).catch(()=>{});return await q.evaluate(`
1
+ import{CommandExecutionError as a}from"@jackwener/opencli/errors";import{cli as y,Strategy as x}from"@jackwener/opencli/registry";import{buildProductUrl as f,buildProvenance as b,cleanText as n,extractAsin as S,isAmazonEntity as d,normalizeProductUrl as v,PRIMARY_PRICE_SELECTORS as g,parsePriceText as z,assertUsableState as q,gotoAndReadState as C}from"./shared.js";const w=["#sellerProfileTriggerId","#shipsFromSoldByInsideBuyBox_feature_div","#fulfillerInfoFeature_feature_div","#merchantInfoFeature_feature_div","#tabular-buybox-container","#merchant-info"].join(", ");function m(e){const r=n(e).split(" ").filter(Boolean),t=[];for(const o of r){if(t[t.length-1]===o)continue;t.push(o)}return t.join(" ")}function l(e){const t=n(e).match(/Ships from\s+(.+?)(?=Sold by|and Fulfilled by|$)/i);return t?m(t[1].replace(/Ships from/ig,"")):null}function c(e){const t=n(e).match(/Sold by\s+(.+?)(?=and Fulfilled by|Ships from|$)/i);return t?m(t[1]):null}function _(e){const r=n(e).toLowerCase();return r.includes("cannot be shipped to your selected delivery location")||r.includes("similar items shipping to")||r.includes("deliver to hong kong")}function h(e){const r=S(e.href??"")??null,t=n(e.href)||f(e.href??""),o=z(e.price_text),i=n(e.merchant_info)||null,s=n(e.sold_by)||c(e.ships_from_text??"")||c(i??"")||null,u=l(e.ships_from_text??"")||l(i??"")||n(e.ships_from_text)||null,p=b(t);return{asin:r,product_url:v(e.href),...p,price_text:o.price_text,price_value:o.price_value,currency:o.currency,merchant_info_text:i,sold_by:s,ships_from:u,offer_listing_url:n(e.offer_link)||null,review_url:n(e.review_url)||null,qa_url:n(e.qa_url)||null,is_amazon_sold:d(s),is_amazon_fulfilled:d(u)||/fulfilled by amazon/i.test(i??"")}}async function B(e,r){const t=f(r),o=await C(e,t,2500,"offer");q(o,"offer");await e.wait({selector:w,timeout:6}).catch(()=>{});return await e.evaluate(`
2
2
  (() => ({
3
3
  href: window.location.href,
4
4
  title: document.title || '',
5
5
  price_text: (() => {
6
- const selectors = ${JSON.stringify(I)};
6
+ const selectors = ${JSON.stringify(g)};
7
7
  for (const selector of selectors) {
8
8
  const text = document.querySelector(selector)?.textContent || '';
9
9
  if (text.trim()) return text;
@@ -26,4 +26,4 @@ import{CommandExecutionError as $}from"@jackwener/opencli/errors";import{cli as
26
26
  || document.querySelector('#buybox')?.textContent
27
27
  || '',
28
28
  }))()
29
- `)}L({site:"amazon",name:"offer",access:"read",description:"Amazon seller, buy box, and fulfillment facts from the product page",domain:"amazon.com",strategy:_.COOKIE,navigateBefore:!1,args:[{name:"input",required:!0,positional:!0,help:"ASIN or product URL, for example B0FJS72893"}],columns:["asin","price_text","sold_by","ships_from","is_amazon_sold","is_amazon_fulfilled"],func:async(q,H)=>{const G=String(H.input??""),J=await b(q,G),N=v(J);if(!N.sold_by&&!N.ships_from&&!N.merchant_info_text){if(D(J.buybox_text))throw new $("amazon offer buy box is blocked by the current delivery location","The shared Chrome profile is not set to the target US delivery address. Switch Amazon delivery location to the requested US destination, reopen the product page, and retry.");throw new $("amazon offer surface did not expose seller or fulfillment facts","The product page may have changed. Open the product page in Chrome, make sure the buy box is visible, and retry.")}return[N]}});export const __test__={extractShipsFrom:Q,extractSoldBy:V,isDeliveryLocationBlocked:D,normalizeOfferPayload:v};
29
+ `)}y({site:"amazon",name:"offer",access:"read",description:"Amazon seller, buy box, and fulfillment facts from the product page",domain:"amazon.com",strategy:x.COOKIE,navigateBefore:!1,args:[{name:"input",required:!0,positional:!0,help:"ASIN or product URL, for example B0FJS72893"}],columns:["asin","price_text","sold_by","ships_from","is_amazon_sold","is_amazon_fulfilled"],func:async(e,r)=>{const t=String(r.input??""),o=await B(e,t),i=h(o);if(!i.sold_by&&!i.ships_from&&!i.merchant_info_text){if(_(o.buybox_text))throw new a("amazon offer buy box is blocked by the current delivery location","The shared Chrome profile is not set to the target US delivery address. Switch Amazon delivery location to the requested US destination, reopen the product page, and retry.");throw new a("amazon offer surface did not expose seller or fulfillment facts","The product page may have changed. Open the product page in Chrome, make sure the buy box is visible, and retry.")}return[i]}});export const __test__={extractShipsFrom:l,extractSoldBy:c,isDeliveryLocationBlocked:_,normalizeOfferPayload:h};
@@ -1,11 +1,11 @@
1
- import{CommandExecutionError as M}from"@jackwener/opencli/errors";import{cli as V,Strategy as q}from"@jackwener/opencli/registry";import{buildProductUrl as W,buildProvenance as Y,cleanText as F,extractAsin as h,PRIMARY_PRICE_SELECTORS as j,parsePriceText as z,parseRatingValue as A,parseReviewCount as D,normalizeProductUrl as N,uniqueNonEmpty as X,assertUsableState as b,gotoAndReadState as k}from"./shared.js";const Z='#productTitle, #title span, [data-feature-name="title"] h1 span',v='#bylineInfo, [data-feature-name="bylineInfo"] #bylineInfo';function $(f){const H=F(f.href)||W(F(f.product_title)||F(f.href)),J=h(f.href??"")??null,G=z(f.price_text),K=F(f.rating_text)||null,Q=F(f.review_count_text)||null,B=Y(H);return{asin:J,title:F(f.product_title)||F(f.title)||null,product_url:N(f.href),...B,brand_text:F(f.byline)||null,price_text:G.price_text,price_value:G.price_value,currency:G.currency,rating_text:K,rating_value:A(K),review_count_text:Q,review_count:D(Q),review_url:F(f.review_url)||null,qa_url:F(f.qa_url)||null,breadcrumbs:X(f.breadcrumbs??[]),bullet_points:X(f.bullets??[])}}async function I(f,H){const J=W(H),G=await k(f,J,2500,"product");b(G,"product");await f.wait({selector:Z,timeout:6}).catch(()=>{});return await f.evaluate(`
1
+ import{CommandExecutionError as m}from"@jackwener/opencli/errors";import{cli as _,Strategy as f}from"@jackwener/opencli/registry";import{buildProductUrl as i,buildProvenance as p,cleanText as t,extractAsin as x,PRIMARY_PRICE_SELECTORS as v,parsePriceText as b,parseRatingValue as S,parseReviewCount as w,normalizeProductUrl as g,uniqueNonEmpty as a,assertUsableState as h,gotoAndReadState as y}from"./shared.js";const l='#productTitle, #title span, [data-feature-name="title"] h1 span',C='#bylineInfo, [data-feature-name="bylineInfo"] #bylineInfo';function s(e){const n=t(e.href)||i(t(e.product_title)||t(e.href)),o=x(e.href??"")??null,r=b(e.price_text),c=t(e.rating_text)||null,u=t(e.review_count_text)||null,d=p(n);return{asin:o,title:t(e.product_title)||t(e.title)||null,product_url:g(e.href),...d,brand_text:t(e.byline)||null,price_text:r.price_text,price_value:r.price_value,currency:r.currency,rating_text:c,rating_value:S(c),review_count_text:u,review_count:w(u),review_url:t(e.review_url)||null,qa_url:t(e.qa_url)||null,breadcrumbs:a(e.breadcrumbs??[]),bullet_points:a(e.bullets??[])}}async function q(e,n){const o=i(n),r=await y(e,o,2500,"product");h(r,"product");await e.wait({selector:l,timeout:6}).catch(()=>{});return await e.evaluate(`
2
2
  (() => ({
3
3
  href: window.location.href,
4
4
  title: document.title || '',
5
- product_title: document.querySelector(${JSON.stringify(Z)})?.textContent || '',
6
- byline: document.querySelector(${JSON.stringify(v)})?.textContent || '',
5
+ product_title: document.querySelector(${JSON.stringify(l)})?.textContent || '',
6
+ byline: document.querySelector(${JSON.stringify(C)})?.textContent || '',
7
7
  price_text: (() => {
8
- const selectors = ${JSON.stringify(j)};
8
+ const selectors = ${JSON.stringify(v)};
9
9
  for (const selector of selectors) {
10
10
  const text = document.querySelector(selector)?.textContent || '';
11
11
  if (text.trim()) return text;
@@ -22,4 +22,4 @@ import{CommandExecutionError as M}from"@jackwener/opencli/errors";import{cli as
22
22
  bullets: Array.from(document.querySelectorAll('#feature-bullets li .a-list-item')).map((node) => node.textContent || ''),
23
23
  breadcrumbs: Array.from(document.querySelectorAll('#wayfinding-breadcrumbs_feature_div a')).map((node) => node.textContent || ''),
24
24
  }))()
25
- `)}V({site:"amazon",name:"product",access:"read",description:"Amazon product page facts for candidate validation",domain:"amazon.com",strategy:q.COOKIE,navigateBefore:!1,args:[{name:"input",required:!0,positional:!0,help:"ASIN or product URL, for example B0FJS72893"}],columns:["asin","title","price_text","rating_value","review_count"],func:async(f,H)=>{const J=String(H.input??""),G=await I(f,J);if(!F(G.product_title))throw new M("amazon product page did not expose product content","The product page may have changed or hit a robot check. Open the product page in Chrome and retry.");return[$(G)]}});export const __test__={normalizeProductPayload:$};
25
+ `)}_({site:"amazon",name:"product",access:"read",description:"Amazon product page facts for candidate validation",domain:"amazon.com",strategy:f.COOKIE,navigateBefore:!1,args:[{name:"input",required:!0,positional:!0,help:"ASIN or product URL, for example B0FJS72893"}],columns:["asin","title","price_text","rating_value","review_count"],func:async(e,n)=>{const o=String(n.input??""),r=await q(e,o);if(!t(r.product_title))throw new m("amazon product page did not expose product content","The product page may have changed or hit a robot check. Open the product page in Chrome and retry.");return[s(r)]}});export const __test__={normalizeProductPayload:s};
@@ -1,11 +1,11 @@
1
- import{ArgumentError as q,CommandExecutionError as K}from"@jackwener/opencli/errors";export const SITE="amazon",DOMAIN="amazon.com",HOME_URL="https://www.amazon.com/",BESTSELLERS_URL="https://www.amazon.com/Best-Sellers/zgbs",NEW_RELEASES_URL="https://www.amazon.com/gp/new-releases",MOVERS_SHAKERS_URL="https://www.amazon.com/gp/movers-and-shakers",SEARCH_URL_PREFIX="https://www.amazon.com/s?k=",PRODUCT_URL_PREFIX="https://www.amazon.com/dp/",DISCUSSION_URL_PREFIX="https://www.amazon.com/product-reviews/",STRATEGY="cookie",PRIMARY_PRICE_SELECTORS=["#corePrice_feature_div .a-offscreen","#corePriceDisplay_desktop_feature_div .a-offscreen","#corePrice_desktop .a-offscreen","#apex_desktop .a-offscreen","#newAccordionRow_0 .a-offscreen","#price_inside_buybox","#priceblock_ourprice","#priceblock_dealprice","#tp_price_block_total_price_ww"];const V=["Sorry, we just need to make sure you're not a robot","Enter the characters you see below","Type the characters you see in this image","To discuss automated access to Amazon data please contact"],f={bestsellers:{commandName:"bestsellers",rootUrl:BESTSELLERS_URL,pathPattern:/(?:^|\/)zgbs(?:\/|$)/i,invalidInputMessage:"amazon bestsellers expects a best sellers URL or /zgbs path",invalidInputHint:"Example: ppcli amazon bestsellers https://www.amazon.com/Best-Sellers/zgbs"},new_releases:{commandName:"new-releases",rootUrl:NEW_RELEASES_URL,pathPattern:/\/gp\/new-releases(?:\/|$)/i,invalidInputMessage:"amazon new-releases expects a new releases URL or /gp/new-releases path",invalidInputHint:"Example: ppcli amazon new-releases https://www.amazon.com/gp/new-releases"},movers_shakers:{commandName:"movers-shakers",rootUrl:MOVERS_SHAKERS_URL,pathPattern:/\/gp\/movers-and-shakers(?:\/|$)/i,invalidInputMessage:"amazon movers-shakers expects a movers-and-shakers URL or /gp/movers-and-shakers path",invalidInputHint:"Example: ppcli amazon movers-shakers https://www.amazon.com/gp/movers-and-shakers"}};export function cleanText(Q){return typeof Q==="string"?Q.replace(/\u00a0/g," ").replace(/\s+/g," ").trim():""}export function cleanMultilineText(Q){return typeof Q==="string"?Q.replace(/\u00a0/g," ").split(`
2
- `).map((J)=>J.replace(/\s+/g," ").trim()).filter(Boolean).join(`
3
- `):""}export function uniqueNonEmpty(Q){return[...new Set(Q.map((J)=>cleanText(J)).filter(Boolean))]}export function buildProvenance(Q){return{source_url:Q,fetched_at:new Date().toISOString(),strategy:STRATEGY}}export function buildSearchUrl(Q){const J=cleanText(Q);if(!J)throw new q("amazon search query cannot be empty");return`${SEARCH_URL_PREFIX}${encodeURIComponent(J)}`}export function extractAsin(Q){const J=cleanText(Q);if(!J)return null;if(/^[A-Z0-9]{10}$/i.test(J))return J.toUpperCase();const $=J.match(/\/(?:dp|gp\/product|product-reviews)\/([A-Z0-9]{10})/i);return $?$[1].toUpperCase():null}export function buildProductUrl(Q){const J=extractAsin(Q);if(!J)throw new q("amazon product expects an ASIN or product URL","Example: ppcli amazon product B0FJS72893");return`${PRODUCT_URL_PREFIX}${J}`}export function buildDiscussionUrl(Q){const J=extractAsin(Q);if(!J)throw new q("amazon discussion expects an ASIN or product URL","Example: ppcli amazon discussion B0FJS72893");return`${DISCUSSION_URL_PREFIX}${J}`}function Y(Q){return f[Q]}export function isSupportedRankingPath(Q,J){try{const $=new URL(J);return Y(Q).pathPattern.test($.pathname)}catch{return!1}}export function resolveRankingUrl(Q,J){const $=Y(Q),j=cleanText(J);if(!j||j==="root")return $.rootUrl;let W;if(j.startsWith("/"))W=new URL(j,HOME_URL).toString();else if(/^https?:\/\//i.test(j))W=canonicalizeAmazonUrl(j);else if(j.includes("amazon.")&&j.includes("/"))W=canonicalizeAmazonUrl(`https://${j.replace(/^\/+/,"")}`);else throw new q($.invalidInputMessage,$.invalidInputHint);if(!isSupportedRankingPath(Q,W))throw new q($.invalidInputMessage,$.invalidInputHint);return w(W)}function w(Q){try{const J=new URL(Q),$=J.pathname.split("/").filter(Boolean).filter((j)=>!/^ref=/i.test(j));J.pathname=`/${$.join("/")}`;J.hash="";J.searchParams.delete("ref");return J.toString()}catch{return Q}}export function isRankingPaginationUrl(Q,J){const $=toAbsoluteAmazonUrl(J);if(!$||!isSupportedRankingPath(Q,$))return!1;try{const j=new URL($),W=cleanText(j.searchParams.get("ref")).toLowerCase();return j.searchParams.has("pg")||/(?:^|_)pg(?:_|$)/.test(W)||/zg_bs(?:nr|ms)?_pg_/.test(W)}catch{return!1}}export function extractCategoryNodeId(Q){const J=toAbsoluteAmazonUrl(Q);if(!J)return null;try{const $=new URL(J);for(const B of["node","nodeid","nodeId","browseNode"]){const G=cleanText($.searchParams.get(B));if(/^\d{4,}$/.test(G))return G}const j=cleanText($.searchParams.get("rh")),W=decodeURIComponent(j).match(/(?:^|,)\s*n:(\d{4,})(?:,|$)/i);if(W)return W[1];const Z=[...$.pathname.matchAll(/\/(\d{4,})(?=\/|$)/g)];if(Z.length>0)return Z[Z.length-1][1]}catch{return null}return null}export function resolveBestsellersUrl(Q){return resolveRankingUrl("bestsellers",Q)}export function canonicalizeAmazonUrl(Q){try{const J=new URL(Q);if(!J.hostname.endsWith(DOMAIN))throw Error("not-amazon");return J.toString()}catch{throw new q("Invalid Amazon URL")}}export function toAbsoluteAmazonUrl(Q){const J=cleanText(Q);if(!J)return null;try{return new URL(J,HOME_URL).toString()}catch{return null}}export function normalizeProductUrl(Q){const J=cleanText(Q),$=extractAsin(J);if($)return buildProductUrl($);return toAbsoluteAmazonUrl(J)}export function parsePriceText(Q){const J=cleanText(Q),$=J.match(/([$€£])\s*(\d+(?:,\d{3})*(?:\.\d+)?)/);if(!$)return{price_text:J||null,price_value:null,currency:null};const j={$:"USD","€":"EUR","£":"GBP"};return{price_text:`${$[1]}${$[2]}`,price_value:Number.parseFloat($[2].replace(/,/g,"")),currency:j[$[1]]??null}}export function parseRatingValue(Q){const $=cleanText(Q).match(/(\d+(?:\.\d+)?)\s*out of 5/i);return $?Number.parseFloat($[1]):null}export function parseReviewCount(Q){const J=cleanText(Q),$=J.match(/(\d+(?:\.\d+)?)\s*([kKmM])/);if($){const W=Number.parseFloat($[1]),Z=/m/i.test($[2])?1e6:1000;return Number.isFinite(W)?Math.round(W*Z):null}const j=J.match(/([\d,]+)/);return j?Number.parseInt(j[1].replace(/,/g,""),10):null}export function extractReviewCountFromCardText(Q){const J=cleanMultilineText(Q),$=J.match(/out of 5 stars(?:, rating details)?\s*([\d,]+)/i);if($)return $[1];return J.split(`
4
- `).map((W)=>cleanText(W)).find((W)=>/^[\d,]+$/.test(W))??null}export function isAmazonEntity(Q){return cleanText(Q).toLowerCase().includes("amazon")}export function firstMeaningfulLine(Q){return cleanMultilineText(Q).split(`
5
- `).map((J)=>cleanText(J)).find(Boolean)??""}export function trimRatingPrefix(Q){const J=cleanText(Q);if(!J)return null;return J.replace(/^\d+(?:\.\d+)?\s*out of 5 stars\s*/i,"").trim()||J}export function isRobotState(Q){const J=cleanText(Q.title),$=cleanMultilineText(Q.body_text);return V.some((j)=>J.includes(j)||$.includes(j))}export function buildChallengeHint(Q){return[`Open a clean Amazon ${Q} page in the shared Chrome profile and clear any robot check first.`,"If you are using CDP, set OPENCLI_CDP_TARGET=amazon.com and avoid parallel Amazon commands against the same browser target."].join(" ")}export async function readPageState(Q){const J=await Q.evaluate(`
1
+ import{ArgumentError as s,CommandExecutionError as i}from"@jackwener/opencli/errors";export const SITE="amazon",DOMAIN="amazon.com",HOME_URL="https://www.amazon.com/",BESTSELLERS_URL="https://www.amazon.com/Best-Sellers/zgbs",NEW_RELEASES_URL="https://www.amazon.com/gp/new-releases",MOVERS_SHAKERS_URL="https://www.amazon.com/gp/movers-and-shakers",SEARCH_URL_PREFIX="https://www.amazon.com/s?k=",PRODUCT_URL_PREFIX="https://www.amazon.com/dp/",DISCUSSION_URL_PREFIX="https://www.amazon.com/product-reviews/",STRATEGY="cookie",PRIMARY_PRICE_SELECTORS=["#corePrice_feature_div .a-offscreen","#corePriceDisplay_desktop_feature_div .a-offscreen","#corePrice_desktop .a-offscreen","#apex_desktop .a-offscreen","#newAccordionRow_0 .a-offscreen","#price_inside_buybox","#priceblock_ourprice","#priceblock_dealprice","#tp_price_block_total_price_ww"];const p=["Sorry, we just need to make sure you're not a robot","Enter the characters you see below","Type the characters you see in this image","To discuss automated access to Amazon data please contact"],m={bestsellers:{commandName:"bestsellers",rootUrl:BESTSELLERS_URL,pathPattern:/(?:^|\/)zgbs(?:\/|$)/i,invalidInputMessage:"amazon bestsellers expects a best sellers URL or /zgbs path",invalidInputHint:"Example: ppcli amazon bestsellers https://www.amazon.com/Best-Sellers/zgbs"},new_releases:{commandName:"new-releases",rootUrl:NEW_RELEASES_URL,pathPattern:/\/gp\/new-releases(?:\/|$)/i,invalidInputMessage:"amazon new-releases expects a new releases URL or /gp/new-releases path",invalidInputHint:"Example: ppcli amazon new-releases https://www.amazon.com/gp/new-releases"},movers_shakers:{commandName:"movers-shakers",rootUrl:MOVERS_SHAKERS_URL,pathPattern:/\/gp\/movers-and-shakers(?:\/|$)/i,invalidInputMessage:"amazon movers-shakers expects a movers-and-shakers URL or /gp/movers-and-shakers path",invalidInputHint:"Example: ppcli amazon movers-shakers https://www.amazon.com/gp/movers-and-shakers"}};export function cleanText(t){return typeof t==="string"?t.replace(/\u00a0/g," ").replace(/\s+/g," ").trim():""}export function cleanMultilineText(t){return typeof t==="string"?t.replace(/\u00a0/g," ").split(`
2
+ `).map((e)=>e.replace(/\s+/g," ").trim()).filter(Boolean).join(`
3
+ `):""}export function uniqueNonEmpty(t){return[...new Set(t.map((e)=>cleanText(e)).filter(Boolean))]}export function buildProvenance(t){return{source_url:t,fetched_at:new Date().toISOString(),strategy:STRATEGY}}export function buildSearchUrl(t){const e=cleanText(t);if(!e)throw new s("amazon search query cannot be empty");return`${SEARCH_URL_PREFIX}${encodeURIComponent(e)}`}export function extractAsin(t){const e=cleanText(t);if(!e)return null;if(/^[A-Z0-9]{10}$/i.test(e))return e.toUpperCase();const n=e.match(/\/(?:dp|gp\/product|product-reviews)\/([A-Z0-9]{10})/i);return n?n[1].toUpperCase():null}export function buildProductUrl(t){const e=extractAsin(t);if(!e)throw new s("amazon product expects an ASIN or product URL","Example: ppcli amazon product B0FJS72893");return`${PRODUCT_URL_PREFIX}${e}`}export function buildDiscussionUrl(t){const e=extractAsin(t);if(!e)throw new s("amazon discussion expects an ASIN or product URL","Example: ppcli amazon discussion B0FJS72893");return`${DISCUSSION_URL_PREFIX}${e}`}function l(t){return m[t]}export function isSupportedRankingPath(t,e){try{const n=new URL(e);return l(t).pathPattern.test(n.pathname)}catch{return!1}}export function resolveRankingUrl(t,e){const n=l(t),r=cleanText(e);if(!r||r==="root")return n.rootUrl;let o;if(r.startsWith("/"))o=new URL(r,HOME_URL).toString();else if(/^https?:\/\//i.test(r))o=canonicalizeAmazonUrl(r);else if(r.includes("amazon.")&&r.includes("/"))o=canonicalizeAmazonUrl(`https://${r.replace(/^\/+/,"")}`);else throw new s(n.invalidInputMessage,n.invalidInputHint);if(!isSupportedRankingPath(t,o))throw new s(n.invalidInputMessage,n.invalidInputHint);return d(o)}function d(t){try{const e=new URL(t),n=e.pathname.split("/").filter(Boolean).filter((r)=>!/^ref=/i.test(r));e.pathname=`/${n.join("/")}`;e.hash="";e.searchParams.delete("ref");return e.toString()}catch{return t}}export function isRankingPaginationUrl(t,e){const n=toAbsoluteAmazonUrl(e);if(!n||!isSupportedRankingPath(t,n))return!1;try{const r=new URL(n),o=cleanText(r.searchParams.get("ref")).toLowerCase();return r.searchParams.has("pg")||/(?:^|_)pg(?:_|$)/.test(o)||/zg_bs(?:nr|ms)?_pg_/.test(o)}catch{return!1}}export function extractCategoryNodeId(t){const e=toAbsoluteAmazonUrl(t);if(!e)return null;try{const n=new URL(e);for(const u of["node","nodeid","nodeId","browseNode"]){const c=cleanText(n.searchParams.get(u));if(/^\d{4,}$/.test(c))return c}const r=cleanText(n.searchParams.get("rh")),o=decodeURIComponent(r).match(/(?:^|,)\s*n:(\d{4,})(?:,|$)/i);if(o)return o[1];const a=[...n.pathname.matchAll(/\/(\d{4,})(?=\/|$)/g)];if(a.length>0)return a[a.length-1][1]}catch{return null}return null}export function resolveBestsellersUrl(t){return resolveRankingUrl("bestsellers",t)}export function canonicalizeAmazonUrl(t){try{const e=new URL(t);if(!e.hostname.endsWith(DOMAIN))throw Error("not-amazon");return e.toString()}catch{throw new s("Invalid Amazon URL")}}export function toAbsoluteAmazonUrl(t){const e=cleanText(t);if(!e)return null;try{return new URL(e,HOME_URL).toString()}catch{return null}}export function normalizeProductUrl(t){const e=cleanText(t),n=extractAsin(e);if(n)return buildProductUrl(n);return toAbsoluteAmazonUrl(e)}export function parsePriceText(t){const e=cleanText(t),n=e.match(/([$€£])\s*(\d+(?:,\d{3})*(?:\.\d+)?)/);if(!n)return{price_text:e||null,price_value:null,currency:null};const r={$:"USD","€":"EUR","£":"GBP"};return{price_text:`${n[1]}${n[2]}`,price_value:Number.parseFloat(n[2].replace(/,/g,"")),currency:r[n[1]]??null}}export function parseRatingValue(t){const n=cleanText(t).match(/(\d+(?:\.\d+)?)\s*out of 5/i);return n?Number.parseFloat(n[1]):null}export function parseReviewCount(t){const e=cleanText(t),n=e.match(/(\d+(?:\.\d+)?)\s*([kKmM])/);if(n){const o=Number.parseFloat(n[1]),a=/m/i.test(n[2])?1e6:1000;return Number.isFinite(o)?Math.round(o*a):null}const r=e.match(/([\d,]+)/);return r?Number.parseInt(r[1].replace(/,/g,""),10):null}export function extractReviewCountFromCardText(t){const e=cleanMultilineText(t),n=e.match(/out of 5 stars(?:, rating details)?\s*([\d,]+)/i);if(n)return n[1];return e.split(`
4
+ `).map((o)=>cleanText(o)).find((o)=>/^[\d,]+$/.test(o))??null}export function isAmazonEntity(t){return cleanText(t).toLowerCase().includes("amazon")}export function firstMeaningfulLine(t){return cleanMultilineText(t).split(`
5
+ `).map((e)=>cleanText(e)).find(Boolean)??""}export function trimRatingPrefix(t){const e=cleanText(t);if(!e)return null;return e.replace(/^\d+(?:\.\d+)?\s*out of 5 stars\s*/i,"").trim()||e}export function isRobotState(t){const e=cleanText(t.title),n=cleanMultilineText(t.body_text);return p.some((r)=>e.includes(r)||n.includes(r))}export function buildChallengeHint(t){return[`Open a clean Amazon ${t} page in the shared Chrome profile and clear any robot check first.`,"If you are using CDP, set OPENCLI_CDP_TARGET=amazon.com and avoid parallel Amazon commands against the same browser target."].join(" ")}export async function readPageState(t){const e=await t.evaluate(`
6
6
  (() => ({
7
7
  href: window.location.href,
8
8
  title: document.title || '',
9
9
  body_text: document.body ? document.body.innerText || '' : '',
10
10
  }))()
11
- `);return{href:cleanText(J.href),title:cleanText(J.title),body_text:cleanMultilineText(J.body_text)}}export async function gotoAndReadState(Q,J,$=2500,j="page"){try{await Q.goto(J,{settleMs:$});await Q.wait(1.5);return await readPageState(Q)}catch(W){const Z=W instanceof Error?W.message:String(W);if(Z.includes("Inspected target navigated or closed")||Z.includes("Cannot find context with specified id")||Z.includes("Target closed"))throw new K(`amazon ${j} navigation lost the current browser target`,`${buildChallengeHint(j)} If CDP is attached to a stale tab, open a fresh Amazon tab and retry.`);throw W}}export function assertUsableState(Q,J){if(!isRobotState(Q))return;throw new K(`amazon ${J} hit a robot check`,buildChallengeHint(J))}export const __test__={buildSearchUrl,extractAsin,buildProductUrl,buildDiscussionUrl,resolveBestsellersUrl,resolveRankingUrl,isSupportedRankingPath,isRankingPaginationUrl,extractCategoryNodeId,parsePriceText,parseRatingValue,parseReviewCount,extractReviewCountFromCardText,isAmazonEntity,trimRatingPrefix,isRobotState,PRIMARY_PRICE_SELECTORS};
11
+ `);return{href:cleanText(e.href),title:cleanText(e.title),body_text:cleanMultilineText(e.body_text)}}export async function gotoAndReadState(t,e,n=2500,r="page"){try{await t.goto(e,{settleMs:n});await t.wait(1.5);return await readPageState(t)}catch(o){const a=o instanceof Error?o.message:String(o);if(a.includes("Inspected target navigated or closed")||a.includes("Cannot find context with specified id")||a.includes("Target closed"))throw new i(`amazon ${r} navigation lost the current browser target`,`${buildChallengeHint(r)} If CDP is attached to a stale tab, open a fresh Amazon tab and retry.`);throw o}}export function assertUsableState(t,e){if(!isRobotState(t))return;throw new i(`amazon ${e} hit a robot check`,buildChallengeHint(e))}export const __test__={buildSearchUrl,extractAsin,buildProductUrl,buildDiscussionUrl,resolveBestsellersUrl,resolveRankingUrl,isSupportedRankingPath,isRankingPaginationUrl,extractCategoryNodeId,parsePriceText,parseRatingValue,parseReviewCount,extractReviewCountFromCardText,isAmazonEntity,trimRatingPrefix,isRobotState,PRIMARY_PRICE_SELECTORS};
@@ -1,18 +1,18 @@
1
- import{CommandExecutionError as V,selectorError as W}from"@jackwener/opencli/errors";const H="convo-pill-";export function unwrapEvaluateResult(q){if(q&&typeof q==="object"&&Object.prototype.hasOwnProperty.call(q,"data")&&Object.prototype.hasOwnProperty.call(q,"session"))return q.data;return q}export function buildPillTestId(q){return`${H}${String(q).toLowerCase()}`}export async function listConversations(q){const x=unwrapEvaluateResult(await q.evaluate(`(function() {
2
- return Array.from(document.querySelectorAll('[data-testid^="${H}"]'))
1
+ import{CommandExecutionError as d,selectorError as m}from"@jackwener/opencli/errors";const i="convo-pill-";export function unwrapEvaluateResult(t){if(t&&typeof t==="object"&&Object.prototype.hasOwnProperty.call(t,"data")&&Object.prototype.hasOwnProperty.call(t,"session"))return t.data;return t}export function buildPillTestId(t){return`${i}${String(t).toLowerCase()}`}export async function listConversations(t){const e=unwrapEvaluateResult(await t.evaluate(`(function() {
2
+ return Array.from(document.querySelectorAll('[data-testid^="${i}"]'))
3
3
  .filter((el) => el.offsetParent)
4
4
  .map((el, idx) => ({
5
5
  index: idx + 1,
6
- id: el.getAttribute('data-testid').slice(${H.length}),
6
+ id: el.getAttribute('data-testid').slice(${i.length}),
7
7
  title: (el.textContent || '').trim().slice(0, 200),
8
8
  }));
9
- })()`));return Array.isArray(x)?x:[]}export async function conversationVisible(q,x){const z=buildPillTestId(x);return!!unwrapEvaluateResult(await q.evaluate(`(() => {
10
- const el = document.querySelector(${JSON.stringify(`[data-testid="${z}"]`)});
9
+ })()`));return Array.isArray(e)?e:[]}export async function conversationVisible(t,e){const n=buildPillTestId(e);return!!unwrapEvaluateResult(await t.evaluate(`(() => {
10
+ const el = document.querySelector(${JSON.stringify(`[data-testid="${n}"]`)});
11
11
  return !!(el && el.offsetParent);
12
- })()`))}export async function getConversationMenuLabels(q,x){const z=buildPillTestId(x);return unwrapEvaluateResult(await q.evaluate(`(async () => {
12
+ })()`))}export async function getConversationMenuLabels(t,e){const n=buildPillTestId(e);return unwrapEvaluateResult(await t.evaluate(`(async () => {
13
13
  const wait = (ms) => new Promise((r) => setTimeout(r, ms));
14
- const pill = document.querySelector(${JSON.stringify(`[data-testid="${z}"]`)});
15
- if (!pill) return { ok: false, reason: 'Conversation pill not found.', detail: 'testid=${z}' };
14
+ const pill = document.querySelector(${JSON.stringify(`[data-testid="${n}"]`)});
15
+ if (!pill) return { ok: false, reason: 'Conversation pill not found.', detail: 'testid=${n}' };
16
16
  let row = pill;
17
17
  for (let i = 0; i < 3; i++) row = row.parentElement || row;
18
18
  row.scrollIntoView({ block: 'center' });
@@ -50,10 +50,10 @@ import{CommandExecutionError as V,selectorError as W}from"@jackwener/opencli/err
50
50
  }).filter(Boolean);
51
51
  document.body.click();
52
52
  return { ok: true, labels };
53
- })()`))||{ok:!1,reason:"Empty result from page.evaluate."}}export async function clickConversationMenuItem(q,x,z){const B=buildPillTestId(x),M=JSON.stringify(B),N=JSON.stringify(z);let K;try{K=unwrapEvaluateResult(await q.evaluate(`(async () => {
53
+ })()`))||{ok:!1,reason:"Empty result from page.evaluate."}}export async function clickConversationMenuItem(t,e,n){const o=buildPillTestId(e),l=JSON.stringify(o),a=JSON.stringify(n);let s;try{s=unwrapEvaluateResult(await t.evaluate(`(async () => {
54
54
  const wait = (ms) => new Promise((r) => setTimeout(r, ms));
55
- const testId = ${M};
56
- const labels = ${N};
55
+ const testId = ${l};
56
+ const labels = ${a};
57
57
 
58
58
  const pill = document.querySelector(\`[data-testid="\${testId}"]\`);
59
59
  if (!pill) {
@@ -158,7 +158,7 @@ import{CommandExecutionError as V,selectorError as W}from"@jackwener/opencli/err
158
158
  } catch {}
159
159
  });
160
160
  return { ok: true, clicked: matchedLabel };
161
- })()`))}catch(G){const Q=String(G?.message||G);if(/Promise was collected|timed out after \d+s|Runtime\.evaluate/i.test(Q))return{ok:!0,clicked:z[0],note:"eval reply destroyed by post-click re-render; click likely fired"};throw G}return K||{ok:!1,reason:"Empty result from page.evaluate."}}export async function confirmDeleteDialog(q,x){const z=JSON.stringify(x);return unwrapEvaluateResult(await q.evaluate(`(async () => {
161
+ })()`))}catch(r){const u=String(r?.message||r);if(/Promise was collected|timed out after \d+s|Runtime\.evaluate/i.test(u))return{ok:!0,clicked:n[0],note:"eval reply destroyed by post-click re-render; click likely fired"};throw r}return s||{ok:!1,reason:"Empty result from page.evaluate."}}export async function confirmDeleteDialog(t,e){const n=JSON.stringify(e);return unwrapEvaluateResult(await t.evaluate(`(async () => {
162
162
  const wait = (ms) => new Promise((r) => setTimeout(r, ms));
163
163
  let dialog = null;
164
164
  for (let attempt = 0; attempt < 15; attempt += 1) {
@@ -170,7 +170,7 @@ import{CommandExecutionError as V,selectorError as W}from"@jackwener/opencli/err
170
170
  return { ok: false, reason: 'Delete confirm dialog did not appear.' };
171
171
  }
172
172
  const buttons = Array.from(dialog.querySelectorAll('button'));
173
- const labels = ${z};
173
+ const labels = ${n};
174
174
  const confirmBtn = buttons.find((b) => {
175
175
  const t = (b.textContent || '').trim();
176
176
  return labels.some((l) => t === l || t.toLowerCase() === l.toLowerCase());
@@ -1,6 +1,6 @@
1
- import{cli as D,Strategy as G}from"@jackwener/opencli/registry";import{ArgumentError as M,CommandExecutionError as H,EmptyResultError as K}from"@jackwener/opencli/errors";import{unwrapEvaluateResult as B}from"./_actions.js";function J(h){return`(() => {
1
+ import{cli as o,Strategy as a}from"@jackwener/opencli/registry";import{ArgumentError as d,CommandExecutionError as s,EmptyResultError as u}from"@jackwener/opencli/errors";import{unwrapEvaluateResult as i}from"./_actions.js";function c(t){return`(() => {
2
2
  const isVis = (el) => { const r = el.getBoundingClientRect(); return r.width > 1 && r.height > 1; };
3
- for (const sel of ${JSON.stringify(h)}) {
3
+ for (const sel of ${JSON.stringify(t)}) {
4
4
  const t = Array.from(document.querySelectorAll(sel)).filter(isVis)[0];
5
5
  if (t) {
6
6
  const r = t.getBoundingClientRect();
@@ -14,9 +14,9 @@ import{cli as D,Strategy as G}from"@jackwener/opencli/registry";import{ArgumentE
14
14
  }
15
15
  }
16
16
  return { ok: false, reason: 'No matching visible element.' };
17
- })()`}function N(h){return`(() => {
17
+ })()`}function p(t){return`(() => {
18
18
  const isVis = (el) => { const r = el.getBoundingClientRect(); return r.width > 1 && r.height > 1; };
19
- for (const sel of ${JSON.stringify(h)}) {
19
+ for (const sel of ${JSON.stringify(t)}) {
20
20
  const found = Array.from(document.querySelectorAll(sel)).filter(isVis);
21
21
  if (found.length) {
22
22
  const t = found[found.length - 1];
@@ -31,7 +31,7 @@ import{cli as D,Strategy as G}from"@jackwener/opencli/registry";import{ArgumentE
31
31
  }
32
32
  }
33
33
  return { ok: false, reason: 'No matching visible element.' };
34
- })()`}D({site:"antigravity",name:"react",access:"write",description:'Click "Good response" or "Bad response" on the LAST assistant message.',domain:"127.0.0.1",strategy:G.UI,browser:!0,args:[{name:"kind",positional:!0,required:!0,help:"good or bad"}],columns:["Status","Reaction"],func:async(h,f)=>{const q=String(f?.kind||"").trim().toLowerCase();if(q!=="good"&&q!=="bad")throw new M("kind",'must be "good" or "bad"');const z=q==="good"?"Good response":"Bad response",I=B(await h.evaluate(N([`button[aria-label="${z}"]`])));if(!I?.ok)throw new H(I?.reason||`${z} click failed`,"");return[{Status:"clicked",Reaction:q}]}});D({site:"antigravity",name:"copy-message",access:"write",description:"Return the text of the last assistant message (best-effort: walks up from the last visible Copy button).",domain:"127.0.0.1",strategy:G.UI,browser:!0,args:[{name:"click-button",type:"boolean",default:!1,help:"Also click the in-UI Copy button"}],columns:["Field","Value"],func:async(h,f)=>{const q=B(await h.evaluate(`(() => {
34
+ })()`}o({site:"antigravity",name:"react",access:"write",description:'Click "Good response" or "Bad response" on the LAST assistant message.',domain:"127.0.0.1",strategy:a.UI,browser:!0,args:[{name:"kind",positional:!0,required:!0,help:"good or bad"}],columns:["Status","Reaction"],func:async(t,e)=>{const n=String(e?.kind||"").trim().toLowerCase();if(n!=="good"&&n!=="bad")throw new d("kind",'must be "good" or "bad"');const r=n==="good"?"Good response":"Bad response",l=i(await t.evaluate(p([`button[aria-label="${r}"]`])));if(!l?.ok)throw new s(l?.reason||`${r} click failed`,"");return[{Status:"clicked",Reaction:n}]}});o({site:"antigravity",name:"copy-message",access:"write",description:"Return the text of the last assistant message (best-effort: walks up from the last visible Copy button).",domain:"127.0.0.1",strategy:a.UI,browser:!0,args:[{name:"click-button",type:"boolean",default:!1,help:"Also click the in-UI Copy button"}],columns:["Field","Value"],func:async(t,e)=>{const n=i(await t.evaluate(`(() => {
35
35
  const isVis = (el) => { const r = el.getBoundingClientRect(); return r.width > 1 && r.height > 1; };
36
36
  // Antigravity has both "Copy" (message) and "Copy code" (code block) buttons.
37
37
  // We want the bottom-of-message Copy, not the code-block Copy.
@@ -47,19 +47,19 @@ import{cli as D,Strategy as G}from"@jackwener/opencli/registry";import{ArgumentE
47
47
  if (best.length > 200) break;
48
48
  }
49
49
  return { text: best };
50
- })()`));if(!q)throw new K("antigravity copy-message","No Copy buttons visible — make sure an assistant reply is on screen.");if(f?.["click-button"]===!0||f?.["click-button"]==="true"){const z=B(await h.evaluate(N(['button[aria-label="Copy"]'])));if(!z?.ok)throw new H(z?.reason||"Copy button click failed","")}return[{Field:"Length",Value:String((q.text||"").length)+" chars"},{Field:"ClipboardClicked",Value:f?.["click-button"]===!0||f?.["click-button"]==="true"?"yes":"no"},{Field:"Text",Value:q.text||""}]}});D({site:"antigravity",name:"copy-code",access:"read",description:"Return the text of a code block in the current conversation. Default: last code block; pass --index N (1-based from top) to pick a specific one.",domain:"127.0.0.1",strategy:G.UI,browser:!0,args:[{name:"index",type:"int",required:!1,help:"1-based index of code block (default: last)"}],columns:["Field","Value"],func:async(h,f)=>{const q=Number.isInteger(f?.index)?f.index:null,z=B(await h.evaluate(`(() => {
50
+ })()`));if(!n)throw new u("antigravity copy-message","No Copy buttons visible — make sure an assistant reply is on screen.");if(e?.["click-button"]===!0||e?.["click-button"]==="true"){const r=i(await t.evaluate(p(['button[aria-label="Copy"]'])));if(!r?.ok)throw new s(r?.reason||"Copy button click failed","")}return[{Field:"Length",Value:String((n.text||"").length)+" chars"},{Field:"ClipboardClicked",Value:e?.["click-button"]===!0||e?.["click-button"]==="true"?"yes":"no"},{Field:"Text",Value:n.text||""}]}});o({site:"antigravity",name:"copy-code",access:"read",description:"Return the text of a code block in the current conversation. Default: last code block; pass --index N (1-based from top) to pick a specific one.",domain:"127.0.0.1",strategy:a.UI,browser:!0,args:[{name:"index",type:"int",required:!1,help:"1-based index of code block (default: last)"}],columns:["Field","Value"],func:async(t,e)=>{const n=Number.isInteger(e?.index)?e.index:null,r=i(await t.evaluate(`(() => {
51
51
  const isVis = (el) => { const r = el.getBoundingClientRect(); return r.width > 1 && r.height > 1; };
52
52
  const btns = Array.from(document.querySelectorAll('button[aria-label="Copy code"]')).filter(isVis);
53
53
  if (!btns.length) return null;
54
- const idx = ${q===null?"btns.length - 1":q-1};
54
+ const idx = ${n===null?"btns.length - 1":n-1};
55
55
  const btn = btns[idx];
56
- if (!btn) return { err: 'index ' + (${q} ?? 'last') + ' out of range. Have ' + btns.length + ' code blocks.' };
56
+ if (!btn) return { err: 'index ' + (${n} ?? 'last') + ' out of range. Have ' + btns.length + ' code blocks.' };
57
57
  // Find the <code> or <pre> element inside the parent block.
58
58
  let container = btn;
59
59
  for (let i = 0; i < 6 && container.parentElement; i++) container = container.parentElement;
60
60
  const code = container.querySelector('pre, code');
61
61
  return { text: code ? (code.innerText || '').trim() : (container.innerText || '').trim(), total: btns.length };
62
- })()`));if(!z)throw new K("antigravity copy-code","No code blocks visible.");if(z.err)throw new H(z.err,"");return[{Field:"TotalCodeBlocks",Value:String(z.total)},{Field:"PickedIndex",Value:String(q===null?z.total:q)},{Field:"Length",Value:String((z.text||"").length)+" chars"},{Field:"Code",Value:z.text||""}]}});D({site:"antigravity",name:"settings",access:"write",description:'Click the Antigravity settings button (matched by data-testid="settings-button").',domain:"127.0.0.1",strategy:G.UI,browser:!0,args:[],columns:["Status"],func:async(h)=>{const f=B(await h.evaluate(J(['[data-testid="settings-button"]','button[aria-label="Settings"]'])));if(!f?.ok)throw new H(f?.reason||"settings click failed","");await h.wait(0.6);return[{Status:`clicked via ${f.sel}`}]}});D({site:"antigravity",name:"sidebar-toggle",access:"write",description:"Click Toggle Sidebar (collapses/expands the Antigravity sidebar).",domain:"127.0.0.1",strategy:G.UI,browser:!0,args:[],columns:["Status"],func:async(h)=>{const f=B(await h.evaluate(J(['button[aria-label="Toggle Sidebar"]'])));if(!f?.ok)throw new H(f?.reason||"sidebar-toggle failed","");return[{Status:"toggled"}]}});D({site:"antigravity",name:"nav",access:"write",description:"Click Go Back or Go Forward (Antigravity in-app history).",domain:"127.0.0.1",strategy:G.UI,browser:!0,args:[{name:"direction",positional:!0,required:!0,help:"back or forward"}],columns:["Status"],func:async(h,f)=>{const q=String(f?.direction||"").trim().toLowerCase();if(q!=="back"&&q!=="forward")throw new M("direction",'must be "back" or "forward"');const z=q==="back"?"Go Back":"Go Forward",I=B(await h.evaluate(J([`button[aria-label="${z}"]`])));if(!I?.ok)throw new H(I?.reason||`${z} click failed`,"");return[{Status:`${q} clicked`}]}});D({site:"antigravity",name:"toggle-aux",access:"write",description:"Toggle the Auxiliary Pane (Antigravity's secondary panel for code/preview).",domain:"127.0.0.1",strategy:G.UI,browser:!0,args:[],columns:["Status"],func:async(h)=>{const f=B(await h.evaluate(J(['button[aria-label="Toggle Auxiliary Pane"]'])));if(!f?.ok)throw new H(f?.reason||"toggle-aux failed","");return[{Status:"toggled"}]}});D({site:"antigravity",name:"display-options",access:"read",description:"Open the Display Options menu and list its items.",domain:"127.0.0.1",strategy:G.UI,browser:!0,args:[],columns:["Index","Item"],func:async(h)=>{const f=B(await h.evaluate(J(['button[aria-label="Display Options"]'])));if(!f?.ok)throw new H(f?.reason||"display-options click failed","");await h.wait(0.4);const q=B(await h.evaluate(`(() => {
62
+ })()`));if(!r)throw new u("antigravity copy-code","No code blocks visible.");if(r.err)throw new s(r.err,"");return[{Field:"TotalCodeBlocks",Value:String(r.total)},{Field:"PickedIndex",Value:String(n===null?r.total:n)},{Field:"Length",Value:String((r.text||"").length)+" chars"},{Field:"Code",Value:r.text||""}]}});o({site:"antigravity",name:"settings",access:"write",description:'Click the Antigravity settings button (matched by data-testid="settings-button").',domain:"127.0.0.1",strategy:a.UI,browser:!0,args:[],columns:["Status"],func:async(t)=>{const e=i(await t.evaluate(c(['[data-testid="settings-button"]','button[aria-label="Settings"]'])));if(!e?.ok)throw new s(e?.reason||"settings click failed","");await t.wait(0.6);return[{Status:`clicked via ${e.sel}`}]}});o({site:"antigravity",name:"sidebar-toggle",access:"write",description:"Click Toggle Sidebar (collapses/expands the Antigravity sidebar).",domain:"127.0.0.1",strategy:a.UI,browser:!0,args:[],columns:["Status"],func:async(t)=>{const e=i(await t.evaluate(c(['button[aria-label="Toggle Sidebar"]'])));if(!e?.ok)throw new s(e?.reason||"sidebar-toggle failed","");return[{Status:"toggled"}]}});o({site:"antigravity",name:"nav",access:"write",description:"Click Go Back or Go Forward (Antigravity in-app history).",domain:"127.0.0.1",strategy:a.UI,browser:!0,args:[{name:"direction",positional:!0,required:!0,help:"back or forward"}],columns:["Status"],func:async(t,e)=>{const n=String(e?.direction||"").trim().toLowerCase();if(n!=="back"&&n!=="forward")throw new d("direction",'must be "back" or "forward"');const r=n==="back"?"Go Back":"Go Forward",l=i(await t.evaluate(c([`button[aria-label="${r}"]`])));if(!l?.ok)throw new s(l?.reason||`${r} click failed`,"");return[{Status:`${n} clicked`}]}});o({site:"antigravity",name:"toggle-aux",access:"write",description:"Toggle the Auxiliary Pane (Antigravity's secondary panel for code/preview).",domain:"127.0.0.1",strategy:a.UI,browser:!0,args:[],columns:["Status"],func:async(t)=>{const e=i(await t.evaluate(c(['button[aria-label="Toggle Auxiliary Pane"]'])));if(!e?.ok)throw new s(e?.reason||"toggle-aux failed","");return[{Status:"toggled"}]}});o({site:"antigravity",name:"display-options",access:"read",description:"Open the Display Options menu and list its items.",domain:"127.0.0.1",strategy:a.UI,browser:!0,args:[],columns:["Index","Item"],func:async(t)=>{const e=i(await t.evaluate(c(['button[aria-label="Display Options"]'])));if(!e?.ok)throw new s(e?.reason||"display-options click failed","");await t.wait(0.4);const n=i(await t.evaluate(`(() => {
63
63
  const isVis = (el) => { const r = el.getBoundingClientRect(); return r.width > 1 && r.height > 1; };
64
64
  const candidates = Array.from(document.querySelectorAll('[role="menu"], [role="dialog"], [class*="popover"i]'))
65
65
  .filter(isVis)
@@ -75,4 +75,4 @@ import{cli as D,Strategy as G}from"@jackwener/opencli/registry";import{ArgumentE
75
75
  .filter(isVis)
76
76
  .map((it) => (it.innerText || '').trim().replace(/\\s+/g, ' '))
77
77
  .filter(Boolean);
78
- })()`));try{await h.evaluate("document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));")}catch{}if(!q.length)throw new K("antigravity display-options","Menu opened but no items detected.");return q.map((z,I)=>({Index:I+1,Item:z}))}});D({site:"antigravity",name:"add-context",access:"write",description:"Click the Add context button in the composer (opens file/URL picker for context attachment).",domain:"127.0.0.1",strategy:G.UI,browser:!0,args:[],columns:["Status"],func:async(h)=>{const f=B(await h.evaluate(J(['button[aria-label="Add context"]'])));if(!f?.ok)throw new H(f?.reason||"add-context click failed","");await h.wait(0.4);return[{Status:"clicked — picker should be open"}]}});D({site:"antigravity",name:"revert",access:"write",description:"Click the revert button (per-message revert for agent changes). Requires --yes (this modifies your workspace).",domain:"127.0.0.1",strategy:G.UI,browser:!0,args:[{name:"yes",type:"boolean",default:!1,help:"Actually revert (default: dry-run)"}],columns:["Status"],func:async(h,f)=>{if(!(f?.yes===!0||f?.yes==="true"||f?.yes==="1"))return[{Status:"dry-run — pass --yes to revert (modifies workspace)"}];const z=B(await h.evaluate(J(['[data-testid="revert-button"]','button[aria-label="Revert"]'])));if(!z?.ok)throw new H(z?.reason||"revert click failed","");await h.wait(1);return[{Status:"reverted"}]}});
78
+ })()`));try{await t.evaluate("document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));")}catch{}if(!n.length)throw new u("antigravity display-options","Menu opened but no items detected.");return n.map((r,l)=>({Index:l+1,Item:r}))}});o({site:"antigravity",name:"add-context",access:"write",description:"Click the Add context button in the composer (opens file/URL picker for context attachment).",domain:"127.0.0.1",strategy:a.UI,browser:!0,args:[],columns:["Status"],func:async(t)=>{const e=i(await t.evaluate(c(['button[aria-label="Add context"]'])));if(!e?.ok)throw new s(e?.reason||"add-context click failed","");await t.wait(0.4);return[{Status:"clicked — picker should be open"}]}});o({site:"antigravity",name:"revert",access:"write",description:"Click the revert button (per-message revert for agent changes). Requires --yes (this modifies your workspace).",domain:"127.0.0.1",strategy:a.UI,browser:!0,args:[{name:"yes",type:"boolean",default:!1,help:"Actually revert (default: dry-run)"}],columns:["Status"],func:async(t,e)=>{if(!(e?.yes===!0||e?.yes==="true"||e?.yes==="1"))return[{Status:"dry-run — pass --yes to revert (modifies workspace)"}];const r=i(await t.evaluate(c(['[data-testid="revert-button"]','button[aria-label="Revert"]'])));if(!r?.ok)throw new s(r?.reason||"revert click failed","");await t.wait(1);return[{Status:"reverted"}]}});
@@ -1 +1 @@
1
- import{cli as K,Strategy as L}from"@jackwener/opencli/registry";import{CommandExecutionError as H}from"@jackwener/opencli/errors";import{clickConversationMenuItem as N,confirmDeleteDialog as O,conversationVisible as P,conversationTargetArgs as Q}from"./_actions.js";K({site:"antigravity",name:"delete",access:"write",description:"Delete an Antigravity conversation by ID. Antigravity asks for confirmation; we click through it. Require --yes to actually delete.",domain:"127.0.0.1",strategy:L.UI,browser:!0,args:[...Q,{name:"yes",type:"boolean",default:!1,help:"Actually delete (default: dry-run preview)"}],columns:["status","id"],func:async(q,B)=>{const z=String(B.id);if(!(B.yes===!0||B.yes==="true"||B.yes==="1"))return[{status:"dry-run (pass --yes to actually delete)",id:z}];const F=await N(q,z,["Delete Conversation","Delete"]);if(!F.ok)throw new H(`${F.reason}${F.detail?" "+F.detail:""}`,"Make sure Antigravity is in the foreground and the sidebar is open.");const G=await O(q,["Delete","Delete Conversation","Confirm","OK"]);if(!G.ok)throw new H(`${G.reason}${G.detail?" "+G.detail:""}`,"Delete menu fired but the confirm dialog did not show / its button was not found.");await q.wait(1);for(let J=0;J<10;J+=1){if(!await P(q,z))return[{status:"deleted",id:z}];await q.wait(0.5)}throw new H(`Delete did not remove conversation ${z} from the visible sidebar.`,"The delete click/confirmation may have failed or the selector contract drifted.")}});
1
+ import{cli as s,Strategy as l}from"@jackwener/opencli/registry";import{CommandExecutionError as n}from"@jackwener/opencli/errors";import{clickConversationMenuItem as d,confirmDeleteDialog as c,conversationVisible as u,conversationTargetArgs as f}from"./_actions.js";s({site:"antigravity",name:"delete",access:"write",description:"Delete an Antigravity conversation by ID. Antigravity asks for confirmation; we click through it. Require --yes to actually delete.",domain:"127.0.0.1",strategy:l.UI,browser:!0,args:[...f,{name:"yes",type:"boolean",default:!1,help:"Actually delete (default: dry-run preview)"}],columns:["status","id"],func:async(e,i)=>{const t=String(i.id);if(!(i.yes===!0||i.yes==="true"||i.yes==="1"))return[{status:"dry-run (pass --yes to actually delete)",id:t}];const o=await d(e,t,["Delete Conversation","Delete"]);if(!o.ok)throw new n(`${o.reason}${o.detail?" "+o.detail:""}`,"Make sure Antigravity is in the foreground and the sidebar is open.");const r=await c(e,["Delete","Delete Conversation","Confirm","OK"]);if(!r.ok)throw new n(`${r.reason}${r.detail?" "+r.detail:""}`,"Delete menu fired but the confirm dialog did not show / its button was not found.");await e.wait(1);for(let a=0;a<10;a+=1){if(!await u(e,t))return[{status:"deleted",id:t}];await e.wait(0.5)}throw new n(`Delete did not remove conversation ${t} from the visible sidebar.`,"The delete click/confirmation may have failed or the selector contract drifted.")}});
@@ -1,10 +1,10 @@
1
- import{cli as N,Strategy as P}from"@jackwener/opencli/registry";import{ArgumentError as Q,CommandExecutionError as L,selectorError as T}from"@jackwener/opencli/errors";import{unwrapEvaluateResult as J}from"./_actions.js";N({site:"antigravity",name:"model",access:"write",description:"Read or switch the active model in Antigravity. Without arguments, reports the current model. With <name> (substring, case-insensitive), switches.",domain:"127.0.0.1",strategy:P.UI,browser:!0,args:[{name:"name",required:!1,positional:!0,help:"Substring (case-insensitive) of target model name. Omit to read current."},{name:"list",type:"boolean",default:!1,help:"List models in the picker (does not switch)"}],columns:["Status","Model"],func:async(F,G)=>{const K=String(G.name||"").trim().toLowerCase(),H=G.list===!0||G.list==="true",B=(D)=>String(D||"").trim().replace(/\s+/g," ").toLowerCase(),I=J(await F.evaluate(`(function() {
1
+ import{cli as m,Strategy as h}from"@jackwener/opencli/registry";import{ArgumentError as b,CommandExecutionError as u,selectorError as g}from"@jackwener/opencli/errors";import{unwrapEvaluateResult as l}from"./_actions.js";m({site:"antigravity",name:"model",access:"write",description:"Read or switch the active model in Antigravity. Without arguments, reports the current model. With <name> (substring, case-insensitive), switches.",domain:"127.0.0.1",strategy:h.UI,browser:!0,args:[{name:"name",required:!1,positional:!0,help:"Substring (case-insensitive) of target model name. Omit to read current."},{name:"list",type:"boolean",default:!1,help:"List models in the picker (does not switch)"}],columns:["Status","Model"],func:async(i,o)=>{const c=String(o.name||"").trim().toLowerCase(),a=o.list===!0||o.list==="true",n=(r)=>String(r||"").trim().replace(/\s+/g," ").toLowerCase(),s=l(await i.evaluate(`(function() {
2
2
  const btn = document.querySelector('button[aria-label^="Select model, current:"]');
3
3
  if (!btn) return '';
4
4
  const aria = btn.getAttribute('aria-label') || '';
5
5
  const m = aria.match(/current:\\s*(.*)$/i);
6
6
  return m ? m[1].trim() : (btn.textContent || '').trim();
7
- })()`));if(!I)throw T('Antigravity model button (button[aria-label^="Select model, current:"]). Make sure a chat is open in the foreground.');if(!K&&!H)return[{Status:"Active",Model:I}];const M=JSON.stringify(K),h=J(await F.evaluate(`(async () => {
7
+ })()`));if(!s)throw g('Antigravity model button (button[aria-label^="Select model, current:"]). Make sure a chat is open in the foreground.');if(!c&&!a)return[{Status:"Active",Model:s}];const d=JSON.stringify(c),e=l(await i.evaluate(`(async () => {
8
8
  const wait = (ms) => new Promise((r) => setTimeout(r, ms));
9
9
  const trigger = document.querySelector('button[aria-label^="Select model, current:"]');
10
10
  if (!trigger) return { ok: false, reason: 'trigger missing' };
@@ -41,8 +41,8 @@ import{cli as N,Strategy as P}from"@jackwener/opencli/registry";import{ArgumentE
41
41
  }
42
42
 
43
43
  const labels = rows.map((r) => (r.innerText || r.textContent || '').trim().slice(0, 80));
44
- const target = ${M};
45
- const listOnly = ${H?"true":"false"};
44
+ const target = ${d};
45
+ const listOnly = ${a?"true":"false"};
46
46
  if (!target || listOnly) {
47
47
  // Close picker (Esc) and return list.
48
48
  document.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Escape' }));
@@ -81,10 +81,10 @@ import{cli as N,Strategy as P}from"@jackwener/opencli/registry";import{ArgumentE
81
81
  } catch {}
82
82
  });
83
83
  return { ok: true, switched: true, chosen: chosenLabel, labels };
84
- })()`));if(!h.ok){if(h.reason==="Ambiguous model match.")throw new Q(h.detail||"Ambiguous model match.");throw new L(h.reason,h.detail||"")}if(H)return h.labels.map((D)=>({Status:D.startsWith(I.slice(0,20))?"Active":"Available",Model:D}));await F.wait(0.8);let q="";for(let D=0;D<8;D+=1){q=J(await F.evaluate(`(function() {
84
+ })()`));if(!e.ok){if(e.reason==="Ambiguous model match.")throw new b(e.detail||"Ambiguous model match.");throw new u(e.reason,e.detail||"")}if(a)return e.labels.map((r)=>({Status:r.startsWith(s.slice(0,20))?"Active":"Available",Model:r}));await i.wait(0.8);let t="";for(let r=0;r<8;r+=1){t=l(await i.evaluate(`(function() {
85
85
  const btn = document.querySelector('button[aria-label^="Select model, current:"]');
86
86
  if (!btn) return '';
87
87
  const aria = btn.getAttribute('aria-label') || '';
88
88
  const m = aria.match(/current:\\s*(.*)$/i);
89
89
  return m ? m[1].trim() : (btn.textContent || '').trim();
90
- })()`));if(B(q)&&(B(h.chosen).includes(B(q))||B(q).includes(B(h.chosen))))return[{Status:"switched",Model:q}];if(B(q)===B(h.chosen))return[{Status:"switched",Model:q}];await F.wait(0.4)}throw new L(`Could not verify Antigravity model switched to ${h.chosen}.`,`Read back current model: ${q||"(empty)"}`)}});
90
+ })()`));if(n(t)&&(n(e.chosen).includes(n(t))||n(t).includes(n(e.chosen))))return[{Status:"switched",Model:t}];if(n(t)===n(e.chosen))return[{Status:"switched",Model:t}];await i.wait(0.4)}throw new u(`Could not verify Antigravity model switched to ${e.chosen}.`,`Read back current model: ${t||"(empty)"}`)}});
@@ -1,4 +1,4 @@
1
- import{cli as d,Strategy as f}from"@jackwener/opencli/registry";export const newCommand=d({site:"antigravity",name:"new",access:"read",description:"Start a new conversation / clear context in Antigravity",domain:"localhost",strategy:f.UI,browser:!0,args:[],columns:["status"],func:async(b)=>{await b.evaluate(`
1
+ import{cli as n,Strategy as a}from"@jackwener/opencli/registry";export const newCommand=n({site:"antigravity",name:"new",access:"read",description:"Start a new conversation / clear context in Antigravity",domain:"localhost",strategy:a.UI,browser:!0,args:[],columns:["status"],func:async(t)=>{await t.evaluate(`
2
2
  async () => {
3
3
  const btn = document.querySelector('[data-tooltip-id="new-conversation-tooltip"]');
4
4
  if (!btn) throw new Error('Could not find New Conversation button');
@@ -6,4 +6,4 @@ import{cli as d,Strategy as f}from"@jackwener/opencli/registry";export const new
6
6
  // In case it's disabled, we must check, but we'll try to click it anyway
7
7
  btn.click();
8
8
  }
9
- `);await b.wait(0.5);return[{status:"Successfully started a new conversation"}]}});
9
+ `);await t.wait(0.5);return[{status:"Successfully started a new conversation"}]}});