@jackwener/opencli 1.6.1 → 1.6.2

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 (384) hide show
  1. package/CONTRIBUTING.md +1 -1
  2. package/README.md +27 -45
  3. package/README.zh-CN.md +32 -34
  4. package/autoresearch/browse-tasks.json +18 -20
  5. package/autoresearch/commands/debug.ts +163 -0
  6. package/autoresearch/commands/fix.ts +145 -0
  7. package/autoresearch/commands/plan.ts +88 -0
  8. package/autoresearch/commands/run.ts +138 -0
  9. package/autoresearch/config.ts +82 -0
  10. package/autoresearch/engine.ts +359 -0
  11. package/autoresearch/eval-all.ts +127 -0
  12. package/autoresearch/eval-browse.ts +1 -1
  13. package/autoresearch/eval-publish.ts +238 -0
  14. package/autoresearch/eval-save.ts +249 -0
  15. package/autoresearch/eval-skill.ts +14 -8
  16. package/autoresearch/eval-v2ex.ts +220 -0
  17. package/autoresearch/eval-zhihu.ts +230 -0
  18. package/autoresearch/logger.ts +69 -0
  19. package/autoresearch/presets/combined-reliability.ts +27 -0
  20. package/autoresearch/presets/index.ts +23 -0
  21. package/autoresearch/presets/operate-reliability.ts +24 -0
  22. package/autoresearch/presets/save-reliability.ts +26 -0
  23. package/autoresearch/presets/skill-quality.ts +20 -0
  24. package/autoresearch/presets/v2ex-reliability.ts +24 -0
  25. package/autoresearch/presets/zhihu-reliability.ts +25 -0
  26. package/autoresearch/publish-tasks.json +345 -0
  27. package/autoresearch/run-save.sh +11 -0
  28. package/autoresearch/save-adapters/xhs-explore-deep.ts +64 -0
  29. package/autoresearch/save-adapters/xhs-note-comments.ts +61 -0
  30. package/autoresearch/save-adapters/xhs-search-full.ts +62 -0
  31. package/autoresearch/save-adapters/zhihu-hot-detail.ts +52 -0
  32. package/autoresearch/save-adapters/zhihu-question-full.ts +57 -0
  33. package/autoresearch/save-adapters/zhihu-search-detail.ts +53 -0
  34. package/autoresearch/save-tasks.json +281 -0
  35. package/autoresearch/v2ex-tasks.json +899 -0
  36. package/autoresearch/zhihu-tasks.json +848 -0
  37. package/dist/browser/base-page.d.ts +4 -2
  38. package/dist/browser/base-page.js +37 -4
  39. package/dist/browser/bridge.js +10 -8
  40. package/dist/browser/cdp.js +2 -6
  41. package/dist/browser/daemon-client.d.ts +11 -1
  42. package/dist/browser/daemon-client.js +3 -0
  43. package/dist/browser/dom-helpers.d.ts +4 -2
  44. package/dist/browser/dom-helpers.js +42 -31
  45. package/dist/browser/dom-snapshot.js +23 -1
  46. package/dist/browser/page.d.ts +7 -2
  47. package/dist/browser/page.js +112 -30
  48. package/dist/browser.test.js +1 -1
  49. package/dist/build-manifest.d.ts +1 -0
  50. package/dist/build-manifest.js +1 -0
  51. package/dist/cli-manifest.json +1135 -184
  52. package/dist/cli.d.ts +2 -0
  53. package/dist/cli.js +48 -7
  54. package/dist/cli.test.d.ts +1 -0
  55. package/dist/cli.test.js +88 -0
  56. package/dist/clis/1688/item.d.ts +70 -0
  57. package/dist/clis/1688/item.js +187 -0
  58. package/dist/clis/1688/item.test.d.ts +1 -0
  59. package/dist/clis/1688/item.test.js +67 -0
  60. package/dist/clis/1688/search.d.ts +56 -0
  61. package/dist/clis/1688/search.js +309 -0
  62. package/dist/clis/1688/search.test.d.ts +1 -0
  63. package/dist/clis/1688/search.test.js +75 -0
  64. package/dist/clis/1688/shared.d.ts +112 -0
  65. package/dist/clis/1688/shared.js +514 -0
  66. package/dist/clis/1688/shared.test.d.ts +1 -0
  67. package/dist/clis/1688/shared.test.js +57 -0
  68. package/dist/clis/1688/store.d.ts +45 -0
  69. package/dist/clis/1688/store.js +226 -0
  70. package/dist/clis/1688/store.test.d.ts +1 -0
  71. package/dist/clis/1688/store.test.js +62 -0
  72. package/dist/clis/amazon/bestsellers.d.ts +0 -20
  73. package/dist/clis/amazon/bestsellers.js +6 -129
  74. package/dist/clis/amazon/bestsellers.test.js +12 -3
  75. package/dist/clis/amazon/movers-shakers.d.ts +1 -0
  76. package/dist/clis/amazon/movers-shakers.js +7 -0
  77. package/dist/clis/amazon/new-releases.d.ts +1 -0
  78. package/dist/clis/amazon/new-releases.js +7 -0
  79. package/dist/clis/amazon/rankings.d.ts +59 -0
  80. package/dist/clis/amazon/rankings.js +226 -0
  81. package/dist/clis/amazon/rankings.test.d.ts +1 -0
  82. package/dist/clis/amazon/rankings.test.js +41 -0
  83. package/dist/clis/amazon/shared.d.ts +11 -0
  84. package/dist/clis/amazon/shared.js +121 -11
  85. package/dist/clis/amazon/shared.test.js +11 -0
  86. package/dist/clis/bilibili/comments.js +2 -2
  87. package/dist/clis/bilibili/comments.test.js +3 -2
  88. package/dist/clis/bilibili/download.js +2 -1
  89. package/dist/clis/bilibili/subtitle.js +4 -3
  90. package/dist/clis/bilibili/subtitle.test.js +2 -1
  91. package/dist/clis/bilibili/utils.d.ts +5 -0
  92. package/dist/clis/bilibili/utils.js +30 -0
  93. package/dist/clis/bilibili/utils.test.d.ts +1 -0
  94. package/dist/clis/bilibili/utils.test.js +17 -0
  95. package/dist/clis/douban/marks.js +1 -1
  96. package/dist/clis/douban/subject.yaml +50 -19
  97. package/dist/clis/doubao/utils.js +32 -12
  98. package/dist/clis/douyin/_shared/browser-fetch.test.js +0 -1
  99. package/dist/clis/douyin/_shared/transcode.test.js +0 -2
  100. package/dist/clis/douyin/draft.test.js +0 -2
  101. package/dist/clis/facebook/search.test.js +0 -2
  102. package/dist/clis/gemini/ask.js +9 -3
  103. package/dist/clis/gemini/ask.test.d.ts +1 -0
  104. package/dist/clis/gemini/ask.test.js +100 -0
  105. package/dist/clis/gemini/reply-state.test.d.ts +1 -0
  106. package/dist/clis/gemini/reply-state.test.js +641 -0
  107. package/dist/clis/gemini/utils.d.ts +44 -1
  108. package/dist/clis/gemini/utils.js +528 -61
  109. package/dist/clis/gemini/utils.test.js +149 -2
  110. package/dist/clis/hupu/detail.d.ts +1 -0
  111. package/dist/clis/hupu/detail.js +72 -0
  112. package/dist/clis/hupu/hot.yaml +43 -0
  113. package/dist/clis/hupu/like.d.ts +1 -0
  114. package/dist/clis/hupu/like.js +75 -0
  115. package/dist/clis/hupu/reply.d.ts +1 -0
  116. package/dist/clis/hupu/reply.js +71 -0
  117. package/dist/clis/hupu/search.d.ts +1 -0
  118. package/dist/clis/hupu/search.js +59 -0
  119. package/dist/clis/hupu/unlike.d.ts +1 -0
  120. package/dist/clis/hupu/unlike.js +75 -0
  121. package/dist/clis/hupu/utils.d.ts +20 -0
  122. package/dist/clis/hupu/utils.js +319 -0
  123. package/dist/clis/instagram/_shared/private-publish.d.ts +138 -0
  124. package/dist/clis/instagram/_shared/private-publish.js +1030 -0
  125. package/dist/clis/instagram/_shared/private-publish.test.d.ts +1 -0
  126. package/dist/clis/instagram/_shared/private-publish.test.js +705 -0
  127. package/dist/clis/instagram/_shared/protocol-capture.d.ts +26 -0
  128. package/dist/clis/instagram/_shared/protocol-capture.js +282 -0
  129. package/dist/clis/instagram/_shared/protocol-capture.test.d.ts +1 -0
  130. package/dist/clis/instagram/_shared/protocol-capture.test.js +114 -0
  131. package/dist/clis/instagram/_shared/runtime-info.d.ts +9 -0
  132. package/dist/clis/instagram/_shared/runtime-info.js +81 -0
  133. package/dist/clis/instagram/note.d.ts +1 -0
  134. package/dist/clis/instagram/note.js +222 -0
  135. package/dist/clis/instagram/note.test.d.ts +1 -0
  136. package/dist/clis/instagram/note.test.js +81 -0
  137. package/dist/clis/instagram/post.d.ts +4 -0
  138. package/dist/clis/instagram/post.js +1496 -0
  139. package/dist/clis/instagram/post.test.d.ts +1 -0
  140. package/dist/clis/instagram/post.test.js +1647 -0
  141. package/dist/clis/instagram/reel.d.ts +1 -0
  142. package/dist/clis/instagram/reel.js +826 -0
  143. package/dist/clis/instagram/reel.test.d.ts +1 -0
  144. package/dist/clis/instagram/reel.test.js +167 -0
  145. package/dist/clis/instagram/story.d.ts +1 -0
  146. package/dist/clis/instagram/story.js +115 -0
  147. package/dist/clis/instagram/story.test.d.ts +1 -0
  148. package/dist/clis/instagram/story.test.js +167 -0
  149. package/dist/clis/sinafinance/stock-rank.d.ts +4 -0
  150. package/dist/clis/sinafinance/stock-rank.js +65 -0
  151. package/dist/clis/substack/utils.test.js +0 -2
  152. package/dist/clis/twitter/post.js +72 -45
  153. package/dist/clis/twitter/post.test.d.ts +1 -0
  154. package/dist/clis/twitter/post.test.js +116 -0
  155. package/dist/clis/twitter/reply.d.ts +12 -0
  156. package/dist/clis/twitter/reply.js +257 -35
  157. package/dist/clis/twitter/reply.test.d.ts +1 -0
  158. package/dist/clis/twitter/reply.test.js +151 -0
  159. package/dist/clis/xianyu/chat.d.ts +7 -0
  160. package/dist/clis/xianyu/chat.js +146 -0
  161. package/dist/clis/xianyu/chat.test.d.ts +1 -0
  162. package/dist/clis/xianyu/chat.test.js +15 -0
  163. package/dist/clis/xianyu/item.d.ts +7 -0
  164. package/dist/clis/xianyu/item.js +152 -0
  165. package/dist/clis/xianyu/item.test.d.ts +1 -0
  166. package/dist/clis/xianyu/item.test.js +56 -0
  167. package/dist/clis/xianyu/search.d.ts +10 -0
  168. package/dist/clis/xianyu/search.js +134 -0
  169. package/dist/clis/xianyu/search.test.d.ts +1 -0
  170. package/dist/clis/xianyu/search.test.js +17 -0
  171. package/dist/clis/xianyu/utils.d.ts +1 -0
  172. package/dist/clis/xianyu/utils.js +8 -0
  173. package/dist/clis/xiaoe/catalog.yaml +129 -0
  174. package/dist/clis/xiaoe/content.yaml +43 -0
  175. package/dist/clis/xiaoe/courses.yaml +73 -0
  176. package/dist/clis/xiaoe/detail.yaml +39 -0
  177. package/dist/clis/xiaoe/play-url.yaml +124 -0
  178. package/dist/clis/xiaohongshu/comments.test.js +0 -2
  179. package/dist/clis/xiaohongshu/creator-note-detail.test.js +0 -2
  180. package/dist/clis/xiaohongshu/creator-notes.test.js +0 -2
  181. package/dist/clis/xiaohongshu/download.test.js +0 -2
  182. package/dist/clis/xiaohongshu/note.test.js +0 -2
  183. package/dist/clis/xiaohongshu/publish.test.js +0 -2
  184. package/dist/clis/xiaohongshu/search.js +29 -20
  185. package/dist/clis/xiaohongshu/search.test.js +56 -48
  186. package/dist/clis/yuanbao/ask.d.ts +21 -0
  187. package/dist/clis/yuanbao/ask.js +427 -0
  188. package/dist/clis/yuanbao/ask.test.d.ts +1 -0
  189. package/dist/clis/yuanbao/ask.test.js +124 -0
  190. package/dist/clis/yuanbao/new.d.ts +1 -0
  191. package/dist/clis/yuanbao/new.js +70 -0
  192. package/dist/clis/yuanbao/new.test.d.ts +1 -0
  193. package/dist/clis/yuanbao/new.test.js +30 -0
  194. package/dist/clis/yuanbao/shared.d.ts +13 -0
  195. package/dist/clis/yuanbao/shared.js +49 -0
  196. package/dist/clis/zhihu/question.js +30 -19
  197. package/dist/clis/zhihu/question.test.js +34 -16
  198. package/dist/commanderAdapter.js +8 -4
  199. package/dist/commanderAdapter.test.js +42 -0
  200. package/dist/completion.js +3 -1
  201. package/dist/completion.test.d.ts +1 -0
  202. package/dist/completion.test.js +23 -0
  203. package/dist/doctor.js +1 -1
  204. package/dist/electron-apps.d.ts +2 -0
  205. package/dist/electron-apps.js +7 -1
  206. package/dist/errors.js +1 -1
  207. package/dist/execution.js +25 -35
  208. package/dist/explore.js +1 -1
  209. package/dist/launcher.d.ts +4 -0
  210. package/dist/launcher.js +64 -8
  211. package/dist/launcher.test.js +88 -7
  212. package/dist/output.d.ts +2 -0
  213. package/dist/output.js +10 -1
  214. package/dist/output.test.d.ts +0 -3
  215. package/dist/output.test.js +59 -92
  216. package/dist/pipeline/executor.test.js +0 -2
  217. package/dist/pipeline/steps/download.test.js +0 -2
  218. package/dist/registry.d.ts +2 -0
  219. package/dist/serialization.d.ts +1 -0
  220. package/dist/serialization.js +1 -0
  221. package/dist/types.d.ts +9 -2
  222. package/docs/.vitepress/config.mts +4 -0
  223. package/docs/adapters/browser/1688.md +52 -0
  224. package/docs/adapters/browser/36kr.md +2 -1
  225. package/docs/adapters/browser/doubao.md +5 -1
  226. package/docs/adapters/browser/hupu.md +53 -0
  227. package/docs/adapters/browser/sinafinance.md +32 -2
  228. package/docs/adapters/browser/weibo.md +6 -1
  229. package/docs/adapters/browser/wikipedia.md +2 -0
  230. package/docs/adapters/browser/xianyu.md +42 -0
  231. package/docs/adapters/browser/xiaoe.md +44 -0
  232. package/docs/adapters/browser/yuanbao.md +64 -0
  233. package/docs/adapters/index.md +14 -5
  234. package/docs/comparison.md +1 -1
  235. package/docs/developer/ai-workflow.md +2 -2
  236. package/docs/developer/contributing.md +1 -1
  237. package/docs/developer/testing.md +2 -0
  238. package/docs/guide/plugins.md +1 -0
  239. package/docs/guide/troubleshooting.md +11 -0
  240. package/docs/superpowers/specs/2026-04-03-v2ex-autoresearch-design.md +41 -0
  241. package/docs/zh/guide/plugins.md +1 -0
  242. package/extension/dist/background.js +1127 -0
  243. package/extension/src/background.test.ts +39 -0
  244. package/extension/src/background.ts +223 -34
  245. package/extension/src/cdp.ts +194 -4
  246. package/extension/src/protocol.ts +22 -1
  247. package/package.json +3 -2
  248. package/scripts/postinstall.js +1 -1
  249. package/skills/opencli-explorer/SKILL.md +1 -1
  250. package/skills/opencli-oneshot/SKILL.md +2 -2
  251. package/skills/opencli-operate/SKILL.md +120 -27
  252. package/skills/opencli-usage/SKILL.md +31 -20
  253. package/skills/opencli-usage/browser.md +114 -16
  254. package/skills/opencli-usage/public-api.md +32 -3
  255. package/skills/smart-search/SKILL.md +156 -0
  256. package/skills/smart-search/references/sources-ai.md +74 -0
  257. package/skills/smart-search/references/sources-info.md +43 -0
  258. package/skills/smart-search/references/sources-media.md +50 -0
  259. package/skills/smart-search/references/sources-other.md +42 -0
  260. package/skills/smart-search/references/sources-shopping.md +31 -0
  261. package/skills/smart-search/references/sources-social.md +51 -0
  262. package/skills/smart-search/references/sources-tech.md +42 -0
  263. package/skills/smart-search/references/sources-travel.md +20 -0
  264. package/src/browser/base-page.ts +41 -6
  265. package/src/browser/bridge.ts +11 -8
  266. package/src/browser/cdp.ts +1 -8
  267. package/src/browser/daemon-client.ts +11 -1
  268. package/src/browser/dom-helpers.ts +43 -31
  269. package/src/browser/dom-snapshot.ts +23 -1
  270. package/src/browser/page.ts +115 -31
  271. package/src/browser.test.ts +1 -1
  272. package/src/build-manifest.ts +2 -0
  273. package/src/cli.test.ts +133 -0
  274. package/src/cli.ts +73 -11
  275. package/src/clis/1688/item.test.ts +69 -0
  276. package/src/clis/1688/item.ts +282 -0
  277. package/src/clis/1688/search.test.ts +81 -0
  278. package/src/clis/1688/search.ts +402 -0
  279. package/src/clis/1688/shared.test.ts +75 -0
  280. package/src/clis/1688/shared.ts +623 -0
  281. package/src/clis/1688/store.test.ts +69 -0
  282. package/src/clis/1688/store.ts +300 -0
  283. package/src/clis/amazon/bestsellers.test.ts +12 -3
  284. package/src/clis/amazon/bestsellers.ts +6 -178
  285. package/src/clis/amazon/movers-shakers.ts +8 -0
  286. package/src/clis/amazon/new-releases.ts +8 -0
  287. package/src/clis/amazon/rankings.test.ts +47 -0
  288. package/src/clis/amazon/rankings.ts +312 -0
  289. package/src/clis/amazon/shared.test.ts +16 -0
  290. package/src/clis/amazon/shared.ts +134 -12
  291. package/src/clis/bilibili/comments.test.ts +4 -3
  292. package/src/clis/bilibili/comments.ts +2 -2
  293. package/src/clis/bilibili/download.ts +2 -1
  294. package/src/clis/bilibili/subtitle.test.ts +2 -1
  295. package/src/clis/bilibili/subtitle.ts +4 -3
  296. package/src/clis/bilibili/utils.test.ts +21 -0
  297. package/src/clis/bilibili/utils.ts +27 -0
  298. package/src/clis/douban/marks.ts +1 -1
  299. package/src/clis/douban/subject.yaml +50 -19
  300. package/src/clis/doubao/utils.ts +32 -12
  301. package/src/clis/douyin/_shared/browser-fetch.test.ts +0 -1
  302. package/src/clis/douyin/_shared/transcode.test.ts +0 -2
  303. package/src/clis/douyin/draft.test.ts +0 -2
  304. package/src/clis/facebook/search.test.ts +0 -2
  305. package/src/clis/gemini/ask.test.ts +116 -0
  306. package/src/clis/gemini/ask.ts +10 -3
  307. package/src/clis/gemini/reply-state.test.ts +708 -0
  308. package/src/clis/gemini/utils.test.ts +184 -2
  309. package/src/clis/gemini/utils.ts +588 -60
  310. package/src/clis/hupu/detail.ts +126 -0
  311. package/src/clis/hupu/hot.yaml +43 -0
  312. package/src/clis/hupu/like.ts +76 -0
  313. package/src/clis/hupu/reply.ts +76 -0
  314. package/src/clis/hupu/search.ts +95 -0
  315. package/src/clis/hupu/unlike.ts +76 -0
  316. package/src/clis/hupu/utils.ts +381 -0
  317. package/src/clis/instagram/_shared/private-publish.test.ts +827 -0
  318. package/src/clis/instagram/_shared/private-publish.ts +1303 -0
  319. package/src/clis/instagram/_shared/protocol-capture.test.ts +148 -0
  320. package/src/clis/instagram/_shared/protocol-capture.ts +321 -0
  321. package/src/clis/instagram/_shared/runtime-info.ts +91 -0
  322. package/src/clis/instagram/note.test.ts +96 -0
  323. package/src/clis/instagram/note.ts +254 -0
  324. package/src/clis/instagram/post.test.ts +1716 -0
  325. package/src/clis/instagram/post.ts +1620 -0
  326. package/src/clis/instagram/reel.test.ts +191 -0
  327. package/src/clis/instagram/reel.ts +886 -0
  328. package/src/clis/instagram/story.test.ts +191 -0
  329. package/src/clis/instagram/story.ts +151 -0
  330. package/src/clis/sinafinance/stock-rank.ts +68 -0
  331. package/src/clis/substack/utils.test.ts +0 -2
  332. package/src/clis/twitter/post.test.ts +157 -0
  333. package/src/clis/twitter/post.ts +82 -48
  334. package/src/clis/twitter/reply.test.ts +177 -0
  335. package/src/clis/twitter/reply.ts +285 -39
  336. package/src/clis/xianyu/chat.test.ts +20 -0
  337. package/src/clis/xianyu/chat.ts +175 -0
  338. package/src/clis/xianyu/item.test.ts +67 -0
  339. package/src/clis/xianyu/item.ts +172 -0
  340. package/src/clis/xianyu/search.test.ts +22 -0
  341. package/src/clis/xianyu/search.ts +151 -0
  342. package/src/clis/xianyu/utils.ts +9 -0
  343. package/src/clis/xiaoe/catalog.yaml +129 -0
  344. package/src/clis/xiaoe/content.yaml +43 -0
  345. package/src/clis/xiaoe/courses.yaml +73 -0
  346. package/src/clis/xiaoe/detail.yaml +39 -0
  347. package/src/clis/xiaoe/play-url.yaml +124 -0
  348. package/src/clis/xiaohongshu/comments.test.ts +0 -2
  349. package/src/clis/xiaohongshu/creator-note-detail.test.ts +0 -2
  350. package/src/clis/xiaohongshu/creator-notes.test.ts +0 -2
  351. package/src/clis/xiaohongshu/download.test.ts +0 -2
  352. package/src/clis/xiaohongshu/note.test.ts +0 -2
  353. package/src/clis/xiaohongshu/publish.test.ts +0 -2
  354. package/src/clis/xiaohongshu/search.test.ts +59 -48
  355. package/src/clis/xiaohongshu/search.ts +31 -21
  356. package/src/clis/yuanbao/ask.test.ts +156 -0
  357. package/src/clis/yuanbao/ask.ts +522 -0
  358. package/src/clis/yuanbao/new.test.ts +36 -0
  359. package/src/clis/yuanbao/new.ts +81 -0
  360. package/src/clis/yuanbao/shared.ts +57 -0
  361. package/src/clis/zhihu/question.test.ts +42 -17
  362. package/src/clis/zhihu/question.ts +31 -26
  363. package/src/commanderAdapter.test.ts +51 -0
  364. package/src/commanderAdapter.ts +8 -4
  365. package/src/completion.test.ts +30 -0
  366. package/src/completion.ts +3 -1
  367. package/src/doctor.ts +1 -1
  368. package/src/electron-apps.ts +9 -1
  369. package/src/errors.ts +1 -1
  370. package/src/execution.ts +26 -30
  371. package/src/explore.ts +1 -1
  372. package/src/launcher.test.ts +121 -7
  373. package/src/launcher.ts +87 -9
  374. package/src/output.test.ts +50 -90
  375. package/src/output.ts +10 -1
  376. package/src/pipeline/executor.test.ts +0 -2
  377. package/src/pipeline/steps/download.test.ts +0 -2
  378. package/src/registry.ts +2 -0
  379. package/src/serialization.ts +2 -0
  380. package/src/types.ts +9 -2
  381. package/tests/e2e/browser-auth.test.ts +9 -0
  382. package/CLI-EXPLORER.md +0 -724
  383. package/CLI-ONESHOT.md +0 -216
  384. package/SKILL.md +0 -59
@@ -1,5 +1,31 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { collectGeminiTranscriptAdditions, sanitizeGeminiResponseText } from './utils.js';
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { __test__, collectGeminiTranscriptAdditions, sanitizeGeminiResponseText, sendGeminiMessage, } from './utils.js';
3
+ function createPageMock() {
4
+ return {
5
+ goto: vi.fn().mockResolvedValue(undefined),
6
+ evaluate: vi.fn(),
7
+ getCookies: vi.fn().mockResolvedValue([]),
8
+ snapshot: vi.fn().mockResolvedValue(undefined),
9
+ click: vi.fn().mockResolvedValue(undefined),
10
+ typeText: vi.fn().mockResolvedValue(undefined),
11
+ pressKey: vi.fn().mockResolvedValue(undefined),
12
+ scrollTo: vi.fn().mockResolvedValue(undefined),
13
+ getFormState: vi.fn().mockResolvedValue({}),
14
+ wait: vi.fn().mockResolvedValue(undefined),
15
+ tabs: vi.fn().mockResolvedValue([]),
16
+ selectTab: vi.fn().mockResolvedValue(undefined),
17
+ networkRequests: vi.fn().mockResolvedValue([]),
18
+ consoleMessages: vi.fn().mockResolvedValue([]),
19
+ scroll: vi.fn().mockResolvedValue(undefined),
20
+ autoScroll: vi.fn().mockResolvedValue(undefined),
21
+ installInterceptor: vi.fn().mockResolvedValue(undefined),
22
+ getInterceptedRequests: vi.fn().mockResolvedValue([]),
23
+ waitForCapture: vi.fn().mockResolvedValue(undefined),
24
+ screenshot: vi.fn().mockResolvedValue(''),
25
+ nativeType: vi.fn().mockResolvedValue(undefined),
26
+ nativeKeyPress: vi.fn().mockResolvedValue(undefined),
27
+ };
28
+ }
3
29
  describe('sanitizeGeminiResponseText', () => {
4
30
  it('strips a prompt echo only when it appears as a prefixed block', () => {
5
31
  const prompt = 'Reply with the word opencli';
@@ -28,4 +54,125 @@ describe('collectGeminiTranscriptAdditions', () => {
28
54
  const current = ['Previous', 'Tell me a haiku', 'Tell me a haiku\n\nSoft spring rain arrives'];
29
55
  expect(collectGeminiTranscriptAdditions(before, current, prompt)).toBe('Soft spring rain arrives');
30
56
  });
57
+ it('keeps a reply line that quotes the prompt inside the answer body', () => {
58
+ const prompt = '请只回复:OK';
59
+ const before = ['baseline'];
60
+ const current = ['baseline', '关于“请只回复:OK”,这里是解释。'];
61
+ expect(collectGeminiTranscriptAdditions(before, current, prompt)).toBe('关于“请只回复:OK”,这里是解释。');
62
+ });
63
+ });
64
+ describe('gemini send strategy', () => {
65
+ it('includes structural composer selectors instead of relying only on english aria labels', () => {
66
+ expect(__test__.GEMINI_COMPOSER_SELECTORS).toContain('.ql-editor[contenteditable="true"]');
67
+ expect(__test__.GEMINI_COMPOSER_SELECTORS).toContain('.ql-editor[role="textbox"]');
68
+ });
69
+ it('prefers native text insertion before submitting the composer', async () => {
70
+ const page = createPageMock();
71
+ const evaluate = vi.mocked(page.evaluate);
72
+ const nativeType = vi.mocked(page.nativeType);
73
+ const nativeKeyPress = vi.mocked(page.nativeKeyPress);
74
+ evaluate
75
+ .mockResolvedValueOnce('https://gemini.google.com/app')
76
+ .mockResolvedValueOnce({ ok: true })
77
+ .mockResolvedValueOnce({ hasText: true })
78
+ .mockResolvedValueOnce('button');
79
+ const result = await sendGeminiMessage(page, '你好');
80
+ expect(nativeType).toHaveBeenCalledWith('你好');
81
+ expect(nativeKeyPress).not.toHaveBeenCalled();
82
+ expect(result).toBe('button');
83
+ });
84
+ it('falls back when native insertion does not update the composer', async () => {
85
+ const page = createPageMock();
86
+ const evaluate = vi.mocked(page.evaluate);
87
+ const nativeType = vi.mocked(page.nativeType);
88
+ const nativeKeyPress = vi.mocked(page.nativeKeyPress);
89
+ evaluate
90
+ .mockResolvedValueOnce('https://gemini.google.com/app')
91
+ .mockResolvedValueOnce({ ok: true })
92
+ .mockResolvedValueOnce({ hasText: false })
93
+ .mockResolvedValueOnce({ hasText: true })
94
+ .mockResolvedValueOnce('enter');
95
+ const result = await sendGeminiMessage(page, '你好');
96
+ expect(nativeType).toHaveBeenCalledWith('你好');
97
+ expect(nativeKeyPress).toHaveBeenCalledWith('Enter');
98
+ expect(evaluate).toHaveBeenCalledTimes(5);
99
+ expect(result).toBe('enter');
100
+ });
101
+ it('falls back when native insertion throws', async () => {
102
+ const page = createPageMock();
103
+ const evaluate = vi.mocked(page.evaluate);
104
+ const nativeType = vi.mocked(page.nativeType);
105
+ nativeType.mockRejectedValueOnce(new Error('Unknown action: cdp'));
106
+ evaluate
107
+ .mockResolvedValueOnce('https://gemini.google.com/app')
108
+ .mockResolvedValueOnce({ ok: true })
109
+ .mockResolvedValueOnce({ hasText: true })
110
+ .mockResolvedValueOnce('button');
111
+ const result = await sendGeminiMessage(page, '你好');
112
+ expect(nativeType).toHaveBeenCalledWith('你好');
113
+ expect(result).toBe('button');
114
+ });
115
+ it('retries composer preparation until a slow-loading composer appears', async () => {
116
+ const page = createPageMock();
117
+ const evaluate = vi.mocked(page.evaluate);
118
+ const wait = vi.mocked(page.wait);
119
+ evaluate
120
+ .mockResolvedValueOnce('https://gemini.google.com/app')
121
+ .mockResolvedValueOnce({ ok: false, reason: 'Could not find Gemini composer' })
122
+ .mockResolvedValueOnce({ ok: false, reason: 'Could not find Gemini composer' })
123
+ .mockResolvedValueOnce({ ok: true })
124
+ .mockResolvedValueOnce({ hasText: true })
125
+ .mockResolvedValueOnce('button');
126
+ const result = await sendGeminiMessage(page, '你好');
127
+ expect(result).toBe('button');
128
+ expect(wait.mock.calls.filter(([value]) => value === 1)).toHaveLength(3);
129
+ });
130
+ it('keeps retrying until a composer that appears on the fourth attempt is ready', async () => {
131
+ const page = createPageMock();
132
+ const evaluate = vi.mocked(page.evaluate);
133
+ const wait = vi.mocked(page.wait);
134
+ evaluate
135
+ .mockResolvedValueOnce('https://gemini.google.com/app')
136
+ .mockResolvedValueOnce({ ok: false, reason: 'Could not find Gemini composer' })
137
+ .mockResolvedValueOnce({ ok: false, reason: 'Could not find Gemini composer' })
138
+ .mockResolvedValueOnce({ ok: false, reason: 'Could not find Gemini composer' })
139
+ .mockResolvedValueOnce({ ok: true })
140
+ .mockResolvedValueOnce({ hasText: true })
141
+ .mockResolvedValueOnce('button');
142
+ const result = await sendGeminiMessage(page, '你好');
143
+ expect(result).toBe('button');
144
+ expect(wait.mock.calls.filter(([value]) => value === 1)).toHaveLength(4);
145
+ });
146
+ it('avoids innerHTML in the fallback insertion path for trusted types pages', () => {
147
+ expect(__test__.insertComposerTextFallbackScript('你好')).not.toContain('innerHTML');
148
+ expect(__test__.insertComposerTextFallbackScript('你好')).toContain('replaceChildren');
149
+ });
150
+ it('keeps a button submit path in the generated submit script', () => {
151
+ expect(__test__.submitComposerScript()).toContain('.click()');
152
+ });
153
+ it('supports localized new chat labels in the generated new-chat script', () => {
154
+ expect(__test__.clickNewChatScript()).toContain('发起新对话');
155
+ });
156
+ });
157
+ describe('gemini turn normalization', () => {
158
+ it('collapses only adjacent duplicate turns so identical replies across rounds remain visible', () => {
159
+ const turns = [
160
+ { Role: 'User', Text: '你说\n\n请只回复:OK' },
161
+ { Role: 'User', Text: '请只回复:OK' },
162
+ { Role: 'Assistant', Text: 'OK' },
163
+ { Role: 'Assistant', Text: 'OK' },
164
+ { Role: 'User', Text: '你说\n\n请只回复:OK' },
165
+ { Role: 'User', Text: '请只回复:OK' },
166
+ { Role: 'Assistant', Text: 'OK' },
167
+ { Role: 'Assistant', Text: 'OK' },
168
+ ];
169
+ expect(__test__.collapseAdjacentGeminiTurns(turns)).toEqual([
170
+ { Role: 'User', Text: '你说\n\n请只回复:OK' },
171
+ { Role: 'User', Text: '请只回复:OK' },
172
+ { Role: 'Assistant', Text: 'OK' },
173
+ { Role: 'User', Text: '你说\n\n请只回复:OK' },
174
+ { Role: 'User', Text: '请只回复:OK' },
175
+ { Role: 'Assistant', Text: 'OK' },
176
+ ]);
177
+ });
31
178
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,72 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+ import { getHupuThreadUrl, readHupuNextData, stripHtml } from './utils.js';
3
+ cli({
4
+ site: 'hupu',
5
+ name: 'detail',
6
+ description: '获取虎扑帖子详情 (使用Next.js JSON数据)',
7
+ domain: 'bbs.hupu.com',
8
+ strategy: Strategy.PUBLIC,
9
+ browser: true,
10
+ navigateBefore: false,
11
+ args: [
12
+ {
13
+ name: 'tid',
14
+ required: true,
15
+ positional: true,
16
+ help: '帖子ID(9位数字)'
17
+ },
18
+ {
19
+ name: 'replies',
20
+ type: 'boolean',
21
+ default: false,
22
+ help: '是否包含热门回复'
23
+ }
24
+ ],
25
+ columns: ['title', 'author', 'content', 'replies', 'lights', 'url'],
26
+ func: async (page, kwargs) => {
27
+ const { tid, replies: includeReplies = false } = kwargs;
28
+ const url = getHupuThreadUrl(tid).replace(/-1\.html$/, '.html');
29
+ const data = await readHupuNextData(page, url, 'Read Hupu thread detail', {
30
+ expectedTid: String(tid),
31
+ });
32
+ // 检查错误信息(只有当code不是200时才报错)
33
+ const errorInfo = data.props.pageProps.detail_error_info;
34
+ if (errorInfo && errorInfo.code !== 200) {
35
+ throw new Error(`帖子访问失败: ${errorInfo.message} (code: ${errorInfo.code})`);
36
+ }
37
+ // 获取帖子信息
38
+ const thread = data.props.pageProps.detail?.thread;
39
+ if (!thread) {
40
+ throw new Error('帖子不存在或已被删除');
41
+ }
42
+ const authorName = thread.author?.puname || '未知作者';
43
+ const content = stripHtml(thread.content);
44
+ const contentPreview = content.length > 300 ? content.substring(0, 300) + '...' : content;
45
+ // 构建结果
46
+ const result = {
47
+ title: thread.title,
48
+ author: authorName,
49
+ content: contentPreview,
50
+ replies: thread.replies || 0,
51
+ lights: thread.lights || 0,
52
+ url: `https://bbs.hupu.com/${tid}.html`
53
+ };
54
+ // 如果需要包含回复,添加回复信息到内容中
55
+ if (includeReplies) {
56
+ const replyList = data.props.pageProps.detail?.lights || [];
57
+ const topReplies = replyList.slice(0, 3);
58
+ if (topReplies.length > 0) {
59
+ let replyText = '\n\n【热门回复】\n';
60
+ topReplies.forEach((reply, index) => {
61
+ const userName = reply.author?.puname || '未知用户';
62
+ const replyContent = stripHtml(reply.content).substring(0, 100);
63
+ const replyLights = reply.allLightCount || 0; // 修复:使用正确的字段名
64
+ const replyTime = reply.created_at_format || '未知时间';
65
+ replyText += `${index + 1}. ${userName} (亮${replyLights} ${replyTime}):\n ${replyContent}\n\n`;
66
+ });
67
+ result.content = contentPreview + replyText;
68
+ }
69
+ }
70
+ return [result];
71
+ },
72
+ });
@@ -0,0 +1,43 @@
1
+ site: hupu
2
+ name: hot
3
+ description: 虎扑热门帖子
4
+ domain: bbs.hupu.com
5
+
6
+ args:
7
+ limit:
8
+ type: int
9
+ default: 20
10
+ description: Number of hot posts
11
+
12
+ pipeline:
13
+ - navigate: https://bbs.hupu.com/
14
+
15
+ - evaluate: |
16
+ (async () => {
17
+ // 从HTML中提取帖子信息(适配新的HTML结构)
18
+ const html = document.documentElement.outerHTML;
19
+ const posts = [];
20
+
21
+ // 匹配当前虎扑页面结构的正则表达式
22
+ // 结构: <a href="/638249612.html"...><span class="t-title">标题</span></a>
23
+ const regex = /<a[^>]*href="\/(\d{9})\.html"[^>]*><span[^>]*class="t-title"[^>]*>([^<]+)<\/span><\/a>/g;
24
+ let match;
25
+
26
+ while ((match = regex.exec(html)) !== null && posts.length < ${{ args.limit }}) {
27
+ posts.push({
28
+ tid: match[1],
29
+ title: match[2].trim()
30
+ });
31
+ }
32
+
33
+ return posts;
34
+ })()
35
+
36
+ - map:
37
+ rank: ${{ index + 1 }}
38
+ title: ${{ item.title }}
39
+ url: https://bbs.hupu.com/${{ item.tid }}.html
40
+
41
+ - limit: ${{ args.limit }}
42
+
43
+ columns: [rank, title, url]
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ import { CliError } from '../../errors.js';
2
+ import { cli, Strategy } from '../../registry.js';
3
+ import { postHupuJson } from './utils.js';
4
+ cli({
5
+ site: 'hupu',
6
+ name: 'like',
7
+ description: '点赞虎扑回复 (需要登录)',
8
+ domain: 'bbs.hupu.com',
9
+ strategy: Strategy.COOKIE, // 需要Cookie认证
10
+ navigateBefore: false,
11
+ args: [
12
+ {
13
+ name: 'tid',
14
+ required: true,
15
+ positional: true,
16
+ help: '帖子ID(9位数字)'
17
+ },
18
+ {
19
+ name: 'pid',
20
+ required: true,
21
+ positional: true,
22
+ help: '回复ID'
23
+ },
24
+ {
25
+ name: 'fid',
26
+ required: true,
27
+ help: '板块ID(如278汽车区)'
28
+ }
29
+ ],
30
+ columns: ['status', 'message'],
31
+ func: async (page, kwargs) => {
32
+ const { tid, pid, fid } = kwargs;
33
+ const url = 'https://bbs.hupu.com/pcmapi/pc/bbs/v1/reply/light';
34
+ // 构建请求体
35
+ const body = {
36
+ tid,
37
+ pid,
38
+ puid: '',
39
+ fid,
40
+ shumei_id: '',
41
+ deviceid: ''
42
+ };
43
+ try {
44
+ const result = await postHupuJson(page, tid, url, body, 'Like Hupu reply');
45
+ // 处理响应
46
+ if (result.code === 1) {
47
+ return [{
48
+ status: '✅ 点赞成功',
49
+ message: ''
50
+ }];
51
+ }
52
+ else if (result.code === 0 && result.msg === '你已经点亮过这个回帖了') {
53
+ return [{
54
+ status: '⚠️ 已经点赞过了',
55
+ message: result.msg || ''
56
+ }];
57
+ }
58
+ else if (result.code === 0) {
59
+ return [{
60
+ status: '⚠️ 操作未执行',
61
+ message: result.msg || result.message || ''
62
+ }];
63
+ }
64
+ else {
65
+ throw new Error(`接口错误 code=${result.code}: ${result.msg || result.message}`);
66
+ }
67
+ }
68
+ catch (error) {
69
+ if (error instanceof CliError)
70
+ throw error;
71
+ const errorMessage = error instanceof Error ? error.message : String(error);
72
+ throw new Error(`点赞失败: ${errorMessage}`);
73
+ }
74
+ },
75
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,71 @@
1
+ import { CliError } from '../../errors.js';
2
+ import { cli, Strategy } from '../../registry.js';
3
+ import { postHupuJson } from './utils.js';
4
+ cli({
5
+ site: 'hupu',
6
+ name: 'reply',
7
+ description: '回复虎扑帖子 (需要登录)',
8
+ domain: 'bbs.hupu.com',
9
+ strategy: Strategy.COOKIE, // 需要Cookie认证
10
+ navigateBefore: false,
11
+ args: [
12
+ {
13
+ name: 'tid',
14
+ required: true,
15
+ positional: true,
16
+ help: '帖子ID(9位数字)'
17
+ },
18
+ {
19
+ name: 'topic_id',
20
+ required: true,
21
+ help: '板块ID,即接口中的 topicId(如 502 篮球资讯)'
22
+ },
23
+ {
24
+ name: 'text',
25
+ required: true,
26
+ positional: true,
27
+ help: '回复内容'
28
+ },
29
+ {
30
+ name: 'quote_id',
31
+ help: '被引用回复的 pid;填写后会以“回复某条热门回复”的方式发言'
32
+ }
33
+ ],
34
+ columns: ['status', 'message'],
35
+ func: async (page, kwargs) => {
36
+ const { tid, topic_id, text, quote_id } = kwargs;
37
+ const url = 'https://bbs.hupu.com/pcmapi/pc/bbs/v1/createReply';
38
+ // 虎扑内容用 <p> 包裹
39
+ const content = `<p>${text}</p>`;
40
+ // 构建请求体
41
+ const body = {
42
+ topicId: topic_id,
43
+ content,
44
+ shumeiId: '',
45
+ deviceid: '',
46
+ tid
47
+ };
48
+ // 如果有引用回复ID,添加到请求体
49
+ if (quote_id) {
50
+ body.quoteId = quote_id;
51
+ }
52
+ try {
53
+ const result = await postHupuJson(page, tid, url, body, 'Reply to Hupu thread', 'reply');
54
+ if (result.code === 1) {
55
+ return [{
56
+ status: '✅ 回复成功',
57
+ message: result.msg || result.message || ''
58
+ }];
59
+ }
60
+ else {
61
+ throw new Error(`接口错误 code=${result.code}: ${result.msg || result.message}`);
62
+ }
63
+ }
64
+ catch (error) {
65
+ if (error instanceof CliError)
66
+ throw error;
67
+ const errorMessage = error instanceof Error ? error.message : String(error);
68
+ throw new Error(`回复失败: ${errorMessage}`);
69
+ }
70
+ },
71
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,59 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+ import { decodeHtmlEntities, getHupuSearchUrl, readHupuSearchData, stripHtml } from './utils.js';
3
+ cli({
4
+ site: 'hupu',
5
+ name: 'search',
6
+ description: '搜索虎扑帖子 (使用官方API)',
7
+ domain: 'bbs.hupu.com',
8
+ strategy: Strategy.PUBLIC, // 公开API,不需要Cookie
9
+ browser: true,
10
+ navigateBefore: false,
11
+ args: [
12
+ {
13
+ name: 'query',
14
+ required: true,
15
+ positional: true,
16
+ help: '搜索关键词'
17
+ },
18
+ {
19
+ name: 'page',
20
+ type: 'int',
21
+ default: 1,
22
+ help: '结果页码'
23
+ },
24
+ {
25
+ name: 'limit',
26
+ type: 'int',
27
+ default: 20,
28
+ help: '返回结果数量'
29
+ },
30
+ {
31
+ name: 'forum',
32
+ help: '板块ID过滤 (可选)'
33
+ },
34
+ {
35
+ name: 'sort',
36
+ default: 'general',
37
+ help: '排序方式: general/createtime/replytime/light/reply'
38
+ }
39
+ ],
40
+ columns: ['rank', 'title', 'author', 'replies', 'lights', 'forum', 'url'],
41
+ func: async (page, kwargs) => {
42
+ const { query, page: pageNum = 1, limit = 20, forum, sort = 'general' } = kwargs;
43
+ const searchUrl = getHupuSearchUrl(query, pageNum, forum, sort);
44
+ const data = await readHupuSearchData(page, searchUrl, 'Search Hupu threads');
45
+ // 提取搜索结果
46
+ const results = data.searchRes?.data || [];
47
+ // 处理结果:清理HTML标签,解码HTML实体
48
+ const processedResults = results.slice(0, Number(limit)).map((item, index) => ({
49
+ rank: index + 1,
50
+ title: decodeHtmlEntities(stripHtml(item.title)),
51
+ author: item.username || '未知用户',
52
+ replies: item.replies || '0',
53
+ lights: item.lights || '0',
54
+ forum: item.forum_name || '未知板块',
55
+ url: `https://bbs.hupu.com/${item.id}.html`
56
+ }));
57
+ return processedResults;
58
+ },
59
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ import { CliError } from '../../errors.js';
2
+ import { cli, Strategy } from '../../registry.js';
3
+ import { postHupuJson } from './utils.js';
4
+ cli({
5
+ site: 'hupu',
6
+ name: 'unlike',
7
+ description: '取消点赞虎扑回复 (需要登录)',
8
+ domain: 'bbs.hupu.com',
9
+ strategy: Strategy.COOKIE, // 需要Cookie认证
10
+ navigateBefore: false,
11
+ args: [
12
+ {
13
+ name: 'tid',
14
+ required: true,
15
+ positional: true,
16
+ help: '帖子ID(9位数字)'
17
+ },
18
+ {
19
+ name: 'pid',
20
+ required: true,
21
+ positional: true,
22
+ help: '回复ID'
23
+ },
24
+ {
25
+ name: 'fid',
26
+ required: true,
27
+ help: '板块ID(如278汽车区)'
28
+ }
29
+ ],
30
+ columns: ['status', 'message'],
31
+ func: async (page, kwargs) => {
32
+ const { tid, pid, fid } = kwargs;
33
+ const url = 'https://bbs.hupu.com/pcmapi/pc/bbs/v1/reply/cancelLight';
34
+ // 构建请求体(与点赞相同)
35
+ const body = {
36
+ tid,
37
+ pid,
38
+ puid: '',
39
+ fid,
40
+ shumei_id: '',
41
+ deviceid: ''
42
+ };
43
+ try {
44
+ const result = await postHupuJson(page, tid, url, body, 'Unlike Hupu reply');
45
+ // 处理响应
46
+ if (result.code === 1) {
47
+ return [{
48
+ status: '✅ 取消点赞成功',
49
+ message: ''
50
+ }];
51
+ }
52
+ else if (result.code === 0 && result.msg === '你还没有点亮过这个回帖') {
53
+ return [{
54
+ status: '⚠️ 你还没点赞过',
55
+ message: result.msg || ''
56
+ }];
57
+ }
58
+ else if (result.code === 0) {
59
+ return [{
60
+ status: '⚠️ 操作未执行',
61
+ message: result.msg || result.message || ''
62
+ }];
63
+ }
64
+ else {
65
+ throw new Error(`接口错误 code=${result.code}: ${result.msg || result.message}`);
66
+ }
67
+ }
68
+ catch (error) {
69
+ if (error instanceof CliError)
70
+ throw error;
71
+ const errorMessage = error instanceof Error ? error.message : String(error);
72
+ throw new Error(`取消点赞失败: ${errorMessage}`);
73
+ }
74
+ },
75
+ });
@@ -0,0 +1,20 @@
1
+ import type { IPage } from '../../types.js';
2
+ export interface HupuApiResponse {
3
+ code?: number;
4
+ msg?: string;
5
+ message?: string;
6
+ }
7
+ export declare function stripHtml(html: string): string;
8
+ export declare function decodeHtmlEntities(html: string): string;
9
+ export declare function getHupuThreadUrl(tid: unknown): string;
10
+ export declare function getHupuSearchUrl(query: unknown, page: unknown, forum?: unknown, sort?: unknown): string;
11
+ export declare function readHupuNextData<T>(page: IPage, url: string, actionLabel: string, options?: {
12
+ expectedTid?: string;
13
+ timeoutMs?: number;
14
+ }): Promise<T>;
15
+ export declare function readHupuSearchData<T>(page: IPage, url: string, actionLabel: string): Promise<T>;
16
+ /**
17
+ * Execute authenticated Hupu JSON requests inside the browser page so
18
+ * cookies and the thread referer come from the live logged-in session.
19
+ */
20
+ export declare function postHupuJson(page: IPage, tid: unknown, apiUrl: string, body: Record<string, unknown>, actionLabel: string, mode?: 'default' | 'reply'): Promise<HupuApiResponse>;