publishport-opencli 1.8.4-pp.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2171) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +300 -0
  3. package/README.zh-CN.md +341 -0
  4. package/cli-manifest.json +44212 -0
  5. package/clis/12306/auth.js +59 -0
  6. package/clis/12306/me.js +73 -0
  7. package/clis/12306/orders.js +96 -0
  8. package/clis/12306/passengers.js +90 -0
  9. package/clis/12306/price.js +166 -0
  10. package/clis/12306/stations.js +66 -0
  11. package/clis/12306/train.js +91 -0
  12. package/clis/12306/trains.js +166 -0
  13. package/clis/12306/utils.js +272 -0
  14. package/clis/12306/utils.test.js +424 -0
  15. package/clis/1688/assets.js +205 -0
  16. package/clis/1688/assets.test.js +39 -0
  17. package/clis/1688/auth.js +46 -0
  18. package/clis/1688/download.js +77 -0
  19. package/clis/1688/download.test.js +31 -0
  20. package/clis/1688/item.js +188 -0
  21. package/clis/1688/item.test.js +67 -0
  22. package/clis/1688/search.js +310 -0
  23. package/clis/1688/search.test.js +75 -0
  24. package/clis/1688/shared.js +557 -0
  25. package/clis/1688/shared.test.js +57 -0
  26. package/clis/1688/store.js +227 -0
  27. package/clis/1688/store.test.js +62 -0
  28. package/clis/1point3acres/auth.js +52 -0
  29. package/clis/1point3acres/digest.js +35 -0
  30. package/clis/1point3acres/forum.js +51 -0
  31. package/clis/1point3acres/forums.js +44 -0
  32. package/clis/1point3acres/hot.js +35 -0
  33. package/clis/1point3acres/latest.js +35 -0
  34. package/clis/1point3acres/notifications.js +64 -0
  35. package/clis/1point3acres/search.js +71 -0
  36. package/clis/1point3acres/thread.js +117 -0
  37. package/clis/1point3acres/user.js +77 -0
  38. package/clis/1point3acres/utils.js +247 -0
  39. package/clis/36kr/article.js +66 -0
  40. package/clis/36kr/article.test.js +46 -0
  41. package/clis/36kr/hot.js +87 -0
  42. package/clis/36kr/hot.test.js +15 -0
  43. package/clis/36kr/news.js +52 -0
  44. package/clis/36kr/news.test.js +85 -0
  45. package/clis/36kr/search.js +79 -0
  46. package/clis/51job/company.js +126 -0
  47. package/clis/51job/detail.js +109 -0
  48. package/clis/51job/hot.js +56 -0
  49. package/clis/51job/search.js +80 -0
  50. package/clis/51job/utils.js +302 -0
  51. package/clis/51job/utils.test.js +69 -0
  52. package/clis/_atlassian/shared.js +577 -0
  53. package/clis/_atlassian/shared.test.js +170 -0
  54. package/clis/_shared/common.js +32 -0
  55. package/clis/_shared/desktop-commands.js +112 -0
  56. package/clis/_shared/search-adapter.js +70 -0
  57. package/clis/_shared/site-auth.js +118 -0
  58. package/clis/_shared/site-auth.test.js +98 -0
  59. package/clis/aibase/news.js +110 -0
  60. package/clis/aibase/news.test.js +59 -0
  61. package/clis/amazon/auth.js +53 -0
  62. package/clis/amazon/bestsellers.js +8 -0
  63. package/clis/amazon/bestsellers.test.js +29 -0
  64. package/clis/amazon/discussion.js +123 -0
  65. package/clis/amazon/discussion.test.js +124 -0
  66. package/clis/amazon/movers-shakers.js +8 -0
  67. package/clis/amazon/new-releases.js +8 -0
  68. package/clis/amazon/offer.js +141 -0
  69. package/clis/amazon/offer.test.js +29 -0
  70. package/clis/amazon/product.js +93 -0
  71. package/clis/amazon/product.test.js +24 -0
  72. package/clis/amazon/rankings.js +227 -0
  73. package/clis/amazon/rankings.test.js +41 -0
  74. package/clis/amazon/search.js +88 -0
  75. package/clis/amazon/search.test.js +22 -0
  76. package/clis/amazon/shared.js +365 -0
  77. package/clis/amazon/shared.test.js +44 -0
  78. package/clis/antigravity/SKILL.md +38 -0
  79. package/clis/antigravity/_actions.js +318 -0
  80. package/clis/antigravity/antigravity.test.js +172 -0
  81. package/clis/antigravity/audit-extras.js +341 -0
  82. package/clis/antigravity/delete.js +60 -0
  83. package/clis/antigravity/dump.js +29 -0
  84. package/clis/antigravity/extract-code.js +33 -0
  85. package/clis/antigravity/history.js +26 -0
  86. package/clis/antigravity/mark-read.js +52 -0
  87. package/clis/antigravity/model.js +161 -0
  88. package/clis/antigravity/new.js +26 -0
  89. package/clis/antigravity/read.js +35 -0
  90. package/clis/antigravity/rename.js +33 -0
  91. package/clis/antigravity/send.js +36 -0
  92. package/clis/antigravity/serve.js +558 -0
  93. package/clis/antigravity/status.js +19 -0
  94. package/clis/antigravity/storage.js +366 -0
  95. package/clis/antigravity/watch.js +43 -0
  96. package/clis/apple-podcasts/commands.test.js +119 -0
  97. package/clis/apple-podcasts/episodes.js +29 -0
  98. package/clis/apple-podcasts/search.js +31 -0
  99. package/clis/apple-podcasts/top.js +45 -0
  100. package/clis/apple-podcasts/utils.js +30 -0
  101. package/clis/apple-podcasts/utils.test.js +57 -0
  102. package/clis/archive/archive.test.js +262 -0
  103. package/clis/archive/item.js +92 -0
  104. package/clis/archive/search.js +115 -0
  105. package/clis/archive/snapshots.js +129 -0
  106. package/clis/archive/wayback.js +83 -0
  107. package/clis/arxiv/arxiv.test.js +112 -0
  108. package/clis/arxiv/author.js +44 -0
  109. package/clis/arxiv/paper.js +22 -0
  110. package/clis/arxiv/recent.js +33 -0
  111. package/clis/arxiv/search.js +36 -0
  112. package/clis/arxiv/utils.js +112 -0
  113. package/clis/autohome/__fixtures__/catalog.html +11 -0
  114. package/clis/autohome/__fixtures__/koubei.json +116 -0
  115. package/clis/autohome/autohome.test.js +115 -0
  116. package/clis/autohome/brand.js +108 -0
  117. package/clis/autohome/score.js +103 -0
  118. package/clis/autohome/utils.js +157 -0
  119. package/clis/baidu-scholar/search.js +87 -0
  120. package/clis/baidu-scholar/search.test.js +23 -0
  121. package/clis/band/auth.js +62 -0
  122. package/clis/band/bands.js +73 -0
  123. package/clis/band/mentions.js +128 -0
  124. package/clis/band/post.js +176 -0
  125. package/clis/band/posts.js +95 -0
  126. package/clis/barchart/flow.js +116 -0
  127. package/clis/barchart/greeks.js +208 -0
  128. package/clis/barchart/greeks.test.js +138 -0
  129. package/clis/barchart/options.js +107 -0
  130. package/clis/barchart/quote.js +135 -0
  131. package/clis/bbc/news.js +42 -0
  132. package/clis/bbc/topic.js +57 -0
  133. package/clis/bbc/utils.js +79 -0
  134. package/clis/bilibili/auth.js +36 -0
  135. package/clis/bilibili/comment.js +107 -0
  136. package/clis/bilibili/comment.test.js +153 -0
  137. package/clis/bilibili/comments.js +136 -0
  138. package/clis/bilibili/comments.test.js +142 -0
  139. package/clis/bilibili/download.js +178 -0
  140. package/clis/bilibili/download.test.js +173 -0
  141. package/clis/bilibili/dynamic.js +34 -0
  142. package/clis/bilibili/dynamic.test.js +68 -0
  143. package/clis/bilibili/favorite.js +47 -0
  144. package/clis/bilibili/feed.js +220 -0
  145. package/clis/bilibili/follow.js +140 -0
  146. package/clis/bilibili/follow.test.js +203 -0
  147. package/clis/bilibili/following.js +44 -0
  148. package/clis/bilibili/history.js +45 -0
  149. package/clis/bilibili/hot.js +40 -0
  150. package/clis/bilibili/hot.test.js +17 -0
  151. package/clis/bilibili/me.js +13 -0
  152. package/clis/bilibili/ranking.js +25 -0
  153. package/clis/bilibili/search.js +24 -0
  154. package/clis/bilibili/subtitle.js +141 -0
  155. package/clis/bilibili/subtitle.test.js +240 -0
  156. package/clis/bilibili/summary.js +167 -0
  157. package/clis/bilibili/summary.test.js +210 -0
  158. package/clis/bilibili/unfollow.js +121 -0
  159. package/clis/bilibili/user-videos.js +39 -0
  160. package/clis/bilibili/utils.js +273 -0
  161. package/clis/bilibili/utils.test.js +97 -0
  162. package/clis/bilibili/video.js +155 -0
  163. package/clis/bilibili/video.test.js +308 -0
  164. package/clis/binance/asks.js +22 -0
  165. package/clis/binance/commands.test.js +70 -0
  166. package/clis/binance/depth.js +21 -0
  167. package/clis/binance/gainers.js +23 -0
  168. package/clis/binance/klines.js +22 -0
  169. package/clis/binance/losers.js +23 -0
  170. package/clis/binance/pairs.js +22 -0
  171. package/clis/binance/price.js +19 -0
  172. package/clis/binance/prices.js +20 -0
  173. package/clis/binance/ticker.js +22 -0
  174. package/clis/binance/top.js +22 -0
  175. package/clis/binance/trades.js +21 -0
  176. package/clis/bloomberg/businessweek.js +125 -0
  177. package/clis/bloomberg/businessweek.test.js +149 -0
  178. package/clis/bloomberg/crypto.js +18 -0
  179. package/clis/bloomberg/economics.js +18 -0
  180. package/clis/bloomberg/feeds.js +16 -0
  181. package/clis/bloomberg/green.js +18 -0
  182. package/clis/bloomberg/industries.js +18 -0
  183. package/clis/bloomberg/main.js +18 -0
  184. package/clis/bloomberg/markets.js +18 -0
  185. package/clis/bloomberg/news.js +106 -0
  186. package/clis/bloomberg/opinions.js +18 -0
  187. package/clis/bloomberg/politics.js +18 -0
  188. package/clis/bloomberg/pursuits.js +18 -0
  189. package/clis/bloomberg/tech.js +18 -0
  190. package/clis/bloomberg/utils.js +380 -0
  191. package/clis/bloomberg/utils.test.js +129 -0
  192. package/clis/bluesky/feeds.js +28 -0
  193. package/clis/bluesky/followers.js +28 -0
  194. package/clis/bluesky/following.js +28 -0
  195. package/clis/bluesky/profile.js +30 -0
  196. package/clis/bluesky/search.js +29 -0
  197. package/clis/bluesky/starter-packs.js +29 -0
  198. package/clis/bluesky/thread.js +31 -0
  199. package/clis/bluesky/trending.js +20 -0
  200. package/clis/bluesky/user.js +35 -0
  201. package/clis/booking/booking.test.js +356 -0
  202. package/clis/booking/search.js +351 -0
  203. package/clis/boss/auth.js +47 -0
  204. package/clis/boss/batchgreet.js +61 -0
  205. package/clis/boss/chatlist.js +116 -0
  206. package/clis/boss/chatlist.test.js +211 -0
  207. package/clis/boss/chatmsg.js +117 -0
  208. package/clis/boss/chatmsg.test.js +230 -0
  209. package/clis/boss/detail.js +63 -0
  210. package/clis/boss/exchange.js +46 -0
  211. package/clis/boss/greet.js +49 -0
  212. package/clis/boss/invite.js +64 -0
  213. package/clis/boss/joblist.js +31 -0
  214. package/clis/boss/mark.js +70 -0
  215. package/clis/boss/recommend.js +45 -0
  216. package/clis/boss/resume.js +150 -0
  217. package/clis/boss/search.js +197 -0
  218. package/clis/boss/search.test.js +78 -0
  219. package/clis/boss/send.js +44 -0
  220. package/clis/boss/stats.js +66 -0
  221. package/clis/boss/utils.js +468 -0
  222. package/clis/boss/utils.test.js +34 -0
  223. package/clis/brave/search.js +80 -0
  224. package/clis/brave/search.test.js +76 -0
  225. package/clis/chaoxing/assignments.js +76 -0
  226. package/clis/chaoxing/auth.js +54 -0
  227. package/clis/chaoxing/exams.js +75 -0
  228. package/clis/chaoxing/utils.js +223 -0
  229. package/clis/chaoxing/utils.test.js +45 -0
  230. package/clis/chatgpt/ask.js +129 -0
  231. package/clis/chatgpt/auth.js +52 -0
  232. package/clis/chatgpt/commands.test.js +204 -0
  233. package/clis/chatgpt/detail.js +63 -0
  234. package/clis/chatgpt/envelope.test.js +108 -0
  235. package/clis/chatgpt/history.js +39 -0
  236. package/clis/chatgpt/image.js +167 -0
  237. package/clis/chatgpt/image.test.js +207 -0
  238. package/clis/chatgpt/model.js +31 -0
  239. package/clis/chatgpt/model.test.js +36 -0
  240. package/clis/chatgpt/new.js +32 -0
  241. package/clis/chatgpt/project-file-add.js +74 -0
  242. package/clis/chatgpt/project-list.js +37 -0
  243. package/clis/chatgpt/read.js +44 -0
  244. package/clis/chatgpt/send.js +68 -0
  245. package/clis/chatgpt/status.js +29 -0
  246. package/clis/chatgpt/utils.js +2226 -0
  247. package/clis/chatgpt/utils.test.js +1453 -0
  248. package/clis/chatgpt-app/ask.js +87 -0
  249. package/clis/chatgpt-app/ax.js +603 -0
  250. package/clis/chatgpt-app/ax.test.js +95 -0
  251. package/clis/chatgpt-app/commands.test.js +45 -0
  252. package/clis/chatgpt-app/model.js +25 -0
  253. package/clis/chatgpt-app/new.js +61 -0
  254. package/clis/chatgpt-app/read.js +32 -0
  255. package/clis/chatgpt-app/send.js +37 -0
  256. package/clis/chatgpt-app/status.js +26 -0
  257. package/clis/chatwise/ask.js +54 -0
  258. package/clis/chatwise/composer.test.js +186 -0
  259. package/clis/chatwise/export.js +47 -0
  260. package/clis/chatwise/history.js +61 -0
  261. package/clis/chatwise/model.js +85 -0
  262. package/clis/chatwise/new.js +2 -0
  263. package/clis/chatwise/read.js +40 -0
  264. package/clis/chatwise/screenshot.js +2 -0
  265. package/clis/chatwise/send.js +28 -0
  266. package/clis/chatwise/status.js +2 -0
  267. package/clis/chatwise/utils.js +143 -0
  268. package/clis/chess/analyze.js +35 -0
  269. package/clis/chess/analyze.test.js +79 -0
  270. package/clis/chess/game.js +114 -0
  271. package/clis/chess/game.test.js +178 -0
  272. package/clis/chess/games.js +67 -0
  273. package/clis/chess/games.test.js +164 -0
  274. package/clis/chess/stats.js +32 -0
  275. package/clis/chess/stats.test.js +79 -0
  276. package/clis/chess/utils.js +170 -0
  277. package/clis/chess/utils.test.js +230 -0
  278. package/clis/claude/ask.js +144 -0
  279. package/clis/claude/ask.test.js +338 -0
  280. package/clis/claude/auth.js +55 -0
  281. package/clis/claude/commands.test.js +118 -0
  282. package/clis/claude/detail.js +38 -0
  283. package/clis/claude/history.js +33 -0
  284. package/clis/claude/new.js +29 -0
  285. package/clis/claude/read.js +27 -0
  286. package/clis/claude/send.js +48 -0
  287. package/clis/claude/status.js +26 -0
  288. package/clis/claude/utils.js +463 -0
  289. package/clis/claude/utils.test.js +148 -0
  290. package/clis/cnki/search.js +61 -0
  291. package/clis/cnki/search.test.js +18 -0
  292. package/clis/codex/_actions.js +270 -0
  293. package/clis/codex/archive.js +37 -0
  294. package/clis/codex/ask.js +78 -0
  295. package/clis/codex/dump.js +2 -0
  296. package/clis/codex/export.js +38 -0
  297. package/clis/codex/extract-diff.js +45 -0
  298. package/clis/codex/history.js +27 -0
  299. package/clis/codex/model.js +264 -0
  300. package/clis/codex/new.js +2 -0
  301. package/clis/codex/pin.js +30 -0
  302. package/clis/codex/projects.js +28 -0
  303. package/clis/codex/read.js +41 -0
  304. package/clis/codex/rename.js +86 -0
  305. package/clis/codex/screenshot.js +2 -0
  306. package/clis/codex/send.js +51 -0
  307. package/clis/codex/sidebar.js +356 -0
  308. package/clis/codex/sidebar.test.js +388 -0
  309. package/clis/codex/status.js +2 -0
  310. package/clis/coingecko/categories.js +75 -0
  311. package/clis/coingecko/coin.js +107 -0
  312. package/clis/coingecko/coingecko.test.js +109 -0
  313. package/clis/coingecko/derivatives.js +84 -0
  314. package/clis/coingecko/exchanges.js +74 -0
  315. package/clis/coingecko/global.js +71 -0
  316. package/clis/coingecko/top.js +64 -0
  317. package/clis/coingecko/trending.js +55 -0
  318. package/clis/confluence/commands.test.js +195 -0
  319. package/clis/confluence/create.js +39 -0
  320. package/clis/confluence/page.js +23 -0
  321. package/clis/confluence/search.js +34 -0
  322. package/clis/confluence/shared.js +173 -0
  323. package/clis/confluence/update.js +38 -0
  324. package/clis/coupang/add-to-cart.js +149 -0
  325. package/clis/coupang/auth.js +48 -0
  326. package/clis/coupang/coupang.test.js +159 -0
  327. package/clis/coupang/product.js +257 -0
  328. package/clis/coupang/search.js +474 -0
  329. package/clis/coupang/utils.js +316 -0
  330. package/clis/coupang/utils.test.js +62 -0
  331. package/clis/crates/crate.js +62 -0
  332. package/clis/crates/search.js +44 -0
  333. package/clis/crates/utils.js +72 -0
  334. package/clis/ctrip/auth.js +50 -0
  335. package/clis/ctrip/ctrip.test.js +719 -0
  336. package/clis/ctrip/flight.js +136 -0
  337. package/clis/ctrip/hotel-search.js +132 -0
  338. package/clis/ctrip/hotel-suggest.js +45 -0
  339. package/clis/ctrip/search.js +41 -0
  340. package/clis/ctrip/utils.js +473 -0
  341. package/clis/cursor/ask.js +74 -0
  342. package/clis/cursor/composer.js +43 -0
  343. package/clis/cursor/dump.js +2 -0
  344. package/clis/cursor/export.js +52 -0
  345. package/clis/cursor/extract-code.js +36 -0
  346. package/clis/cursor/history.js +44 -0
  347. package/clis/cursor/model.js +54 -0
  348. package/clis/cursor/new.js +2 -0
  349. package/clis/cursor/read.js +45 -0
  350. package/clis/cursor/screenshot.js +2 -0
  351. package/clis/cursor/send.js +41 -0
  352. package/clis/cursor/status.js +2 -0
  353. package/clis/dblp/author.js +133 -0
  354. package/clis/dblp/dblp.test.js +397 -0
  355. package/clis/dblp/paper.js +40 -0
  356. package/clis/dblp/search.js +45 -0
  357. package/clis/dblp/utils.js +290 -0
  358. package/clis/dblp/venue.js +64 -0
  359. package/clis/deepseek/ask.js +158 -0
  360. package/clis/deepseek/ask.test.js +399 -0
  361. package/clis/deepseek/auth.js +54 -0
  362. package/clis/deepseek/detail.js +46 -0
  363. package/clis/deepseek/detail.test.js +81 -0
  364. package/clis/deepseek/history.js +27 -0
  365. package/clis/deepseek/new.js +33 -0
  366. package/clis/deepseek/read.js +25 -0
  367. package/clis/deepseek/send.js +140 -0
  368. package/clis/deepseek/send.test.js +107 -0
  369. package/clis/deepseek/status.js +26 -0
  370. package/clis/deepseek/utils.js +487 -0
  371. package/clis/deepseek/utils.test.js +370 -0
  372. package/clis/defillama/defillama.test.js +99 -0
  373. package/clis/defillama/protocol.js +84 -0
  374. package/clis/defillama/protocols.js +55 -0
  375. package/clis/defillama/utils.js +99 -0
  376. package/clis/devto/devto.test.js +236 -0
  377. package/clis/devto/latest.js +74 -0
  378. package/clis/devto/read.js +103 -0
  379. package/clis/devto/tag.js +36 -0
  380. package/clis/devto/top.js +30 -0
  381. package/clis/devto/user.js +35 -0
  382. package/clis/dianping/__fixtures__/search.html +168 -0
  383. package/clis/dianping/__fixtures__/shop.html +6 -0
  384. package/clis/dianping/auth.js +49 -0
  385. package/clis/dianping/cityResolver.js +185 -0
  386. package/clis/dianping/dianping.test.js +578 -0
  387. package/clis/dianping/search.js +157 -0
  388. package/clis/dianping/shop.js +173 -0
  389. package/clis/dianping/utils.js +157 -0
  390. package/clis/dictionary/examples.js +28 -0
  391. package/clis/dictionary/search.js +30 -0
  392. package/clis/dictionary/synonyms.js +28 -0
  393. package/clis/discord-app/channels.js +26 -0
  394. package/clis/discord-app/commands.test.js +395 -0
  395. package/clis/discord-app/delete.js +115 -0
  396. package/clis/discord-app/goto.js +38 -0
  397. package/clis/discord-app/members.js +39 -0
  398. package/clis/discord-app/read.js +42 -0
  399. package/clis/discord-app/search.js +67 -0
  400. package/clis/discord-app/send.js +28 -0
  401. package/clis/discord-app/servers.js +26 -0
  402. package/clis/discord-app/status.js +17 -0
  403. package/clis/discord-app/thread-read.js +45 -0
  404. package/clis/discord-app/threads.js +37 -0
  405. package/clis/discord-app/utils.js +610 -0
  406. package/clis/dockerhub/image.js +52 -0
  407. package/clis/dockerhub/search.js +47 -0
  408. package/clis/dockerhub/utils.js +100 -0
  409. package/clis/dongchedi/__fixtures__/search.json +60 -0
  410. package/clis/dongchedi/__fixtures__/series-detail.json +911 -0
  411. package/clis/dongchedi/__fixtures__/series-score.json +1396 -0
  412. package/clis/dongchedi/dongchedi.test.js +191 -0
  413. package/clis/dongchedi/koubei.js +85 -0
  414. package/clis/dongchedi/models.js +91 -0
  415. package/clis/dongchedi/score.js +83 -0
  416. package/clis/dongchedi/search.js +83 -0
  417. package/clis/dongchedi/series.js +87 -0
  418. package/clis/dongchedi/specs.js +115 -0
  419. package/clis/dongchedi/utils.js +180 -0
  420. package/clis/douban/auth.js +50 -0
  421. package/clis/douban/book-hot.js +15 -0
  422. package/clis/douban/download.js +68 -0
  423. package/clis/douban/download.test.js +170 -0
  424. package/clis/douban/marks.js +116 -0
  425. package/clis/douban/movie-hot.js +15 -0
  426. package/clis/douban/movie-hot.test.js +14 -0
  427. package/clis/douban/photos.js +35 -0
  428. package/clis/douban/reviews.js +107 -0
  429. package/clis/douban/search.js +18 -0
  430. package/clis/douban/search.test.js +11 -0
  431. package/clis/douban/subject.js +46 -0
  432. package/clis/douban/subject.test.js +11 -0
  433. package/clis/douban/top250.js +68 -0
  434. package/clis/douban/utils.js +657 -0
  435. package/clis/douban/utils.test.js +488 -0
  436. package/clis/doubao/ask.js +40 -0
  437. package/clis/doubao/auth.js +66 -0
  438. package/clis/doubao/detail.js +35 -0
  439. package/clis/doubao/detail.test.js +42 -0
  440. package/clis/doubao/history.js +30 -0
  441. package/clis/doubao/history.test.js +37 -0
  442. package/clis/doubao/meeting-summary.js +41 -0
  443. package/clis/doubao/meeting-transcript.js +38 -0
  444. package/clis/doubao/new.js +22 -0
  445. package/clis/doubao/read.js +21 -0
  446. package/clis/doubao/send.js +24 -0
  447. package/clis/doubao/status.js +26 -0
  448. package/clis/doubao/utils.js +1156 -0
  449. package/clis/doubao/utils.test.js +396 -0
  450. package/clis/doubao-app/ask.js +54 -0
  451. package/clis/doubao-app/dump.js +25 -0
  452. package/clis/doubao-app/new.js +21 -0
  453. package/clis/doubao-app/read.js +19 -0
  454. package/clis/doubao-app/screenshot.js +19 -0
  455. package/clis/doubao-app/send.js +28 -0
  456. package/clis/doubao-app/status.js +17 -0
  457. package/clis/doubao-app/utils.js +110 -0
  458. package/clis/douyin/_shared/browser-fetch.js +66 -0
  459. package/clis/douyin/_shared/browser-fetch.test.js +64 -0
  460. package/clis/douyin/_shared/creation-id.js +5 -0
  461. package/clis/douyin/_shared/creation-id.test.js +22 -0
  462. package/clis/douyin/_shared/evaluate-result.js +16 -0
  463. package/clis/douyin/_shared/imagex-upload.js +53 -0
  464. package/clis/douyin/_shared/imagex-upload.test.js +87 -0
  465. package/clis/douyin/_shared/public-api.js +29 -0
  466. package/clis/douyin/_shared/sts2.js +21 -0
  467. package/clis/douyin/_shared/sts2.test.js +27 -0
  468. package/clis/douyin/_shared/text-extra.js +15 -0
  469. package/clis/douyin/_shared/text-extra.test.js +37 -0
  470. package/clis/douyin/_shared/timing.js +22 -0
  471. package/clis/douyin/_shared/timing.test.js +28 -0
  472. package/clis/douyin/_shared/tos-upload-short-read.test.js +83 -0
  473. package/clis/douyin/_shared/tos-upload.js +331 -0
  474. package/clis/douyin/_shared/tos-upload.test.js +229 -0
  475. package/clis/douyin/_shared/transcode.js +45 -0
  476. package/clis/douyin/_shared/transcode.test.js +91 -0
  477. package/clis/douyin/_shared/types.js +1 -0
  478. package/clis/douyin/_shared/vod-upload.js +212 -0
  479. package/clis/douyin/_shared/vod-upload.test.js +38 -0
  480. package/clis/douyin/activities.js +23 -0
  481. package/clis/douyin/activities.test.js +55 -0
  482. package/clis/douyin/auth.js +39 -0
  483. package/clis/douyin/collections.js +23 -0
  484. package/clis/douyin/collections.test.js +45 -0
  485. package/clis/douyin/delete.js +152 -0
  486. package/clis/douyin/delete.test.js +100 -0
  487. package/clis/douyin/draft.js +355 -0
  488. package/clis/douyin/draft.test.js +335 -0
  489. package/clis/douyin/drafts.js +24 -0
  490. package/clis/douyin/drafts.test.js +11 -0
  491. package/clis/douyin/hashtag.js +114 -0
  492. package/clis/douyin/hashtag.test.js +171 -0
  493. package/clis/douyin/location.js +25 -0
  494. package/clis/douyin/location.test.js +23 -0
  495. package/clis/douyin/profile.js +29 -0
  496. package/clis/douyin/profile.test.js +46 -0
  497. package/clis/douyin/publish-upload-id.test.js +206 -0
  498. package/clis/douyin/publish.js +341 -0
  499. package/clis/douyin/publish.test.js +38 -0
  500. package/clis/douyin/search.js +308 -0
  501. package/clis/douyin/search.test.js +307 -0
  502. package/clis/douyin/stats.js +28 -0
  503. package/clis/douyin/stats.test.js +22 -0
  504. package/clis/douyin/update.js +32 -0
  505. package/clis/douyin/update.test.js +11 -0
  506. package/clis/douyin/user-videos.js +82 -0
  507. package/clis/douyin/user-videos.test.js +151 -0
  508. package/clis/douyin/videos.js +52 -0
  509. package/clis/douyin/videos.test.js +54 -0
  510. package/clis/duckduckgo/search.js +131 -0
  511. package/clis/duckduckgo/search.test.js +128 -0
  512. package/clis/duckduckgo/suggest.js +45 -0
  513. package/clis/duckduckgo/suggest.test.js +66 -0
  514. package/clis/eastmoney/_secid.js +78 -0
  515. package/clis/eastmoney/announcement.js +53 -0
  516. package/clis/eastmoney/convertible.js +74 -0
  517. package/clis/eastmoney/etf.js +66 -0
  518. package/clis/eastmoney/holders.js +79 -0
  519. package/clis/eastmoney/hot-rank.js +51 -0
  520. package/clis/eastmoney/hot-rank.test.js +59 -0
  521. package/clis/eastmoney/index-board.js +97 -0
  522. package/clis/eastmoney/kline.js +88 -0
  523. package/clis/eastmoney/kuaixun.js +55 -0
  524. package/clis/eastmoney/longhu.js +68 -0
  525. package/clis/eastmoney/money-flow.js +79 -0
  526. package/clis/eastmoney/northbound.js +58 -0
  527. package/clis/eastmoney/quote.js +108 -0
  528. package/clis/eastmoney/rank.js +95 -0
  529. package/clis/eastmoney/sectors.js +77 -0
  530. package/clis/endoflife/endoflife.test.js +51 -0
  531. package/clis/endoflife/product.js +55 -0
  532. package/clis/endoflife/utils.js +89 -0
  533. package/clis/facebook/__fixtures__/notifications-page.html +13 -0
  534. package/clis/facebook/add-friend.js +44 -0
  535. package/clis/facebook/auth.js +43 -0
  536. package/clis/facebook/events.js +41 -0
  537. package/clis/facebook/feed.js +305 -0
  538. package/clis/facebook/feed.test.js +169 -0
  539. package/clis/facebook/friends.js +39 -0
  540. package/clis/facebook/groups.js +47 -0
  541. package/clis/facebook/join-group.js +45 -0
  542. package/clis/facebook/marketplace-inbox.js +84 -0
  543. package/clis/facebook/marketplace-listings.js +84 -0
  544. package/clis/facebook/marketplace.test.js +91 -0
  545. package/clis/facebook/memories.js +36 -0
  546. package/clis/facebook/notifications.js +333 -0
  547. package/clis/facebook/notifications.test.js +458 -0
  548. package/clis/facebook/profile.js +38 -0
  549. package/clis/facebook/search.js +39 -0
  550. package/clis/facebook/search.test.js +55 -0
  551. package/clis/flathub/app.js +71 -0
  552. package/clis/flathub/flathub.test.js +90 -0
  553. package/clis/flathub/search.js +80 -0
  554. package/clis/flathub/utils.js +114 -0
  555. package/clis/flomo/auth.js +55 -0
  556. package/clis/flomo/memos.js +228 -0
  557. package/clis/flomo/memos.test.js +144 -0
  558. package/clis/gemini/ask.js +51 -0
  559. package/clis/gemini/ask.test.js +100 -0
  560. package/clis/gemini/auth.js +48 -0
  561. package/clis/gemini/commands.test.js +212 -0
  562. package/clis/gemini/deep-research-result.js +96 -0
  563. package/clis/gemini/deep-research-result.test.js +155 -0
  564. package/clis/gemini/deep-research.js +103 -0
  565. package/clis/gemini/deep-research.test.js +181 -0
  566. package/clis/gemini/detail.js +82 -0
  567. package/clis/gemini/history.js +70 -0
  568. package/clis/gemini/image.js +111 -0
  569. package/clis/gemini/new.js +22 -0
  570. package/clis/gemini/read.js +36 -0
  571. package/clis/gemini/reply-state.test.js +671 -0
  572. package/clis/gemini/status.js +32 -0
  573. package/clis/gemini/utils.js +2056 -0
  574. package/clis/gemini/utils.test.js +392 -0
  575. package/clis/geogebra/add-circle.js +46 -0
  576. package/clis/geogebra/add-line.js +35 -0
  577. package/clis/geogebra/add-point.js +27 -0
  578. package/clis/geogebra/add-polygon.js +25 -0
  579. package/clis/geogebra/eval.js +35 -0
  580. package/clis/geogebra/geogebra.test.js +175 -0
  581. package/clis/geogebra/hexagon.js +62 -0
  582. package/clis/geogebra/info.js +72 -0
  583. package/clis/geogebra/list.js +35 -0
  584. package/clis/geogebra/triangle.js +60 -0
  585. package/clis/geogebra/utils.js +271 -0
  586. package/clis/gitee/auth.js +41 -0
  587. package/clis/gitee/index.js +3 -0
  588. package/clis/gitee/search.js +137 -0
  589. package/clis/gitee/search.test.js +65 -0
  590. package/clis/gitee/trending.js +568 -0
  591. package/clis/gitee/user.js +200 -0
  592. package/clis/gitee/user.test.js +63 -0
  593. package/clis/github/auth.js +44 -0
  594. package/clis/github-trending/repos.js +168 -0
  595. package/clis/github-trending/repos.test.js +149 -0
  596. package/clis/google/news.js +59 -0
  597. package/clis/google/search.js +138 -0
  598. package/clis/google/suggest.js +35 -0
  599. package/clis/google/trends.js +39 -0
  600. package/clis/google/utils.js +23 -0
  601. package/clis/google/utils.test.js +75 -0
  602. package/clis/google-scholar/cite.js +74 -0
  603. package/clis/google-scholar/cite.test.js +47 -0
  604. package/clis/google-scholar/profile.js +92 -0
  605. package/clis/google-scholar/profile.test.js +49 -0
  606. package/clis/google-scholar/search.js +73 -0
  607. package/clis/google-scholar/search.test.js +71 -0
  608. package/clis/goproxy/goproxy.test.js +103 -0
  609. package/clis/goproxy/module.js +47 -0
  610. package/clis/goproxy/utils.js +165 -0
  611. package/clis/goproxy/versions.js +59 -0
  612. package/clis/gov-law/commands.test.js +39 -0
  613. package/clis/gov-law/recent.js +22 -0
  614. package/clis/gov-law/search.js +41 -0
  615. package/clis/gov-law/shared.js +51 -0
  616. package/clis/gov-policy/__fixtures__/recent.html +16 -0
  617. package/clis/gov-policy/__fixtures__/search.html +41 -0
  618. package/clis/gov-policy/gov-policy.test.js +224 -0
  619. package/clis/gov-policy/recent.js +90 -0
  620. package/clis/gov-policy/search.js +91 -0
  621. package/clis/gov-policy/utils.js +54 -0
  622. package/clis/grok/ask.js +77 -0
  623. package/clis/grok/ask.test.js +29 -0
  624. package/clis/grok/auth.js +52 -0
  625. package/clis/grok/delete.js +54 -0
  626. package/clis/grok/detail.js +60 -0
  627. package/clis/grok/export-all.js +409 -0
  628. package/clis/grok/export-utils.js +94 -0
  629. package/clis/grok/export.js +189 -0
  630. package/clis/grok/export.test.js +210 -0
  631. package/clis/grok/history.js +48 -0
  632. package/clis/grok/image.js +343 -0
  633. package/clis/grok/image.test.ts +127 -0
  634. package/clis/grok/new.js +20 -0
  635. package/clis/grok/pin.js +68 -0
  636. package/clis/grok/read.js +39 -0
  637. package/clis/grok/send.js +50 -0
  638. package/clis/grok/status.js +41 -0
  639. package/clis/grok/utils.js +640 -0
  640. package/clis/grok/utils.test.js +141 -0
  641. package/clis/guazi/__fixtures__/detail.html +7 -0
  642. package/clis/guazi/__fixtures__/list.html +5 -0
  643. package/clis/guazi/browse.js +95 -0
  644. package/clis/guazi/car.js +110 -0
  645. package/clis/guazi/guazi.test.js +126 -0
  646. package/clis/guazi/utils.js +133 -0
  647. package/clis/hackernews/ask.js +31 -0
  648. package/clis/hackernews/best.js +31 -0
  649. package/clis/hackernews/hackernews.test.js +132 -0
  650. package/clis/hackernews/jobs.js +29 -0
  651. package/clis/hackernews/new.js +31 -0
  652. package/clis/hackernews/read.js +188 -0
  653. package/clis/hackernews/search.js +38 -0
  654. package/clis/hackernews/show.js +31 -0
  655. package/clis/hackernews/top.js +31 -0
  656. package/clis/hackernews/user.js +23 -0
  657. package/clis/hf/auth.js +41 -0
  658. package/clis/hf/datasets.js +88 -0
  659. package/clis/hf/hf.test.js +16 -0
  660. package/clis/hf/models.js +91 -0
  661. package/clis/hf/paper.js +79 -0
  662. package/clis/hf/spaces.js +101 -0
  663. package/clis/hf/top.js +121 -0
  664. package/clis/homebrew/cask.js +39 -0
  665. package/clis/homebrew/formula.js +41 -0
  666. package/clis/homebrew/popular.js +54 -0
  667. package/clis/homebrew/utils.js +100 -0
  668. package/clis/huodongxing/events.js +322 -0
  669. package/clis/huodongxing/events.test.js +296 -0
  670. package/clis/hupu/__fixtures__/hot-home.html +64 -0
  671. package/clis/hupu/auth.js +47 -0
  672. package/clis/hupu/detail.js +72 -0
  673. package/clis/hupu/hot.js +163 -0
  674. package/clis/hupu/hot.test.js +224 -0
  675. package/clis/hupu/like.js +76 -0
  676. package/clis/hupu/mentions.js +160 -0
  677. package/clis/hupu/reply.js +72 -0
  678. package/clis/hupu/search.js +60 -0
  679. package/clis/hupu/unlike.js +76 -0
  680. package/clis/hupu/utils.js +319 -0
  681. package/clis/imdb/person.js +204 -0
  682. package/clis/imdb/reviews.js +89 -0
  683. package/clis/imdb/search.js +162 -0
  684. package/clis/imdb/title.js +94 -0
  685. package/clis/imdb/top.js +54 -0
  686. package/clis/imdb/trending.js +53 -0
  687. package/clis/imdb/utils.js +285 -0
  688. package/clis/imdb/utils.test.js +88 -0
  689. package/clis/indeed/indeed.test.js +375 -0
  690. package/clis/indeed/job.js +86 -0
  691. package/clis/indeed/search.js +110 -0
  692. package/clis/indeed/utils.js +152 -0
  693. package/clis/instagram/_shared/private-publish.js +1030 -0
  694. package/clis/instagram/_shared/private-publish.test.js +705 -0
  695. package/clis/instagram/_shared/protocol-capture.js +282 -0
  696. package/clis/instagram/_shared/protocol-capture.test.js +114 -0
  697. package/clis/instagram/_shared/runtime-info.js +81 -0
  698. package/clis/instagram/auth.js +57 -0
  699. package/clis/instagram/collection-create.js +58 -0
  700. package/clis/instagram/collection-delete.js +92 -0
  701. package/clis/instagram/comment.js +48 -0
  702. package/clis/instagram/download.js +226 -0
  703. package/clis/instagram/download.test.js +118 -0
  704. package/clis/instagram/explore.js +42 -0
  705. package/clis/instagram/follow.js +44 -0
  706. package/clis/instagram/followers.js +46 -0
  707. package/clis/instagram/following.js +90 -0
  708. package/clis/instagram/instagram.test.js +381 -0
  709. package/clis/instagram/like.js +46 -0
  710. package/clis/instagram/note.js +223 -0
  711. package/clis/instagram/note.test.js +53 -0
  712. package/clis/instagram/post.js +1497 -0
  713. package/clis/instagram/post.test.js +1647 -0
  714. package/clis/instagram/profile.js +40 -0
  715. package/clis/instagram/reel.js +827 -0
  716. package/clis/instagram/reel.test.js +167 -0
  717. package/clis/instagram/save.js +46 -0
  718. package/clis/instagram/saved.js +53 -0
  719. package/clis/instagram/search.js +39 -0
  720. package/clis/instagram/story.js +116 -0
  721. package/clis/instagram/story.test.js +134 -0
  722. package/clis/instagram/unfollow.js +41 -0
  723. package/clis/instagram/unlike.js +46 -0
  724. package/clis/instagram/unsave.js +46 -0
  725. package/clis/instagram/user.js +49 -0
  726. package/clis/jd/add-cart.js +72 -0
  727. package/clis/jd/auth.js +46 -0
  728. package/clis/jd/cart.js +80 -0
  729. package/clis/jd/commands.test.js +41 -0
  730. package/clis/jd/detail.js +63 -0
  731. package/clis/jd/item.js +730 -0
  732. package/clis/jd/item.test.js +354 -0
  733. package/clis/jd/item.test.ts +517 -0
  734. package/clis/jd/reviews.js +55 -0
  735. package/clis/jd/search.js +66 -0
  736. package/clis/jianyu/auth.js +62 -0
  737. package/clis/jianyu/detail.js +21 -0
  738. package/clis/jianyu/search.js +634 -0
  739. package/clis/jianyu/search.test.js +153 -0
  740. package/clis/jianyu/shared/china-bid-search.js +165 -0
  741. package/clis/jianyu/shared/procurement-contract.js +324 -0
  742. package/clis/jianyu/shared/procurement-contract.test.js +72 -0
  743. package/clis/jianyu/shared/procurement-detail.js +107 -0
  744. package/clis/jianyu/shared/procurement-detail.test.js +84 -0
  745. package/clis/jike/auth.js +48 -0
  746. package/clis/jike/comment.js +107 -0
  747. package/clis/jike/create.js +106 -0
  748. package/clis/jike/feed.js +68 -0
  749. package/clis/jike/like.js +61 -0
  750. package/clis/jike/notifications.js +169 -0
  751. package/clis/jike/post.js +72 -0
  752. package/clis/jike/read.test.js +86 -0
  753. package/clis/jike/repost.js +103 -0
  754. package/clis/jike/search.js +68 -0
  755. package/clis/jike/topic.js +65 -0
  756. package/clis/jike/user.js +65 -0
  757. package/clis/jike/utils.js +25 -0
  758. package/clis/jimeng/auth.js +46 -0
  759. package/clis/jimeng/generate.js +84 -0
  760. package/clis/jimeng/history.js +48 -0
  761. package/clis/jimeng/new.js +44 -0
  762. package/clis/jimeng/workspaces.js +42 -0
  763. package/clis/jira/attachments.js +28 -0
  764. package/clis/jira/commands.test.js +287 -0
  765. package/clis/jira/comments.js +28 -0
  766. package/clis/jira/issue.js +28 -0
  767. package/clis/jira/links.js +28 -0
  768. package/clis/jira/search.js +47 -0
  769. package/clis/jira/shared.js +256 -0
  770. package/clis/juejin/hot.js +37 -0
  771. package/clis/juejin/juejin.test.js +241 -0
  772. package/clis/juejin/recommend.js +61 -0
  773. package/clis/juejin/utils.js +199 -0
  774. package/clis/ke/auth.js +46 -0
  775. package/clis/ke/chengjiao.js +78 -0
  776. package/clis/ke/ershoufang.js +101 -0
  777. package/clis/ke/utils.js +104 -0
  778. package/clis/ke/xiaoqu.js +78 -0
  779. package/clis/ke/zufang.js +95 -0
  780. package/clis/kimi/_utils.js +110 -0
  781. package/clis/kimi/audit-extras.js +268 -0
  782. package/clis/kimi/auth.js +57 -0
  783. package/clis/kimi/chat.js +470 -0
  784. package/clis/kimi/kimi.test.js +187 -0
  785. package/clis/kimi/storage.js +169 -0
  786. package/clis/kimi/ui.js +314 -0
  787. package/clis/kimi/usage.js +144 -0
  788. package/clis/lesswrong/_helpers.js +75 -0
  789. package/clis/lesswrong/comments.js +65 -0
  790. package/clis/lesswrong/curated.js +31 -0
  791. package/clis/lesswrong/frontpage.js +31 -0
  792. package/clis/lesswrong/frontpage.test.js +37 -0
  793. package/clis/lesswrong/new.js +31 -0
  794. package/clis/lesswrong/read.js +46 -0
  795. package/clis/lesswrong/sequences.js +28 -0
  796. package/clis/lesswrong/shortform.js +31 -0
  797. package/clis/lesswrong/tag.js +46 -0
  798. package/clis/lesswrong/tags.js +28 -0
  799. package/clis/lesswrong/top-month.js +31 -0
  800. package/clis/lesswrong/top-week.js +31 -0
  801. package/clis/lesswrong/top-year.js +31 -0
  802. package/clis/lesswrong/top.js +31 -0
  803. package/clis/lesswrong/user-posts.js +42 -0
  804. package/clis/lesswrong/user.js +45 -0
  805. package/clis/lichess/lichess.test.js +85 -0
  806. package/clis/lichess/top.js +46 -0
  807. package/clis/lichess/user.js +91 -0
  808. package/clis/lichess/utils.js +97 -0
  809. package/clis/linkedin/auth.js +62 -0
  810. package/clis/linkedin/connect.js +524 -0
  811. package/clis/linkedin/connect.test.js +328 -0
  812. package/clis/linkedin/inbox.js +234 -0
  813. package/clis/linkedin/inbox.test.js +152 -0
  814. package/clis/linkedin/job-detail.js +167 -0
  815. package/clis/linkedin/job-detail.test.js +38 -0
  816. package/clis/linkedin/jobs-preferences.js +113 -0
  817. package/clis/linkedin/jobs-preferences.test.js +43 -0
  818. package/clis/linkedin/people-search.js +262 -0
  819. package/clis/linkedin/people-search.test.js +216 -0
  820. package/clis/linkedin/post-analytics.js +74 -0
  821. package/clis/linkedin/post-analytics.test.js +40 -0
  822. package/clis/linkedin/posts-core.js +241 -0
  823. package/clis/linkedin/posts.js +22 -0
  824. package/clis/linkedin/posts.test.js +40 -0
  825. package/clis/linkedin/profile-analytics.js +104 -0
  826. package/clis/linkedin/profile-analytics.test.js +67 -0
  827. package/clis/linkedin/profile-experience.js +671 -0
  828. package/clis/linkedin/profile-experience.test.js +152 -0
  829. package/clis/linkedin/profile-projects.js +311 -0
  830. package/clis/linkedin/profile-projects.test.js +111 -0
  831. package/clis/linkedin/profile-read.js +148 -0
  832. package/clis/linkedin/profile-read.test.js +77 -0
  833. package/clis/linkedin/safe-send.js +357 -0
  834. package/clis/linkedin/safe-send.test.js +204 -0
  835. package/clis/linkedin/salesnav-inbox.js +210 -0
  836. package/clis/linkedin/salesnav-inbox.test.js +113 -0
  837. package/clis/linkedin/salesnav-message.js +360 -0
  838. package/clis/linkedin/salesnav-message.test.js +172 -0
  839. package/clis/linkedin/salesnav-search.js +186 -0
  840. package/clis/linkedin/salesnav-search.test.js +76 -0
  841. package/clis/linkedin/salesnav-thread.js +212 -0
  842. package/clis/linkedin/salesnav-thread.test.js +79 -0
  843. package/clis/linkedin/search.js +462 -0
  844. package/clis/linkedin/search.test.js +222 -0
  845. package/clis/linkedin/sent-invitations.js +92 -0
  846. package/clis/linkedin/sent-invitations.test.js +62 -0
  847. package/clis/linkedin/services-read.js +213 -0
  848. package/clis/linkedin/services-read.test.js +105 -0
  849. package/clis/linkedin/shared.js +124 -0
  850. package/clis/linkedin/thread-snapshot.js +214 -0
  851. package/clis/linkedin/thread-snapshot.test.js +89 -0
  852. package/clis/linkedin/timeline.js +511 -0
  853. package/clis/linkedin/timeline.test.js +81 -0
  854. package/clis/linkedin-learning/auth.js +62 -0
  855. package/clis/linkedin-learning/course.js +138 -0
  856. package/clis/linkedin-learning/course.test.js +114 -0
  857. package/clis/linkedin-learning/search.js +155 -0
  858. package/clis/linkedin-learning/search.test.js +144 -0
  859. package/clis/linkedin-learning/trending.js +133 -0
  860. package/clis/linkedin-learning/trending.test.js +123 -0
  861. package/clis/linux-do/auth.js +55 -0
  862. package/clis/linux-do/categories.js +50 -0
  863. package/clis/linux-do/feed.js +395 -0
  864. package/clis/linux-do/feed.test.js +153 -0
  865. package/clis/linux-do/search.js +29 -0
  866. package/clis/linux-do/tags.js +28 -0
  867. package/clis/linux-do/topic-content.js +155 -0
  868. package/clis/linux-do/topic-content.test.js +59 -0
  869. package/clis/linux-do/topic.js +53 -0
  870. package/clis/linux-do/user-posts.js +57 -0
  871. package/clis/linux-do/user-topics.js +37 -0
  872. package/clis/lobsters/active.js +29 -0
  873. package/clis/lobsters/domain.js +92 -0
  874. package/clis/lobsters/hot.js +29 -0
  875. package/clis/lobsters/lobsters.test.js +169 -0
  876. package/clis/lobsters/newest.js +29 -0
  877. package/clis/lobsters/read.js +196 -0
  878. package/clis/lobsters/tag.js +35 -0
  879. package/clis/maimai/auth.js +47 -0
  880. package/clis/maimai/search-talents.js +192 -0
  881. package/clis/maimai/search-talents.test.js +29 -0
  882. package/clis/manus/_utils.js +133 -0
  883. package/clis/manus/auth.js +54 -0
  884. package/clis/manus/connectors.js +40 -0
  885. package/clis/manus/credits.js +39 -0
  886. package/clis/manus/list.js +66 -0
  887. package/clis/manus/manus.test.js +382 -0
  888. package/clis/manus/read.js +63 -0
  889. package/clis/manus/skills.js +61 -0
  890. package/clis/manus/status.js +43 -0
  891. package/clis/maven/artifact.js +49 -0
  892. package/clis/maven/search.js +51 -0
  893. package/clis/maven/utils.js +110 -0
  894. package/clis/mdn/search.js +97 -0
  895. package/clis/medium/feed.js +16 -0
  896. package/clis/medium/search.js +16 -0
  897. package/clis/medium/tag.js +135 -0
  898. package/clis/medium/user.js +16 -0
  899. package/clis/medium/utils.js +79 -0
  900. package/clis/mubu/doc.js +41 -0
  901. package/clis/mubu/docs.js +44 -0
  902. package/clis/mubu/notes.js +245 -0
  903. package/clis/mubu/recent.js +28 -0
  904. package/clis/mubu/search.js +63 -0
  905. package/clis/mubu/utils.js +304 -0
  906. package/clis/notebooklm/add-source.js +269 -0
  907. package/clis/notebooklm/add-source.test.js +97 -0
  908. package/clis/notebooklm/auth.js +55 -0
  909. package/clis/notebooklm/compat.test.js +16 -0
  910. package/clis/notebooklm/create.js +76 -0
  911. package/clis/notebooklm/create.test.js +58 -0
  912. package/clis/notebooklm/current.js +28 -0
  913. package/clis/notebooklm/generate-audio.js +91 -0
  914. package/clis/notebooklm/generate-audio.test.js +63 -0
  915. package/clis/notebooklm/generate-slides.js +106 -0
  916. package/clis/notebooklm/generate-slides.test.js +75 -0
  917. package/clis/notebooklm/get.js +37 -0
  918. package/clis/notebooklm/history.js +25 -0
  919. package/clis/notebooklm/history.test.js +58 -0
  920. package/clis/notebooklm/list.js +36 -0
  921. package/clis/notebooklm/note-list.js +28 -0
  922. package/clis/notebooklm/note-list.test.js +56 -0
  923. package/clis/notebooklm/notes-get.js +47 -0
  924. package/clis/notebooklm/notes-get.test.js +72 -0
  925. package/clis/notebooklm/open.js +42 -0
  926. package/clis/notebooklm/open.test.js +63 -0
  927. package/clis/notebooklm/rpc.js +203 -0
  928. package/clis/notebooklm/rpc.test.js +131 -0
  929. package/clis/notebooklm/shared.js +3 -0
  930. package/clis/notebooklm/source-fulltext.js +44 -0
  931. package/clis/notebooklm/source-fulltext.test.js +106 -0
  932. package/clis/notebooklm/source-get.js +40 -0
  933. package/clis/notebooklm/source-get.test.js +84 -0
  934. package/clis/notebooklm/source-guide.js +44 -0
  935. package/clis/notebooklm/source-guide.test.js +104 -0
  936. package/clis/notebooklm/source-list.js +30 -0
  937. package/clis/notebooklm/status.js +31 -0
  938. package/clis/notebooklm/summary.js +30 -0
  939. package/clis/notebooklm/summary.test.js +78 -0
  940. package/clis/notebooklm/utils.js +814 -0
  941. package/clis/notebooklm/utils.test.js +449 -0
  942. package/clis/notebooklm/write-note.js +103 -0
  943. package/clis/notebooklm/write-note.test.js +70 -0
  944. package/clis/nowcoder/auth.js +65 -0
  945. package/clis/nowcoder/companies.js +24 -0
  946. package/clis/nowcoder/creators.js +28 -0
  947. package/clis/nowcoder/detail.js +62 -0
  948. package/clis/nowcoder/experience.js +37 -0
  949. package/clis/nowcoder/hot.js +25 -0
  950. package/clis/nowcoder/jobs.js +22 -0
  951. package/clis/nowcoder/notifications.js +30 -0
  952. package/clis/nowcoder/papers.js +41 -0
  953. package/clis/nowcoder/practice.js +38 -0
  954. package/clis/nowcoder/recommend.js +31 -0
  955. package/clis/nowcoder/referral.js +40 -0
  956. package/clis/nowcoder/salary.js +41 -0
  957. package/clis/nowcoder/search.js +50 -0
  958. package/clis/nowcoder/suggest.js +34 -0
  959. package/clis/nowcoder/topics.js +28 -0
  960. package/clis/nowcoder/trending.js +26 -0
  961. package/clis/npm/downloads.js +59 -0
  962. package/clis/npm/package.js +70 -0
  963. package/clis/npm/search.js +49 -0
  964. package/clis/npm/utils.js +76 -0
  965. package/clis/nuget/nuget.test.js +111 -0
  966. package/clis/nuget/package.js +101 -0
  967. package/clis/nuget/search.js +69 -0
  968. package/clis/nuget/utils.js +87 -0
  969. package/clis/nvd/cve.js +121 -0
  970. package/clis/oeis/oeis.test.js +88 -0
  971. package/clis/oeis/search.js +63 -0
  972. package/clis/oeis/sequence.js +71 -0
  973. package/clis/oeis/utils.js +88 -0
  974. package/clis/ones/common.js +144 -0
  975. package/clis/ones/enrich-tasks.js +37 -0
  976. package/clis/ones/login.js +81 -0
  977. package/clis/ones/logout.js +18 -0
  978. package/clis/ones/me.js +31 -0
  979. package/clis/ones/my-tasks.js +121 -0
  980. package/clis/ones/resolve-labels.js +64 -0
  981. package/clis/ones/task-helpers.js +212 -0
  982. package/clis/ones/task-helpers.test.js +12 -0
  983. package/clis/ones/task.js +67 -0
  984. package/clis/ones/tasks.js +80 -0
  985. package/clis/ones/token-info.js +43 -0
  986. package/clis/ones/worklog.js +268 -0
  987. package/clis/ones/worklog.test.js +20 -0
  988. package/clis/openalex/search.js +69 -0
  989. package/clis/openalex/utils.js +160 -0
  990. package/clis/openalex/work.js +65 -0
  991. package/clis/openfda/drug-label.js +74 -0
  992. package/clis/openfda/food-recall.js +65 -0
  993. package/clis/openfda/openfda.test.js +114 -0
  994. package/clis/openfda/utils.js +67 -0
  995. package/clis/openreview/author.js +58 -0
  996. package/clis/openreview/openreview.test.js +427 -0
  997. package/clis/openreview/paper.js +43 -0
  998. package/clis/openreview/reviews.js +131 -0
  999. package/clis/openreview/search.js +46 -0
  1000. package/clis/openreview/utils.js +172 -0
  1001. package/clis/openreview/venue.js +63 -0
  1002. package/clis/osv/osv.test.js +97 -0
  1003. package/clis/osv/query.js +72 -0
  1004. package/clis/osv/utils.js +169 -0
  1005. package/clis/osv/vulnerability.js +54 -0
  1006. package/clis/packagist/package.js +49 -0
  1007. package/clis/packagist/search.js +43 -0
  1008. package/clis/packagist/utils.js +113 -0
  1009. package/clis/paperreview/commands.test.js +243 -0
  1010. package/clis/paperreview/feedback.js +53 -0
  1011. package/clis/paperreview/review.js +38 -0
  1012. package/clis/paperreview/submit.js +86 -0
  1013. package/clis/paperreview/utils.js +197 -0
  1014. package/clis/paperreview/utils.test.js +49 -0
  1015. package/clis/pixiv/auth.js +64 -0
  1016. package/clis/pixiv/detail.js +66 -0
  1017. package/clis/pixiv/detail.test.js +93 -0
  1018. package/clis/pixiv/download.js +82 -0
  1019. package/clis/pixiv/download.test.js +97 -0
  1020. package/clis/pixiv/illusts.js +66 -0
  1021. package/clis/pixiv/illusts.test.js +99 -0
  1022. package/clis/pixiv/ranking.js +60 -0
  1023. package/clis/pixiv/search.js +44 -0
  1024. package/clis/pixiv/search.test.js +83 -0
  1025. package/clis/pixiv/user.js +59 -0
  1026. package/clis/pixiv/user.test.js +100 -0
  1027. package/clis/pixiv/utils.js +98 -0
  1028. package/clis/powerchina/auth.js +52 -0
  1029. package/clis/powerchina/search.js +251 -0
  1030. package/clis/powerchina/search.test.js +93 -0
  1031. package/clis/producthunt/browse.js +100 -0
  1032. package/clis/producthunt/hot.js +111 -0
  1033. package/clis/producthunt/posts.js +29 -0
  1034. package/clis/producthunt/today.js +36 -0
  1035. package/clis/producthunt/utils.js +99 -0
  1036. package/clis/producthunt/utils.test.js +64 -0
  1037. package/clis/pubmed/article.js +55 -0
  1038. package/clis/pubmed/author.js +64 -0
  1039. package/clis/pubmed/citations.js +36 -0
  1040. package/clis/pubmed/clinical-trial.js +59 -0
  1041. package/clis/pubmed/journal.js +59 -0
  1042. package/clis/pubmed/mesh.js +47 -0
  1043. package/clis/pubmed/pubmed.test.js +464 -0
  1044. package/clis/pubmed/related.js +45 -0
  1045. package/clis/pubmed/review.js +58 -0
  1046. package/clis/pubmed/search.js +75 -0
  1047. package/clis/pubmed/utils.js +325 -0
  1048. package/clis/pypi/downloads.js +66 -0
  1049. package/clis/pypi/package.js +79 -0
  1050. package/clis/pypi/utils.js +55 -0
  1051. package/clis/qoder/_utils.js +191 -0
  1052. package/clis/qoder/composer.js +45 -0
  1053. package/clis/qoder/history.js +49 -0
  1054. package/clis/qoder/qoder.test.js +125 -0
  1055. package/clis/qoder/quest.js +147 -0
  1056. package/clis/qoder/read.js +29 -0
  1057. package/clis/qoder/status.js +21 -0
  1058. package/clis/qoder/ui.js +332 -0
  1059. package/clis/quark/auth.js +47 -0
  1060. package/clis/quark/ls.js +64 -0
  1061. package/clis/quark/mkdir.js +37 -0
  1062. package/clis/quark/mv.js +54 -0
  1063. package/clis/quark/rename.js +27 -0
  1064. package/clis/quark/rm.js +25 -0
  1065. package/clis/quark/save.js +81 -0
  1066. package/clis/quark/share-tree.js +46 -0
  1067. package/clis/quark/utils.js +146 -0
  1068. package/clis/quark/utils.test.js +58 -0
  1069. package/clis/qwen/ask.js +104 -0
  1070. package/clis/qwen/ask.test.js +36 -0
  1071. package/clis/qwen/auth.js +56 -0
  1072. package/clis/qwen/detail.js +62 -0
  1073. package/clis/qwen/history.js +61 -0
  1074. package/clis/qwen/image.js +179 -0
  1075. package/clis/qwen/new.js +23 -0
  1076. package/clis/qwen/read.js +41 -0
  1077. package/clis/qwen/send.js +55 -0
  1078. package/clis/qwen/status.js +37 -0
  1079. package/clis/qwen/utils.js +461 -0
  1080. package/clis/qwen/utils.test.js +128 -0
  1081. package/clis/reddit/auth.js +52 -0
  1082. package/clis/reddit/comment.js +58 -0
  1083. package/clis/reddit/extract-media.test.js +149 -0
  1084. package/clis/reddit/frontpage.js +70 -0
  1085. package/clis/reddit/frontpage.test.js +34 -0
  1086. package/clis/reddit/home.js +146 -0
  1087. package/clis/reddit/home.test.js +170 -0
  1088. package/clis/reddit/hot.js +81 -0
  1089. package/clis/reddit/hot.test.js +32 -0
  1090. package/clis/reddit/popular.js +79 -0
  1091. package/clis/reddit/popular.test.js +26 -0
  1092. package/clis/reddit/read.js +588 -0
  1093. package/clis/reddit/read.test.js +402 -0
  1094. package/clis/reddit/reply.js +182 -0
  1095. package/clis/reddit/reply.test.js +89 -0
  1096. package/clis/reddit/save.js +52 -0
  1097. package/clis/reddit/saved.js +50 -0
  1098. package/clis/reddit/search.js +102 -0
  1099. package/clis/reddit/search.test.js +26 -0
  1100. package/clis/reddit/subreddit-info.js +116 -0
  1101. package/clis/reddit/subreddit-info.test.js +163 -0
  1102. package/clis/reddit/subreddit.js +97 -0
  1103. package/clis/reddit/subreddit.test.js +31 -0
  1104. package/clis/reddit/subscribe.js +51 -0
  1105. package/clis/reddit/subscribed.js +165 -0
  1106. package/clis/reddit/subscribed.test.js +168 -0
  1107. package/clis/reddit/upvote.js +65 -0
  1108. package/clis/reddit/upvoted.js +50 -0
  1109. package/clis/reddit/user-comments.js +45 -0
  1110. package/clis/reddit/user-posts.js +43 -0
  1111. package/clis/reddit/user.js +38 -0
  1112. package/clis/reddit/whoami.js +83 -0
  1113. package/clis/reddit/whoami.test.js +105 -0
  1114. package/clis/rednote/auth.js +52 -0
  1115. package/clis/rednote/comments.js +85 -0
  1116. package/clis/rednote/download.js +59 -0
  1117. package/clis/rednote/feed.js +27 -0
  1118. package/clis/rednote/navigation.test.js +26 -0
  1119. package/clis/rednote/note.js +68 -0
  1120. package/clis/rednote/notifications.js +139 -0
  1121. package/clis/rednote/rednote.test.js +240 -0
  1122. package/clis/rednote/search.js +107 -0
  1123. package/clis/rednote/user.js +55 -0
  1124. package/clis/rest-countries/country.js +65 -0
  1125. package/clis/rest-countries/region.js +64 -0
  1126. package/clis/rest-countries/rest-countries.test.js +83 -0
  1127. package/clis/rest-countries/utils.js +126 -0
  1128. package/clis/reuters/article-detail.js +53 -0
  1129. package/clis/reuters/auth.js +67 -0
  1130. package/clis/reuters/reuters.test.js +299 -0
  1131. package/clis/reuters/search.js +63 -0
  1132. package/clis/reuters/utils.js +159 -0
  1133. package/clis/rfc/rfc.js +52 -0
  1134. package/clis/rfc/rfc.test.js +74 -0
  1135. package/clis/rfc/utils.js +72 -0
  1136. package/clis/rubygems/gem.js +42 -0
  1137. package/clis/rubygems/search.js +47 -0
  1138. package/clis/rubygems/utils.js +86 -0
  1139. package/clis/semanticscholar/citations.js +61 -0
  1140. package/clis/semanticscholar/paper.js +52 -0
  1141. package/clis/semanticscholar/recommendations.js +47 -0
  1142. package/clis/semanticscholar/search.js +48 -0
  1143. package/clis/semanticscholar/semanticscholar.test.js +279 -0
  1144. package/clis/semanticscholar/utils.js +196 -0
  1145. package/clis/sinablog/article.js +15 -0
  1146. package/clis/sinablog/hot.js +15 -0
  1147. package/clis/sinablog/search.js +52 -0
  1148. package/clis/sinablog/user.js +16 -0
  1149. package/clis/sinablog/utils.js +186 -0
  1150. package/clis/sinafinance/news.js +62 -0
  1151. package/clis/sinafinance/rolling-news.js +41 -0
  1152. package/clis/sinafinance/stock-rank.js +66 -0
  1153. package/clis/sinafinance/stock.js +121 -0
  1154. package/clis/sinafinance/stock.test.js +59 -0
  1155. package/clis/slock/api-base-canary.test.js +62 -0
  1156. package/clis/slock/attachment-download.js +72 -0
  1157. package/clis/slock/attachment-download.test.js +102 -0
  1158. package/clis/slock/attachment-upload.js +112 -0
  1159. package/clis/slock/attachment-upload.test.js +131 -0
  1160. package/clis/slock/attachment-url.js +53 -0
  1161. package/clis/slock/attachment-url.test.js +41 -0
  1162. package/clis/slock/auth-verify.js +19 -0
  1163. package/clis/slock/bookmark-add.js +43 -0
  1164. package/clis/slock/bookmark-add.test.js +38 -0
  1165. package/clis/slock/bookmark-list.js +50 -0
  1166. package/clis/slock/bookmark-list.test.js +47 -0
  1167. package/clis/slock/bookmark-remove.js +42 -0
  1168. package/clis/slock/bookmark-remove.test.js +24 -0
  1169. package/clis/slock/channel-action.js +63 -0
  1170. package/clis/slock/channel-action.test.js +65 -0
  1171. package/clis/slock/channel-archive.js +9 -0
  1172. package/clis/slock/channel-create.js +48 -0
  1173. package/clis/slock/channel-create.test.js +40 -0
  1174. package/clis/slock/channel-files.js +51 -0
  1175. package/clis/slock/channel-files.test.js +32 -0
  1176. package/clis/slock/channel-info.js +42 -0
  1177. package/clis/slock/channel-info.test.js +25 -0
  1178. package/clis/slock/channel-join.js +9 -0
  1179. package/clis/slock/channel-leave.js +9 -0
  1180. package/clis/slock/channel-list.js +40 -0
  1181. package/clis/slock/channel-list.test.js +37 -0
  1182. package/clis/slock/channel-mark.js +71 -0
  1183. package/clis/slock/channel-mark.test.js +49 -0
  1184. package/clis/slock/channel-members.js +64 -0
  1185. package/clis/slock/channel-members.test.js +44 -0
  1186. package/clis/slock/channel-unarchive.js +11 -0
  1187. package/clis/slock/cross-command.test.js +21 -0
  1188. package/clis/slock/dm-list.js +41 -0
  1189. package/clis/slock/dm-list.test.js +26 -0
  1190. package/clis/slock/error-detail-canary.test.js +144 -0
  1191. package/clis/slock/errors.js +30 -0
  1192. package/clis/slock/errors.test.js +29 -0
  1193. package/clis/slock/in-page.js +213 -0
  1194. package/clis/slock/in-page.test.js +362 -0
  1195. package/clis/slock/inbox-done.js +40 -0
  1196. package/clis/slock/inbox-done.test.js +21 -0
  1197. package/clis/slock/inbox-read-all.js +32 -0
  1198. package/clis/slock/inbox-read-all.test.js +20 -0
  1199. package/clis/slock/inbox.js +74 -0
  1200. package/clis/slock/inbox.test.js +46 -0
  1201. package/clis/slock/login.js +3 -0
  1202. package/clis/slock/message-read.js +168 -0
  1203. package/clis/slock/message-read.test.js +83 -0
  1204. package/clis/slock/message-search.js +72 -0
  1205. package/clis/slock/message-search.test.js +61 -0
  1206. package/clis/slock/message-send.js +149 -0
  1207. package/clis/slock/message-send.test.js +127 -0
  1208. package/clis/slock/reaction-add.js +42 -0
  1209. package/clis/slock/reaction-add.test.js +36 -0
  1210. package/clis/slock/reaction-remove.js +42 -0
  1211. package/clis/slock/reaction-remove.test.js +29 -0
  1212. package/clis/slock/resolve.js +74 -0
  1213. package/clis/slock/resolve.test.js +102 -0
  1214. package/clis/slock/server-list.js +38 -0
  1215. package/clis/slock/server-list.test.js +24 -0
  1216. package/clis/slock/server-override-canary.test.js +87 -0
  1217. package/clis/slock/server-use.js +53 -0
  1218. package/clis/slock/server-use.test.js +48 -0
  1219. package/clis/slock/shared.js +14 -0
  1220. package/clis/slock/short-id-canary.test.js +86 -0
  1221. package/clis/slock/site-session-canary.test.js +49 -0
  1222. package/clis/slock/task-claim.js +72 -0
  1223. package/clis/slock/task-claim.test.js +81 -0
  1224. package/clis/slock/task-convert.js +106 -0
  1225. package/clis/slock/task-convert.test.js +82 -0
  1226. package/clis/slock/task-create.js +82 -0
  1227. package/clis/slock/task-create.test.js +65 -0
  1228. package/clis/slock/task-delete.js +55 -0
  1229. package/clis/slock/task-delete.test.js +52 -0
  1230. package/clis/slock/task-get.js +59 -0
  1231. package/clis/slock/task-get.test.js +45 -0
  1232. package/clis/slock/task-list-server.js +63 -0
  1233. package/clis/slock/task-list-server.test.js +48 -0
  1234. package/clis/slock/task-list.js +70 -0
  1235. package/clis/slock/task-list.test.js +59 -0
  1236. package/clis/slock/task-status.js +93 -0
  1237. package/clis/slock/task-status.test.js +76 -0
  1238. package/clis/slock/task-unclaim.js +66 -0
  1239. package/clis/slock/task-unclaim.test.js +49 -0
  1240. package/clis/slock/thread-done.js +9 -0
  1241. package/clis/slock/thread-follow.js +46 -0
  1242. package/clis/slock/thread-follow.test.js +34 -0
  1243. package/clis/slock/thread-list.js +44 -0
  1244. package/clis/slock/thread-list.test.js +24 -0
  1245. package/clis/slock/thread-state.js +45 -0
  1246. package/clis/slock/thread-state.test.js +38 -0
  1247. package/clis/slock/thread-undone.js +9 -0
  1248. package/clis/slock/thread-unfollow.js +9 -0
  1249. package/clis/slock/unread-summary.js +49 -0
  1250. package/clis/slock/unread-summary.test.js +31 -0
  1251. package/clis/slock/whoami.js +12 -0
  1252. package/clis/slock/whoami.test.js +23 -0
  1253. package/clis/smzdm/search.js +149 -0
  1254. package/clis/smzdm/search.test.js +114 -0
  1255. package/clis/spotify/spotify.js +327 -0
  1256. package/clis/spotify/utils.js +66 -0
  1257. package/clis/spotify/utils.test.js +67 -0
  1258. package/clis/stackoverflow/bounties.js +35 -0
  1259. package/clis/stackoverflow/hot.js +32 -0
  1260. package/clis/stackoverflow/read.js +314 -0
  1261. package/clis/stackoverflow/related.js +66 -0
  1262. package/clis/stackoverflow/search.js +35 -0
  1263. package/clis/stackoverflow/stackoverflow.test.js +404 -0
  1264. package/clis/stackoverflow/tag.js +60 -0
  1265. package/clis/stackoverflow/unanswered.js +33 -0
  1266. package/clis/stackoverflow/user.js +50 -0
  1267. package/clis/stackoverflow/utils.js +118 -0
  1268. package/clis/steam/app.js +67 -0
  1269. package/clis/steam/search.js +58 -0
  1270. package/clis/steam/steam.test.js +46 -0
  1271. package/clis/steam/top-sellers.js +26 -0
  1272. package/clis/steam/utils.js +107 -0
  1273. package/clis/substack/feed.js +16 -0
  1274. package/clis/substack/publication.js +16 -0
  1275. package/clis/substack/search.js +79 -0
  1276. package/clis/substack/utils.js +136 -0
  1277. package/clis/substack/utils.test.js +44 -0
  1278. package/clis/suno/auth.js +63 -0
  1279. package/clis/suno/commands.test.js +188 -0
  1280. package/clis/suno/download.js +140 -0
  1281. package/clis/suno/download.test.js +151 -0
  1282. package/clis/suno/generate.js +231 -0
  1283. package/clis/suno/generate.test.js +252 -0
  1284. package/clis/suno/list.js +79 -0
  1285. package/clis/suno/status.js +63 -0
  1286. package/clis/suno/utils.js +549 -0
  1287. package/clis/suno/utils.test.js +329 -0
  1288. package/clis/taobao/add-cart.js +150 -0
  1289. package/clis/taobao/auth.js +58 -0
  1290. package/clis/taobao/cart.js +96 -0
  1291. package/clis/taobao/commands.test.js +41 -0
  1292. package/clis/taobao/detail.js +71 -0
  1293. package/clis/taobao/reviews.js +77 -0
  1294. package/clis/taobao/search.js +97 -0
  1295. package/clis/tdx/hot-rank.js +48 -0
  1296. package/clis/tdx/hot-rank.test.js +59 -0
  1297. package/clis/test-utils.js +61 -0
  1298. package/clis/ths/hot-rank.js +50 -0
  1299. package/clis/ths/hot-rank.test.js +64 -0
  1300. package/clis/tieba/commands.test.js +79 -0
  1301. package/clis/tieba/hot.js +48 -0
  1302. package/clis/tieba/posts.js +86 -0
  1303. package/clis/tieba/read.js +141 -0
  1304. package/clis/tieba/search.js +109 -0
  1305. package/clis/tieba/utils.js +240 -0
  1306. package/clis/tieba/utils.test.js +290 -0
  1307. package/clis/tiktok/auth.js +65 -0
  1308. package/clis/tiktok/comment.js +145 -0
  1309. package/clis/tiktok/creator-videos.js +270 -0
  1310. package/clis/tiktok/creator-videos.test.js +113 -0
  1311. package/clis/tiktok/explore.js +144 -0
  1312. package/clis/tiktok/follow.js +122 -0
  1313. package/clis/tiktok/following.js +164 -0
  1314. package/clis/tiktok/friends.js +146 -0
  1315. package/clis/tiktok/like.js +34 -0
  1316. package/clis/tiktok/live.js +144 -0
  1317. package/clis/tiktok/notifications.js +153 -0
  1318. package/clis/tiktok/profile.js +55 -0
  1319. package/clis/tiktok/refactor.test.js +389 -0
  1320. package/clis/tiktok/save.js +30 -0
  1321. package/clis/tiktok/search.js +40 -0
  1322. package/clis/tiktok/unfollow.js +131 -0
  1323. package/clis/tiktok/unlike.js +34 -0
  1324. package/clis/tiktok/unsave.js +32 -0
  1325. package/clis/tiktok/user.js +216 -0
  1326. package/clis/tiktok/utils.js +505 -0
  1327. package/clis/tiktok/write-refactor.test.js +370 -0
  1328. package/clis/toutiao/articles.js +55 -0
  1329. package/clis/toutiao/auth.js +70 -0
  1330. package/clis/toutiao/hot.js +63 -0
  1331. package/clis/toutiao/toutiao.test.js +378 -0
  1332. package/clis/toutiao/utils.js +161 -0
  1333. package/clis/trae-cn/activity.js +22 -0
  1334. package/clis/trae-cn/approve.js +52 -0
  1335. package/clis/trae-cn/ask.js +99 -0
  1336. package/clis/trae-cn/dump.js +5 -0
  1337. package/clis/trae-cn/export.js +40 -0
  1338. package/clis/trae-cn/model.js +22 -0
  1339. package/clis/trae-cn/new.js +63 -0
  1340. package/clis/trae-cn/read.js +23 -0
  1341. package/clis/trae-cn/screenshot.js +5 -0
  1342. package/clis/trae-cn/select-model.js +25 -0
  1343. package/clis/trae-cn/send.js +23 -0
  1344. package/clis/trae-cn/setup.js +75 -0
  1345. package/clis/trae-cn/status.js +27 -0
  1346. package/clis/trae-cn/targets.js +96 -0
  1347. package/clis/trae-cn/trae-cn.test.js +412 -0
  1348. package/clis/trae-cn/utils.js +750 -0
  1349. package/clis/trae-cn/watch.js +119 -0
  1350. package/clis/trae-solo/_actions.js +60 -0
  1351. package/clis/trae-solo/_fs.js +118 -0
  1352. package/clis/trae-solo/_state.js +111 -0
  1353. package/clis/trae-solo/automation.js +97 -0
  1354. package/clis/trae-solo/history.js +54 -0
  1355. package/clis/trae-solo/mode.js +81 -0
  1356. package/clis/trae-solo/model.js +134 -0
  1357. package/clis/trae-solo/renderer-storage.js +170 -0
  1358. package/clis/trae-solo/settings.js +54 -0
  1359. package/clis/trae-solo/skill-fs.js +123 -0
  1360. package/clis/trae-solo/skill.js +220 -0
  1361. package/clis/trae-solo/state-fs.js +156 -0
  1362. package/clis/trae-solo/status.js +2 -0
  1363. package/clis/trae-solo/task-fs.js +181 -0
  1364. package/clis/trae-solo/trae-solo.test.js +67 -0
  1365. package/clis/trae-solo/user-rules.js +29 -0
  1366. package/clis/trae-solo/workspaces-fs.js +113 -0
  1367. package/clis/tvmaze/search.js +61 -0
  1368. package/clis/tvmaze/show.js +60 -0
  1369. package/clis/tvmaze/tvmaze.test.js +93 -0
  1370. package/clis/tvmaze/utils.js +110 -0
  1371. package/clis/twitter/accept.js +204 -0
  1372. package/clis/twitter/article.js +170 -0
  1373. package/clis/twitter/auth.js +41 -0
  1374. package/clis/twitter/block.js +90 -0
  1375. package/clis/twitter/bookmark-folder.js +190 -0
  1376. package/clis/twitter/bookmark-folder.test.js +396 -0
  1377. package/clis/twitter/bookmark-folders.js +114 -0
  1378. package/clis/twitter/bookmark-folders.test.js +152 -0
  1379. package/clis/twitter/bookmark.js +74 -0
  1380. package/clis/twitter/bookmark.test.js +74 -0
  1381. package/clis/twitter/bookmarks.js +181 -0
  1382. package/clis/twitter/bookmarks.test.js +206 -0
  1383. package/clis/twitter/delete.js +85 -0
  1384. package/clis/twitter/delete.test.js +85 -0
  1385. package/clis/twitter/device-follow.js +194 -0
  1386. package/clis/twitter/device-follow.test.js +287 -0
  1387. package/clis/twitter/download.js +481 -0
  1388. package/clis/twitter/download.test.js +457 -0
  1389. package/clis/twitter/follow-batch.js +162 -0
  1390. package/clis/twitter/follow-batch.test.js +137 -0
  1391. package/clis/twitter/follow.js +67 -0
  1392. package/clis/twitter/followers.js +193 -0
  1393. package/clis/twitter/followers.test.js +62 -0
  1394. package/clis/twitter/following.js +274 -0
  1395. package/clis/twitter/following.test.js +359 -0
  1396. package/clis/twitter/hide-reply.js +87 -0
  1397. package/clis/twitter/hide-reply.test.js +76 -0
  1398. package/clis/twitter/like.js +80 -0
  1399. package/clis/twitter/like.test.js +73 -0
  1400. package/clis/twitter/likes.js +241 -0
  1401. package/clis/twitter/likes.test.js +220 -0
  1402. package/clis/twitter/list-add-batch.js +32 -0
  1403. package/clis/twitter/list-add-core.js +245 -0
  1404. package/clis/twitter/list-add.js +20 -0
  1405. package/clis/twitter/list-add.test.js +133 -0
  1406. package/clis/twitter/list-batch-utils.js +95 -0
  1407. package/clis/twitter/list-batch.test.js +113 -0
  1408. package/clis/twitter/list-create.js +155 -0
  1409. package/clis/twitter/list-create.test.js +169 -0
  1410. package/clis/twitter/list-delete.js +158 -0
  1411. package/clis/twitter/list-delete.test.js +102 -0
  1412. package/clis/twitter/list-remove-batch.js +32 -0
  1413. package/clis/twitter/list-remove-core.js +291 -0
  1414. package/clis/twitter/list-remove.js +20 -0
  1415. package/clis/twitter/list-remove.test.js +147 -0
  1416. package/clis/twitter/list-tweets.js +199 -0
  1417. package/clis/twitter/list-tweets.test.js +198 -0
  1418. package/clis/twitter/lists.js +178 -0
  1419. package/clis/twitter/lists.test.js +253 -0
  1420. package/clis/twitter/notifications.js +128 -0
  1421. package/clis/twitter/post.js +316 -0
  1422. package/clis/twitter/post.test.js +275 -0
  1423. package/clis/twitter/profile.js +174 -0
  1424. package/clis/twitter/profile.test.js +150 -0
  1425. package/clis/twitter/quote.js +172 -0
  1426. package/clis/twitter/quote.test.js +194 -0
  1427. package/clis/twitter/reply-dm.js +183 -0
  1428. package/clis/twitter/reply.js +248 -0
  1429. package/clis/twitter/reply.test.js +237 -0
  1430. package/clis/twitter/retweet.js +94 -0
  1431. package/clis/twitter/retweet.test.js +73 -0
  1432. package/clis/twitter/search.js +346 -0
  1433. package/clis/twitter/search.test.js +400 -0
  1434. package/clis/twitter/shared.js +564 -0
  1435. package/clis/twitter/shared.test.js +1012 -0
  1436. package/clis/twitter/thread.js +164 -0
  1437. package/clis/twitter/thread.test.js +58 -0
  1438. package/clis/twitter/timeline.js +211 -0
  1439. package/clis/twitter/timeline.test.js +104 -0
  1440. package/clis/twitter/trending.js +58 -0
  1441. package/clis/twitter/trending.test.js +15 -0
  1442. package/clis/twitter/tweets.js +314 -0
  1443. package/clis/twitter/tweets.test.js +362 -0
  1444. package/clis/twitter/unblock.js +73 -0
  1445. package/clis/twitter/unbookmark.js +71 -0
  1446. package/clis/twitter/unbookmark.test.js +73 -0
  1447. package/clis/twitter/unfollow.js +73 -0
  1448. package/clis/twitter/unlike.js +80 -0
  1449. package/clis/twitter/unlike.test.js +75 -0
  1450. package/clis/twitter/unretweet.js +94 -0
  1451. package/clis/twitter/unretweet.test.js +73 -0
  1452. package/clis/twitter/utils.js +323 -0
  1453. package/clis/twitter/utils.test.js +169 -0
  1454. package/clis/uisdc/news.js +105 -0
  1455. package/clis/uisdc/news.test.js +66 -0
  1456. package/clis/uiverse/_shared.js +427 -0
  1457. package/clis/uiverse/_shared.test.js +62 -0
  1458. package/clis/uiverse/code.js +49 -0
  1459. package/clis/uiverse/navigation.test.js +12 -0
  1460. package/clis/uiverse/preview.js +73 -0
  1461. package/clis/upwork/auth.js +53 -0
  1462. package/clis/upwork/detail.js +132 -0
  1463. package/clis/upwork/feed.js +109 -0
  1464. package/clis/upwork/search.js +115 -0
  1465. package/clis/upwork/upwork.test.js +566 -0
  1466. package/clis/upwork/utils.js +323 -0
  1467. package/clis/v2ex/auth.js +44 -0
  1468. package/clis/v2ex/daily.js +99 -0
  1469. package/clis/v2ex/hot.js +26 -0
  1470. package/clis/v2ex/latest.js +26 -0
  1471. package/clis/v2ex/me.js +100 -0
  1472. package/clis/v2ex/member.js +28 -0
  1473. package/clis/v2ex/node.js +39 -0
  1474. package/clis/v2ex/nodes.js +26 -0
  1475. package/clis/v2ex/notifications.js +72 -0
  1476. package/clis/v2ex/replies.js +27 -0
  1477. package/clis/v2ex/topic.js +31 -0
  1478. package/clis/v2ex/user.js +34 -0
  1479. package/clis/wanfang/search.js +66 -0
  1480. package/clis/wanfang/search.test.js +23 -0
  1481. package/clis/web/read.js +491 -0
  1482. package/clis/web/read.test.js +392 -0
  1483. package/clis/wechat-channels/auth.js +62 -0
  1484. package/clis/wechat-channels/publish.js +732 -0
  1485. package/clis/wechat-channels/publish.test.js +144 -0
  1486. package/clis/weibo/auth.js +53 -0
  1487. package/clis/weibo/comments.js +53 -0
  1488. package/clis/weibo/delete.js +172 -0
  1489. package/clis/weibo/delete.test.js +94 -0
  1490. package/clis/weibo/envelope.test.js +85 -0
  1491. package/clis/weibo/favorites.js +170 -0
  1492. package/clis/weibo/favorites.test.js +114 -0
  1493. package/clis/weibo/feed.js +69 -0
  1494. package/clis/weibo/hot.js +39 -0
  1495. package/clis/weibo/me.js +75 -0
  1496. package/clis/weibo/post.js +75 -0
  1497. package/clis/weibo/publish.js +306 -0
  1498. package/clis/weibo/publish.test.js +192 -0
  1499. package/clis/weibo/search.js +80 -0
  1500. package/clis/weibo/user-posts.js +234 -0
  1501. package/clis/weibo/user-posts.test.js +92 -0
  1502. package/clis/weibo/user.js +63 -0
  1503. package/clis/weibo/utils.js +59 -0
  1504. package/clis/weibo/utils.test.js +36 -0
  1505. package/clis/weixin/create-draft.js +226 -0
  1506. package/clis/weixin/download.js +371 -0
  1507. package/clis/weixin/download.test.js +106 -0
  1508. package/clis/weixin/drafts.js +66 -0
  1509. package/clis/weixin/drafts.test.js +69 -0
  1510. package/clis/weixin/search.js +157 -0
  1511. package/clis/weixin/search.test.js +227 -0
  1512. package/clis/weread/ai-outline.js +171 -0
  1513. package/clis/weread/ai-outline.test.js +83 -0
  1514. package/clis/weread/auth.js +59 -0
  1515. package/clis/weread/book-search.js +438 -0
  1516. package/clis/weread/book-search.test.js +242 -0
  1517. package/clis/weread/book.js +233 -0
  1518. package/clis/weread/commands.test.js +398 -0
  1519. package/clis/weread/highlights.js +24 -0
  1520. package/clis/weread/notebooks.js +22 -0
  1521. package/clis/weread/notes.js +30 -0
  1522. package/clis/weread/private-api-regression.test.js +369 -0
  1523. package/clis/weread/ranking.js +29 -0
  1524. package/clis/weread/search-regression.test.js +494 -0
  1525. package/clis/weread/search.js +186 -0
  1526. package/clis/weread/shelf.js +66 -0
  1527. package/clis/weread/utils.js +321 -0
  1528. package/clis/weread/utils.test.js +128 -0
  1529. package/clis/weread-official/book.js +135 -0
  1530. package/clis/weread-official/commands.test.js +385 -0
  1531. package/clis/weread-official/discover.js +107 -0
  1532. package/clis/weread-official/list-apis.js +95 -0
  1533. package/clis/weread-official/notes.js +171 -0
  1534. package/clis/weread-official/readdata.js +158 -0
  1535. package/clis/weread-official/review.js +93 -0
  1536. package/clis/weread-official/search.js +106 -0
  1537. package/clis/weread-official/shelf.js +97 -0
  1538. package/clis/weread-official/utils.js +293 -0
  1539. package/clis/weread-official/utils.test.js +242 -0
  1540. package/clis/wikidata/entity.js +60 -0
  1541. package/clis/wikidata/search.js +50 -0
  1542. package/clis/wikidata/utils.js +117 -0
  1543. package/clis/wikidata/wikidata.test.js +83 -0
  1544. package/clis/wikipedia/page.js +95 -0
  1545. package/clis/wikipedia/random.js +20 -0
  1546. package/clis/wikipedia/search.js +31 -0
  1547. package/clis/wikipedia/summary.js +24 -0
  1548. package/clis/wikipedia/trending.js +40 -0
  1549. package/clis/wikipedia/trending.test.js +57 -0
  1550. package/clis/wikipedia/utils.js +31 -0
  1551. package/clis/wttr/current.js +63 -0
  1552. package/clis/wttr/forecast.js +71 -0
  1553. package/clis/wttr/utils.js +50 -0
  1554. package/clis/wttr/wttr.test.js +84 -0
  1555. package/clis/xianyu/auth.js +68 -0
  1556. package/clis/xianyu/chat.js +74 -0
  1557. package/clis/xianyu/chat.test.js +84 -0
  1558. package/clis/xianyu/im.js +322 -0
  1559. package/clis/xianyu/im.test.js +253 -0
  1560. package/clis/xianyu/inbox.js +96 -0
  1561. package/clis/xianyu/item.js +153 -0
  1562. package/clis/xianyu/item.test.js +56 -0
  1563. package/clis/xianyu/messages.js +91 -0
  1564. package/clis/xianyu/publish.js +485 -0
  1565. package/clis/xianyu/publish.test.js +220 -0
  1566. package/clis/xianyu/reply.js +82 -0
  1567. package/clis/xianyu/search.js +135 -0
  1568. package/clis/xianyu/search.test.js +17 -0
  1569. package/clis/xianyu/utils.js +8 -0
  1570. package/clis/xiaoe/auth.js +55 -0
  1571. package/clis/xiaoe/catalog.js +227 -0
  1572. package/clis/xiaoe/content.js +175 -0
  1573. package/clis/xiaoe/courses.js +127 -0
  1574. package/clis/xiaoe/detail.js +36 -0
  1575. package/clis/xiaoe/play-url.js +121 -0
  1576. package/clis/xiaoe/xiaoe.test.js +486 -0
  1577. package/clis/xiaohongshu/ask.js +426 -0
  1578. package/clis/xiaohongshu/ask.test.js +271 -0
  1579. package/clis/xiaohongshu/auth.js +53 -0
  1580. package/clis/xiaohongshu/collection-helpers.js +283 -0
  1581. package/clis/xiaohongshu/collection-helpers.test.js +125 -0
  1582. package/clis/xiaohongshu/comments.js +228 -0
  1583. package/clis/xiaohongshu/comments.test.js +320 -0
  1584. package/clis/xiaohongshu/creator-note-detail.js +482 -0
  1585. package/clis/xiaohongshu/creator-note-detail.test.js +451 -0
  1586. package/clis/xiaohongshu/creator-notes-summary.js +98 -0
  1587. package/clis/xiaohongshu/creator-notes-summary.test.js +94 -0
  1588. package/clis/xiaohongshu/creator-notes.js +479 -0
  1589. package/clis/xiaohongshu/creator-notes.test.js +281 -0
  1590. package/clis/xiaohongshu/creator-profile.js +55 -0
  1591. package/clis/xiaohongshu/creator-stats.js +76 -0
  1592. package/clis/xiaohongshu/creator-stats.test.js +24 -0
  1593. package/clis/xiaohongshu/delete-note.js +260 -0
  1594. package/clis/xiaohongshu/delete-note.test.js +172 -0
  1595. package/clis/xiaohongshu/download.js +249 -0
  1596. package/clis/xiaohongshu/download.test.js +339 -0
  1597. package/clis/xiaohongshu/draft-clear.js +91 -0
  1598. package/clis/xiaohongshu/draft-delete.js +103 -0
  1599. package/clis/xiaohongshu/draft-open.js +43 -0
  1600. package/clis/xiaohongshu/draft-utils.js +142 -0
  1601. package/clis/xiaohongshu/drafts.js +28 -0
  1602. package/clis/xiaohongshu/drafts.test.js +172 -0
  1603. package/clis/xiaohongshu/feed.js +163 -0
  1604. package/clis/xiaohongshu/feed.test.js +126 -0
  1605. package/clis/xiaohongshu/follow.js +226 -0
  1606. package/clis/xiaohongshu/follow.test.js +211 -0
  1607. package/clis/xiaohongshu/liked.js +29 -0
  1608. package/clis/xiaohongshu/liked.test.js +82 -0
  1609. package/clis/xiaohongshu/navigation.test.js +38 -0
  1610. package/clis/xiaohongshu/note-helpers.js +69 -0
  1611. package/clis/xiaohongshu/note.js +104 -0
  1612. package/clis/xiaohongshu/note.test.js +249 -0
  1613. package/clis/xiaohongshu/notifications.js +46 -0
  1614. package/clis/xiaohongshu/publish.js +1423 -0
  1615. package/clis/xiaohongshu/publish.test.js +1141 -0
  1616. package/clis/xiaohongshu/saved.js +29 -0
  1617. package/clis/xiaohongshu/saved.test.js +143 -0
  1618. package/clis/xiaohongshu/search.js +330 -0
  1619. package/clis/xiaohongshu/search.test.js +325 -0
  1620. package/clis/xiaohongshu/unfollow.js +285 -0
  1621. package/clis/xiaohongshu/user-helpers.js +78 -0
  1622. package/clis/xiaohongshu/user-helpers.test.js +160 -0
  1623. package/clis/xiaohongshu/user.js +140 -0
  1624. package/clis/xiaohongshu/user.test.js +141 -0
  1625. package/clis/xiaoyuzhou/auth.js +303 -0
  1626. package/clis/xiaoyuzhou/auth.test.js +124 -0
  1627. package/clis/xiaoyuzhou/download.js +54 -0
  1628. package/clis/xiaoyuzhou/download.test.js +135 -0
  1629. package/clis/xiaoyuzhou/episode.js +34 -0
  1630. package/clis/xiaoyuzhou/podcast-episodes.js +41 -0
  1631. package/clis/xiaoyuzhou/podcast-episodes.test.js +78 -0
  1632. package/clis/xiaoyuzhou/podcast.js +33 -0
  1633. package/clis/xiaoyuzhou/transcript.js +77 -0
  1634. package/clis/xiaoyuzhou/transcript.test.js +195 -0
  1635. package/clis/xiaoyuzhou/utils.js +15 -0
  1636. package/clis/xiaoyuzhou/utils.test.js +39 -0
  1637. package/clis/xueqiu/auth.js +63 -0
  1638. package/clis/xueqiu/comments.js +355 -0
  1639. package/clis/xueqiu/comments.test.js +696 -0
  1640. package/clis/xueqiu/danjuan-utils.js +126 -0
  1641. package/clis/xueqiu/danjuan-utils.test.js +41 -0
  1642. package/clis/xueqiu/earnings-date.js +45 -0
  1643. package/clis/xueqiu/feed.js +39 -0
  1644. package/clis/xueqiu/fund-holdings.js +29 -0
  1645. package/clis/xueqiu/fund-snapshot.js +26 -0
  1646. package/clis/xueqiu/groups.js +23 -0
  1647. package/clis/xueqiu/hot-stock.js +32 -0
  1648. package/clis/xueqiu/hot.js +37 -0
  1649. package/clis/xueqiu/kline.js +44 -0
  1650. package/clis/xueqiu/search.js +37 -0
  1651. package/clis/xueqiu/stock.js +65 -0
  1652. package/clis/xueqiu/utils.js +48 -0
  1653. package/clis/xueqiu/utils.test.js +26 -0
  1654. package/clis/xueqiu/watchlist.js +37 -0
  1655. package/clis/yahoo/search.js +92 -0
  1656. package/clis/yahoo/search.test.js +94 -0
  1657. package/clis/yahoo-finance/quote.js +74 -0
  1658. package/clis/yollomi/background.js +46 -0
  1659. package/clis/yollomi/edit.js +57 -0
  1660. package/clis/yollomi/face-swap.js +44 -0
  1661. package/clis/yollomi/generate.js +101 -0
  1662. package/clis/yollomi/models.js +34 -0
  1663. package/clis/yollomi/object-remover.js +43 -0
  1664. package/clis/yollomi/remove-bg.js +39 -0
  1665. package/clis/yollomi/restore.js +39 -0
  1666. package/clis/yollomi/try-on.js +47 -0
  1667. package/clis/yollomi/upload.js +72 -0
  1668. package/clis/yollomi/upscale.js +54 -0
  1669. package/clis/yollomi/utils.js +180 -0
  1670. package/clis/yollomi/video.js +57 -0
  1671. package/clis/youdao/note.js +258 -0
  1672. package/clis/youdao/note.test.js +99 -0
  1673. package/clis/youtube/auth.js +54 -0
  1674. package/clis/youtube/channel.js +227 -0
  1675. package/clis/youtube/channel.test.js +283 -0
  1676. package/clis/youtube/comments.js +96 -0
  1677. package/clis/youtube/feed.js +121 -0
  1678. package/clis/youtube/feed.test.js +131 -0
  1679. package/clis/youtube/history.js +119 -0
  1680. package/clis/youtube/like.js +67 -0
  1681. package/clis/youtube/playlist.js +98 -0
  1682. package/clis/youtube/search.js +100 -0
  1683. package/clis/youtube/subscribe.js +76 -0
  1684. package/clis/youtube/subscriptions.js +58 -0
  1685. package/clis/youtube/transcript-group.js +226 -0
  1686. package/clis/youtube/transcript-group.test.js +99 -0
  1687. package/clis/youtube/transcript.js +656 -0
  1688. package/clis/youtube/transcript.test.js +296 -0
  1689. package/clis/youtube/unlike.js +67 -0
  1690. package/clis/youtube/unsubscribe.js +76 -0
  1691. package/clis/youtube/utils.js +220 -0
  1692. package/clis/youtube/utils.test.js +84 -0
  1693. package/clis/youtube/video.js +152 -0
  1694. package/clis/youtube/video.test.js +111 -0
  1695. package/clis/youtube/watch-later.js +77 -0
  1696. package/clis/yuanbao/ask.js +362 -0
  1697. package/clis/yuanbao/ask.test.js +124 -0
  1698. package/clis/yuanbao/auth.js +59 -0
  1699. package/clis/yuanbao/detail.js +65 -0
  1700. package/clis/yuanbao/history.js +51 -0
  1701. package/clis/yuanbao/new.js +72 -0
  1702. package/clis/yuanbao/new.test.js +30 -0
  1703. package/clis/yuanbao/read.js +38 -0
  1704. package/clis/yuanbao/send.js +57 -0
  1705. package/clis/yuanbao/shared.js +341 -0
  1706. package/clis/yuanbao/shared.test.js +80 -0
  1707. package/clis/yuanbao/status.js +44 -0
  1708. package/clis/zhihu/answer-comments.js +280 -0
  1709. package/clis/zhihu/answer-comments.test.js +287 -0
  1710. package/clis/zhihu/answer-detail.js +216 -0
  1711. package/clis/zhihu/answer-detail.test.js +338 -0
  1712. package/clis/zhihu/answer.js +54 -0
  1713. package/clis/zhihu/answer.test.js +54 -0
  1714. package/clis/zhihu/auth.js +59 -0
  1715. package/clis/zhihu/collection.js +199 -0
  1716. package/clis/zhihu/collection.test.js +337 -0
  1717. package/clis/zhihu/collections.js +128 -0
  1718. package/clis/zhihu/collections.test.js +182 -0
  1719. package/clis/zhihu/comment.js +55 -0
  1720. package/clis/zhihu/comment.test.js +50 -0
  1721. package/clis/zhihu/download.js +80 -0
  1722. package/clis/zhihu/download.test.js +12 -0
  1723. package/clis/zhihu/favorite.js +87 -0
  1724. package/clis/zhihu/favorite.test.js +59 -0
  1725. package/clis/zhihu/follow.js +50 -0
  1726. package/clis/zhihu/follow.test.js +42 -0
  1727. package/clis/zhihu/followers.js +40 -0
  1728. package/clis/zhihu/followers.test.js +42 -0
  1729. package/clis/zhihu/following.js +40 -0
  1730. package/clis/zhihu/following.test.js +44 -0
  1731. package/clis/zhihu/hot.js +44 -0
  1732. package/clis/zhihu/like.js +47 -0
  1733. package/clis/zhihu/like.test.js +41 -0
  1734. package/clis/zhihu/paginate.js +128 -0
  1735. package/clis/zhihu/pins.js +41 -0
  1736. package/clis/zhihu/pins.test.js +44 -0
  1737. package/clis/zhihu/question.js +113 -0
  1738. package/clis/zhihu/question.test.js +251 -0
  1739. package/clis/zhihu/recommend.js +103 -0
  1740. package/clis/zhihu/recommend.test.js +143 -0
  1741. package/clis/zhihu/search.js +206 -0
  1742. package/clis/zhihu/search.test.js +198 -0
  1743. package/clis/zhihu/target.js +91 -0
  1744. package/clis/zhihu/target.test.js +77 -0
  1745. package/clis/zhihu/text.js +29 -0
  1746. package/clis/zhihu/text.test.js +24 -0
  1747. package/clis/zhihu/user-answers.js +41 -0
  1748. package/clis/zhihu/user-answers.test.js +69 -0
  1749. package/clis/zhihu/user-arg.js +34 -0
  1750. package/clis/zhihu/user-articles.js +40 -0
  1751. package/clis/zhihu/user-articles.test.js +44 -0
  1752. package/clis/zhihu/user.js +59 -0
  1753. package/clis/zhihu/user.test.js +73 -0
  1754. package/clis/zhihu/write-shared.js +228 -0
  1755. package/clis/zhihu/write-shared.test.js +176 -0
  1756. package/clis/zlibrary/commands.test.js +65 -0
  1757. package/clis/zlibrary/info.js +48 -0
  1758. package/clis/zlibrary/search.js +47 -0
  1759. package/clis/zlibrary/utils.js +136 -0
  1760. package/clis/zsxq/auth.js +68 -0
  1761. package/clis/zsxq/dynamics.js +48 -0
  1762. package/clis/zsxq/groups.js +33 -0
  1763. package/clis/zsxq/search.js +44 -0
  1764. package/clis/zsxq/search.test.js +24 -0
  1765. package/clis/zsxq/topic.js +50 -0
  1766. package/clis/zsxq/topic.test.js +30 -0
  1767. package/clis/zsxq/topics.js +26 -0
  1768. package/clis/zsxq/topics.test.js +24 -0
  1769. package/clis/zsxq/utils.js +233 -0
  1770. package/clis/zsxq/utils.test.js +31 -0
  1771. package/dist/src/adapter-shadow.d.ts +11 -0
  1772. package/dist/src/adapter-shadow.js +72 -0
  1773. package/dist/src/adapter-shadow.test.d.ts +1 -0
  1774. package/dist/src/adapter-shadow.test.js +49 -0
  1775. package/dist/src/adapter-source.d.ts +11 -0
  1776. package/dist/src/adapter-source.js +24 -0
  1777. package/dist/src/adapter-source.test.d.ts +1 -0
  1778. package/dist/src/adapter-source.test.js +29 -0
  1779. package/dist/src/browser/analyze.d.ts +118 -0
  1780. package/dist/src/browser/analyze.js +405 -0
  1781. package/dist/src/browser/analyze.test.d.ts +1 -0
  1782. package/dist/src/browser/analyze.test.js +209 -0
  1783. package/dist/src/browser/article-extract.d.ts +57 -0
  1784. package/dist/src/browser/article-extract.e2e.test.d.ts +1 -0
  1785. package/dist/src/browser/article-extract.e2e.test.js +105 -0
  1786. package/dist/src/browser/article-extract.js +169 -0
  1787. package/dist/src/browser/article-extract.test.d.ts +1 -0
  1788. package/dist/src/browser/article-extract.test.js +94 -0
  1789. package/dist/src/browser/ax-snapshot.d.ts +37 -0
  1790. package/dist/src/browser/ax-snapshot.js +217 -0
  1791. package/dist/src/browser/ax-snapshot.test.d.ts +1 -0
  1792. package/dist/src/browser/ax-snapshot.test.js +91 -0
  1793. package/dist/src/browser/base-page.d.ts +151 -0
  1794. package/dist/src/browser/base-page.js +1060 -0
  1795. package/dist/src/browser/base-page.test.d.ts +1 -0
  1796. package/dist/src/browser/base-page.test.js +803 -0
  1797. package/dist/src/browser/bridge.d.ts +28 -0
  1798. package/dist/src/browser/bridge.js +172 -0
  1799. package/dist/src/browser/cdp-click-fixture.test.d.ts +1 -0
  1800. package/dist/src/browser/cdp-click-fixture.test.js +87 -0
  1801. package/dist/src/browser/cdp.d.ts +46 -0
  1802. package/dist/src/browser/cdp.js +536 -0
  1803. package/dist/src/browser/cdp.test.d.ts +1 -0
  1804. package/dist/src/browser/cdp.test.js +79 -0
  1805. package/dist/src/browser/compound.d.ts +59 -0
  1806. package/dist/src/browser/compound.js +112 -0
  1807. package/dist/src/browser/compound.test.d.ts +1 -0
  1808. package/dist/src/browser/compound.test.js +175 -0
  1809. package/dist/src/browser/daemon-client.d.ts +133 -0
  1810. package/dist/src/browser/daemon-client.js +154 -0
  1811. package/dist/src/browser/daemon-client.test.d.ts +1 -0
  1812. package/dist/src/browser/daemon-client.test.js +199 -0
  1813. package/dist/src/browser/daemon-lifecycle.d.ts +23 -0
  1814. package/dist/src/browser/daemon-lifecycle.js +67 -0
  1815. package/dist/src/browser/daemon-version.d.ts +4 -0
  1816. package/dist/src/browser/daemon-version.js +12 -0
  1817. package/dist/src/browser/dom-helpers.d.ts +41 -0
  1818. package/dist/src/browser/dom-helpers.js +239 -0
  1819. package/dist/src/browser/dom-helpers.test.d.ts +1 -0
  1820. package/dist/src/browser/dom-helpers.test.js +106 -0
  1821. package/dist/src/browser/dom-snapshot.d.ts +93 -0
  1822. package/dist/src/browser/dom-snapshot.js +894 -0
  1823. package/dist/src/browser/dom-snapshot.test.d.ts +11 -0
  1824. package/dist/src/browser/dom-snapshot.test.js +313 -0
  1825. package/dist/src/browser/errors.d.ts +38 -0
  1826. package/dist/src/browser/errors.js +78 -0
  1827. package/dist/src/browser/errors.test.d.ts +1 -0
  1828. package/dist/src/browser/errors.test.js +60 -0
  1829. package/dist/src/browser/extract.d.ts +69 -0
  1830. package/dist/src/browser/extract.js +132 -0
  1831. package/dist/src/browser/extract.test.d.ts +1 -0
  1832. package/dist/src/browser/extract.test.js +129 -0
  1833. package/dist/src/browser/find.d.ts +84 -0
  1834. package/dist/src/browser/find.js +398 -0
  1835. package/dist/src/browser/find.test.d.ts +1 -0
  1836. package/dist/src/browser/find.test.js +180 -0
  1837. package/dist/src/browser/html-tree.d.ts +75 -0
  1838. package/dist/src/browser/html-tree.js +112 -0
  1839. package/dist/src/browser/html-tree.test.d.ts +1 -0
  1840. package/dist/src/browser/html-tree.test.js +181 -0
  1841. package/dist/src/browser/index.d.ts +14 -0
  1842. package/dist/src/browser/index.js +12 -0
  1843. package/dist/src/browser/network-cache.d.ts +49 -0
  1844. package/dist/src/browser/network-cache.js +78 -0
  1845. package/dist/src/browser/network-cache.test.d.ts +1 -0
  1846. package/dist/src/browser/network-cache.test.js +75 -0
  1847. package/dist/src/browser/network-key.d.ts +22 -0
  1848. package/dist/src/browser/network-key.js +66 -0
  1849. package/dist/src/browser/network-key.test.d.ts +1 -0
  1850. package/dist/src/browser/network-key.test.js +49 -0
  1851. package/dist/src/browser/page.d.ts +83 -0
  1852. package/dist/src/browser/page.js +453 -0
  1853. package/dist/src/browser/page.test.d.ts +1 -0
  1854. package/dist/src/browser/page.test.js +406 -0
  1855. package/dist/src/browser/profile.d.ts +14 -0
  1856. package/dist/src/browser/profile.js +85 -0
  1857. package/dist/src/browser/shape-filter.d.ts +52 -0
  1858. package/dist/src/browser/shape-filter.js +101 -0
  1859. package/dist/src/browser/shape-filter.test.d.ts +1 -0
  1860. package/dist/src/browser/shape-filter.test.js +101 -0
  1861. package/dist/src/browser/shape.d.ts +23 -0
  1862. package/dist/src/browser/shape.js +95 -0
  1863. package/dist/src/browser/shape.test.d.ts +1 -0
  1864. package/dist/src/browser/shape.test.js +82 -0
  1865. package/dist/src/browser/stealth.d.ts +11 -0
  1866. package/dist/src/browser/stealth.js +359 -0
  1867. package/dist/src/browser/stealth.test.d.ts +1 -0
  1868. package/dist/src/browser/stealth.test.js +134 -0
  1869. package/dist/src/browser/tabs.d.ts +13 -0
  1870. package/dist/src/browser/tabs.js +70 -0
  1871. package/dist/src/browser/target-errors.d.ts +39 -0
  1872. package/dist/src/browser/target-errors.js +45 -0
  1873. package/dist/src/browser/target-errors.test.d.ts +1 -0
  1874. package/dist/src/browser/target-errors.test.js +94 -0
  1875. package/dist/src/browser/target-resolver.d.ts +158 -0
  1876. package/dist/src/browser/target-resolver.js +570 -0
  1877. package/dist/src/browser/target-resolver.test.d.ts +1 -0
  1878. package/dist/src/browser/target-resolver.test.js +118 -0
  1879. package/dist/src/browser/utils.d.ts +20 -0
  1880. package/dist/src/browser/utils.js +64 -0
  1881. package/dist/src/browser/utils.test.d.ts +1 -0
  1882. package/dist/src/browser/utils.test.js +29 -0
  1883. package/dist/src/browser/verify-fixture.d.ts +65 -0
  1884. package/dist/src/browser/verify-fixture.js +318 -0
  1885. package/dist/src/browser/verify-fixture.test.d.ts +1 -0
  1886. package/dist/src/browser/verify-fixture.test.js +219 -0
  1887. package/dist/src/browser/visual-refs.d.ts +11 -0
  1888. package/dist/src/browser/visual-refs.js +108 -0
  1889. package/dist/src/browser.test.d.ts +1 -0
  1890. package/dist/src/browser.test.js +287 -0
  1891. package/dist/src/build-manifest.d.ts +103 -0
  1892. package/dist/src/build-manifest.js +363 -0
  1893. package/dist/src/build-manifest.test.d.ts +1 -0
  1894. package/dist/src/build-manifest.test.js +341 -0
  1895. package/dist/src/capabilityRouting.d.ts +19 -0
  1896. package/dist/src/capabilityRouting.js +56 -0
  1897. package/dist/src/capabilityRouting.test.d.ts +1 -0
  1898. package/dist/src/capabilityRouting.test.js +63 -0
  1899. package/dist/src/cli-argv-preprocess.d.ts +63 -0
  1900. package/dist/src/cli-argv-preprocess.js +303 -0
  1901. package/dist/src/cli-argv-preprocess.test.d.ts +1 -0
  1902. package/dist/src/cli-argv-preprocess.test.js +304 -0
  1903. package/dist/src/cli.d.ts +79 -0
  1904. package/dist/src/cli.js +3357 -0
  1905. package/dist/src/cli.test.d.ts +1 -0
  1906. package/dist/src/cli.test.js +3003 -0
  1907. package/dist/src/commanderAdapter.d.ts +21 -0
  1908. package/dist/src/commanderAdapter.js +208 -0
  1909. package/dist/src/commanderAdapter.test.d.ts +1 -0
  1910. package/dist/src/commanderAdapter.test.js +352 -0
  1911. package/dist/src/commands/auth.d.ts +40 -0
  1912. package/dist/src/commands/auth.js +434 -0
  1913. package/dist/src/commands/auth.test.d.ts +1 -0
  1914. package/dist/src/commands/auth.test.js +252 -0
  1915. package/dist/src/commands/daemon.d.ts +9 -0
  1916. package/dist/src/commands/daemon.js +98 -0
  1917. package/dist/src/commands/daemon.test.d.ts +1 -0
  1918. package/dist/src/commands/daemon.test.js +283 -0
  1919. package/dist/src/completion-fast.d.ts +22 -0
  1920. package/dist/src/completion-fast.js +88 -0
  1921. package/dist/src/completion-shared.d.ts +13 -0
  1922. package/dist/src/completion-shared.js +60 -0
  1923. package/dist/src/completion.d.ts +21 -0
  1924. package/dist/src/completion.js +68 -0
  1925. package/dist/src/completion.test.d.ts +1 -0
  1926. package/dist/src/completion.test.js +24 -0
  1927. package/dist/src/constants.d.ts +15 -0
  1928. package/dist/src/constants.js +32 -0
  1929. package/dist/src/convention-audit.d.ts +50 -0
  1930. package/dist/src/convention-audit.js +553 -0
  1931. package/dist/src/convention-audit.test.d.ts +1 -0
  1932. package/dist/src/convention-audit.test.js +247 -0
  1933. package/dist/src/daemon-utils.d.ts +18 -0
  1934. package/dist/src/daemon-utils.js +37 -0
  1935. package/dist/src/daemon.d.ts +22 -0
  1936. package/dist/src/daemon.js +437 -0
  1937. package/dist/src/daemon.test.d.ts +1 -0
  1938. package/dist/src/daemon.test.js +60 -0
  1939. package/dist/src/discovery.d.ts +43 -0
  1940. package/dist/src/discovery.js +266 -0
  1941. package/dist/src/doctor.d.ts +39 -0
  1942. package/dist/src/doctor.js +230 -0
  1943. package/dist/src/doctor.test.d.ts +1 -0
  1944. package/dist/src/doctor.test.js +263 -0
  1945. package/dist/src/download/article-download.d.ts +72 -0
  1946. package/dist/src/download/article-download.js +305 -0
  1947. package/dist/src/download/article-download.test.d.ts +1 -0
  1948. package/dist/src/download/article-download.test.js +235 -0
  1949. package/dist/src/download/index.d.ts +71 -0
  1950. package/dist/src/download/index.js +363 -0
  1951. package/dist/src/download/index.test.d.ts +1 -0
  1952. package/dist/src/download/index.test.js +174 -0
  1953. package/dist/src/download/media-download.d.ts +49 -0
  1954. package/dist/src/download/media-download.js +127 -0
  1955. package/dist/src/download/media-download.test.d.ts +1 -0
  1956. package/dist/src/download/media-download.test.js +112 -0
  1957. package/dist/src/download/progress.d.ts +36 -0
  1958. package/dist/src/download/progress.js +120 -0
  1959. package/dist/src/download/progress.test.d.ts +1 -0
  1960. package/dist/src/download/progress.test.js +36 -0
  1961. package/dist/src/electron-apps.d.ts +31 -0
  1962. package/dist/src/electron-apps.js +91 -0
  1963. package/dist/src/electron-apps.test.d.ts +1 -0
  1964. package/dist/src/electron-apps.test.js +76 -0
  1965. package/dist/src/engine.test.d.ts +1 -0
  1966. package/dist/src/engine.test.js +312 -0
  1967. package/dist/src/errors.d.ts +114 -0
  1968. package/dist/src/errors.js +174 -0
  1969. package/dist/src/errors.test.d.ts +1 -0
  1970. package/dist/src/errors.test.js +109 -0
  1971. package/dist/src/execution.d.ts +24 -0
  1972. package/dist/src/execution.js +509 -0
  1973. package/dist/src/execution.test.d.ts +1 -0
  1974. package/dist/src/execution.test.js +645 -0
  1975. package/dist/src/extension-manifest-regression.test.d.ts +1 -0
  1976. package/dist/src/extension-manifest-regression.test.js +12 -0
  1977. package/dist/src/external-clis.yaml +109 -0
  1978. package/dist/src/external.d.ts +49 -0
  1979. package/dist/src/external.js +211 -0
  1980. package/dist/src/external.test.d.ts +1 -0
  1981. package/dist/src/external.test.js +110 -0
  1982. package/dist/src/help.d.ts +88 -0
  1983. package/dist/src/help.js +555 -0
  1984. package/dist/src/help.test.d.ts +1 -0
  1985. package/dist/src/help.test.js +58 -0
  1986. package/dist/src/hooks.d.ts +46 -0
  1987. package/dist/src/hooks.js +58 -0
  1988. package/dist/src/hooks.test.d.ts +4 -0
  1989. package/dist/src/hooks.test.js +92 -0
  1990. package/dist/src/interceptor.d.ts +44 -0
  1991. package/dist/src/interceptor.js +183 -0
  1992. package/dist/src/interceptor.test.d.ts +4 -0
  1993. package/dist/src/interceptor.test.js +81 -0
  1994. package/dist/src/launcher.d.ts +41 -0
  1995. package/dist/src/launcher.js +226 -0
  1996. package/dist/src/launcher.test.d.ts +1 -0
  1997. package/dist/src/launcher.test.js +153 -0
  1998. package/dist/src/logger.d.ts +26 -0
  1999. package/dist/src/logger.js +49 -0
  2000. package/dist/src/main.d.ts +5 -0
  2001. package/dist/src/main.js +171 -0
  2002. package/dist/src/manifest-types.d.ts +43 -0
  2003. package/dist/src/manifest-types.js +9 -0
  2004. package/dist/src/node-network.d.ts +10 -0
  2005. package/dist/src/node-network.js +174 -0
  2006. package/dist/src/node-network.test.d.ts +1 -0
  2007. package/dist/src/node-network.test.js +55 -0
  2008. package/dist/src/observation/artifact.d.ts +16 -0
  2009. package/dist/src/observation/artifact.js +260 -0
  2010. package/dist/src/observation/artifact.test.d.ts +1 -0
  2011. package/dist/src/observation/artifact.test.js +121 -0
  2012. package/dist/src/observation/events.d.ts +89 -0
  2013. package/dist/src/observation/events.js +1 -0
  2014. package/dist/src/observation/index.d.ts +7 -0
  2015. package/dist/src/observation/index.js +7 -0
  2016. package/dist/src/observation/manager.d.ts +9 -0
  2017. package/dist/src/observation/manager.js +27 -0
  2018. package/dist/src/observation/manager.test.d.ts +1 -0
  2019. package/dist/src/observation/manager.test.js +13 -0
  2020. package/dist/src/observation/redaction.d.ts +11 -0
  2021. package/dist/src/observation/redaction.js +81 -0
  2022. package/dist/src/observation/redaction.test.d.ts +1 -0
  2023. package/dist/src/observation/redaction.test.js +32 -0
  2024. package/dist/src/observation/retention.d.ts +32 -0
  2025. package/dist/src/observation/retention.js +160 -0
  2026. package/dist/src/observation/retention.test.d.ts +1 -0
  2027. package/dist/src/observation/retention.test.js +118 -0
  2028. package/dist/src/observation/ring-buffer.d.ts +22 -0
  2029. package/dist/src/observation/ring-buffer.js +45 -0
  2030. package/dist/src/observation/ring-buffer.test.d.ts +1 -0
  2031. package/dist/src/observation/ring-buffer.test.js +22 -0
  2032. package/dist/src/observation/session.d.ts +25 -0
  2033. package/dist/src/observation/session.js +50 -0
  2034. package/dist/src/output.d.ts +14 -0
  2035. package/dist/src/output.js +152 -0
  2036. package/dist/src/output.test.d.ts +1 -0
  2037. package/dist/src/output.test.js +53 -0
  2038. package/dist/src/package-exports.test.d.ts +1 -0
  2039. package/dist/src/package-exports.test.js +112 -0
  2040. package/dist/src/package-paths.d.ts +8 -0
  2041. package/dist/src/package-paths.js +41 -0
  2042. package/dist/src/pipeline/executor.d.ts +11 -0
  2043. package/dist/src/pipeline/executor.js +93 -0
  2044. package/dist/src/pipeline/executor.test.d.ts +4 -0
  2045. package/dist/src/pipeline/executor.test.js +182 -0
  2046. package/dist/src/pipeline/index.d.ts +5 -0
  2047. package/dist/src/pipeline/index.js +5 -0
  2048. package/dist/src/pipeline/registry.d.ts +28 -0
  2049. package/dist/src/pipeline/registry.js +55 -0
  2050. package/dist/src/pipeline/steps/browser.d.ts +13 -0
  2051. package/dist/src/pipeline/steps/browser.js +86 -0
  2052. package/dist/src/pipeline/steps/download.d.ts +18 -0
  2053. package/dist/src/pipeline/steps/download.js +252 -0
  2054. package/dist/src/pipeline/steps/download.test.d.ts +1 -0
  2055. package/dist/src/pipeline/steps/download.test.js +102 -0
  2056. package/dist/src/pipeline/steps/fetch.d.ts +5 -0
  2057. package/dist/src/pipeline/steps/fetch.js +122 -0
  2058. package/dist/src/pipeline/steps/fetch.test.d.ts +1 -0
  2059. package/dist/src/pipeline/steps/fetch.test.js +117 -0
  2060. package/dist/src/pipeline/steps/intercept.d.ts +5 -0
  2061. package/dist/src/pipeline/steps/intercept.js +50 -0
  2062. package/dist/src/pipeline/steps/tap.d.ts +12 -0
  2063. package/dist/src/pipeline/steps/tap.js +93 -0
  2064. package/dist/src/pipeline/steps/transform.d.ts +9 -0
  2065. package/dist/src/pipeline/steps/transform.js +70 -0
  2066. package/dist/src/pipeline/template.d.ts +17 -0
  2067. package/dist/src/pipeline/template.js +338 -0
  2068. package/dist/src/pipeline/template.test.d.ts +4 -0
  2069. package/dist/src/pipeline/template.test.js +175 -0
  2070. package/dist/src/pipeline/transform.test.d.ts +4 -0
  2071. package/dist/src/pipeline/transform.test.js +133 -0
  2072. package/dist/src/plugin-manifest.d.ts +70 -0
  2073. package/dist/src/plugin-manifest.js +160 -0
  2074. package/dist/src/plugin-manifest.test.d.ts +4 -0
  2075. package/dist/src/plugin-manifest.test.js +179 -0
  2076. package/dist/src/plugin-scaffold.d.ts +28 -0
  2077. package/dist/src/plugin-scaffold.js +143 -0
  2078. package/dist/src/plugin-scaffold.test.d.ts +4 -0
  2079. package/dist/src/plugin-scaffold.test.js +83 -0
  2080. package/dist/src/plugin.d.ts +145 -0
  2081. package/dist/src/plugin.js +1245 -0
  2082. package/dist/src/plugin.test.d.ts +4 -0
  2083. package/dist/src/plugin.test.js +1324 -0
  2084. package/dist/src/registry-api.d.ts +13 -0
  2085. package/dist/src/registry-api.js +10 -0
  2086. package/dist/src/registry.d.ts +123 -0
  2087. package/dist/src/registry.js +125 -0
  2088. package/dist/src/registry.test.d.ts +4 -0
  2089. package/dist/src/registry.test.js +209 -0
  2090. package/dist/src/runtime-detect.d.ts +31 -0
  2091. package/dist/src/runtime-detect.js +47 -0
  2092. package/dist/src/runtime-detect.test.d.ts +1 -0
  2093. package/dist/src/runtime-detect.test.js +39 -0
  2094. package/dist/src/runtime.d.ts +47 -0
  2095. package/dist/src/runtime.js +66 -0
  2096. package/dist/src/scripts/framework.d.ts +7 -0
  2097. package/dist/src/scripts/framework.js +25 -0
  2098. package/dist/src/scripts/interact.d.ts +4 -0
  2099. package/dist/src/scripts/interact.js +20 -0
  2100. package/dist/src/scripts/store.d.ts +13 -0
  2101. package/dist/src/scripts/store.js +48 -0
  2102. package/dist/src/serialization.d.ts +42 -0
  2103. package/dist/src/serialization.js +102 -0
  2104. package/dist/src/serialization.test.d.ts +1 -0
  2105. package/dist/src/serialization.test.js +78 -0
  2106. package/dist/src/skills.d.ts +14 -0
  2107. package/dist/src/skills.js +127 -0
  2108. package/dist/src/skills.test.d.ts +1 -0
  2109. package/dist/src/skills.test.js +71 -0
  2110. package/dist/src/snapshotFormatter.d.ts +11 -0
  2111. package/dist/src/snapshotFormatter.js +338 -0
  2112. package/dist/src/snapshotFormatter.test.d.ts +7 -0
  2113. package/dist/src/snapshotFormatter.test.js +521 -0
  2114. package/dist/src/tui.d.ts +28 -0
  2115. package/dist/src/tui.js +179 -0
  2116. package/dist/src/tui.test.d.ts +1 -0
  2117. package/dist/src/tui.test.js +19 -0
  2118. package/dist/src/types.d.ts +236 -0
  2119. package/dist/src/types.js +7 -0
  2120. package/dist/src/update-check.d.ts +66 -0
  2121. package/dist/src/update-check.js +202 -0
  2122. package/dist/src/update-check.test.d.ts +1 -0
  2123. package/dist/src/update-check.test.js +116 -0
  2124. package/dist/src/utils.d.ts +57 -0
  2125. package/dist/src/utils.js +152 -0
  2126. package/dist/src/utils.test.d.ts +1 -0
  2127. package/dist/src/utils.test.js +155 -0
  2128. package/dist/src/validate.d.ts +21 -0
  2129. package/dist/src/validate.js +129 -0
  2130. package/dist/src/validate.test.d.ts +9 -0
  2131. package/dist/src/validate.test.js +90 -0
  2132. package/dist/src/verify.d.ts +26 -0
  2133. package/dist/src/verify.js +67 -0
  2134. package/dist/src/version.d.ts +4 -0
  2135. package/dist/src/version.js +22 -0
  2136. package/dist/src/weixin-download.test.d.ts +1 -0
  2137. package/dist/src/weixin-download.test.js +45 -0
  2138. package/package.json +109 -0
  2139. package/scripts/check-doc-coverage.sh +81 -0
  2140. package/scripts/check-listing-id-pairing.mjs +193 -0
  2141. package/scripts/check-silent-column-drop.mjs +105 -0
  2142. package/scripts/check-typed-error-lint.mjs +118 -0
  2143. package/scripts/clean-dist.cjs +13 -0
  2144. package/scripts/copy-yaml.cjs +12 -0
  2145. package/scripts/fetch-adapters.js +293 -0
  2146. package/scripts/postinstall.js +174 -0
  2147. package/scripts/silent-column-drop-baseline.json +910 -0
  2148. package/scripts/typed-error-lint-baseline.json +1162 -0
  2149. package/skills/opencli-adapter-author/SKILL.md +256 -0
  2150. package/skills/opencli-adapter-author/references/adapter-template.md +471 -0
  2151. package/skills/opencli-adapter-author/references/api-discovery.md +295 -0
  2152. package/skills/opencli-adapter-author/references/coverage-matrix.md +81 -0
  2153. package/skills/opencli-adapter-author/references/field-conventions.md +172 -0
  2154. package/skills/opencli-adapter-author/references/field-decode-playbook.md +181 -0
  2155. package/skills/opencli-adapter-author/references/jsdom-fixture-pattern.md +196 -0
  2156. package/skills/opencli-adapter-author/references/output-design.md +149 -0
  2157. package/skills/opencli-adapter-author/references/site-memory/bilibili.md +70 -0
  2158. package/skills/opencli-adapter-author/references/site-memory/eastmoney.md +70 -0
  2159. package/skills/opencli-adapter-author/references/site-memory/tonghuashun.md +51 -0
  2160. package/skills/opencli-adapter-author/references/site-memory/xueqiu.md +58 -0
  2161. package/skills/opencli-adapter-author/references/site-memory.md +231 -0
  2162. package/skills/opencli-adapter-author/references/site-recon.md +134 -0
  2163. package/skills/opencli-adapter-author/references/strategy-selection.md +169 -0
  2164. package/skills/opencli-adapter-author/references/success-rate-pitfalls.md +162 -0
  2165. package/skills/opencli-adapter-author/references/typed-errors.md +234 -0
  2166. package/skills/opencli-autofix/SKILL.md +297 -0
  2167. package/skills/opencli-browser/SKILL.md +444 -0
  2168. package/skills/opencli-browser-sitemap/SKILL.md +96 -0
  2169. package/skills/opencli-sitemap-author/SKILL.md +161 -0
  2170. package/skills/opencli-sitemap-author/references/sitemap-schema.md +660 -0
  2171. package/skills/opencli-usage/SKILL.md +169 -0
@@ -0,0 +1,2226 @@
1
+ /**
2
+ * ChatGPT web browser automation helpers.
3
+ * Cross-platform: works on Linux/macOS/Windows via OpenCLI's CDP browser automation.
4
+ */
5
+
6
+ import { htmlToMarkdown } from '@jackwener/opencli/utils';
7
+ import { ArgumentError, AuthRequiredError, CommandExecutionError, TimeoutError } from '@jackwener/opencli/errors';
8
+
9
+ export const CHATGPT_DOMAIN = 'chatgpt.com';
10
+ export const CHATGPT_URL = 'https://chatgpt.com';
11
+
12
+ const CHATGPT_MODEL_TARGETS = {
13
+ instant: {
14
+ label: 'Instant',
15
+ labels: ['Instant', '即时', '极速'],
16
+ optionLabels: ['Instant', '极速', '即时'],
17
+ testIds: ['model-switcher-gpt-5-5'],
18
+ intelligenceOrder: 0,
19
+ },
20
+ medium: {
21
+ label: 'Medium',
22
+ labels: ['Medium', '均衡'],
23
+ optionLabels: ['Medium', '均衡'],
24
+ testIds: [],
25
+ intelligenceOrder: 1,
26
+ },
27
+ high: {
28
+ label: 'High',
29
+ labels: ['High', '高级', 'Thinking', '思考'],
30
+ optionLabels: ['High', '高级', 'Thinking', '思考'],
31
+ testIds: ['model-switcher-gpt-5-5-thinking'],
32
+ intelligenceOrder: 2,
33
+ },
34
+ 'extra-high': {
35
+ label: 'Extra High',
36
+ labels: ['Extra High', '超高'],
37
+ optionLabels: ['Extra High', '超高'],
38
+ testIds: [],
39
+ intelligenceOrder: 3,
40
+ },
41
+ pro: {
42
+ label: 'Pro',
43
+ labels: ['Pro', '进阶专业', '专业'],
44
+ optionLabels: ['专业', 'Pro', '进阶专业'],
45
+ testIds: ['model-switcher-gpt-5-5-pro'],
46
+ intelligenceOrder: 4,
47
+ },
48
+ };
49
+ const CHATGPT_MODEL_ALIASES = {
50
+ thinking: 'high',
51
+ };
52
+ export const CHATGPT_MODEL_CHOICES = [
53
+ ...Object.keys(CHATGPT_MODEL_TARGETS),
54
+ ...Object.keys(CHATGPT_MODEL_ALIASES),
55
+ ];
56
+
57
+ const CHATGPT_TOOL_OPTIONS = {
58
+ 'deep-research': { label: 'Deep Research', labels: ['深度研究', 'Deep Research'] },
59
+ 'web-search': { label: 'Web Search', labels: ['网页搜索', '搜索', 'Web Search', 'Search'] },
60
+ };
61
+ export const CHATGPT_TOOL_CHOICES = Object.keys(CHATGPT_TOOL_OPTIONS);
62
+
63
+ // Selectors
64
+ const COMPOSER_SELECTORS = [
65
+ '[contenteditable="true"][role="textbox"]',
66
+ '#prompt-textarea[contenteditable="true"]',
67
+ '[aria-label="Chat with ChatGPT"]',
68
+ '[aria-label="与 ChatGPT 聊天"]',
69
+ '[placeholder="Ask anything"]',
70
+ '[placeholder="有问题,尽管问"]',
71
+ '#prompt-textarea',
72
+ '[data-testid="prompt-textarea"]',
73
+ ];
74
+ const SEND_BUTTON_SELECTOR = 'button[data-testid="send-button"]:not([disabled])';
75
+ const SEND_BUTTON_FALLBACK_SELECTORS = [
76
+ '#composer-submit-button:not([disabled])',
77
+ ];
78
+ const SEND_BUTTON_LABELS = [
79
+ 'Send prompt',
80
+ 'Send message',
81
+ 'Send',
82
+ '发送',
83
+ '发送消息',
84
+ '发送提示',
85
+ ];
86
+ const CLOSE_SIDEBAR_LABELS = [
87
+ 'Close sidebar',
88
+ '关闭边栏',
89
+ ];
90
+
91
+ function isSameChatGPTConversation(currentUrl, expectedUrl) {
92
+ if (!currentUrl || !expectedUrl) return false;
93
+ return currentUrl === expectedUrl
94
+ || currentUrl.startsWith(`${expectedUrl}?`)
95
+ || currentUrl.startsWith(`${expectedUrl}#`);
96
+ }
97
+
98
+ function buildComposerLocatorScript() {
99
+ const markerAttr = 'data-opencli-chatgpt-composer';
100
+ return `
101
+ const isVisible = (el) => {
102
+ if (!(el instanceof HTMLElement)) return false;
103
+ const style = window.getComputedStyle(el);
104
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
105
+ const rect = el.getBoundingClientRect();
106
+ return rect.width > 0 && rect.height > 0;
107
+ };
108
+
109
+ const markerAttr = ${JSON.stringify(markerAttr)};
110
+ const clearMarkers = (active) => {
111
+ document.querySelectorAll('[' + markerAttr + ']').forEach(node => {
112
+ if (node !== active) node.removeAttribute(markerAttr);
113
+ });
114
+ };
115
+
116
+ const findComposer = () => {
117
+ for (const selector of ${JSON.stringify(COMPOSER_SELECTORS)}) {
118
+ const candidates = Array.from(document.querySelectorAll(selector)).filter(c => c instanceof HTMLElement && isVisible(c));
119
+ const node = candidates.find(c => c.isContentEditable) || candidates[0];
120
+ if (node instanceof HTMLElement) {
121
+ clearMarkers(node);
122
+ node.setAttribute(markerAttr, '1');
123
+ return node;
124
+ }
125
+ }
126
+ return null;
127
+ };
128
+
129
+ findComposer.toString = () => 'findComposer';
130
+ `;
131
+ }
132
+
133
+ export function normalizeBooleanFlag(value, fallback = false) {
134
+ if (typeof value === 'boolean') return value;
135
+ if (value == null || value === '') return fallback;
136
+ const normalized = String(value).trim().toLowerCase();
137
+ return normalized === 'true' || normalized === '1' || normalized === 'yes' || normalized === 'on';
138
+ }
139
+
140
+ export function requireNonEmptyPrompt(prompt, commandName) {
141
+ const text = String(prompt ?? '').trim();
142
+ if (!text) {
143
+ throw new ArgumentError(
144
+ `${commandName} prompt cannot be empty`,
145
+ `Example: opencli ${commandName} "hello"`,
146
+ );
147
+ }
148
+ return text;
149
+ }
150
+
151
+ export function requirePositiveInt(value, flagLabel, hint) {
152
+ if (!Number.isInteger(value) || value < 1) {
153
+ throw new ArgumentError(`${flagLabel} must be a positive integer`, hint);
154
+ }
155
+ return value;
156
+ }
157
+
158
+ export function requireNonNegativeInt(value, flagLabel, hint) {
159
+ if (!Number.isInteger(value) || value < 0) {
160
+ throw new ArgumentError(`${flagLabel} must be a non-negative integer`, hint);
161
+ }
162
+ return value;
163
+ }
164
+
165
+ // ─────────────────────────────────────────────────────────────────────────────
166
+ // page.evaluate envelope helpers.
167
+ //
168
+ // The browser bridge wraps every `page.evaluate(...)` return value in a
169
+ // `{ session, data }` envelope. Adapters that read `.length` or
170
+ // `Array.isArray(payload)` directly on the envelope silently see "no data" —
171
+ // this matches the failure mode fixed for xiaohongshu/rednote (#1561) and
172
+ // weibo (#1568).
173
+ //
174
+ // `unwrapEvaluateResult` is a defensive ternary: it unwraps when the payload
175
+ // looks like an envelope, otherwise passes the value through unchanged so
176
+ // older bridge versions and primitive return values still work.
177
+ // ─────────────────────────────────────────────────────────────────────────────
178
+ export function unwrapEvaluateResult(payload) {
179
+ if (payload && !Array.isArray(payload) && typeof payload === 'object' && 'session' in payload && 'data' in payload) {
180
+ return payload.data;
181
+ }
182
+ return payload;
183
+ }
184
+
185
+ export function requireArrayEvaluateResult(payload, label) {
186
+ if (!Array.isArray(payload)) {
187
+ if (payload && typeof payload === 'object' && 'error' in payload) {
188
+ throw new CommandExecutionError(`${label}: ${String(payload.error)}`);
189
+ }
190
+ throw new CommandExecutionError(`${label} returned malformed extraction payload`);
191
+ }
192
+ return payload;
193
+ }
194
+
195
+ export function requireObjectEvaluateResult(payload, label) {
196
+ if (!payload || Array.isArray(payload) || typeof payload !== 'object') {
197
+ throw new CommandExecutionError(`${label} returned malformed extraction payload`);
198
+ }
199
+ return payload;
200
+ }
201
+
202
+ export function requireBooleanEvaluateResult(payload, label) {
203
+ if (typeof payload !== 'boolean') {
204
+ throw new CommandExecutionError(`${label} returned malformed extraction payload`);
205
+ }
206
+ return payload;
207
+ }
208
+
209
+ function isTrustedChatGPTHost(hostname) {
210
+ return hostname === CHATGPT_DOMAIN || hostname.endsWith(`.${CHATGPT_DOMAIN}`);
211
+ }
212
+
213
+ function projectIdFromPathname(pathname) {
214
+ const match = String(pathname || '').match(/^\/g\/g-p-([a-f0-9]{8,})(?:[-/]|$)/i);
215
+ return match ? match[1].toLowerCase() : '';
216
+ }
217
+
218
+ function projectIdFromUrl(value) {
219
+ try {
220
+ const url = new URL(String(value || ''), CHATGPT_URL);
221
+ if (url.protocol !== 'https:' || !isTrustedChatGPTHost(url.hostname)) return '';
222
+ return projectIdFromPathname(url.pathname);
223
+ } catch {
224
+ return '';
225
+ }
226
+ }
227
+
228
+ export function parseChatGPTConversationId(value) {
229
+ const raw = String(value ?? '').trim();
230
+ if (/^https?:\/\//i.test(raw)) {
231
+ try {
232
+ const parsed = new URL(raw);
233
+ if (parsed.protocol !== 'https:' || (parsed.hostname !== CHATGPT_DOMAIN && !parsed.hostname.endsWith(`.${CHATGPT_DOMAIN}`))) {
234
+ throw new Error('off-domain');
235
+ }
236
+ const match = parsed.pathname.match(/^\/(?:g\/g-p-[^/]+\/)?c\/([A-Za-z0-9_-]{8,})$/);
237
+ if (match) return match[1];
238
+ } catch {
239
+ // Fall through to the shared typed ArgumentError below.
240
+ }
241
+ throw new ArgumentError(
242
+ 'chatgpt detail requires a conversation id or chatgpt.com /c/<id> URL',
243
+ 'Example: opencli chatgpt detail https://chatgpt.com/c/123e4567-e89b-12d3-a456-426614174000',
244
+ );
245
+ }
246
+ const pathMatch = raw.match(/^\/(?:g\/g-p-[^/]+\/)?c\/([A-Za-z0-9_-]{8,})(?:[?#].*)?$/);
247
+ if (pathMatch) return pathMatch[1];
248
+ if (/^[A-Za-z0-9_-]{8,}$/.test(raw)) return raw;
249
+ throw new ArgumentError(
250
+ 'chatgpt detail requires a conversation id or chatgpt.com /c/<id> URL',
251
+ 'Example: opencli chatgpt detail 123e4567-e89b-12d3-a456-426614174000',
252
+ );
253
+ }
254
+
255
+ export async function currentChatGPTUrl(page) {
256
+ const url = unwrapEvaluateResult(await page.evaluate('window.location.href').catch(() => ''));
257
+ return typeof url === 'string' ? url : '';
258
+ }
259
+
260
+ export async function isOnChatGPT(page) {
261
+ const url = await currentChatGPTUrl(page);
262
+ if (!url) return false;
263
+ try {
264
+ const host = new URL(url).hostname;
265
+ return host === CHATGPT_DOMAIN || host.endsWith(`.${CHATGPT_DOMAIN}`);
266
+ } catch {
267
+ return false;
268
+ }
269
+ }
270
+
271
+ // Comma-joined CSS selector list passed to page.wait({ selector }) so the
272
+ // wait succeeds as soon as any composer flavour mounts (querySelectorAll
273
+ // matches all of them). Tracks the most stable subset of COMPOSER_SELECTORS;
274
+ // we only need to know "the composer is ready", not which variant rendered.
275
+ const COMPOSER_WAIT_SELECTOR = '#prompt-textarea, [data-testid="prompt-textarea"]';
276
+ const CONVERSATION_LINK_SELECTOR = 'a[href*="/c/"]';
277
+ const PROJECT_LINK_SELECTOR = 'a[href*="/g/g-p-"]';
278
+ // Selector used by detail.js to wait for at least one rendered message bubble
279
+ // after navigating to /c/<id>; mirrors the markup queried by getVisibleMessages.
280
+ export const CONVERSATION_MESSAGE_SELECTOR = '[data-message-author-role], article[data-testid*="conversation-turn"]';
281
+
282
+ export async function ensureOnChatGPT(page) {
283
+ if (await isOnChatGPT(page)) return false;
284
+ await page.goto(CHATGPT_URL, { settleMs: 2000 });
285
+ try {
286
+ await page.wait({ selector: COMPOSER_WAIT_SELECTOR, timeout: 8 });
287
+ } catch {
288
+ // Composer didn't mount; downstream ensureChatGPTLogin / ensureChatGPTComposer surfaces a typed error.
289
+ }
290
+ return true;
291
+ }
292
+
293
+ export async function startNewChat(page) {
294
+ await page.goto(`${CHATGPT_URL}/new`, { settleMs: 2000 });
295
+ try {
296
+ await page.wait({ selector: COMPOSER_WAIT_SELECTOR, timeout: 8 });
297
+ } catch {
298
+ // Composer didn't mount; downstream ensureChatGPTComposer surfaces a typed error.
299
+ }
300
+ }
301
+
302
+ export async function openChatGPTConversation(page, value) {
303
+ const id = parseChatGPTConversationId(value);
304
+ await page.goto(`${CHATGPT_URL}/c/${id}`, { settleMs: 2000 });
305
+ try {
306
+ await page.wait({ selector: COMPOSER_WAIT_SELECTOR, timeout: 8 });
307
+ } catch {
308
+ // Composer didn't mount; downstream ensureChatGPTLogin / ensureChatGPTComposer surfaces a typed error.
309
+ }
310
+ return id;
311
+ }
312
+
313
+ export async function getPageState(page) {
314
+ return requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
315
+ const isVisible = (el) => {
316
+ if (!(el instanceof HTMLElement)) return false;
317
+ const style = window.getComputedStyle(el);
318
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
319
+ const rect = el.getBoundingClientRect();
320
+ return rect.width > 0 && rect.height > 0;
321
+ };
322
+ const composerSelectors = ${JSON.stringify(COMPOSER_SELECTORS)};
323
+ const hasComposer = composerSelectors.some((selector) =>
324
+ Array.from(document.querySelectorAll(selector)).some((node) => isVisible(node))
325
+ );
326
+ const text = (document.body?.innerText || '').replace(/\\s+/g, ' ').trim();
327
+ const loginLink = Array.from(document.querySelectorAll('a, button')).find((node) => {
328
+ const label = ((node.innerText || node.textContent || '') + ' ' + (node.getAttribute('aria-label') || '')).trim().toLowerCase();
329
+ return isVisible(node) && /^(log in|login|sign up|sign in)$/.test(label);
330
+ });
331
+ const userMenu = document.querySelector('[data-testid="profile-button"], [aria-label*="Profile"], [aria-label*="Account"], button[id*="headlessui-menu-button"]');
332
+ const hasLoginGate = !!loginLink || /log in to chatgpt|sign up to chatgpt|welcome to chatgpt/i.test(text);
333
+ return {
334
+ url: window.location.href,
335
+ title: document.title,
336
+ hasComposer,
337
+ isLoggedIn: hasComposer || !!userMenu || !hasLoginGate,
338
+ hasLoginGate,
339
+ };
340
+ })()`)), 'chatgpt page state');
341
+ }
342
+
343
+ export async function ensureChatGPTLogin(page, message = 'ChatGPT requires a logged-in browser session.') {
344
+ const state = await getPageState(page);
345
+ if (!state.isLoggedIn || state.hasLoginGate) {
346
+ throw new AuthRequiredError(CHATGPT_DOMAIN, message);
347
+ }
348
+ return state;
349
+ }
350
+
351
+ export async function ensureChatGPTComposer(page, message = 'ChatGPT composer is not available on the current page.') {
352
+ const state = await ensureChatGPTLogin(page, message);
353
+ if (!state.hasComposer) {
354
+ throw new CommandExecutionError(message);
355
+ }
356
+ return state;
357
+ }
358
+
359
+ function requireKnownChatGPTModel(model) {
360
+ const key = String(model ?? '').trim().toLowerCase();
361
+ const targetKey = CHATGPT_MODEL_ALIASES[key] || key;
362
+ const option = CHATGPT_MODEL_TARGETS[targetKey];
363
+ if (!option) {
364
+ throw new ArgumentError(
365
+ `Unknown ChatGPT model "${model}"`,
366
+ `Choose one of: ${CHATGPT_MODEL_CHOICES.join(', ')}`,
367
+ );
368
+ }
369
+ return { key: targetKey, alias: key !== targetKey ? key : null, ...option };
370
+ }
371
+
372
+ function requireKnownChatGPTTool(tool) {
373
+ const key = String(tool ?? '').trim().toLowerCase();
374
+ const option = CHATGPT_TOOL_OPTIONS[key];
375
+ if (!option) {
376
+ throw new ArgumentError(
377
+ `Unknown ChatGPT tool "${tool}"`,
378
+ `Choose one of: ${CHATGPT_TOOL_CHOICES.join(', ')}`,
379
+ );
380
+ }
381
+ return { key, ...option };
382
+ }
383
+
384
+ export async function getCurrentChatGPTModel(page) {
385
+ return requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
386
+ const isVisible = (el) => {
387
+ if (!(el instanceof HTMLElement)) return false;
388
+ const style = window.getComputedStyle(el);
389
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
390
+ const rect = el.getBoundingClientRect();
391
+ return rect.width > 0 && rect.height > 0;
392
+ };
393
+ const normalize = (value) => String(value || '').replace(/\\s+/g, ' ').trim();
394
+ const escapeRegExp = (value) => String(value).replace(/[|\\\\{}()[\\]^$+*?.]/g, '\\\\$&');
395
+ const textMatchesLabel = (text, label) => {
396
+ const normalizedText = normalize(text);
397
+ const normalizedLabel = normalize(label);
398
+ if (!normalizedText || !normalizedLabel) return false;
399
+ if (normalizedText === normalizedLabel) return true;
400
+ if (/^[\\x00-\\x7F]+$/.test(normalizedLabel)) {
401
+ return new RegExp('(^|\\\\b)' + escapeRegExp(normalizedLabel) + '(\\\\b|$)', 'i').test(normalizedText);
402
+ }
403
+ return normalizedText.replace(/\\s+/g, '').includes(normalizedLabel.replace(/\\s+/g, ''));
404
+ };
405
+ const labels = ${JSON.stringify(CHATGPT_MODEL_TARGETS)};
406
+ const findEntryForText = (text) => {
407
+ const matches = [];
408
+ for (const [key, value] of Object.entries(labels)) {
409
+ for (const item of value.labels || []) {
410
+ if (textMatchesLabel(text, item)) {
411
+ matches.push({ key, value, length: normalize(item).length });
412
+ }
413
+ }
414
+ }
415
+ matches.sort((a, b) => b.length - a.length);
416
+ return matches[0] || null;
417
+ };
418
+ const form = Array.from(document.querySelectorAll('form')).find((node) => node instanceof HTMLElement && isVisible(node));
419
+ const testIdNode = form
420
+ ? Array.from(form.querySelectorAll('[data-testid]')).find((node) => {
421
+ if (!(node instanceof HTMLElement) || !isVisible(node)) return false;
422
+ const testId = node.getAttribute('data-testid');
423
+ return Object.values(labels).some((entry) => (entry.testIds || []).includes(testId));
424
+ })
425
+ : null;
426
+ const testId = testIdNode?.getAttribute('data-testid') || '';
427
+ const testIdEntry = Object.entries(labels).find(([, value]) => (value.testIds || []).includes(testId));
428
+ if (testIdEntry) {
429
+ return {
430
+ model: testIdEntry[0],
431
+ label: testIdEntry[1].label,
432
+ };
433
+ }
434
+ const button = Array.from((form || document).querySelectorAll('button')).find((node) => {
435
+ if (!isVisible(node)) return false;
436
+ const text = normalize(node.textContent);
437
+ return Object.values(labels).some((entry) => entry.labels.some((label) => textMatchesLabel(text, label)));
438
+ });
439
+ const label = normalize(button?.textContent || '');
440
+ const entry = findEntryForText(label);
441
+ return {
442
+ model: entry?.key ?? null,
443
+ label: entry?.value?.label ?? null,
444
+ };
445
+ })()`)), 'chatgpt current model');
446
+ }
447
+
448
+ export async function selectChatGPTModel(page, model) {
449
+ const target = requireKnownChatGPTModel(model);
450
+ if (typeof page.nativeClick !== 'function') {
451
+ throw new CommandExecutionError('ChatGPT model selection requires native browser click support.');
452
+ }
453
+ await ensureOnChatGPT(page);
454
+ await ensureChatGPTComposer(page, 'ChatGPT model selection requires a logged-in ChatGPT session with a visible composer.');
455
+
456
+ const before = await getCurrentChatGPTModel(page);
457
+ if (before.model === target.key) {
458
+ return { Status: 'Already selected', Model: target.label };
459
+ }
460
+
461
+ const menuButton = requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
462
+ const isVisible = (el) => {
463
+ if (!(el instanceof HTMLElement)) return false;
464
+ const style = window.getComputedStyle(el);
465
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
466
+ const rect = el.getBoundingClientRect();
467
+ return rect.width > 0 && rect.height > 0;
468
+ };
469
+ const normalize = (value) => String(value || '').replace(/\\s+/g, ' ').trim();
470
+ const escapeRegExp = (value) => String(value).replace(/[|\\\\{}()[\\]^$+*?.]/g, '\\\\$&');
471
+ const textMatchesLabel = (text, label) => {
472
+ const normalizedText = normalize(text);
473
+ const normalizedLabel = normalize(label);
474
+ if (!normalizedText || !normalizedLabel) return false;
475
+ if (normalizedText === normalizedLabel) return true;
476
+ if (/^[\\x00-\\x7F]+$/.test(normalizedLabel)) {
477
+ return new RegExp('(^|\\\\b)' + escapeRegExp(normalizedLabel) + '(\\\\b|$)', 'i').test(normalizedText);
478
+ }
479
+ return normalizedText.replace(/\\s+/g, '').includes(normalizedLabel.replace(/\\s+/g, ''));
480
+ };
481
+ const labels = ${JSON.stringify(Object.values(CHATGPT_MODEL_TARGETS).flatMap((entry) => entry.labels))};
482
+ const menuButtonSelectors = [
483
+ 'button[data-testid="model-switcher-dropdown-button"]',
484
+ 'button[aria-label*="model" i]',
485
+ 'button[aria-label*="模型"]',
486
+ 'button[aria-label*="智能"]',
487
+ ];
488
+ let button = Array.from(document.querySelectorAll('form button')).find((node) =>
489
+ isVisible(node) && labels.some((label) => textMatchesLabel(node.textContent, label))
490
+ );
491
+ if (!button) {
492
+ button = menuButtonSelectors
493
+ .map((selector) => document.querySelector(selector))
494
+ .find((node) => node instanceof HTMLElement && isVisible(node));
495
+ }
496
+ if (!button) return { found: false };
497
+ button.scrollIntoView({ block: 'center', inline: 'center' });
498
+ const rect = button.getBoundingClientRect();
499
+ return {
500
+ found: true,
501
+ x: Math.round(rect.left + rect.width / 2),
502
+ y: Math.round(rect.top + rect.height / 2),
503
+ };
504
+ })()`)), 'chatgpt model menu button');
505
+ if (!menuButton.found) {
506
+ throw new CommandExecutionError('Could not find the ChatGPT model selector in the composer.');
507
+ }
508
+ await page.nativeClick(Number(menuButton.x), Number(menuButton.y));
509
+ await page.wait(0.5);
510
+
511
+ let optionCenter = null;
512
+ for (let attempt = 0; attempt < 10; attempt += 1) {
513
+ optionCenter = requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
514
+ const isVisible = (el) => {
515
+ if (!(el instanceof HTMLElement)) return false;
516
+ const style = window.getComputedStyle(el);
517
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
518
+ const rect = el.getBoundingClientRect();
519
+ return rect.width > 0 && rect.height > 0;
520
+ };
521
+ const normalize = (value) => String(value || '').replace(/\\s+/g, ' ').trim();
522
+ const escapeRegExp = (value) => String(value).replace(/[|\\\\{}()[\\]^$+*?.]/g, '\\\\$&');
523
+ const textMatchesLabel = (text, label) => {
524
+ const normalizedText = normalize(text);
525
+ const normalizedLabel = normalize(label);
526
+ if (!normalizedText || !normalizedLabel) return false;
527
+ if (normalizedText === normalizedLabel) return true;
528
+ if (/^[\\x00-\\x7F]+$/.test(normalizedLabel)) {
529
+ return new RegExp('(^|\\\\b)' + escapeRegExp(normalizedLabel) + '(\\\\b|$)', 'i').test(normalizedText);
530
+ }
531
+ return normalizedText.replace(/\\s+/g, '').includes(normalizedLabel.replace(/\\s+/g, ''));
532
+ };
533
+ const target = ${JSON.stringify(target)};
534
+ const clickableSelector = '[role="menuitemradio"], [role="menuitem"], [role="option"], button, [data-testid^="model-switcher"]';
535
+ const intelligenceContent = document.querySelector('[data-testid="composer-intelligence-picker-content"]');
536
+ const intelligenceOptions = intelligenceContent
537
+ ? Array.from(intelligenceContent.querySelectorAll('[role="menuitemradio"]')).filter(isVisible)
538
+ : [];
539
+ let option = null;
540
+ for (const testId of target.testIds || []) {
541
+ option = Array.from(document.querySelectorAll('[data-testid]')).find((candidate) =>
542
+ candidate instanceof HTMLElement && candidate.getAttribute('data-testid') === testId
543
+ ) || null;
544
+ if (option instanceof HTMLElement && isVisible(option)) break;
545
+ option = null;
546
+ }
547
+ const clickables = intelligenceOptions.length ? intelligenceOptions : Array.from(document.querySelectorAll(clickableSelector));
548
+ for (const label of target.optionLabels || target.labels || []) {
549
+ option = clickables.find((candidate) =>
550
+ candidate instanceof HTMLElement
551
+ && isVisible(candidate)
552
+ && textMatchesLabel(candidate.textContent, label)
553
+ ) || null;
554
+ if (option) break;
555
+
556
+ const labelRoot = intelligenceContent || document;
557
+ const labelNode = Array.from(labelRoot.querySelectorAll('span, div, p')).find((candidate) =>
558
+ candidate instanceof HTMLElement
559
+ && isVisible(candidate)
560
+ && textMatchesLabel(candidate.textContent, label)
561
+ );
562
+ option = labelNode?.closest(clickableSelector) || null;
563
+ if (option instanceof HTMLElement && isVisible(option)) break;
564
+ option = null;
565
+ }
566
+ if (!option && Number.isInteger(target.intelligenceOrder)) {
567
+ if (intelligenceOptions.length === 5) {
568
+ option = intelligenceOptions[target.intelligenceOrder] || null;
569
+ }
570
+ }
571
+ if (!(option instanceof HTMLElement) || !isVisible(option)) return { found: false };
572
+ option.scrollIntoView({ block: 'center', inline: 'center' });
573
+ const rect = option.getBoundingClientRect();
574
+ return {
575
+ found: true,
576
+ x: Math.round(rect.left + rect.width / 2),
577
+ y: Math.round(rect.top + rect.height / 2),
578
+ };
579
+ })()`)), 'chatgpt model option click');
580
+ if (optionCenter.found) break;
581
+ await page.wait(0.5);
582
+ }
583
+ if (!optionCenter?.found) {
584
+ throw new CommandExecutionError(`Could not click the ChatGPT ${target.label} model option.`);
585
+ }
586
+ await page.nativeClick(Number(optionCenter.x), Number(optionCenter.y));
587
+
588
+ await page.wait(0.5);
589
+ const after = await getCurrentChatGPTModel(page);
590
+ if (after.model !== target.key) {
591
+ await page.nativeClick(Number(menuButton.x), Number(menuButton.y));
592
+ await page.wait(0.5);
593
+ const checked = requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
594
+ const isVisible = (el) => {
595
+ if (!(el instanceof HTMLElement)) return false;
596
+ const style = window.getComputedStyle(el);
597
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
598
+ const rect = el.getBoundingClientRect();
599
+ return rect.width > 0 && rect.height > 0;
600
+ };
601
+ const target = ${JSON.stringify(target)};
602
+ const intelligenceContent = document.querySelector('[data-testid="composer-intelligence-picker-content"]');
603
+ const options = intelligenceContent
604
+ ? Array.from(intelligenceContent.querySelectorAll('[role="menuitemradio"]')).filter(isVisible)
605
+ : [];
606
+ const checkedIndex = options.findIndex((node) => node.getAttribute('aria-checked') === 'true');
607
+ return {
608
+ recognized: options.length === 5 && Number.isInteger(target.intelligenceOrder),
609
+ checkedIndex,
610
+ };
611
+ })()`)), 'chatgpt model checked intelligence option');
612
+ if (checked.recognized && checked.checkedIndex === target.intelligenceOrder) {
613
+ await page.nativeClick(Number(menuButton.x), Number(menuButton.y));
614
+ return { Status: 'Success', Model: target.label };
615
+ }
616
+ await page.nativeClick(Number(menuButton.x), Number(menuButton.y));
617
+ throw new CommandExecutionError(`ChatGPT model did not switch to ${target.label}.`);
618
+ }
619
+ return { Status: 'Success', Model: target.label };
620
+ }
621
+
622
+ export async function getCurrentChatGPTTool(page) {
623
+ return requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
624
+ const isVisible = (el) => {
625
+ if (!(el instanceof HTMLElement)) return false;
626
+ const style = window.getComputedStyle(el);
627
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
628
+ const rect = el.getBoundingClientRect();
629
+ return rect.width > 0 && rect.height > 0;
630
+ };
631
+ const normalize = (value) => String(value || '').replace(/\\s+/g, ' ').trim();
632
+ const labels = ${JSON.stringify(CHATGPT_TOOL_OPTIONS)};
633
+ const form = Array.from(document.querySelectorAll('form')).find((node) => node instanceof HTMLElement && isVisible(node));
634
+ const root = form || document.body;
635
+ const nodes = Array.from(root.querySelectorAll('button, [role="button"], [role="menuitemradio"], span, div'));
636
+ const node = nodes.find((candidate) => {
637
+ if (!isVisible(candidate)) return false;
638
+ const text = normalize(candidate.textContent);
639
+ return Object.values(labels).some((entry) => entry.labels.includes(text));
640
+ });
641
+ const label = normalize(node?.textContent || '');
642
+ const entry = Object.entries(labels).find(([, value]) => value.labels.includes(label));
643
+ return {
644
+ tool: entry?.[0] ?? null,
645
+ label: entry?.[1]?.label ?? null,
646
+ };
647
+ })()`)), 'chatgpt current tool');
648
+ }
649
+
650
+ export async function selectChatGPTTool(page, tool) {
651
+ const target = requireKnownChatGPTTool(tool);
652
+ if (typeof page.nativeClick !== 'function') {
653
+ throw new CommandExecutionError('ChatGPT tool selection requires native browser click support.');
654
+ }
655
+ await ensureOnChatGPT(page);
656
+ await ensureChatGPTComposer(page, 'ChatGPT tool selection requires a logged-in ChatGPT session with a visible composer.');
657
+
658
+ const before = await getCurrentChatGPTTool(page);
659
+ if (before.tool === target.key) {
660
+ return { Status: 'Already selected', Tool: target.label };
661
+ }
662
+
663
+ const menuButton = requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
664
+ const isVisible = (el) => {
665
+ if (!(el instanceof HTMLElement)) return false;
666
+ const style = window.getComputedStyle(el);
667
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
668
+ const rect = el.getBoundingClientRect();
669
+ return rect.width > 0 && rect.height > 0;
670
+ };
671
+ const button = document.querySelector('button[data-testid="composer-plus-btn"]');
672
+ if (!(button instanceof HTMLElement) || !isVisible(button)) return { found: false };
673
+ button.scrollIntoView({ block: 'center', inline: 'center' });
674
+ const rect = button.getBoundingClientRect();
675
+ return {
676
+ found: true,
677
+ x: Math.round(rect.left + rect.width / 2),
678
+ y: Math.round(rect.top + rect.height / 2),
679
+ };
680
+ })()`)), 'chatgpt tools menu button');
681
+ if (!menuButton.found) {
682
+ throw new CommandExecutionError('Could not find the ChatGPT tools menu button in the composer.');
683
+ }
684
+ await page.nativeClick(Number(menuButton.x), Number(menuButton.y));
685
+ await page.wait(0.5);
686
+
687
+ let optionCenter = null;
688
+ for (let attempt = 0; attempt < 10; attempt += 1) {
689
+ optionCenter = requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
690
+ const isVisible = (el) => {
691
+ if (!(el instanceof HTMLElement)) return false;
692
+ const style = window.getComputedStyle(el);
693
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
694
+ const rect = el.getBoundingClientRect();
695
+ return rect.width > 0 && rect.height > 0;
696
+ };
697
+ const normalize = (value) => String(value || '').replace(/\\s+/g, ' ').trim();
698
+ const labels = ${JSON.stringify(target.labels)};
699
+ const options = Array.from(document.querySelectorAll('[role="menuitemradio"]'));
700
+ const option = options.find((node) => node instanceof HTMLElement && isVisible(node) && labels.includes(normalize(node.textContent)));
701
+ if (!(option instanceof HTMLElement)) return { found: false };
702
+ const checked = option.getAttribute('aria-checked') === 'true';
703
+ option.scrollIntoView({ block: 'center', inline: 'center' });
704
+ const rect = option.getBoundingClientRect();
705
+ return {
706
+ found: true,
707
+ checked,
708
+ x: Math.round(rect.left + rect.width / 2),
709
+ y: Math.round(rect.top + rect.height / 2),
710
+ };
711
+ })()`)), 'chatgpt tool option click');
712
+ if (optionCenter.found) break;
713
+ await page.wait(0.5);
714
+ }
715
+ if (!optionCenter?.found) {
716
+ throw new CommandExecutionError(`Could not find the ChatGPT ${target.label} tool option.`);
717
+ }
718
+ if (!optionCenter.checked) {
719
+ await page.nativeClick(Number(optionCenter.x), Number(optionCenter.y));
720
+ }
721
+
722
+ await page.wait(0.5);
723
+ const after = await getCurrentChatGPTTool(page);
724
+ if (after.tool !== target.key) {
725
+ throw new CommandExecutionError(`ChatGPT tool did not switch to ${target.label}.`);
726
+ }
727
+ return { Status: optionCenter.checked ? 'Already selected' : 'Success', Tool: target.label };
728
+ }
729
+
730
+ export async function clearChatGPTDraft(page) {
731
+ await page.evaluate(`
732
+ (() => {
733
+ const removeLabels = [/^remove file/i, /^移除文件/];
734
+ for (let pass = 0; pass < 10; pass += 1) {
735
+ const button = Array.from(document.querySelectorAll('button')).find((node) => {
736
+ const label = node.getAttribute('aria-label') || '';
737
+ return removeLabels.some((pattern) => pattern.test(label));
738
+ });
739
+ if (!button) break;
740
+ button.click();
741
+ }
742
+
743
+ const selectors = ${JSON.stringify(COMPOSER_SELECTORS)};
744
+ for (const selector of selectors) {
745
+ for (const node of document.querySelectorAll(selector)) {
746
+ if (!(node instanceof HTMLElement)) continue;
747
+ if (node instanceof HTMLTextAreaElement || node instanceof HTMLInputElement) {
748
+ node.value = '';
749
+ } else if (node.isContentEditable) {
750
+ node.textContent = '';
751
+ node.innerHTML = '<p><br></p>';
752
+ } else {
753
+ node.textContent = '';
754
+ }
755
+ node.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'deleteContentBackward', data: null }));
756
+ node.dispatchEvent(new Event('change', { bubbles: true }));
757
+ }
758
+ }
759
+ })()
760
+ `);
761
+ await page.wait(0.5);
762
+ }
763
+
764
+ export function parseChatGPTProjectId(value) {
765
+ const raw = String(value ?? '').trim();
766
+ if (/^https?:\/\//i.test(raw) || raw.startsWith('/')) {
767
+ const id = projectIdFromUrl(raw);
768
+ if (id) return id;
769
+ throw new ArgumentError(
770
+ 'chatgpt project commands require a chatgpt.com project id or /g/g-p-<id> URL',
771
+ 'Example: opencli chatgpt project-file-add report.pdf --id 12345678',
772
+ );
773
+ }
774
+ // Accept project slug pattern: g-p-{hex_id}-{slug} or just hex id
775
+ const slugMatch = raw.match(/^g-p-([a-f0-9]{8,})/i);
776
+ if (slugMatch) return slugMatch[1].toLowerCase();
777
+ if (/^[a-f0-9]{8,}$/i.test(raw)) return raw.toLowerCase();
778
+ throw new ArgumentError(
779
+ 'chatgpt project commands require a project id or /g/g-p-<id> URL',
780
+ 'Example: opencli chatgpt project-file-add report.pdf --id 12345678',
781
+ );
782
+ }
783
+
784
+ /**
785
+ * Send a message to the ChatGPT composer and submit it.
786
+ * Returns true if the message was sent successfully.
787
+ */
788
+ export async function sendChatGPTMessage(page, text) {
789
+ // Close sidebar if open (it can cover the chat composer)
790
+ await page.evaluate(`
791
+ (() => {
792
+ const labels = ${JSON.stringify(CLOSE_SIDEBAR_LABELS)};
793
+ const closeBtn = Array.from(document.querySelectorAll('button')).find(b => labels.includes(b.getAttribute('aria-label') || ''));
794
+ if (closeBtn) closeBtn.click();
795
+ })()
796
+ `);
797
+ // The previous 0.5 s + 1.5 s pre-composer settles are dropped: the next
798
+ // page.evaluate roundtrip flushes the close-sidebar React update and
799
+ // findComposer() retries inside a single CDP call, so no fixed sleep is
800
+ // needed before reading the composer.
801
+
802
+ const typeResult = requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`
803
+ (() => {
804
+ ${buildComposerLocatorScript()}
805
+ const composer = findComposer();
806
+ if (!composer) return { ready: false };
807
+ composer.focus();
808
+ if (composer instanceof HTMLTextAreaElement || composer instanceof HTMLInputElement) {
809
+ composer.value = '';
810
+ } else if (composer.isContentEditable) {
811
+ composer.textContent = '';
812
+ composer.innerHTML = '<p><br></p>';
813
+ } else {
814
+ composer.textContent = '';
815
+ }
816
+ composer.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'deleteContentBackward', data: null }));
817
+ composer.dispatchEvent(new Event('change', { bubbles: true }));
818
+ composer.scrollIntoView({ block: 'center', inline: 'center' });
819
+ const rect = composer.getBoundingClientRect();
820
+ return {
821
+ ready: true,
822
+ x: Math.round(rect.left + Math.max(8, Math.min(rect.width / 2, rect.width - 8))),
823
+ y: Math.round(rect.top + Math.max(8, Math.min(rect.height / 2, rect.height - 8))),
824
+ };
825
+ })()
826
+ `)), 'chatgpt composer readiness');
827
+
828
+ if (!typeResult.ready) return false;
829
+
830
+ // Use page.type() which is Playwright's native method
831
+ try {
832
+ if (page.nativeType) {
833
+ if (typeof page.nativeClick === 'function') {
834
+ await page.nativeClick(Number(typeResult.x), Number(typeResult.y));
835
+ await page.wait(0.2);
836
+ }
837
+ await page.nativeType(text);
838
+ } else {
839
+ throw new Error('nativeType unavailable');
840
+ }
841
+ } catch (e) {
842
+ // Fallback: use execCommand
843
+ await page.evaluate(`
844
+ (() => {
845
+ var composer = null;
846
+ var sels = ${JSON.stringify(COMPOSER_SELECTORS)};
847
+ for (var si = 0; si < sels.length; si++) { composer = document.querySelector(sels[si]); if (composer) break; }
848
+ if (!composer) return;
849
+ composer.focus();
850
+ document.execCommand('insertText', false, ${JSON.stringify(text)});
851
+ })()
852
+ `);
853
+ }
854
+
855
+ let sent = null;
856
+ for (let attempt = 0; attempt < 20; attempt += 1) {
857
+ await page.wait(0.5);
858
+ sent = requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`
859
+ (() => {
860
+ const isVisible = (el) => {
861
+ if (!(el instanceof HTMLElement)) return false;
862
+ const style = window.getComputedStyle(el);
863
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
864
+ const rect = el.getBoundingClientRect();
865
+ return rect.width > 0 && rect.height > 0;
866
+ };
867
+ const isUsable = (button) => button
868
+ && isVisible(button)
869
+ && !button.disabled
870
+ && button.getAttribute('aria-disabled') !== 'true';
871
+ const form = Array.from(document.querySelectorAll('form')).find((node) => node instanceof HTMLElement && isVisible(node));
872
+ const root = form || document.body;
873
+ const primary = root.querySelector(${JSON.stringify(SEND_BUTTON_SELECTOR)})
874
+ || ${JSON.stringify(SEND_BUTTON_FALLBACK_SELECTORS)}.map(selector => root.querySelector(selector)).find(Boolean);
875
+ const btns = Array.from(root.querySelectorAll('button'));
876
+ const labels = ${JSON.stringify(SEND_BUTTON_LABELS)};
877
+ const looksLikeSend = (button) => {
878
+ const label = button.getAttribute('aria-label') || '';
879
+ const text = (button.innerText || button.textContent || '').replace(/\\s+/g, ' ').trim();
880
+ return labels.includes(label) || labels.includes(text) || /send|发送/i.test(label) || /send|发送/i.test(text);
881
+ };
882
+ const sendBtn = isUsable(primary)
883
+ ? primary
884
+ : btns.find(b => looksLikeSend(b) && isUsable(b));
885
+ return { sendBtnFound: !!sendBtn };
886
+ })()
887
+ `)), 'chatgpt send button readiness');
888
+ if (sent?.sendBtnFound) break;
889
+ }
890
+
891
+ if (!sent?.sendBtnFound) {
892
+ return false;
893
+ }
894
+
895
+ await page.evaluate(`
896
+ (() => {
897
+ const isVisible = (el) => {
898
+ if (!(el instanceof HTMLElement)) return false;
899
+ const style = window.getComputedStyle(el);
900
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
901
+ const rect = el.getBoundingClientRect();
902
+ return rect.width > 0 && rect.height > 0;
903
+ };
904
+ const isUsable = (button) => button
905
+ && isVisible(button)
906
+ && !button.disabled
907
+ && button.getAttribute('aria-disabled') !== 'true';
908
+ const form = Array.from(document.querySelectorAll('form')).find((node) => node instanceof HTMLElement && isVisible(node));
909
+ const root = form || document.body;
910
+ const primary = root.querySelector(${JSON.stringify(SEND_BUTTON_SELECTOR)})
911
+ || ${JSON.stringify(SEND_BUTTON_FALLBACK_SELECTORS)}.map(selector => root.querySelector(selector)).find(Boolean);
912
+ const labels = ${JSON.stringify(SEND_BUTTON_LABELS)};
913
+ const looksLikeSend = (button) => {
914
+ const label = button.getAttribute('aria-label') || '';
915
+ const text = (button.innerText || button.textContent || '').replace(/\\s+/g, ' ').trim();
916
+ return labels.includes(label) || labels.includes(text) || /send|发送/i.test(label) || /send|发送/i.test(text);
917
+ };
918
+ const sendBtn = isUsable(primary)
919
+ ? primary
920
+ : Array.from(root.querySelectorAll('button')).find(b => looksLikeSend(b) && isUsable(b));
921
+ if (sendBtn) sendBtn.click();
922
+ })()
923
+ `);
924
+ return true;
925
+ }
926
+
927
+ export async function getVisibleMessages(page) {
928
+ const result = requireArrayEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
929
+ const isVisible = (el) => {
930
+ if (!(el instanceof HTMLElement)) return false;
931
+ const style = window.getComputedStyle(el);
932
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
933
+ const rect = el.getBoundingClientRect();
934
+ return rect.width > 0 && rect.height > 0;
935
+ };
936
+ const normalize = (value) => String(value || '').replace(/\\u00a0/g, ' ').replace(/[ \\t]+\\n/g, '\\n').replace(/\\n{3,}/g, '\\n\\n').trim();
937
+ const roleOf = (node) => {
938
+ const attr = node.getAttribute('data-message-author-role') || node.getAttribute('data-author') || '';
939
+ if (/assistant/i.test(attr)) return 'Assistant';
940
+ if (/user/i.test(attr)) return 'User';
941
+ const testid = node.getAttribute('data-testid') || '';
942
+ if (/assistant/i.test(testid)) return 'Assistant';
943
+ if (/user/i.test(testid)) return 'User';
944
+ const label = node.getAttribute('aria-label') || '';
945
+ if (/assistant|chatgpt/i.test(label)) return 'Assistant';
946
+ if (/you|user/i.test(label)) return 'User';
947
+ return '';
948
+ };
949
+
950
+ let nodes = Array.from(document.querySelectorAll('[data-message-author-role], article[data-testid*="conversation-turn"]'));
951
+ nodes = nodes.filter((node) => node instanceof HTMLElement && isVisible(node));
952
+
953
+ const rows = [];
954
+ const seen = new Set();
955
+ for (const node of nodes) {
956
+ let role = roleOf(node);
957
+ const roleNode = node.querySelector('[data-message-author-role], [data-author]');
958
+ if (!role && roleNode) role = roleOf(roleNode);
959
+ if (!role) continue;
960
+
961
+ const contentNode = node.querySelector('[data-message-author-role] .markdown')
962
+ || node.querySelector('.markdown')
963
+ || node.querySelector('[data-message-author-role]')
964
+ || node;
965
+ const html = contentNode instanceof HTMLElement ? (contentNode.innerHTML || '') : '';
966
+ const text = normalize(contentNode instanceof HTMLElement ? (contentNode.innerText || contentNode.textContent || '') : '');
967
+ if (!text) continue;
968
+ const key = role + '\\n' + text;
969
+ if (seen.has(key)) continue;
970
+ seen.add(key);
971
+ rows.push({ role, text, html });
972
+ }
973
+ return rows;
974
+ })()`)), 'chatgpt visible messages');
975
+ return result.map((item, index) => ({
976
+ Index: index + 1,
977
+ Role: item?.role === 'Assistant' ? 'Assistant' : 'User',
978
+ Text: String(item?.text || '').trim(),
979
+ Html: String(item?.html || ''),
980
+ })).filter((item) => item.Text);
981
+ }
982
+
983
+ function formatChatGPTDetailMessages(messages, { wantMarkdown, generating, stableSeconds }) {
984
+ return messages.map((message) => ({
985
+ Index: message.Index,
986
+ Role: message.Role,
987
+ Text: wantMarkdown && message.Role === 'Assistant' && message.Html
988
+ ? (messageHtmlToMarkdown(message.Html) || message.Text)
989
+ : message.Text,
990
+ Generating: generating,
991
+ StableSeconds: stableSeconds,
992
+ }));
993
+ }
994
+
995
+ export async function getChatGPTDetailRows(page, { wantMarkdown = false, stableSeconds = 0 } = {}) {
996
+ const generating = await isGenerating(page);
997
+ const messages = await getVisibleMessages(page);
998
+ return {
999
+ messages,
1000
+ rows: formatChatGPTDetailMessages(messages, { wantMarkdown, generating, stableSeconds }),
1001
+ generating,
1002
+ };
1003
+ }
1004
+
1005
+ export async function waitForChatGPTDetailRows(page, { wantMarkdown = false, timeoutSeconds = 120, stableSeconds = 6 } = {}) {
1006
+ const startTime = Date.now();
1007
+ let lastKey = '';
1008
+ let stableStartedAt = 0;
1009
+
1010
+ while (Date.now() - startTime < timeoutSeconds * 1000) {
1011
+ const generating = await isGenerating(page);
1012
+ const messages = await getVisibleMessages(page);
1013
+ const key = JSON.stringify(messages.map((message) => [message.Role, message.Text]));
1014
+ if (!generating && messages.length && messages[messages.length - 1]?.Role === 'Assistant') {
1015
+ if (key === lastKey) {
1016
+ if (!stableStartedAt) stableStartedAt = Date.now();
1017
+ const elapsedSeconds = Math.floor((Date.now() - stableStartedAt) / 1000);
1018
+ if (elapsedSeconds >= stableSeconds) {
1019
+ return {
1020
+ messages,
1021
+ rows: formatChatGPTDetailMessages(messages, {
1022
+ wantMarkdown,
1023
+ generating: false,
1024
+ stableSeconds: elapsedSeconds,
1025
+ }),
1026
+ generating: false,
1027
+ };
1028
+ }
1029
+ } else {
1030
+ lastKey = key;
1031
+ stableStartedAt = Date.now();
1032
+ }
1033
+ } else {
1034
+ lastKey = key;
1035
+ stableStartedAt = 0;
1036
+ }
1037
+ await page.wait(3);
1038
+ }
1039
+
1040
+ throw new TimeoutError(
1041
+ 'chatgpt detail',
1042
+ timeoutSeconds,
1043
+ 'Conversation did not finish or stabilize before timeout. Re-run with a higher --timeout if it is still generating.',
1044
+ );
1045
+ }
1046
+
1047
+ export function messageHtmlToMarkdown(html) {
1048
+ try {
1049
+ return htmlToMarkdown(html).trim();
1050
+ } catch {
1051
+ return String(html || '').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
1052
+ }
1053
+ }
1054
+
1055
+ export async function getBubbleCount(page) {
1056
+ const messages = await getVisibleMessages(page);
1057
+ return messages.length;
1058
+ }
1059
+
1060
+ function cleanPromptText(str) {
1061
+ return String(str || '')
1062
+ .replace(/\s+/g, ' ')
1063
+ .trim();
1064
+ }
1065
+
1066
+ function responsePairKey(user, assistant) {
1067
+ return JSON.stringify([
1068
+ cleanPromptText(user?.Text),
1069
+ String(assistant?.Text || '').trim(),
1070
+ ]);
1071
+ }
1072
+
1073
+ export function getChatGPTResponsePairKeys(messages, prompt) {
1074
+ const promptKey = cleanPromptText(prompt);
1075
+ if (!promptKey) return [];
1076
+ const keys = [];
1077
+ for (let index = 0; index < messages.length; index += 1) {
1078
+ const user = messages[index];
1079
+ if (user?.Role !== 'User' || cleanPromptText(user.Text) !== promptKey) continue;
1080
+ const assistant = messages.slice(index + 1).find((message) => message?.Role === 'Assistant');
1081
+ if (!assistant || !String(assistant.Text || '').trim()) continue;
1082
+ keys.push(responsePairKey(user, assistant));
1083
+ }
1084
+ return keys;
1085
+ }
1086
+
1087
+ export function getChatGPTResponsePairCounts(messages, prompt) {
1088
+ const counts = new Map();
1089
+ for (const key of getChatGPTResponsePairKeys(messages, prompt)) {
1090
+ counts.set(key, (counts.get(key) || 0) + 1);
1091
+ }
1092
+ return counts;
1093
+ }
1094
+
1095
+ function normalizeBaselinePairCounts(options) {
1096
+ if (options.baselinePairCounts instanceof Map) return options.baselinePairCounts;
1097
+ return new Map(Array.from(options.baselinePairKeys || []).map((key) => [key, 1]));
1098
+ }
1099
+
1100
+ function findLatestNewAssistantResponse(messages, prompt, baselinePairCounts) {
1101
+ const promptKey = cleanPromptText(prompt);
1102
+ if (!promptKey) return '';
1103
+ const currentPairCounts = getChatGPTResponsePairCounts(messages, prompt);
1104
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
1105
+ const user = messages[index];
1106
+ if (user?.Role !== 'User' || cleanPromptText(user.Text) !== promptKey) continue;
1107
+ const assistantIndex = messages.findIndex((message, candidateIndex) => (
1108
+ candidateIndex > index
1109
+ && message?.Role === 'Assistant'
1110
+ && String(message.Text || '').trim()
1111
+ ));
1112
+ if (assistantIndex < 0) continue;
1113
+ const assistant = messages[assistantIndex];
1114
+ const key = responsePairKey(user, assistant);
1115
+ if ((currentPairCounts.get(key) || 0) <= (baselinePairCounts.get(key) || 0)) continue;
1116
+ return String(assistant.Text || '').trim();
1117
+ }
1118
+ return '';
1119
+ }
1120
+
1121
+ export async function waitForChatGPTResponse(page, baselineCount, prompt, timeoutSeconds, options = {}) {
1122
+ const startTime = Date.now();
1123
+ let lastText = '';
1124
+ let stableCount = 0;
1125
+ const baselinePairCounts = normalizeBaselinePairCounts(options);
1126
+
1127
+ while (Date.now() - startTime < timeoutSeconds * 1000) {
1128
+ await page.wait(3);
1129
+ if (options.conversationUrl) {
1130
+ const currentUrl = await currentChatGPTUrl(page);
1131
+ if (currentUrl && !isSameChatGPTConversation(currentUrl, options.conversationUrl)) {
1132
+ throw new CommandExecutionError(
1133
+ `ChatGPT navigated away from the target conversation (${options.conversationUrl}); current URL is ${currentUrl}`,
1134
+ );
1135
+ }
1136
+ }
1137
+ if (await isGenerating(page)) {
1138
+ stableCount = 0;
1139
+ continue;
1140
+ }
1141
+
1142
+ const messages = await getVisibleMessages(page);
1143
+ const candidate = findLatestNewAssistantResponse(messages, prompt, baselinePairCounts);
1144
+ if (!candidate || candidate === String(prompt || '').trim()) continue;
1145
+
1146
+ if (candidate === lastText) {
1147
+ stableCount += 1;
1148
+ if (stableCount >= 2) return candidate;
1149
+ } else {
1150
+ lastText = candidate;
1151
+ stableCount = 0;
1152
+ }
1153
+ }
1154
+
1155
+ throw new TimeoutError(
1156
+ 'chatgpt ask',
1157
+ timeoutSeconds,
1158
+ 'No ChatGPT response appeared before timeout. Re-run with a higher --timeout if it is still generating.',
1159
+ );
1160
+ }
1161
+
1162
+ export async function getConversationList(page) {
1163
+ // ensureOnChatGPT already waits for the composer selector after navigation,
1164
+ // so the previous standalone 2 s settle is redundant.
1165
+ await ensureOnChatGPT(page);
1166
+
1167
+ const openSidebar = requireBooleanEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
1168
+ const button = Array.from(document.querySelectorAll('button'))
1169
+ .find((node) => /open sidebar/i.test(node.getAttribute('aria-label') || ''));
1170
+ if (button instanceof HTMLElement) {
1171
+ button.click();
1172
+ return true;
1173
+ }
1174
+ return false;
1175
+ })()`)), 'chatgpt sidebar open state');
1176
+ if (openSidebar) {
1177
+ try {
1178
+ await page.wait({ selector: CONVERSATION_LINK_SELECTOR, timeout: 3 });
1179
+ } catch {
1180
+ // Sidebar slide-in didn't surface conversation links; extractConversationLinks below tolerates empty and falls back to home goto.
1181
+ }
1182
+ }
1183
+
1184
+ let items = await extractConversationLinks(page);
1185
+ if (!items.length) {
1186
+ await page.goto(CHATGPT_URL, { settleMs: 2000 });
1187
+ try {
1188
+ await page.wait({ selector: CONVERSATION_LINK_SELECTOR, timeout: 8 });
1189
+ } catch {
1190
+ // No conversation links visible after fallback goto; extractConversationLinks returns empty.
1191
+ }
1192
+ items = await extractConversationLinks(page);
1193
+ }
1194
+
1195
+ return items;
1196
+ }
1197
+
1198
+ async function extractConversationLinks(page) {
1199
+ const items = requireArrayEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
1200
+ const isVisible = (el) => {
1201
+ if (!(el instanceof HTMLElement)) return false;
1202
+ const style = window.getComputedStyle(el);
1203
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
1204
+ const rect = el.getBoundingClientRect();
1205
+ return rect.width > 0 && rect.height > 0;
1206
+ };
1207
+ const links = Array.from(document.querySelectorAll('a[href*="/c/"]'))
1208
+ .filter((link) => link instanceof HTMLAnchorElement && isVisible(link));
1209
+ const seen = new Set();
1210
+ const rows = [];
1211
+ for (const link of links) {
1212
+ const href = link.getAttribute('href') || '';
1213
+ const match = href.match(/\\/c\\/([^/?#]+)/);
1214
+ if (!match || seen.has(match[1])) continue;
1215
+ seen.add(match[1]);
1216
+ const title = (link.innerText || link.textContent || '').replace(/\\s+/g, ' ').trim() || '(untitled)';
1217
+ rows.push({
1218
+ Id: match[1],
1219
+ Title: title,
1220
+ Url: href.startsWith('http') ? href : ('${CHATGPT_URL}' + href),
1221
+ });
1222
+ }
1223
+ return rows;
1224
+ })()`)), 'chatgpt conversation link extraction');
1225
+ return items.map((item, index) => ({
1226
+ Index: index + 1,
1227
+ Id: String(item?.Id || ''),
1228
+ Title: String(item?.Title || '(untitled)').trim() || '(untitled)',
1229
+ Url: String(item?.Url || ''),
1230
+ })).filter((item) => item.Id);
1231
+ }
1232
+
1233
+ function imageMimeFromPath(filePath) {
1234
+ const lower = String(filePath || '').toLowerCase();
1235
+ if (lower.endsWith('.png')) return 'image/png';
1236
+ if (lower.endsWith('.webp')) return 'image/webp';
1237
+ if (lower.endsWith('.gif')) return 'image/gif';
1238
+ if (lower.endsWith('.heic')) return 'image/heic';
1239
+ if (lower.endsWith('.heif')) return 'image/heif';
1240
+ return 'image/jpeg';
1241
+ }
1242
+
1243
+ export async function prepareChatGPTImagePaths(imagePaths) {
1244
+ const fs = await import('node:fs');
1245
+ const path = await import('node:path');
1246
+ const absPaths = imagePaths.map(filePath => path.default.resolve(filePath));
1247
+ const allowedExts = new Set(['.jpg', '.jpeg', '.png', '.webp', '.gif', '.heic', '.heif']);
1248
+
1249
+ for (const absPath of absPaths) {
1250
+ if (!fs.default.existsSync(absPath)) {
1251
+ return { ok: false, reason: `Image not found: ${absPath}` };
1252
+ }
1253
+ const stat = fs.default.statSync(absPath);
1254
+ if (!stat.isFile()) {
1255
+ return { ok: false, reason: `Not a file: ${absPath}` };
1256
+ }
1257
+ if (stat.size > 25 * 1024 * 1024) {
1258
+ return { ok: false, reason: `Image too large (${(stat.size / 1024 / 1024).toFixed(1)} MB). Max: 25 MB` };
1259
+ }
1260
+ const ext = path.default.extname(absPath).toLowerCase();
1261
+ if (!allowedExts.has(ext)) {
1262
+ return { ok: false, reason: `Unsupported image type: ${absPath}` };
1263
+ }
1264
+ }
1265
+
1266
+ return { ok: true, paths: absPaths };
1267
+ }
1268
+
1269
+ async function waitForChatGPTUploadPreview(page, fileNames) {
1270
+ const namesJson = JSON.stringify(fileNames);
1271
+ for (let attempt = 0; attempt < 10; attempt += 1) {
1272
+ await page.wait(1);
1273
+ const ready = requireBooleanEvaluateResult(unwrapEvaluateResult(await page.evaluate(`
1274
+ (() => {
1275
+ const names = ${namesJson};
1276
+ const text = document.body ? (document.body.innerText || '') : '';
1277
+ const matchedNames = names.filter(name => text.includes(name)).length;
1278
+ if (matchedNames >= names.length) return true;
1279
+
1280
+ const composer = document.querySelector('[aria-label="Chat with ChatGPT"], [placeholder="Ask anything"], #prompt-textarea');
1281
+ let root = composer;
1282
+ for (let i = 0; i < 6 && root && root.parentElement; i += 1) root = root.parentElement;
1283
+ const scope = root || document.body;
1284
+ if (!scope) return false;
1285
+
1286
+ const isVisibleMedia = (node) => {
1287
+ if (!(node instanceof HTMLElement)) return false;
1288
+ const style = window.getComputedStyle(node);
1289
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
1290
+ const rect = node.getBoundingClientRect();
1291
+ const width = node.naturalWidth || node.videoWidth || rect.width || 0;
1292
+ const height = node.naturalHeight || node.videoHeight || rect.height || 0;
1293
+ if (width > 32 && height > 32) return true;
1294
+ const backgroundImage = style.backgroundImage || '';
1295
+ return /url\\(/.test(backgroundImage) && rect.width > 32 && rect.height > 32;
1296
+ };
1297
+ const previewNodes = Array.from(scope.querySelectorAll('img[src], canvas, video, [style*="background-image"]')).filter(isVisibleMedia);
1298
+ return previewNodes.length >= names.length;
1299
+ })()
1300
+ `)), 'chatgpt upload preview detection');
1301
+ if (ready) return true;
1302
+ }
1303
+ return false;
1304
+ }
1305
+
1306
+ export async function uploadChatGPTImages(page, imagePaths) {
1307
+ const fs = await import('node:fs');
1308
+ const path = await import('node:path');
1309
+ const prepared = await prepareChatGPTImagePaths(imagePaths);
1310
+ if (!prepared.ok) return prepared;
1311
+ const absPaths = prepared.paths;
1312
+
1313
+ const fileNames = absPaths.map(filePath => path.default.basename(filePath));
1314
+
1315
+ let uploaded = false;
1316
+ if (page.setFileInput) {
1317
+ try {
1318
+ await page.setFileInput(absPaths, 'input[type="file"]');
1319
+ uploaded = true;
1320
+ } catch (err) {
1321
+ const msg = String(err?.message || err);
1322
+ if (!msg.includes('Unknown action') && !msg.includes('not supported') && !msg.includes('Not allowed') && !msg.includes('No element found')) {
1323
+ throw err;
1324
+ }
1325
+ }
1326
+ }
1327
+
1328
+ if (!uploaded) {
1329
+ const files = absPaths.map(absPath => ({
1330
+ name: path.default.basename(absPath),
1331
+ mime: imageMimeFromPath(absPath),
1332
+ base64: fs.default.readFileSync(absPath).toString('base64'),
1333
+ }));
1334
+ const fallbackResult = requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`
1335
+ (() => {
1336
+ const files = ${JSON.stringify(files)};
1337
+ const input = document.querySelector('input[type="file"]');
1338
+ if (!(input instanceof HTMLInputElement)) {
1339
+ return { ok: false, reason: 'file input not found' };
1340
+ }
1341
+
1342
+ const dt = new DataTransfer();
1343
+ for (const item of files) {
1344
+ const binary = atob(item.base64);
1345
+ const bytes = new Uint8Array(binary.length);
1346
+ for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
1347
+ dt.items.add(new File([bytes], item.name, { type: item.mime }));
1348
+ }
1349
+ input.files = dt.files;
1350
+
1351
+ const propsKey = Object.keys(input).find(key => key.startsWith('__reactProps$'));
1352
+ if (propsKey && input[propsKey] && typeof input[propsKey].onChange === 'function') {
1353
+ const nativeEvent = new Event('change', { bubbles: true });
1354
+ input[propsKey].onChange({
1355
+ target: input,
1356
+ currentTarget: input,
1357
+ nativeEvent,
1358
+ preventDefault() {},
1359
+ stopPropagation() {},
1360
+ isDefaultPrevented() { return false; },
1361
+ isPropagationStopped() { return false; },
1362
+ persist() {},
1363
+ });
1364
+ } else {
1365
+ input.dispatchEvent(new Event('input', { bubbles: true }));
1366
+ input.dispatchEvent(new Event('change', { bubbles: true }));
1367
+ }
1368
+ return { ok: true };
1369
+ })()
1370
+ `)), 'chatgpt image upload fallback');
1371
+ if (fallbackResult && !fallbackResult.ok) return fallbackResult;
1372
+ }
1373
+
1374
+ const ready = await waitForChatGPTUploadPreview(page, fileNames);
1375
+ if (!ready) return { ok: false, reason: 'image upload preview did not appear' };
1376
+
1377
+ return { ok: true, files: absPaths };
1378
+ }
1379
+
1380
+ /**
1381
+ * Check if ChatGPT is still generating a response.
1382
+ */
1383
+ export async function isGenerating(page) {
1384
+ return requireBooleanEvaluateResult(unwrapEvaluateResult(await page.evaluate(`
1385
+ (() => {
1386
+ const text = (document.body?.innerText || '').replace(/\\s+/g, ' ');
1387
+ if (/正在思考|停止生成|Thinking/.test(text)) return true;
1388
+ return Array.from(document.querySelectorAll('button')).some(b => {
1389
+ const label = b.getAttribute('aria-label') || '';
1390
+ return label === 'Stop generating'
1391
+ || label.includes('Thinking')
1392
+ || label.includes('停止生成')
1393
+ || label.includes('正在思考');
1394
+ });
1395
+ })()
1396
+ `)), 'chatgpt generation state');
1397
+ }
1398
+
1399
+ /**
1400
+ * Get visible image URLs from the ChatGPT page (excluding profile/avatar images).
1401
+ */
1402
+ export async function getChatGPTVisibleImageUrls(page) {
1403
+ return requireArrayEvaluateResult(unwrapEvaluateResult(await page.evaluate(`
1404
+ (() => {
1405
+ const isVisible = (el) => {
1406
+ if (!(el instanceof HTMLElement)) return false;
1407
+ const style = window.getComputedStyle(el);
1408
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
1409
+ const rect = el.getBoundingClientRect();
1410
+ return rect.width > 32 && rect.height > 32;
1411
+ };
1412
+
1413
+ const urls = [];
1414
+ const seen = new Set();
1415
+ const normalizeUrl = (value) => {
1416
+ const raw = String(value || '').trim();
1417
+ if (!raw || raw === 'none') return '';
1418
+ if (/^(?:https?:|blob:|data:)/i.test(raw)) return raw;
1419
+ try {
1420
+ return new URL(raw, window.location.href).href;
1421
+ } catch {
1422
+ return raw;
1423
+ }
1424
+ };
1425
+ const addUrl = (value) => {
1426
+ const src = normalizeUrl(value);
1427
+ if (!src || seen.has(src)) return;
1428
+ seen.add(src);
1429
+ urls.push(src);
1430
+ };
1431
+ const isDecorative = (el, src = '') => {
1432
+ const alt = (el.getAttribute('alt') || '').toLowerCase();
1433
+ const cls = String(el.className || '').toLowerCase();
1434
+ const testId = (el.getAttribute('data-testid') || '').toLowerCase();
1435
+ const label = (el.getAttribute('aria-label') || '').toLowerCase();
1436
+ const text = [alt, cls, testId, label, src.toLowerCase()].join(' ');
1437
+ return /avatar|profile|logo|icon/.test(text);
1438
+ };
1439
+ const isUserUploadPreview = (img) => {
1440
+ const alt = (img.getAttribute('alt') || '').toLowerCase();
1441
+ const turn = img.closest('section[data-testid^="conversation-turn"]');
1442
+ const heading = (turn?.querySelector('h4')?.innerText || '').toLowerCase();
1443
+ if (/you said|你说/.test(heading)) return true;
1444
+ if (/chatgpt|assistant|助手/.test(heading)) return false;
1445
+ const openButtonLabel = (img.closest('button[aria-label^="Open image:"]')?.getAttribute('aria-label') || '').toLowerCase();
1446
+ const previewText = [alt, openButtonLabel].join(' ');
1447
+ return /\.(png|jpe?g|webp|gif|heic|heif)(?:\b|$)/i.test(previewText)
1448
+ || /ref-|reference|参考|upload|uploaded|attachment/.test(previewText);
1449
+ };
1450
+
1451
+ const imgs = Array.from(document.querySelectorAll('img')).filter(img =>
1452
+ img instanceof HTMLImageElement && isVisible(img)
1453
+ );
1454
+
1455
+ for (const img of imgs) {
1456
+ const src = img.currentSrc || img.src || '';
1457
+ const width = img.naturalWidth || img.width || 0;
1458
+ const height = img.naturalHeight || img.height || 0;
1459
+
1460
+ if (!src) continue;
1461
+ if (isDecorative(img, src)) continue;
1462
+ if (isUserUploadPreview(img)) continue;
1463
+ if (width < 128 && height < 128) continue;
1464
+ addUrl(src);
1465
+ }
1466
+
1467
+ // ChatGPT occasionally renders generated images as CSS background
1468
+ // thumbnails instead of plain <img> nodes. Treat visible, large
1469
+ // background images as generated-image candidates too.
1470
+ for (const el of Array.from(document.querySelectorAll('[style*="background-image"], [style*="background"]'))) {
1471
+ if (!(el instanceof HTMLElement) || !isVisible(el) || isDecorative(el)) continue;
1472
+ const rect = el.getBoundingClientRect();
1473
+ if (rect.width < 128 && rect.height < 128) continue;
1474
+ const backgroundImage = window.getComputedStyle(el).backgroundImage || '';
1475
+ for (const match of backgroundImage.matchAll(/url\\((['"]?)(.*?)\\1\\)/g)) {
1476
+ const src = match[2];
1477
+ if (!src || isDecorative(el, src)) continue;
1478
+ addUrl(src);
1479
+ }
1480
+ }
1481
+
1482
+ // Some ChatGPT image surfaces mount large transparent canvases as
1483
+ // placeholders/overlays before the real backend image is ready. If
1484
+ // those data URLs are accepted as generated assets, the adapter can
1485
+ // save a blank transparent PNG while reporting success. Prefer real
1486
+ // <img>/background URLs; only keep a canvas if it contains at least
1487
+ // one non-transparent/non-white sampled pixel.
1488
+ for (const canvas of Array.from(document.querySelectorAll('canvas'))) {
1489
+ if (!(canvas instanceof HTMLCanvasElement) || !isVisible(canvas) || isDecorative(canvas)) continue;
1490
+ const width = canvas.width || canvas.getBoundingClientRect().width || 0;
1491
+ const height = canvas.height || canvas.getBoundingClientRect().height || 0;
1492
+ if (width < 128 && height < 128) continue;
1493
+ try {
1494
+ const ctx = canvas.getContext('2d', { willReadFrequently: true });
1495
+ if (!ctx) continue;
1496
+ const sourceWidth = Math.max(1, Math.floor(canvas.width || width));
1497
+ const sourceHeight = Math.max(1, Math.floor(canvas.height || height));
1498
+ const xCount = Math.min(sourceWidth, 16);
1499
+ const yCount = Math.min(sourceHeight, 16);
1500
+ let hasContent = false;
1501
+ for (let yi = 0; yi < yCount && !hasContent; yi += 1) {
1502
+ const y = Math.min(sourceHeight - 1, Math.floor((yi + 0.5) * sourceHeight / yCount));
1503
+ for (let xi = 0; xi < xCount && !hasContent; xi += 1) {
1504
+ const x = Math.min(sourceWidth - 1, Math.floor((xi + 0.5) * sourceWidth / xCount));
1505
+ const pixel = ctx.getImageData(x, y, 1, 1).data;
1506
+ const r = pixel[0];
1507
+ const g = pixel[1];
1508
+ const b = pixel[2];
1509
+ const a = pixel[3];
1510
+ if (a > 0 && !(r > 248 && g > 248 && b > 248)) {
1511
+ hasContent = true;
1512
+ break;
1513
+ }
1514
+ }
1515
+ }
1516
+ if (hasContent) addUrl(canvas.toDataURL('image/png'));
1517
+ } catch { }
1518
+ }
1519
+ return urls;
1520
+ })()
1521
+ `)), 'chatgpt visible image url extraction');
1522
+ }
1523
+
1524
+ /**
1525
+ * Wait for new images to appear after sending a prompt.
1526
+ */
1527
+ export async function waitForChatGPTImages(page, beforeUrls, timeoutSeconds, convUrl) {
1528
+ const beforeSet = new Set(beforeUrls);
1529
+ const pollIntervalSeconds = 3;
1530
+ const maxPolls = Math.max(1, Math.ceil(timeoutSeconds / pollIntervalSeconds));
1531
+ let lastUrls = [];
1532
+ let stableCount = 0;
1533
+
1534
+ for (let i = 0; i < maxPolls; i++) {
1535
+ await page.wait(i === 0 ? 3 : pollIntervalSeconds);
1536
+
1537
+ let currentUrl = '';
1538
+ if (convUrl && convUrl.includes('/c/')) {
1539
+ currentUrl = unwrapEvaluateResult(await page.evaluate('window.location.href').catch(() => ''));
1540
+ if (currentUrl && !isSameChatGPTConversation(currentUrl, convUrl)) {
1541
+ await page.goto(convUrl);
1542
+ await page.wait(3);
1543
+ }
1544
+ }
1545
+
1546
+ const generating = await isGenerating(page);
1547
+ if (generating) continue;
1548
+
1549
+ if (convUrl && convUrl.includes('/c/') && i > 0 && i % 5 === 0) {
1550
+ const onConversation = !currentUrl || isSameChatGPTConversation(currentUrl, convUrl);
1551
+ if (onConversation) {
1552
+ await page.goto(convUrl);
1553
+ await page.wait(3);
1554
+ }
1555
+ }
1556
+
1557
+ const urls = (await getChatGPTVisibleImageUrls(page)).filter(url => !beforeSet.has(url));
1558
+ if (urls.length === 0) continue;
1559
+
1560
+ const key = urls.join('\n');
1561
+ const prevKey = lastUrls.join('\n');
1562
+ if (key === prevKey) {
1563
+ stableCount += 1;
1564
+ } else {
1565
+ lastUrls = urls;
1566
+ stableCount = 1;
1567
+ }
1568
+
1569
+ if (stableCount >= 2 || i === maxPolls - 1) {
1570
+ return lastUrls;
1571
+ }
1572
+ }
1573
+ return lastUrls;
1574
+ }
1575
+
1576
+ /**
1577
+ * Get the list of ChatGPT Projects from the sidebar.
1578
+ * Navigates to chatgpt.com if not already there, opens the sidebar,
1579
+ * and extracts project links (matching /g/g-p-*).
1580
+ */
1581
+ export async function getProjectList(page) {
1582
+ await ensureOnChatGPT(page);
1583
+
1584
+ // Ensure sidebar is open
1585
+ const openSidebar = requireBooleanEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
1586
+ const button = Array.from(document.querySelectorAll('button'))
1587
+ .find((node) => /open sidebar/i.test(node.getAttribute('aria-label') || ''));
1588
+ if (button instanceof HTMLElement) {
1589
+ button.click();
1590
+ return true;
1591
+ }
1592
+ return false;
1593
+ })()`)), 'chatgpt sidebar open state');
1594
+ if (openSidebar) {
1595
+ await page.wait(0.5);
1596
+ }
1597
+
1598
+ // Click "Show more" to reveal all projects
1599
+ await page.evaluate(`(() => {
1600
+ var btn = Array.from(document.querySelectorAll('button')).find(function(b) {
1601
+ var text = (b.innerText || '').trim();
1602
+ return text === 'Show more' || text === '显示更多' || text === '查看更多';
1603
+ });
1604
+ if (btn instanceof HTMLElement) {
1605
+ btn.click();
1606
+ }
1607
+ })()`);
1608
+ await page.wait(0.5);
1609
+
1610
+ let items = await extractProjectLinks(page);
1611
+ if (!items.length) {
1612
+ await page.goto(CHATGPT_URL, { settleMs: 2000 });
1613
+ await page.wait(1);
1614
+ // Try clicking Show more again on fresh page
1615
+ await page.evaluate(`(() => {
1616
+ var btn = Array.from(document.querySelectorAll('button')).find(function(b) {
1617
+ var text = (b.innerText || '').trim();
1618
+ return text === 'Show more' || text === '显示更多' || text === '查看更多';
1619
+ });
1620
+ if (btn instanceof HTMLElement) {
1621
+ btn.click();
1622
+ }
1623
+ })()`);
1624
+ await page.wait(0.5);
1625
+ items = await extractProjectLinks(page);
1626
+ }
1627
+
1628
+ return items;
1629
+ }
1630
+
1631
+ async function extractProjectLinks(page) {
1632
+ const items = requireArrayEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
1633
+ const projectLinkSelector = ${JSON.stringify(PROJECT_LINK_SELECTOR)};
1634
+ const isVisible = (el) => {
1635
+ if (!(el instanceof HTMLElement)) return false;
1636
+ const style = window.getComputedStyle(el);
1637
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
1638
+ const rect = el.getBoundingClientRect();
1639
+ return rect.width > 0 && rect.height > 0;
1640
+ };
1641
+ const cleanText = (value) => String(value || '').replace(new RegExp('\\\\s+', 'g'), ' ').trim();
1642
+ const trustedHost = (hostname) => hostname === '${CHATGPT_DOMAIN}' || hostname.endsWith('.${CHATGPT_DOMAIN}');
1643
+ const projectIdFromPathname = (pathname) => {
1644
+ const match = String(pathname || '').match(new RegExp('^/g/g-p-([a-f0-9]{8,})(?:[-/]|$)', 'i'));
1645
+ return match ? match[1].toLowerCase() : '';
1646
+ };
1647
+ const parseProjectId = (value) => {
1648
+ const raw = String(value || '').trim();
1649
+ if (new RegExp('^https?://', 'i').test(raw) || raw.startsWith('/')) {
1650
+ try {
1651
+ const url = new URL(raw, '${CHATGPT_URL}');
1652
+ if (url.protocol !== 'https:' || !trustedHost(url.hostname)) return '';
1653
+ return projectIdFromPathname(url.pathname);
1654
+ } catch {
1655
+ return '';
1656
+ }
1657
+ }
1658
+ const slugMatch = raw.match(new RegExp('^g-p-([a-f0-9]{8,})', 'i'));
1659
+ if (slugMatch) return slugMatch[1].toLowerCase();
1660
+ if (new RegExp('^[a-f0-9]{8,}$', 'i').test(raw)) return raw.toLowerCase();
1661
+ return '';
1662
+ };
1663
+ const normalizeProjectUrl = (href, projectId) => {
1664
+ try {
1665
+ const url = new URL(href, '${CHATGPT_URL}');
1666
+ if (url.protocol !== 'https:' || !trustedHost(url.hostname)) return '';
1667
+ if (projectIdFromPathname(url.pathname) !== projectId) return '';
1668
+ url.search = '';
1669
+ url.hash = '';
1670
+ return url.href.endsWith('/') ? url.href.slice(0, -1) : url.href;
1671
+ } catch {
1672
+ return '${CHATGPT_URL}' + '/g/g-p-' + projectId;
1673
+ }
1674
+ };
1675
+
1676
+ var seen = new Set();
1677
+ var rows = [];
1678
+ const addRow = (projectId, title, url) => {
1679
+ if (!projectId || seen.has(projectId)) return;
1680
+ seen.add(projectId);
1681
+ rows.push({
1682
+ Id: projectId,
1683
+ Title: cleanText(title) || '(untitled project)',
1684
+ Url: url || ('${CHATGPT_URL}' + '/g/g-p-' + projectId),
1685
+ });
1686
+ };
1687
+
1688
+ // Prefer explicit project anchors when the sidebar exposes them. This is
1689
+ // stable across React internals and matches the URL shape documented by
1690
+ // PROJECT_LINK_SELECTOR.
1691
+ for (const link of Array.from(document.querySelectorAll(projectLinkSelector))) {
1692
+ if (!isVisible(link)) continue;
1693
+ const href = link.getAttribute('href') || link.href || '';
1694
+ const projectId = parseProjectId(href);
1695
+ if (!projectId) continue;
1696
+ const container = link.closest('[data-sidebar-item="true"]') || link;
1697
+ addRow(projectId, cleanText(container.innerText || container.textContent || link.textContent), normalizeProjectUrl(href, projectId));
1698
+ }
1699
+
1700
+ // Fallback for ChatGPT sidebar builds that render project rows without
1701
+ // anchors but keep gizmo data on React Fiber props.
1702
+ const projectEls = Array.from(document.querySelectorAll('[data-sidebar-item="true"]'))
1703
+ .filter(function(el) {
1704
+ if (!isVisible(el)) return false;
1705
+ var icon = el.querySelector('[data-testid="project-folder-icon"]');
1706
+ if (!icon) return false;
1707
+ var text = cleanText(el.innerText || el.textContent);
1708
+ if (!text) return false;
1709
+ if (el.getAttribute('data-testid') === 'accounts-profile-button') return false;
1710
+ return true;
1711
+ });
1712
+
1713
+ for (var i = 0; i < projectEls.length; i++) {
1714
+ var el = projectEls[i];
1715
+ var title = cleanText(el.innerText || el.textContent);
1716
+ var projectId = '';
1717
+ var shortUrl = '';
1718
+ var fiberKey = Object.keys(el).find(function(k) { return k.startsWith('__reactFiber$'); });
1719
+ if (fiberKey) {
1720
+ var f = el[fiberKey];
1721
+ for (var d = 0; f && d < 15; d++) {
1722
+ var props = f.memoizedProps || f.pendingProps;
1723
+ if (props && props.gizmo) {
1724
+ var g = props.gizmo;
1725
+ var gId = g.gizmo && g.gizmo.id ? g.gizmo.id : g.id;
1726
+ var gIdMatch = String(gId || '').match(new RegExp('^g-p-([a-f0-9]{8,})(?:-|$)', 'i'));
1727
+ if (gIdMatch) {
1728
+ projectId = gIdMatch[1].toLowerCase();
1729
+ shortUrl = String(g.short_url || g.gizmo && g.gizmo.short_url || '');
1730
+ break;
1731
+ }
1732
+ }
1733
+ f = f.return;
1734
+ }
1735
+ }
1736
+ if (!projectId) continue;
1737
+ var url = shortUrl ? '${CHATGPT_URL}' + '/g/' + shortUrl : '${CHATGPT_URL}' + '/g/g-p-' + projectId;
1738
+ addRow(projectId, title, url);
1739
+ }
1740
+
1741
+ return rows;
1742
+ })()`)), 'chatgpt project link extraction');
1743
+ return items.map(function(item, index) {
1744
+ return {
1745
+ Index: index + 1,
1746
+ Id: String(item?.Id || ''),
1747
+ Title: String(item?.Title || '(untitled project)').trim() || '(untitled project)',
1748
+ Url: String(item?.Url || ''),
1749
+ };
1750
+ }).filter(function(item) { return item.Id; });
1751
+ }
1752
+
1753
+ /**
1754
+ * Navigate to a ChatGPT project page.
1755
+ */
1756
+ const PROJECT_ADD_FILES_LABELS = [
1757
+ 'Add files',
1758
+ 'Add sources',
1759
+ '添加文件',
1760
+ 'Project files',
1761
+ '项目文件',
1762
+ ];
1763
+
1764
+ const PROJECT_ADD_FILES_DIALOG_SELECTORS = [
1765
+ '[role="tabpanel"][data-state="active"] [data-project-home-sources-surface="true"] input[type="file"]:not([accept])',
1766
+ '[data-project-home-sources-surface="true"] input[type="file"]:not([accept])',
1767
+ '[role="dialog"] input[type="file"]',
1768
+ '[data-testid*="project-files"] input[type="file"]',
1769
+ '[data-testid*="project"] input[type="file"]',
1770
+ ];
1771
+
1772
+ /**
1773
+ * Navigate to a ChatGPT project page.
1774
+ */
1775
+ export async function navigateToProject(page, projectId) {
1776
+ const id = parseChatGPTProjectId(projectId);
1777
+ await page.goto(`${CHATGPT_URL}/g/g-p-${id}`, { settleMs: 2000 });
1778
+ try {
1779
+ await page.wait({ selector: COMPOSER_WAIT_SELECTOR, timeout: 10 });
1780
+ } catch {
1781
+ // Composer may not mount if project requires login; downstream ensureChatGPTLogin handles it.
1782
+ }
1783
+ const state = await getPageState(page);
1784
+ if (projectIdFromUrl(state.url) === id) return id;
1785
+ if (state.hasLoginGate || !state.isLoggedIn) {
1786
+ throw new AuthRequiredError(CHATGPT_DOMAIN, 'ChatGPT project requires a logged-in ChatGPT session.');
1787
+ }
1788
+ throw new CommandExecutionError(
1789
+ `ChatGPT did not open the requested project ${id}.`,
1790
+ `Current URL: ${state.url || '(unknown)'}`,
1791
+ );
1792
+ }
1793
+
1794
+ /**
1795
+ * Open the Project knowledge files dialog by clicking the "Add files" button
1796
+ * in the project header area (NOT the chat composer's plus button).
1797
+ * Returns true if the dialog appeared.
1798
+ */
1799
+ export async function openProjectKnowledgeDialog(page) {
1800
+ const rawOpenResult = unwrapEvaluateResult(await page.evaluate(`
1801
+ (() => {
1802
+ const labels = ${JSON.stringify(PROJECT_ADD_FILES_LABELS)};
1803
+ const isVisible = (el) => {
1804
+ if (!(el instanceof HTMLElement)) return false;
1805
+ const style = window.getComputedStyle(el);
1806
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
1807
+ const rect = el.getBoundingClientRect();
1808
+ return rect.width > 0 && rect.height > 0;
1809
+ };
1810
+
1811
+ // Current ChatGPT project pages expose project knowledge under a
1812
+ // Sources tab. Prefer that surface when present; it contains the
1813
+ // project-source file input and avoids the chat composer's plus menu.
1814
+ const sourceInput = document.querySelector('[data-project-home-sources-surface="true"] input[type="file"]:not([accept])');
1815
+ if (sourceInput instanceof HTMLInputElement) return { ok: true };
1816
+
1817
+ const sourcesTab = Array.from(document.querySelectorAll('[role="tab"], button')).find(el => {
1818
+ const text = (el.innerText || el.textContent || '').trim();
1819
+ const id = el.id || '';
1820
+ return text === 'Sources' || text === '资料' || id.includes('-sources');
1821
+ });
1822
+ if (sourcesTab instanceof HTMLElement) {
1823
+ if (sourcesTab.getAttribute('aria-selected') === 'true') return { ok: true };
1824
+ const rect = sourcesTab.getBoundingClientRect();
1825
+ const centerX = rect.left + rect.width / 2;
1826
+ const centerY = rect.top + rect.height / 2;
1827
+ const nativeClick = rect.width > 0 && rect.height > 0 && Number.isFinite(centerX) && Number.isFinite(centerY) ? {
1828
+ x: centerX,
1829
+ y: centerY,
1830
+ } : null;
1831
+
1832
+ // Radix-powered tabs on the live ChatGPT project page do not
1833
+ // respond reliably to HTMLElement.click(); they activate after
1834
+ // the same pointer/mouse sequence a real browser click emits.
1835
+ const eventInit = {
1836
+ bubbles: true,
1837
+ cancelable: true,
1838
+ composed: true,
1839
+ view: window,
1840
+ clientX: nativeClick ? nativeClick.x : 0,
1841
+ clientY: nativeClick ? nativeClick.y : 0,
1842
+ button: 0,
1843
+ buttons: 1,
1844
+ };
1845
+ for (const type of ['pointerover', 'pointerenter', 'mouseover', 'mouseenter', 'pointermove', 'mousemove', 'pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click']) {
1846
+ const Ctor = type.startsWith('pointer') && typeof PointerEvent !== 'undefined' ? PointerEvent : MouseEvent;
1847
+ sourcesTab.dispatchEvent(new Ctor(type, eventInit));
1848
+ }
1849
+ return { ok: true, nativeClick };
1850
+ }
1851
+
1852
+ // Older project pages opened a dedicated project files dialog.
1853
+ // Strategy 1: aria-label or data-testid
1854
+ const byAttr = Array.from(document.querySelectorAll('button, a, [role="button"]')).find(el => {
1855
+ if (!isVisible(el)) return false;
1856
+ if (el.closest('[role="textbox"], #prompt-textarea, [data-testid="composer"], form[data-type="unified-composer"]')) return false;
1857
+ const aria = (el.getAttribute('aria-label') || '').toLowerCase();
1858
+ const testid = (el.getAttribute('data-testid') || '').toLowerCase();
1859
+ const text = (el.innerText || el.textContent || '').trim();
1860
+ if (aria.includes('add sources') || aria.includes('project files')) return true;
1861
+ if (aria === 'add files') return true;
1862
+ if (testid.includes('add-files') || testid.includes('project-files')) return true;
1863
+ if (labels.some(l => text === l)) return true;
1864
+ return false;
1865
+ });
1866
+ if (byAttr instanceof HTMLElement) { byAttr.click(); return { ok: true }; }
1867
+
1868
+ // Strategy 2: look for buttons that contain "Add files"/"Add sources"
1869
+ // text, but exclude the composer plus button (which has a different role).
1870
+ const allButtons = Array.from(document.querySelectorAll('button'));
1871
+ for (const btn of allButtons) {
1872
+ if (!isVisible(btn)) continue;
1873
+ const text = (btn.innerText || btn.textContent || '').trim();
1874
+ if (labels.some(l => text === l) && !btn.closest('[role="textbox"], #prompt-textarea, [data-testid="composer"]')) {
1875
+ btn.click();
1876
+ return { ok: true };
1877
+ }
1878
+ }
1879
+
1880
+ return { ok: false };
1881
+ })()
1882
+ `));
1883
+ const openResult = typeof rawOpenResult === 'boolean'
1884
+ ? { ok: rawOpenResult }
1885
+ : requireObjectEvaluateResult(rawOpenResult, 'chatgpt project knowledge dialog open');
1886
+
1887
+ if (openResult.ok) {
1888
+ if (openResult.nativeClick && typeof page.nativeClick === 'function') {
1889
+ try { await page.nativeClick(openResult.nativeClick.x, openResult.nativeClick.y); } catch {}
1890
+ }
1891
+ if (openResult.nativeClick && typeof page.click === 'function') {
1892
+ try { await page.click('[role="tab"][id$="-sources"]'); } catch {}
1893
+ }
1894
+ // Wait for the dialog or Sources tab content to appear
1895
+ await page.wait(1);
1896
+ try {
1897
+ await page.wait({ selector: '[role="dialog"], [data-project-home-sources-surface="true"] input[type="file"]', timeout: 5 });
1898
+ } catch {
1899
+ // Dialog/source input may use a different shape; upload selectors surface the precise failure.
1900
+ }
1901
+ return true;
1902
+ }
1903
+ return false;
1904
+ }
1905
+
1906
+ /**
1907
+ * Upload files to a ChatGPT Project's knowledge base.
1908
+ * This navigates to the project page, opens the knowledge files dialog,
1909
+ * and uploads files through the dialog's file input.
1910
+ */
1911
+ export async function uploadChatGPTProjectFiles(page, projectId, filePaths) {
1912
+ const id = parseChatGPTProjectId(projectId);
1913
+ const fs = await import('node:fs');
1914
+ const path = await import('node:path');
1915
+
1916
+ const prepared = await prepareChatGPTFilePaths(filePaths);
1917
+ if (!prepared.ok) return { ...prepared, inputError: true };
1918
+ const absPaths = prepared.paths;
1919
+
1920
+ // Navigate to project and open knowledge dialog
1921
+ await navigateToProject(page, id);
1922
+ await ensureChatGPTLogin(page, 'ChatGPT project file upload requires a logged-in ChatGPT session.');
1923
+
1924
+ const dialogOpened = await openProjectKnowledgeDialog(page);
1925
+ if (!dialogOpened) {
1926
+ return { ok: false, reason: 'could not find or click the project "Add files" button' };
1927
+ }
1928
+
1929
+ // Try uploading via dialog file input (multiple selector patterns)
1930
+ const fileNames = absPaths.map(fp => path.default.basename(fp));
1931
+
1932
+ let uploaded = false;
1933
+ if (page.setFileInput) {
1934
+ for (const selector of PROJECT_ADD_FILES_DIALOG_SELECTORS) {
1935
+ try {
1936
+ await page.setFileInput(absPaths, selector);
1937
+ uploaded = true;
1938
+ break;
1939
+ } catch (err) {
1940
+ const msg = String(err?.message || err);
1941
+ if (!msg.includes('Unknown action') && !msg.includes('not supported') && !msg.includes('Not allowed') && !msg.includes('No element found')) {
1942
+ throw err;
1943
+ }
1944
+ }
1945
+ }
1946
+ }
1947
+
1948
+ if (!uploaded) {
1949
+ // Fallback: try all dialog file inputs via evaluate
1950
+ const files = absPaths.map(absPath => ({
1951
+ name: path.default.basename(absPath),
1952
+ mime: mimeFromFilePath(absPath),
1953
+ base64: fs.default.readFileSync(absPath).toString('base64'),
1954
+ }));
1955
+ const fallbackResult = requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`
1956
+ (() => {
1957
+ const files = ${JSON.stringify(files)};
1958
+
1959
+ // Look for file input inside a dialog or the project area
1960
+ const selectors = ${JSON.stringify(PROJECT_ADD_FILES_DIALOG_SELECTORS)};
1961
+ let input = null;
1962
+ for (const sel of selectors) {
1963
+ input = document.querySelector(sel);
1964
+ if (input instanceof HTMLInputElement) break;
1965
+ }
1966
+ // Last resort: stay scoped to project knowledge containers. Do
1967
+ // not fall back to arbitrary page inputs, because the composer
1968
+ // attachment input can also accept files but uploads them to
1969
+ // the conversation instead of project knowledge.
1970
+ if (!(input instanceof HTMLInputElement)) {
1971
+ const allFileInputs = document.querySelectorAll('[data-project-home-sources-surface="true"] input[type="file"], [role="dialog"] input[type="file"], [data-testid*="project"] input[type="file"]');
1972
+ for (const fi of allFileInputs) {
1973
+ input = fi;
1974
+ break;
1975
+ }
1976
+ }
1977
+ if (!(input instanceof HTMLInputElement)) {
1978
+ return { ok: false, reason: 'project file input not found' };
1979
+ }
1980
+
1981
+ const dt = new DataTransfer();
1982
+ for (const item of files) {
1983
+ const binary = atob(item.base64);
1984
+ const bytes = new Uint8Array(binary.length);
1985
+ for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
1986
+ dt.items.add(new File([bytes], item.name, { type: item.mime }));
1987
+ }
1988
+ input.files = dt.files;
1989
+
1990
+ const propsKey = Object.keys(input).find(key => key.startsWith('__reactProps$'));
1991
+ if (propsKey && input[propsKey] && typeof input[propsKey].onChange === 'function') {
1992
+ const nativeEvent = new Event('change', { bubbles: true });
1993
+ input[propsKey].onChange({
1994
+ target: input,
1995
+ currentTarget: input,
1996
+ nativeEvent,
1997
+ preventDefault() {},
1998
+ stopPropagation() {},
1999
+ isDefaultPrevented() { return false; },
2000
+ isPropagationStopped() { return false; },
2001
+ persist() {},
2002
+ });
2003
+ } else {
2004
+ input.dispatchEvent(new Event('input', { bubbles: true }));
2005
+ input.dispatchEvent(new Event('change', { bubbles: true }));
2006
+ }
2007
+ return { ok: true };
2008
+ })()
2009
+ `)), 'chatgpt project file upload fallback');
2010
+ if (fallbackResult && !fallbackResult.ok) return fallbackResult;
2011
+ }
2012
+
2013
+ const confirmation = await waitForChatGPTProjectUploadConfirmation(page, fileNames);
2014
+ if (!confirmation.ok) return confirmation;
2015
+
2016
+ return { ok: true, files: absPaths };
2017
+ }
2018
+
2019
+ async function waitForChatGPTProjectUploadConfirmation(page, fileNames) {
2020
+ const expectedFileNames = fileNames.map(name => String(name || '').trim()).filter(Boolean);
2021
+ if (!expectedFileNames.length) return { ok: true };
2022
+
2023
+ let lastReason = 'uploaded file did not appear in project knowledge';
2024
+ for (let attempt = 0; attempt < 10; attempt += 1) {
2025
+ const result = requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`
2026
+ (() => {
2027
+ const expectedFileNames = ${JSON.stringify(expectedFileNames)};
2028
+ const normalize = (value) => String(value || '').replace(/\\s+/g, ' ').trim();
2029
+ const root = document.querySelector('[role="dialog"]')
2030
+ || document.querySelector('[data-project-home-sources-surface="true"]')
2031
+ || document.querySelector('[role="tabpanel"][data-state="active"]');
2032
+ if (!root) {
2033
+ return { ok: false, pending: true, reason: 'project knowledge surface was not visible after upload' };
2034
+ }
2035
+ const text = normalize(root?.innerText || root?.textContent || '');
2036
+ const errorNode = Array.from((root || document).querySelectorAll('[role="alert"], [data-testid*="error"], [class*="error"]')).find((node) => {
2037
+ const label = normalize(node.innerText || node.textContent || node.getAttribute('aria-label') || '');
2038
+ return /failed|error|unable|could not|too large|unsupported/i.test(label);
2039
+ });
2040
+ if (errorNode) {
2041
+ return { ok: false, reason: normalize(errorNode.innerText || errorNode.textContent || errorNode.getAttribute('aria-label') || 'project upload failed') };
2042
+ }
2043
+ const missing = expectedFileNames.filter((name) => !text.includes(name));
2044
+ if (!missing.length) return { ok: true };
2045
+ return { ok: false, pending: true, reason: 'uploaded file did not appear in project knowledge: ' + missing.join(', ') };
2046
+ })()
2047
+ `)), 'chatgpt project upload confirmation');
2048
+
2049
+ if (result.ok === true) return { ok: true };
2050
+ lastReason = String(result.reason || lastReason);
2051
+ if (!result.pending) return { ok: false, reason: lastReason };
2052
+ await page.wait(0.5);
2053
+ }
2054
+
2055
+ return { ok: false, reason: lastReason };
2056
+ }
2057
+
2058
+ /**
2059
+ * Validate local file paths for project file upload.
2060
+ * Accepts all file types with a 512 MB per-file limit (matching ChatGPT's project limit).
2061
+ */
2062
+ export async function prepareChatGPTFilePaths(filePaths) {
2063
+ const fs = await import('node:fs');
2064
+ const path = await import('node:path');
2065
+ const absPaths = filePaths.map(filePath => path.default.resolve(filePath));
2066
+
2067
+ for (const absPath of absPaths) {
2068
+ if (!fs.default.existsSync(absPath)) {
2069
+ return { ok: false, reason: `File not found: ${absPath}` };
2070
+ }
2071
+ const stat = fs.default.statSync(absPath);
2072
+ if (!stat.isFile()) {
2073
+ return { ok: false, reason: `Not a file: ${absPath}` };
2074
+ }
2075
+ if (stat.size > 512 * 1024 * 1024) {
2076
+ return { ok: false, reason: `File too large (${(stat.size / 1024 / 1024).toFixed(1)} MB). Max: 512 MB` };
2077
+ }
2078
+ }
2079
+
2080
+ return { ok: true, paths: absPaths };
2081
+ }
2082
+
2083
+ function mimeFromFilePath(filePath) {
2084
+ const lower = String(filePath || '').toLowerCase();
2085
+ if (lower.endsWith('.pdf')) return 'application/pdf';
2086
+ if (lower.endsWith('.doc')) return 'application/msword';
2087
+ if (lower.endsWith('.docx')) return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
2088
+ if (lower.endsWith('.xls')) return 'application/vnd.ms-excel';
2089
+ if (lower.endsWith('.xlsx')) return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
2090
+ if (lower.endsWith('.ppt')) return 'application/vnd.ms-powerpoint';
2091
+ if (lower.endsWith('.pptx')) return 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
2092
+ if (lower.endsWith('.csv')) return 'text/csv';
2093
+ if (lower.endsWith('.txt')) return 'text/plain';
2094
+ if (lower.endsWith('.json')) return 'application/json';
2095
+ if (lower.endsWith('.xml')) return 'application/xml';
2096
+ if (lower.endsWith('.html') || lower.endsWith('.htm')) return 'text/html';
2097
+ if (lower.endsWith('.md')) return 'text/markdown';
2098
+ if (lower.endsWith('.py')) return 'text/x-python';
2099
+ if (lower.endsWith('.js')) return 'text/javascript';
2100
+ if (lower.endsWith('.ts')) return 'application/typescript';
2101
+ if (lower.endsWith('.jsx')) return 'text/jsx';
2102
+ if (lower.endsWith('.tsx')) return 'text/tsx';
2103
+ if (lower.endsWith('.png')) return 'image/png';
2104
+ if (lower.endsWith('.jpg') || lower.endsWith('.jpeg')) return 'image/jpeg';
2105
+ if (lower.endsWith('.gif')) return 'image/gif';
2106
+ if (lower.endsWith('.webp')) return 'image/webp';
2107
+ if (lower.endsWith('.svg')) return 'image/svg+xml';
2108
+ return 'application/octet-stream';
2109
+ }
2110
+
2111
+ export const __test__ = {
2112
+ COMPOSER_SELECTORS,
2113
+ SEND_BUTTON_SELECTOR,
2114
+ SEND_BUTTON_FALLBACK_SELECTORS,
2115
+ SEND_BUTTON_LABELS,
2116
+ CLOSE_SIDEBAR_LABELS,
2117
+ buildComposerLocatorScript,
2118
+ isSameChatGPTConversation,
2119
+ parseChatGPTConversationId,
2120
+ parseChatGPTProjectId,
2121
+ imageMimeFromPath,
2122
+ mimeFromFilePath,
2123
+ PROJECT_LINK_SELECTOR,
2124
+ };
2125
+
2126
+ /**
2127
+ * Export images by URL: fetch from ChatGPT backend API and convert to base64 data URLs.
2128
+ */
2129
+ export async function getChatGPTImageAssets(page, urls) {
2130
+ const urlsJson = JSON.stringify(urls);
2131
+ return requireArrayEvaluateResult(unwrapEvaluateResult(await page.evaluate(`
2132
+ (async (targetUrls) => {
2133
+ const blobToDataUrl = (blob) => new Promise((resolve, reject) => {
2134
+ const reader = new FileReader();
2135
+ reader.onloadend = () => resolve(String(reader.result || ''));
2136
+ reader.onerror = () => reject(new Error('Failed to read blob'));
2137
+ reader.readAsDataURL(blob);
2138
+ });
2139
+
2140
+ const inferMime = (value, fallbackUrl) => {
2141
+ if (value) return value;
2142
+ const lower = String(fallbackUrl || '').toLowerCase();
2143
+ if (lower.includes('.png')) return 'image/png';
2144
+ if (lower.includes('.webp')) return 'image/webp';
2145
+ if (lower.includes('.gif')) return 'image/gif';
2146
+ return 'image/jpeg';
2147
+ };
2148
+
2149
+ const results = [];
2150
+
2151
+ for (const targetUrl of targetUrls) {
2152
+ let dataUrl = '';
2153
+ let mimeType = 'image/jpeg';
2154
+ let width = 0;
2155
+ let height = 0;
2156
+
2157
+ // Try to find the img element for size info
2158
+ const img = Array.from(document.querySelectorAll('img')).find(el =>
2159
+ (el.currentSrc || el.src || '') === targetUrl
2160
+ );
2161
+ if (img) {
2162
+ width = img.naturalWidth || img.width || 0;
2163
+ height = img.naturalHeight || img.height || 0;
2164
+ } else {
2165
+ const backgroundEl = Array.from(document.querySelectorAll('[style*="background-image"], [style*="background"]')).find(el => {
2166
+ if (!(el instanceof HTMLElement)) return false;
2167
+ const backgroundImage = window.getComputedStyle(el).backgroundImage || '';
2168
+ return Array.from(backgroundImage.matchAll(/url\\((['"]?)(.*?)\\1\\)/g)).some(match => {
2169
+ const raw = String(match[2] || '').trim();
2170
+ if (!raw) return false;
2171
+ if (raw === targetUrl) return true;
2172
+ try {
2173
+ return new URL(raw, window.location.href).href === targetUrl;
2174
+ } catch {
2175
+ return false;
2176
+ }
2177
+ });
2178
+ });
2179
+ if (backgroundEl) {
2180
+ const rect = backgroundEl.getBoundingClientRect();
2181
+ width = Math.round(rect.width || 0);
2182
+ height = Math.round(rect.height || 0);
2183
+ }
2184
+ }
2185
+
2186
+ try {
2187
+ if (String(targetUrl).startsWith('data:')) {
2188
+ dataUrl = String(targetUrl);
2189
+ mimeType = (String(targetUrl).match(/^data:([^;]+);/i) || [])[1] || 'image/png';
2190
+ } else {
2191
+ // Try to fetch via CORS from the page's origin
2192
+ const res = await fetch(targetUrl, { credentials: 'include' });
2193
+ if (res.ok) {
2194
+ const blob = await res.blob();
2195
+ mimeType = inferMime(blob.type, targetUrl);
2196
+ dataUrl = await blobToDataUrl(blob);
2197
+ }
2198
+ }
2199
+ } catch (e) {
2200
+ // If fetch fails (CORS), try canvas approach via img element
2201
+ }
2202
+
2203
+ // Fallback: draw img to canvas
2204
+ if (!dataUrl && img && img instanceof HTMLImageElement) {
2205
+ try {
2206
+ const canvas = document.createElement('canvas');
2207
+ canvas.width = img.naturalWidth || img.width || 512;
2208
+ canvas.height = img.naturalHeight || img.height || 512;
2209
+ const ctx = canvas.getContext('2d');
2210
+ if (ctx) {
2211
+ ctx.drawImage(img, 0, 0);
2212
+ dataUrl = canvas.toDataURL('image/png');
2213
+ mimeType = 'image/png';
2214
+ }
2215
+ } catch (e) { }
2216
+ }
2217
+
2218
+ if (dataUrl) {
2219
+ results.push({ url: String(targetUrl), dataUrl, mimeType, width, height });
2220
+ }
2221
+ }
2222
+
2223
+ return results;
2224
+ })(${urlsJson})
2225
+ `)), 'chatgpt image asset export');
2226
+ }