@jackwener/opencli 1.5.5 → 1.5.7

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 (540) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +31 -4
  3. package/README.zh-CN.md +40 -5
  4. package/SKILL.md +1 -1
  5. package/dist/browser/cdp.d.ts +1 -0
  6. package/dist/browser/cdp.js +30 -27
  7. package/dist/browser/daemon-client.d.ts +11 -1
  8. package/dist/browser/daemon-client.js +3 -0
  9. package/dist/browser/dom-helpers.js +1 -0
  10. package/dist/browser/dom-helpers.test.js +14 -1
  11. package/dist/browser/mcp.js +18 -13
  12. package/dist/browser/page.d.ts +6 -0
  13. package/dist/browser/page.js +37 -2
  14. package/dist/browser/page.test.d.ts +1 -0
  15. package/dist/browser/page.test.js +44 -0
  16. package/dist/browser/stealth.js +198 -0
  17. package/dist/browser/stealth.test.d.ts +1 -0
  18. package/dist/browser/stealth.test.js +134 -0
  19. package/dist/browser.test.js +1 -1
  20. package/dist/build-manifest.d.ts +1 -0
  21. package/dist/build-manifest.js +5 -1
  22. package/dist/build-manifest.test.js +2 -0
  23. package/dist/cli-manifest.json +1821 -252
  24. package/dist/cli.js +20 -3
  25. package/dist/clis/antigravity/serve.d.ts +1 -1
  26. package/dist/clis/antigravity/serve.js +5 -8
  27. package/dist/clis/band/bands.d.ts +1 -0
  28. package/dist/clis/band/bands.js +72 -0
  29. package/dist/clis/band/mentions.d.ts +1 -0
  30. package/dist/clis/band/mentions.js +127 -0
  31. package/dist/clis/band/post.d.ts +1 -0
  32. package/dist/clis/band/post.js +175 -0
  33. package/dist/clis/band/posts.d.ts +1 -0
  34. package/dist/clis/band/posts.js +94 -0
  35. package/dist/clis/bilibili/subtitle.js +4 -0
  36. package/dist/clis/bilibili/subtitle.test.d.ts +1 -0
  37. package/dist/clis/bilibili/subtitle.test.js +48 -0
  38. package/dist/clis/chatwise/ask.js +0 -2
  39. package/dist/clis/chatwise/export.js +0 -2
  40. package/dist/clis/chatwise/history.js +0 -2
  41. package/dist/clis/chatwise/model.js +0 -2
  42. package/dist/clis/chatwise/new.js +1 -2
  43. package/dist/clis/chatwise/read.js +0 -2
  44. package/dist/clis/chatwise/screenshot.js +1 -2
  45. package/dist/clis/chatwise/send.js +0 -2
  46. package/dist/clis/chatwise/status.js +1 -2
  47. package/dist/clis/ctrip/search.d.ts +13 -0
  48. package/dist/clis/ctrip/search.js +73 -48
  49. package/dist/clis/ctrip/search.test.d.ts +1 -0
  50. package/dist/clis/ctrip/search.test.js +64 -0
  51. package/dist/clis/doubao/detail.d.ts +1 -0
  52. package/dist/clis/doubao/detail.js +33 -0
  53. package/dist/clis/doubao/detail.test.d.ts +1 -0
  54. package/dist/clis/doubao/detail.test.js +42 -0
  55. package/dist/clis/doubao/history.d.ts +1 -0
  56. package/dist/clis/doubao/history.js +28 -0
  57. package/dist/clis/doubao/history.test.d.ts +1 -0
  58. package/dist/clis/doubao/history.test.js +37 -0
  59. package/dist/clis/doubao/meeting-summary.d.ts +1 -0
  60. package/dist/clis/doubao/meeting-summary.js +39 -0
  61. package/dist/clis/doubao/meeting-transcript.d.ts +1 -0
  62. package/dist/clis/doubao/meeting-transcript.js +36 -0
  63. package/dist/clis/doubao/utils.d.ts +27 -0
  64. package/dist/clis/doubao/utils.js +317 -0
  65. package/dist/clis/doubao/utils.test.d.ts +1 -0
  66. package/dist/clis/doubao/utils.test.js +24 -0
  67. package/dist/clis/douyin/_shared/public-api.d.ts +33 -0
  68. package/dist/clis/douyin/_shared/public-api.js +29 -0
  69. package/dist/clis/douyin/_shared/sts2.js +8 -2
  70. package/dist/clis/douyin/_shared/sts2.test.d.ts +1 -0
  71. package/dist/clis/douyin/_shared/sts2.test.js +27 -0
  72. package/dist/clis/douyin/activities.js +4 -2
  73. package/dist/clis/douyin/activities.test.js +34 -1
  74. package/dist/clis/douyin/collections.js +1 -1
  75. package/dist/clis/douyin/collections.test.js +24 -2
  76. package/dist/clis/douyin/draft.d.ts +8 -11
  77. package/dist/clis/douyin/draft.js +302 -185
  78. package/dist/clis/douyin/draft.test.d.ts +1 -1
  79. package/dist/clis/douyin/draft.test.js +357 -2
  80. package/dist/clis/douyin/hashtag.js +9 -2
  81. package/dist/clis/douyin/hashtag.test.js +35 -2
  82. package/dist/clis/douyin/profile.js +1 -1
  83. package/dist/clis/douyin/profile.test.js +36 -1
  84. package/dist/clis/douyin/user-videos.d.ts +5 -0
  85. package/dist/clis/douyin/user-videos.js +74 -0
  86. package/dist/clis/douyin/user-videos.test.d.ts +1 -0
  87. package/dist/clis/douyin/user-videos.test.js +108 -0
  88. package/dist/clis/douyin/videos.js +22 -5
  89. package/dist/clis/douyin/videos.test.js +45 -2
  90. package/dist/clis/facebook/search.test.d.ts +5 -0
  91. package/dist/clis/facebook/search.test.js +60 -0
  92. package/dist/clis/facebook/search.yaml +4 -3
  93. package/dist/clis/instagram/download.d.ts +16 -0
  94. package/dist/clis/instagram/download.js +225 -0
  95. package/dist/clis/instagram/download.test.d.ts +1 -0
  96. package/dist/clis/instagram/download.test.js +118 -0
  97. package/dist/clis/notebooklm/bind-current.d.ts +1 -0
  98. package/dist/clis/notebooklm/bind-current.js +29 -0
  99. package/dist/clis/notebooklm/bind-current.test.d.ts +1 -0
  100. package/dist/clis/notebooklm/bind-current.test.js +35 -0
  101. package/dist/clis/notebooklm/binding.test.d.ts +1 -0
  102. package/dist/clis/notebooklm/binding.test.js +44 -0
  103. package/dist/clis/notebooklm/compat.test.d.ts +3 -0
  104. package/dist/clis/notebooklm/compat.test.js +16 -0
  105. package/dist/clis/notebooklm/current.d.ts +1 -0
  106. package/dist/clis/notebooklm/current.js +28 -0
  107. package/dist/clis/notebooklm/get.d.ts +1 -0
  108. package/dist/clis/notebooklm/get.js +37 -0
  109. package/dist/clis/notebooklm/history.d.ts +1 -0
  110. package/dist/clis/notebooklm/history.js +25 -0
  111. package/dist/clis/notebooklm/history.test.d.ts +1 -0
  112. package/dist/clis/notebooklm/history.test.js +58 -0
  113. package/dist/clis/notebooklm/list.d.ts +1 -0
  114. package/dist/clis/notebooklm/list.js +35 -0
  115. package/dist/clis/notebooklm/note-list.d.ts +1 -0
  116. package/dist/clis/notebooklm/note-list.js +28 -0
  117. package/dist/clis/notebooklm/note-list.test.d.ts +1 -0
  118. package/dist/clis/notebooklm/note-list.test.js +56 -0
  119. package/dist/clis/notebooklm/notes-get.d.ts +1 -0
  120. package/dist/clis/notebooklm/notes-get.js +47 -0
  121. package/dist/clis/notebooklm/notes-get.test.d.ts +1 -0
  122. package/dist/clis/notebooklm/notes-get.test.js +72 -0
  123. package/dist/clis/notebooklm/rpc.d.ts +36 -0
  124. package/dist/clis/notebooklm/rpc.js +189 -0
  125. package/dist/clis/notebooklm/rpc.test.d.ts +1 -0
  126. package/dist/clis/notebooklm/rpc.test.js +105 -0
  127. package/dist/clis/notebooklm/shared.d.ts +87 -0
  128. package/dist/clis/notebooklm/shared.js +3 -0
  129. package/dist/clis/notebooklm/source-fulltext.d.ts +1 -0
  130. package/dist/clis/notebooklm/source-fulltext.js +44 -0
  131. package/dist/clis/notebooklm/source-fulltext.test.d.ts +1 -0
  132. package/dist/clis/notebooklm/source-fulltext.test.js +106 -0
  133. package/dist/clis/notebooklm/source-get.d.ts +1 -0
  134. package/dist/clis/notebooklm/source-get.js +40 -0
  135. package/dist/clis/notebooklm/source-get.test.d.ts +1 -0
  136. package/dist/clis/notebooklm/source-get.test.js +84 -0
  137. package/dist/clis/notebooklm/source-guide.d.ts +1 -0
  138. package/dist/clis/notebooklm/source-guide.js +44 -0
  139. package/dist/clis/notebooklm/source-guide.test.d.ts +1 -0
  140. package/dist/clis/notebooklm/source-guide.test.js +104 -0
  141. package/dist/clis/notebooklm/source-list.d.ts +1 -0
  142. package/dist/clis/notebooklm/source-list.js +30 -0
  143. package/dist/clis/notebooklm/status.d.ts +1 -0
  144. package/dist/clis/notebooklm/status.js +31 -0
  145. package/dist/clis/notebooklm/summary.d.ts +1 -0
  146. package/dist/clis/notebooklm/summary.js +30 -0
  147. package/dist/clis/notebooklm/summary.test.d.ts +1 -0
  148. package/dist/clis/notebooklm/summary.test.js +78 -0
  149. package/dist/clis/notebooklm/utils.d.ts +37 -0
  150. package/dist/clis/notebooklm/utils.js +739 -0
  151. package/dist/clis/notebooklm/utils.test.d.ts +1 -0
  152. package/dist/clis/notebooklm/utils.test.js +390 -0
  153. package/dist/clis/ones/common.d.ts +32 -0
  154. package/dist/clis/ones/common.js +144 -0
  155. package/dist/clis/ones/enrich-tasks.d.ts +5 -0
  156. package/dist/clis/ones/enrich-tasks.js +37 -0
  157. package/dist/clis/ones/login.d.ts +1 -0
  158. package/dist/clis/ones/login.js +80 -0
  159. package/dist/clis/ones/logout.d.ts +1 -0
  160. package/dist/clis/ones/logout.js +17 -0
  161. package/dist/clis/ones/me.d.ts +1 -0
  162. package/dist/clis/ones/me.js +30 -0
  163. package/dist/clis/ones/my-tasks.d.ts +1 -0
  164. package/dist/clis/ones/my-tasks.js +120 -0
  165. package/dist/clis/ones/resolve-labels.d.ts +10 -0
  166. package/dist/clis/ones/resolve-labels.js +64 -0
  167. package/dist/clis/ones/task-helpers.d.ts +29 -0
  168. package/dist/clis/ones/task-helpers.js +212 -0
  169. package/dist/clis/ones/task-helpers.test.d.ts +1 -0
  170. package/dist/clis/ones/task-helpers.test.js +12 -0
  171. package/dist/clis/ones/task.d.ts +1 -0
  172. package/dist/clis/ones/task.js +66 -0
  173. package/dist/clis/ones/tasks.d.ts +1 -0
  174. package/dist/clis/ones/tasks.js +79 -0
  175. package/dist/clis/ones/token-info.d.ts +1 -0
  176. package/dist/clis/ones/token-info.js +42 -0
  177. package/dist/clis/ones/worklog.d.ts +11 -0
  178. package/dist/clis/ones/worklog.js +267 -0
  179. package/dist/clis/ones/worklog.test.d.ts +1 -0
  180. package/dist/clis/ones/worklog.test.js +20 -0
  181. package/dist/clis/spotify/spotify.d.ts +1 -0
  182. package/dist/clis/spotify/spotify.js +316 -0
  183. package/dist/clis/spotify/utils.d.ts +21 -0
  184. package/dist/clis/spotify/utils.js +66 -0
  185. package/dist/clis/spotify/utils.test.d.ts +1 -0
  186. package/dist/clis/spotify/utils.test.js +67 -0
  187. package/dist/clis/substack/utils.d.ts +4 -0
  188. package/dist/clis/substack/utils.js +8 -2
  189. package/dist/clis/substack/utils.test.d.ts +1 -0
  190. package/dist/clis/substack/utils.test.js +46 -0
  191. package/dist/clis/tieba/commands.test.d.ts +4 -0
  192. package/dist/clis/tieba/commands.test.js +79 -0
  193. package/dist/clis/tieba/hot.d.ts +1 -0
  194. package/dist/clis/tieba/hot.js +48 -0
  195. package/dist/clis/tieba/posts.d.ts +1 -0
  196. package/dist/clis/tieba/posts.js +85 -0
  197. package/dist/clis/tieba/read.d.ts +1 -0
  198. package/dist/clis/tieba/read.js +140 -0
  199. package/dist/clis/tieba/search.d.ts +1 -0
  200. package/dist/clis/tieba/search.js +108 -0
  201. package/dist/clis/tieba/utils.d.ts +101 -0
  202. package/dist/clis/tieba/utils.js +240 -0
  203. package/dist/clis/tieba/utils.test.d.ts +1 -0
  204. package/dist/clis/tieba/utils.test.js +290 -0
  205. package/dist/clis/v2ex/hot.yaml +4 -1
  206. package/dist/clis/v2ex/latest.yaml +4 -1
  207. package/dist/clis/v2ex/topic.yaml +6 -1
  208. package/dist/clis/weixin/download.d.ts +9 -0
  209. package/dist/clis/weixin/download.js +76 -6
  210. package/dist/clis/weread/book.js +206 -13
  211. package/dist/clis/weread/commands.test.js +331 -0
  212. package/dist/clis/weread/private-api-regression.test.d.ts +1 -0
  213. package/dist/{weread-private-api-regression.test.js → clis/weread/private-api-regression.test.js} +92 -30
  214. package/dist/clis/weread/search-regression.test.d.ts +1 -0
  215. package/dist/clis/weread/search-regression.test.js +407 -0
  216. package/dist/clis/weread/search.js +143 -7
  217. package/dist/clis/weread/shelf.js +13 -95
  218. package/dist/clis/weread/utils.d.ts +56 -0
  219. package/dist/clis/weread/utils.js +234 -7
  220. package/dist/clis/weread/utils.test.js +71 -1
  221. package/dist/clis/xiaohongshu/comments.d.ts +3 -0
  222. package/dist/clis/xiaohongshu/comments.js +76 -17
  223. package/dist/clis/xiaohongshu/comments.test.js +70 -9
  224. package/dist/clis/xiaohongshu/download.d.ts +4 -1
  225. package/dist/clis/xiaohongshu/download.js +83 -22
  226. package/dist/clis/xiaohongshu/download.test.d.ts +1 -0
  227. package/dist/clis/xiaohongshu/download.test.js +75 -0
  228. package/dist/clis/xiaohongshu/note-helpers.d.ts +12 -0
  229. package/dist/clis/xiaohongshu/note-helpers.js +23 -0
  230. package/dist/clis/xiaohongshu/note.d.ts +7 -0
  231. package/dist/clis/xiaohongshu/note.js +76 -0
  232. package/dist/clis/xiaohongshu/note.test.d.ts +1 -0
  233. package/dist/clis/xiaohongshu/note.test.js +136 -0
  234. package/dist/clis/xiaohongshu/publish.d.ts +1 -1
  235. package/dist/clis/xiaohongshu/publish.js +78 -31
  236. package/dist/clis/xiaohongshu/publish.test.js +66 -1
  237. package/dist/clis/xiaohongshu/search.js +9 -0
  238. package/dist/clis/xiaohongshu/search.test.js +10 -4
  239. package/dist/clis/xiaohongshu/user-helpers.d.ts +1 -0
  240. package/dist/clis/xiaohongshu/user-helpers.js +2 -0
  241. package/dist/clis/xiaohongshu/user-helpers.test.js +18 -0
  242. package/dist/clis/xueqiu/comments.d.ts +118 -0
  243. package/dist/clis/xueqiu/comments.js +354 -0
  244. package/dist/clis/xueqiu/comments.test.d.ts +1 -0
  245. package/dist/clis/xueqiu/comments.test.js +696 -0
  246. package/dist/clis/youtube/search.js +57 -17
  247. package/dist/clis/youtube/transcript.js +2 -4
  248. package/dist/clis/youtube/utils.d.ts +9 -0
  249. package/dist/clis/youtube/utils.js +67 -3
  250. package/dist/clis/youtube/utils.test.d.ts +1 -0
  251. package/dist/clis/youtube/utils.test.js +37 -0
  252. package/dist/clis/youtube/video.js +16 -15
  253. package/dist/clis/zhihu/question.js +19 -17
  254. package/dist/clis/zhihu/question.test.d.ts +1 -0
  255. package/dist/clis/zhihu/question.test.js +54 -0
  256. package/dist/clis/zsxq/dynamics.d.ts +1 -0
  257. package/dist/clis/zsxq/dynamics.js +47 -0
  258. package/dist/clis/zsxq/groups.d.ts +1 -0
  259. package/dist/clis/zsxq/groups.js +32 -0
  260. package/dist/clis/zsxq/search.d.ts +1 -0
  261. package/dist/clis/zsxq/search.js +43 -0
  262. package/dist/clis/zsxq/search.test.d.ts +1 -0
  263. package/dist/clis/zsxq/search.test.js +24 -0
  264. package/dist/clis/zsxq/topic.d.ts +1 -0
  265. package/dist/clis/zsxq/topic.js +47 -0
  266. package/dist/clis/zsxq/topic.test.d.ts +1 -0
  267. package/dist/clis/zsxq/topic.test.js +29 -0
  268. package/dist/clis/zsxq/topics.d.ts +1 -0
  269. package/dist/clis/zsxq/topics.js +25 -0
  270. package/dist/clis/zsxq/topics.test.d.ts +1 -0
  271. package/dist/clis/zsxq/topics.test.js +24 -0
  272. package/dist/clis/zsxq/utils.d.ts +97 -0
  273. package/dist/clis/zsxq/utils.js +230 -0
  274. package/dist/commanderAdapter.js +10 -1
  275. package/dist/commanderAdapter.test.js +64 -0
  276. package/dist/commands/daemon.d.ts +9 -0
  277. package/dist/commands/daemon.js +124 -0
  278. package/dist/commands/daemon.test.d.ts +1 -0
  279. package/dist/commands/daemon.test.js +185 -0
  280. package/dist/completion.js +3 -1
  281. package/dist/constants.d.ts +2 -0
  282. package/dist/constants.js +2 -0
  283. package/dist/daemon.d.ts +1 -1
  284. package/dist/daemon.js +25 -14
  285. package/dist/daemon.test.d.ts +1 -0
  286. package/dist/daemon.test.js +65 -0
  287. package/dist/discovery.d.ts +9 -0
  288. package/dist/discovery.js +47 -2
  289. package/dist/electron-apps.d.ts +29 -0
  290. package/dist/electron-apps.js +65 -0
  291. package/dist/electron-apps.test.d.ts +1 -0
  292. package/dist/electron-apps.test.js +43 -0
  293. package/dist/engine.test.js +41 -9
  294. package/dist/execution.js +20 -16
  295. package/dist/external-clis.yaml +17 -0
  296. package/dist/idle-manager.d.ts +19 -0
  297. package/dist/idle-manager.js +54 -0
  298. package/dist/launcher.d.ts +36 -0
  299. package/dist/launcher.js +152 -0
  300. package/dist/launcher.test.d.ts +1 -0
  301. package/dist/launcher.test.js +57 -0
  302. package/dist/main.js +3 -3
  303. package/dist/registry.d.ts +1 -0
  304. package/dist/registry.js +31 -3
  305. package/dist/registry.test.js +13 -0
  306. package/dist/runtime.d.ts +5 -3
  307. package/dist/runtime.js +12 -5
  308. package/dist/serialization.d.ts +1 -0
  309. package/dist/serialization.js +3 -0
  310. package/dist/serialization.test.js +17 -1
  311. package/dist/tui.d.ts +7 -0
  312. package/dist/tui.js +52 -0
  313. package/dist/tui.test.d.ts +1 -0
  314. package/dist/tui.test.js +19 -0
  315. package/dist/types.d.ts +5 -0
  316. package/dist/weixin-download.test.js +14 -0
  317. package/docs/.vitepress/config.mts +4 -0
  318. package/docs/adapters/browser/band.md +63 -0
  319. package/docs/adapters/browser/notebooklm.md +69 -0
  320. package/docs/adapters/browser/ones.md +59 -0
  321. package/docs/adapters/browser/spotify.md +62 -0
  322. package/docs/adapters/browser/tieba.md +45 -0
  323. package/docs/adapters/browser/xiaohongshu.md +19 -10
  324. package/docs/adapters/browser/xueqiu.md +5 -0
  325. package/docs/adapters/browser/zsxq.md +49 -0
  326. package/docs/adapters/index.md +67 -63
  327. package/docs/adapters-doc/ones.md +32 -0
  328. package/docs/guide/browser-bridge.md +12 -0
  329. package/docs/guide/troubleshooting.md +9 -4
  330. package/docs/superpowers/plans/2026-03-31-daemon-lifecycle-redesign.md +857 -0
  331. package/docs/superpowers/specs/2026-03-31-daemon-lifecycle-redesign.md +208 -0
  332. package/docs/zh/guide/browser-bridge.md +12 -0
  333. package/extension/dist/background.js +794 -513
  334. package/extension/src/background.test.ts +202 -2
  335. package/extension/src/background.ts +189 -10
  336. package/extension/src/cdp.ts +54 -0
  337. package/extension/src/protocol.ts +11 -5
  338. package/package.json +1 -1
  339. package/scripts/postinstall.js +16 -0
  340. package/src/browser/cdp.ts +24 -17
  341. package/src/browser/daemon-client.ts +11 -1
  342. package/src/browser/dom-helpers.test.ts +15 -1
  343. package/src/browser/dom-helpers.ts +1 -0
  344. package/src/browser/mcp.ts +18 -13
  345. package/src/browser/page.test.ts +58 -0
  346. package/src/browser/page.ts +34 -2
  347. package/src/browser/stealth.test.ts +153 -0
  348. package/src/browser/stealth.ts +198 -0
  349. package/src/browser.test.ts +1 -1
  350. package/src/build-manifest.test.ts +2 -0
  351. package/src/build-manifest.ts +6 -1
  352. package/src/cli.ts +21 -3
  353. package/src/clis/antigravity/SKILL.md +3 -12
  354. package/src/clis/antigravity/serve.ts +5 -10
  355. package/src/clis/band/bands.ts +76 -0
  356. package/src/clis/band/mentions.ts +134 -0
  357. package/src/clis/band/post.ts +187 -0
  358. package/src/clis/band/posts.ts +106 -0
  359. package/src/clis/bilibili/subtitle.test.ts +60 -0
  360. package/src/clis/bilibili/subtitle.ts +4 -0
  361. package/src/clis/chatwise/ask.ts +0 -2
  362. package/src/clis/chatwise/export.ts +0 -2
  363. package/src/clis/chatwise/history.ts +0 -2
  364. package/src/clis/chatwise/model.ts +0 -2
  365. package/src/clis/chatwise/new.ts +1 -2
  366. package/src/clis/chatwise/read.ts +0 -2
  367. package/src/clis/chatwise/screenshot.ts +1 -2
  368. package/src/clis/chatwise/send.ts +0 -2
  369. package/src/clis/chatwise/status.ts +1 -2
  370. package/src/clis/ctrip/search.test.ts +73 -0
  371. package/src/clis/ctrip/search.ts +97 -47
  372. package/src/clis/doubao/detail.test.ts +53 -0
  373. package/src/clis/doubao/detail.ts +41 -0
  374. package/src/clis/doubao/history.test.ts +45 -0
  375. package/src/clis/doubao/history.ts +32 -0
  376. package/src/clis/doubao/meeting-summary.ts +53 -0
  377. package/src/clis/doubao/meeting-transcript.ts +48 -0
  378. package/src/clis/doubao/utils.test.ts +45 -0
  379. package/src/clis/doubao/utils.ts +371 -0
  380. package/src/clis/douyin/_shared/public-api.ts +84 -0
  381. package/src/clis/douyin/_shared/sts2.test.ts +31 -0
  382. package/src/clis/douyin/_shared/sts2.ts +11 -3
  383. package/src/clis/douyin/activities.test.ts +41 -1
  384. package/src/clis/douyin/activities.ts +12 -3
  385. package/src/clis/douyin/collections.test.ts +35 -2
  386. package/src/clis/douyin/collections.ts +1 -1
  387. package/src/clis/douyin/draft.test.ts +444 -2
  388. package/src/clis/douyin/draft.ts +382 -218
  389. package/src/clis/douyin/hashtag.test.ts +42 -2
  390. package/src/clis/douyin/hashtag.ts +11 -3
  391. package/src/clis/douyin/profile.test.ts +43 -1
  392. package/src/clis/douyin/profile.ts +9 -2
  393. package/src/clis/douyin/user-videos.test.ts +122 -0
  394. package/src/clis/douyin/user-videos.ts +101 -0
  395. package/src/clis/douyin/videos.test.ts +52 -2
  396. package/src/clis/douyin/videos.ts +49 -15
  397. package/src/clis/facebook/search.test.ts +70 -0
  398. package/src/clis/facebook/search.yaml +4 -3
  399. package/src/clis/instagram/download.test.ts +159 -0
  400. package/src/clis/instagram/download.ts +286 -0
  401. package/src/clis/notebooklm/bind-current.test.ts +43 -0
  402. package/src/clis/notebooklm/bind-current.ts +36 -0
  403. package/src/clis/notebooklm/binding.test.ts +53 -0
  404. package/src/clis/notebooklm/compat.test.ts +19 -0
  405. package/src/clis/notebooklm/current.ts +38 -0
  406. package/src/clis/notebooklm/get.ts +53 -0
  407. package/src/clis/notebooklm/history.test.ts +70 -0
  408. package/src/clis/notebooklm/history.ts +36 -0
  409. package/src/clis/notebooklm/list.ts +40 -0
  410. package/src/clis/notebooklm/note-list.test.ts +64 -0
  411. package/src/clis/notebooklm/note-list.ts +42 -0
  412. package/src/clis/notebooklm/notes-get.test.ts +88 -0
  413. package/src/clis/notebooklm/notes-get.ts +67 -0
  414. package/src/clis/notebooklm/rpc.test.ts +126 -0
  415. package/src/clis/notebooklm/rpc.ts +286 -0
  416. package/src/clis/notebooklm/shared.ts +98 -0
  417. package/src/clis/notebooklm/source-fulltext.test.ts +123 -0
  418. package/src/clis/notebooklm/source-fulltext.ts +69 -0
  419. package/src/clis/notebooklm/source-get.test.ts +100 -0
  420. package/src/clis/notebooklm/source-get.ts +60 -0
  421. package/src/clis/notebooklm/source-guide.test.ts +121 -0
  422. package/src/clis/notebooklm/source-guide.ts +69 -0
  423. package/src/clis/notebooklm/source-list.ts +45 -0
  424. package/src/clis/notebooklm/status.ts +34 -0
  425. package/src/clis/notebooklm/summary.test.ts +94 -0
  426. package/src/clis/notebooklm/summary.ts +45 -0
  427. package/src/clis/notebooklm/utils.test.ts +446 -0
  428. package/src/clis/notebooklm/utils.ts +893 -0
  429. package/src/clis/ones/common.ts +187 -0
  430. package/src/clis/ones/enrich-tasks.ts +47 -0
  431. package/src/clis/ones/login.ts +103 -0
  432. package/src/clis/ones/logout.ts +19 -0
  433. package/src/clis/ones/me.ts +34 -0
  434. package/src/clis/ones/my-tasks.ts +148 -0
  435. package/src/clis/ones/resolve-labels.ts +80 -0
  436. package/src/clis/ones/task-helpers.test.ts +14 -0
  437. package/src/clis/ones/task-helpers.ts +214 -0
  438. package/src/clis/ones/task.ts +79 -0
  439. package/src/clis/ones/tasks.ts +92 -0
  440. package/src/clis/ones/token-info.ts +46 -0
  441. package/src/clis/ones/worklog.test.ts +24 -0
  442. package/src/clis/ones/worklog.ts +306 -0
  443. package/src/clis/spotify/spotify.ts +328 -0
  444. package/src/clis/spotify/utils.test.ts +87 -0
  445. package/src/clis/spotify/utils.ts +92 -0
  446. package/src/clis/substack/utils.test.ts +54 -0
  447. package/src/clis/substack/utils.ts +10 -2
  448. package/src/clis/tieba/commands.test.ts +86 -0
  449. package/src/clis/tieba/hot.ts +52 -0
  450. package/src/clis/tieba/posts.ts +108 -0
  451. package/src/clis/tieba/read.ts +158 -0
  452. package/src/clis/tieba/search.ts +119 -0
  453. package/src/clis/tieba/utils.test.ts +322 -0
  454. package/src/clis/tieba/utils.ts +348 -0
  455. package/src/clis/v2ex/hot.yaml +4 -1
  456. package/src/clis/v2ex/latest.yaml +4 -1
  457. package/src/clis/v2ex/topic.yaml +6 -1
  458. package/src/clis/weixin/download.ts +95 -6
  459. package/src/clis/weread/book.ts +256 -13
  460. package/src/clis/weread/commands.test.ts +409 -0
  461. package/src/{weread-private-api-regression.test.ts → clis/weread/private-api-regression.test.ts} +108 -30
  462. package/src/clis/weread/search-regression.test.ts +440 -0
  463. package/src/clis/weread/search.ts +189 -9
  464. package/src/clis/weread/shelf.ts +20 -122
  465. package/src/clis/weread/utils.test.ts +81 -1
  466. package/src/clis/weread/utils.ts +293 -7
  467. package/src/clis/xiaohongshu/comments.test.ts +85 -9
  468. package/src/clis/xiaohongshu/comments.ts +76 -17
  469. package/src/clis/xiaohongshu/download.test.ts +96 -0
  470. package/src/clis/xiaohongshu/download.ts +83 -22
  471. package/src/clis/xiaohongshu/note-helpers.ts +25 -0
  472. package/src/clis/xiaohongshu/note.test.ts +164 -0
  473. package/src/clis/xiaohongshu/note.ts +86 -0
  474. package/src/clis/xiaohongshu/publish.test.ts +79 -1
  475. package/src/clis/xiaohongshu/publish.ts +84 -30
  476. package/src/clis/xiaohongshu/search.test.ts +11 -4
  477. package/src/clis/xiaohongshu/search.ts +13 -0
  478. package/src/clis/xiaohongshu/user-helpers.test.ts +23 -0
  479. package/src/clis/xiaohongshu/user-helpers.ts +4 -0
  480. package/src/clis/xueqiu/comments.test.ts +823 -0
  481. package/src/clis/xueqiu/comments.ts +461 -0
  482. package/src/clis/youtube/search.ts +57 -17
  483. package/src/clis/youtube/transcript.ts +2 -4
  484. package/src/clis/youtube/utils.test.ts +43 -0
  485. package/src/clis/youtube/utils.ts +69 -0
  486. package/src/clis/youtube/video.ts +16 -15
  487. package/src/clis/zhihu/question.test.ts +71 -0
  488. package/src/clis/zhihu/question.ts +27 -15
  489. package/src/clis/zsxq/dynamics.ts +60 -0
  490. package/src/clis/zsxq/groups.ts +41 -0
  491. package/src/clis/zsxq/search.test.ts +29 -0
  492. package/src/clis/zsxq/search.ts +54 -0
  493. package/src/clis/zsxq/topic.test.ts +34 -0
  494. package/src/clis/zsxq/topic.ts +68 -0
  495. package/src/clis/zsxq/topics.test.ts +29 -0
  496. package/src/clis/zsxq/topics.ts +36 -0
  497. package/src/clis/zsxq/utils.ts +351 -0
  498. package/src/commanderAdapter.test.ts +77 -0
  499. package/src/commanderAdapter.ts +8 -1
  500. package/src/commands/daemon.test.ts +238 -0
  501. package/src/commands/daemon.ts +135 -0
  502. package/src/completion.ts +2 -1
  503. package/src/constants.ts +3 -0
  504. package/src/daemon.test.ts +88 -0
  505. package/src/daemon.ts +26 -14
  506. package/src/discovery.ts +52 -2
  507. package/src/electron-apps.test.ts +50 -0
  508. package/src/electron-apps.ts +89 -0
  509. package/src/engine.test.ts +45 -9
  510. package/src/execution.ts +24 -19
  511. package/src/external-clis.yaml +17 -0
  512. package/src/idle-manager.ts +60 -0
  513. package/src/launcher.test.ts +67 -0
  514. package/src/launcher.ts +185 -0
  515. package/src/main.ts +3 -2
  516. package/src/registry.test.ts +15 -0
  517. package/src/registry.ts +32 -3
  518. package/src/runtime.ts +13 -7
  519. package/src/serialization.test.ts +19 -1
  520. package/src/serialization.ts +2 -0
  521. package/src/tui.test.ts +23 -0
  522. package/src/tui.ts +65 -0
  523. package/src/types.ts +5 -0
  524. package/src/weixin-download.test.ts +27 -0
  525. package/tests/e2e/band-auth.test.ts +20 -0
  526. package/tests/e2e/browser-auth-helpers.ts +18 -0
  527. package/tests/e2e/browser-auth.test.ts +35 -47
  528. package/tests/e2e/browser-public-extended.test.ts +6 -2
  529. package/tests/e2e/browser-public.test.ts +288 -0
  530. package/tests/e2e/management.test.ts +1 -1
  531. package/tests/e2e/plugin-management.test.ts +1 -1
  532. package/vitest.config.ts +1 -0
  533. package/chatwise-opencli.ps1 +0 -82
  534. package/dist/clis/chatwise/shared.d.ts +0 -2
  535. package/dist/clis/chatwise/shared.js +0 -6
  536. package/dist/weread-private-api-regression.test.d.ts +0 -1
  537. package/dist/weread-search-regression.test.d.ts +0 -1
  538. package/dist/weread-search-regression.test.js +0 -39
  539. package/src/clis/chatwise/shared.ts +0 -8
  540. package/src/weread-search-regression.test.ts +0 -44
@@ -0,0 +1,348 @@
1
+ import { createHash } from 'node:crypto';
2
+
3
+ /**
4
+ * Shared Tieba parsing helpers used by the browser adapters.
5
+ */
6
+
7
+ export const MAX_TIEBA_LIMIT = 20;
8
+ const TIEBA_PC_SIGN_SALT = '36770b1f34c9bbf2e7d1a99d2b82fa9e';
9
+ const TIEBA_TIME_ZONE = 'Asia/Shanghai';
10
+
11
+ export interface RawTiebaPostCard {
12
+ title?: string;
13
+ author?: string;
14
+ descInfo?: string;
15
+ actionTexts?: string[];
16
+ commentCount?: unknown;
17
+ threadId?: unknown;
18
+ url?: unknown;
19
+ }
20
+
21
+ export interface RawTiebaPagePcFeedEntry {
22
+ layout?: string;
23
+ feed?: {
24
+ schema?: unknown;
25
+ log_param?: Array<{ key?: unknown; value?: unknown }>;
26
+ business_info_map?: Record<string, unknown>;
27
+ components?: Array<Record<string, unknown>>;
28
+ };
29
+ }
30
+
31
+ export interface TiebaPostItem {
32
+ rank: number;
33
+ title: string;
34
+ author: string;
35
+ replies: number;
36
+ last_reply: string;
37
+ id: string;
38
+ url: string;
39
+ }
40
+
41
+ export interface RawTiebaSearchItem {
42
+ title?: string;
43
+ forum?: string;
44
+ author?: string;
45
+ time?: string;
46
+ snippet?: string;
47
+ id?: string;
48
+ url?: string;
49
+ }
50
+
51
+ export interface TiebaSearchItem {
52
+ rank: number;
53
+ title: string;
54
+ forum: string;
55
+ author: string;
56
+ time: string;
57
+ snippet: string;
58
+ id: string;
59
+ url: string;
60
+ }
61
+
62
+ export interface RawTiebaMainPost {
63
+ title?: string;
64
+ author?: string;
65
+ fallbackAuthor?: string;
66
+ contentText?: string;
67
+ structuredText?: string;
68
+ visibleTime?: string;
69
+ structuredTime?: unknown;
70
+ hasMedia?: boolean;
71
+ }
72
+
73
+ export interface RawTiebaReply {
74
+ floor?: unknown;
75
+ author?: string;
76
+ content?: string;
77
+ time?: string;
78
+ }
79
+
80
+ export interface RawTiebaReadPayload {
81
+ mainPost?: RawTiebaMainPost | null;
82
+ replies?: RawTiebaReply[];
83
+ }
84
+
85
+ export interface TiebaReadItem {
86
+ floor: number;
87
+ author: string;
88
+ content: string;
89
+ time: string;
90
+ }
91
+
92
+ export interface TiebaReadBuildOptions {
93
+ limit?: unknown;
94
+ includeMainPost?: boolean;
95
+ }
96
+
97
+ /**
98
+ * Keep the public CLI limit contract aligned with the real implementation.
99
+ */
100
+ export function normalizeTiebaLimit(value: unknown, fallback: number = MAX_TIEBA_LIMIT): number {
101
+ const parsed = Number(value ?? fallback);
102
+ if (!Number.isFinite(parsed) || parsed < 1) return fallback;
103
+ return Math.min(Math.trunc(parsed), MAX_TIEBA_LIMIT);
104
+ }
105
+
106
+ export function normalizeText(value: unknown): string {
107
+ return typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
108
+ }
109
+
110
+ /**
111
+ * Match Tieba PC's signed request contract so forum list fetching stays stable.
112
+ */
113
+ export function signTiebaPcParams(params: Record<string, string>): string {
114
+ const payload = Object.keys(params)
115
+ .sort((left, right) => left.localeCompare(right))
116
+ .map((key) => `${key}=${params[key]}`)
117
+ .join('') + TIEBA_PC_SIGN_SALT;
118
+ return createHash('md5').update(payload).digest('hex');
119
+ }
120
+
121
+ export function parseTiebaCount(text: string): number {
122
+ const value = normalizeText(text).toUpperCase();
123
+ if (!value) return 0;
124
+ const compact = value.replace(/[^\d.W万]/g, '');
125
+ if (compact.endsWith('万')) {
126
+ return Math.round(parseFloat(compact.slice(0, -1)) * 10000);
127
+ }
128
+ if (compact.endsWith('W')) {
129
+ return Math.round(parseFloat(compact.slice(0, -1)) * 10000);
130
+ }
131
+ return parseInt(compact.replace(/[^\d]/g, ''), 10) || 0;
132
+ }
133
+
134
+ export function parseTiebaLastReply(text: string): string {
135
+ const normalized = normalizeText(text).replace(/^回复于/, '').trim();
136
+ const match = normalized.match(/(刚刚|\d+\s*(?:分钟|小时|天)前|\d{2}-\d{2}(?:\s+\d{2}:\d{2})?|\d{4}-\d{2}-\d{2}(?:\s+\d{2}:\d{2})?)/);
137
+ return match ? match[1].trim() : normalized;
138
+ }
139
+
140
+ function buildTiebaThreadUrl(id: string, rawUrl?: unknown): string {
141
+ const explicitUrl = normalizeText(rawUrl);
142
+ if (explicitUrl) return explicitUrl;
143
+ return id ? `https://tieba.baidu.com/p/${id}` : '';
144
+ }
145
+
146
+ function resolveTiebaThreadId(raw: RawTiebaPostCard): string {
147
+ const direct = normalizeText(raw.threadId);
148
+ if (direct) return direct;
149
+
150
+ const fromUrl = normalizeText(raw.url).match(/\/p\/(\d+)/);
151
+ return fromUrl ? fromUrl[1] : '';
152
+ }
153
+
154
+ function getTiebaFeedComponent(feed: RawTiebaPagePcFeedEntry['feed'], name: string): Record<string, unknown> {
155
+ const components = Array.isArray(feed?.components) ? feed.components : [];
156
+ const match = components.find((entry) => normalizeText((entry as Record<string, unknown>).component) === name);
157
+ if (!match) return {};
158
+ const payload = (match as Record<string, unknown>)[name];
159
+ return payload && typeof payload === 'object' ? payload as Record<string, unknown> : {};
160
+ }
161
+
162
+ function extractTiebaFeedAuthor(feed: RawTiebaPagePcFeedEntry['feed']): string {
163
+ const head = getTiebaFeedComponent(feed, 'feed_head');
164
+ const mainData = Array.isArray(head.main_data) ? head.main_data : [];
165
+ for (const item of mainData) {
166
+ const textRecord = (item as Record<string, unknown>).text as Record<string, unknown> | undefined;
167
+ const author = normalizeText(textRecord?.text);
168
+ if (author) return author;
169
+ }
170
+ return '';
171
+ }
172
+
173
+ function extractTiebaFeedTitle(feed: RawTiebaPagePcFeedEntry['feed']): string {
174
+ const title = getTiebaFeedComponent(feed, 'feed_title');
175
+ const titleData = Array.isArray(title.data) ? title.data : [];
176
+ const firstTitle = titleData[0] as Record<string, unknown> | undefined;
177
+ const textInfo = firstTitle?.text_info as Record<string, unknown> | undefined;
178
+ return normalizeText(textInfo?.text) || normalizeText(feed?.business_info_map?.title);
179
+ }
180
+
181
+ function extractTiebaFeedCommentCount(feed: RawTiebaPagePcFeedEntry['feed']): number {
182
+ const social = getTiebaFeedComponent(feed, 'feed_social');
183
+ const commentCount = Number(social.comment_num ?? feed?.business_info_map?.comment_num ?? 0);
184
+ return Number.isFinite(commentCount) ? commentCount : 0;
185
+ }
186
+
187
+ function extractTiebaFeedThreadId(feed: RawTiebaPagePcFeedEntry['feed']): string {
188
+ const direct = normalizeText(feed?.business_info_map?.thread_id);
189
+ if (direct) return direct;
190
+
191
+ const logParams = Array.isArray(feed?.log_param) ? feed.log_param : [];
192
+ const fromLog = normalizeText(logParams.find((item) => normalizeText(item?.key) === 'tid')?.value);
193
+ if (fromLog) return fromLog;
194
+
195
+ const fromSchema = normalizeText(feed?.schema).match(/[?&]tid=(\d+)/);
196
+ return fromSchema ? fromSchema[1] : '';
197
+ }
198
+
199
+ function extractTiebaFeedLastReply(feed: RawTiebaPagePcFeedEntry['feed']): string {
200
+ const head = getTiebaFeedComponent(feed, 'feed_head');
201
+ const extraData = Array.isArray(head.extra_data) ? head.extra_data : [];
202
+ const first = extraData[0] as Record<string, unknown> | undefined;
203
+ const prefix = normalizeText((first?.business_info_map as Record<string, unknown> | undefined)?.time_prefix);
204
+ const textRecord = first?.text as Record<string, unknown> | undefined;
205
+ const rawTime = normalizeText(textRecord?.text);
206
+ const formattedTime = /^\d+$/.test(rawTime) ? formatTiebaUnixTime(rawTime) : rawTime;
207
+ return [prefix, formattedTime].filter(Boolean).join('');
208
+ }
209
+
210
+ /**
211
+ * Convert Tieba's signed `page_pc` feed entries into the stable card shape used by the CLI.
212
+ */
213
+ export function buildTiebaPostCardsFromPagePc(rawFeeds: RawTiebaPagePcFeedEntry[]): RawTiebaPostCard[] {
214
+ return rawFeeds
215
+ .filter((entry) => normalizeText(entry.layout) === 'feed' && entry.feed)
216
+ .map((entry) => {
217
+ const feed = entry.feed;
218
+ const threadId = extractTiebaFeedThreadId(feed);
219
+ return {
220
+ title: extractTiebaFeedTitle(feed),
221
+ author: extractTiebaFeedAuthor(feed),
222
+ descInfo: extractTiebaFeedLastReply(feed),
223
+ commentCount: extractTiebaFeedCommentCount(feed),
224
+ actionTexts: [],
225
+ threadId,
226
+ url: buildTiebaThreadUrl(threadId),
227
+ };
228
+ })
229
+ .filter((entry) => normalizeText(entry.title));
230
+ }
231
+
232
+ export function buildTiebaPostItems(rawCards: RawTiebaPostCard[], requestedLimit: unknown): TiebaPostItem[] {
233
+ const limit = normalizeTiebaLimit(requestedLimit);
234
+
235
+ return rawCards
236
+ .map((raw) => {
237
+ const title = normalizeText(raw.title);
238
+ const id = resolveTiebaThreadId(raw);
239
+ const actionTexts = Array.isArray(raw.actionTexts) ? raw.actionTexts.map(normalizeText).filter(Boolean) : [];
240
+ const commentText = actionTexts.find((text) => /评论/.test(text)) || actionTexts[actionTexts.length - 1] || '';
241
+
242
+ return {
243
+ title,
244
+ author: normalizeText(raw.author),
245
+ replies: Number.isFinite(Number(raw.commentCount))
246
+ ? Number(raw.commentCount)
247
+ : parseTiebaCount(commentText),
248
+ last_reply: parseTiebaLastReply(String(raw.descInfo ?? '')),
249
+ id,
250
+ url: buildTiebaThreadUrl(id, raw.url),
251
+ };
252
+ })
253
+ .filter((item) => item.title)
254
+ .slice(0, limit)
255
+ .map((item, index) => ({ rank: index + 1, ...item }));
256
+ }
257
+
258
+ export function buildTiebaSearchItems(rawItems: RawTiebaSearchItem[], requestedLimit: unknown): TiebaSearchItem[] {
259
+ const limit = normalizeTiebaLimit(requestedLimit);
260
+
261
+ return rawItems
262
+ .map((raw) => {
263
+ const url = normalizeText(raw.url);
264
+ const directId = normalizeText(raw.id);
265
+ const idFromUrl = url.match(/\/p\/(\d+)/)?.[1] || '';
266
+
267
+ return {
268
+ title: normalizeText(raw.title),
269
+ forum: normalizeText(raw.forum),
270
+ author: normalizeText(raw.author),
271
+ time: normalizeText(raw.time),
272
+ snippet: normalizeText(raw.snippet).slice(0, 200),
273
+ id: directId || idFromUrl,
274
+ url,
275
+ };
276
+ })
277
+ .filter((item) => item.title)
278
+ .slice(0, limit)
279
+ .map((item, index) => ({ rank: index + 1, ...item }));
280
+ }
281
+
282
+ function formatTiebaUnixTime(value: unknown): string {
283
+ const ts = Number(value || 0);
284
+ if (!Number.isFinite(ts) || ts <= 0) return '';
285
+ const parts = new Intl.DateTimeFormat('sv-SE', {
286
+ timeZone: TIEBA_TIME_ZONE,
287
+ year: 'numeric',
288
+ month: '2-digit',
289
+ day: '2-digit',
290
+ hour: '2-digit',
291
+ minute: '2-digit',
292
+ hour12: false,
293
+ }).formatToParts(new Date(ts * 1000));
294
+ const values = Object.fromEntries(parts.map((part) => [part.type, part.value]));
295
+ return `${values.year}-${values.month}-${values.day} ${values.hour}:${values.minute}`;
296
+ }
297
+
298
+ function parseTiebaReplyTime(text: string): string {
299
+ const normalized = normalizeText(text);
300
+ const withoutFloor = normalized.replace(/^第\d+楼\s+/, '').trim();
301
+ const match = withoutFloor.match(/^(刚刚|昨天|前天|\d+\s*(?:分钟|小时|天)前|\d{2}-\d{2}(?:\s+\d{2}:\d{2})?|\d{4}-\d{2}-\d{2}(?:\s+\d{2}:\d{2})?)/);
302
+ return match ? match[1].trim() : withoutFloor;
303
+ }
304
+
305
+ function buildMainPostItem(mainPost?: RawTiebaMainPost | null): TiebaReadItem | null {
306
+ if (!mainPost) return null;
307
+
308
+ const title = normalizeText(mainPost.title);
309
+ const author = normalizeText(mainPost.author) || normalizeText(mainPost.fallbackAuthor);
310
+ const body = normalizeText(mainPost.contentText) || normalizeText(mainPost.structuredText);
311
+ const hasMedia = Boolean(mainPost.hasMedia);
312
+ const content = [title, body || (hasMedia ? '[media]' : '')].filter(Boolean).join(' ').trim();
313
+
314
+ if (!content) return null;
315
+
316
+ return {
317
+ floor: 1,
318
+ author,
319
+ content,
320
+ time: normalizeText(mainPost.visibleTime) || formatTiebaUnixTime(mainPost.structuredTime),
321
+ };
322
+ }
323
+
324
+ export function buildTiebaReadItems(payload: RawTiebaReadPayload, options: TiebaReadBuildOptions = {}): TiebaReadItem[] {
325
+ const fallback = Number.isFinite(Number(options.limit)) ? Number(options.limit) : 30;
326
+ const limit = Math.max(1, Math.trunc(fallback));
327
+ const includeMainPost = options.includeMainPost !== false;
328
+ const items: TiebaReadItem[] = [];
329
+ const mainPost = buildMainPostItem(payload.mainPost);
330
+
331
+ if (includeMainPost && mainPost) items.push(mainPost);
332
+
333
+ const replies = Array.isArray(payload.replies) ? payload.replies : [];
334
+ const replyItems: TiebaReadItem[] = [];
335
+ for (const reply of replies) {
336
+ const floor = Number(reply.floor || 0);
337
+ const content = normalizeText(reply.content);
338
+ if (!Number.isFinite(floor) || floor < 1 || !content) continue;
339
+ replyItems.push({
340
+ floor,
341
+ author: normalizeText(reply.author),
342
+ content,
343
+ time: parseTiebaReplyTime(String(reply.time ?? '')),
344
+ });
345
+ }
346
+
347
+ return items.concat(replyItems.slice(0, limit));
348
+ }
@@ -16,10 +16,13 @@ pipeline:
16
16
  url: https://www.v2ex.com/api/topics/hot.json
17
17
 
18
18
  - map:
19
+ id: ${{ item.id }}
19
20
  rank: ${{ index + 1 }}
20
21
  title: ${{ item.title }}
22
+ node: ${{ item.node.title }}
21
23
  replies: ${{ item.replies }}
24
+ url: ${{ item.url }}
22
25
 
23
26
  - limit: ${{ args.limit }}
24
27
 
25
- columns: [rank, title, replies]
28
+ columns: [id, rank, title, node, replies, url]
@@ -16,10 +16,13 @@ pipeline:
16
16
  url: https://www.v2ex.com/api/topics/latest.json
17
17
 
18
18
  - map:
19
+ id: ${{ item.id }}
19
20
  rank: ${{ index + 1 }}
20
21
  title: ${{ item.title }}
22
+ node: ${{ item.node.title }}
21
23
  replies: ${{ item.replies }}
24
+ url: ${{ item.url }}
22
25
 
23
26
  - limit: ${{ args.limit }}
24
27
 
25
- columns: [rank, title, replies]
28
+ columns: [id, rank, title, node, replies, url]
@@ -19,10 +19,15 @@ pipeline:
19
19
  id: ${{ args.id }}
20
20
 
21
21
  - map:
22
+ id: ${{ item.id }}
22
23
  title: ${{ item.title }}
24
+ content: ${{ item.content }}
25
+ member: ${{ item.member.username }}
26
+ created: ${{ item.created }}
27
+ node: ${{ item.node.title }}
23
28
  replies: ${{ item.replies }}
24
29
  url: ${{ item.url }}
25
30
 
26
31
  - limit: 1
27
32
 
28
- columns: [title, replies, url]
33
+ columns: [id, title, content, member, created, node, replies, url]
@@ -110,6 +110,37 @@ export function extractWechatPublishTime(
110
110
  return formatWechatTimestamp(rawCreateTime);
111
111
  }
112
112
 
113
+ /**
114
+ * Detect WeChat anti-bot / verification gate pages before we try to parse the article.
115
+ */
116
+ export function detectWechatAccessIssue(
117
+ pageText: string | null | undefined,
118
+ htmlStr: string,
119
+ ): string {
120
+ const normalizedText = (pageText || '').replace(/\s+/g, ' ').trim();
121
+
122
+ if (
123
+ /环境异常/.test(normalizedText) &&
124
+ /(完成验证后即可继续访问|去验证)/.test(normalizedText)
125
+ ) {
126
+ return 'environment verification required';
127
+ }
128
+
129
+ if (/secitptpage\/verify\.html/.test(htmlStr) || /id=["']js_verify["']/.test(htmlStr)) {
130
+ return 'environment verification required';
131
+ }
132
+
133
+ return '';
134
+ }
135
+
136
+ export function pickFirstWechatMetaText(...candidates: Array<string | null | undefined>): string {
137
+ for (const candidate of candidates) {
138
+ const normalized = (candidate || '').replace(/\s+/g, ' ').trim();
139
+ if (normalized && normalized !== 'Name cleared') return normalized;
140
+ }
141
+ return '';
142
+ }
143
+
113
144
  /**
114
145
  * Build a self-contained helper for execution inside page.evaluate().
115
146
  */
@@ -161,6 +192,31 @@ export function buildExtractWechatPublishTimeJs(): string {
161
192
  }.toString()})`;
162
193
  }
163
194
 
195
+ /**
196
+ * Build a self-contained access-issue detector for execution inside page.evaluate().
197
+ */
198
+ export function buildDetectWechatAccessIssueJs(): string {
199
+ return `(${function detectWechatAccessIssueInPage(
200
+ pageText: string | null | undefined,
201
+ htmlStr: string,
202
+ ) {
203
+ const normalizedText = (pageText || '').replace(/\s+/g, ' ').trim();
204
+
205
+ if (
206
+ /环境异常/.test(normalizedText) &&
207
+ /(完成验证后即可继续访问|去验证)/.test(normalizedText)
208
+ ) {
209
+ return 'environment verification required';
210
+ }
211
+
212
+ if (/secitptpage\/verify\.html/.test(htmlStr) || /id=["']js_verify["']/.test(htmlStr)) {
213
+ return 'environment verification required';
214
+ }
215
+
216
+ return '';
217
+ }.toString()})`;
218
+ }
219
+
164
220
  // ============================================================
165
221
  // CLI Registration
166
222
  // ============================================================
@@ -196,18 +252,34 @@ cli({
196
252
  title: '',
197
253
  author: '',
198
254
  publishTime: '',
255
+ errorHint: '',
199
256
  contentHtml: '',
200
257
  codeBlocks: [],
201
258
  imageUrls: []
202
259
  };
203
260
 
204
- // Title: #activity-name
205
- const titleEl = document.querySelector('#activity-name');
206
- result.title = titleEl ? titleEl.textContent.trim() : '';
261
+ const pickFirstText = (...selectors) => {
262
+ for (const selector of selectors) {
263
+ const text = document.querySelector(selector)?.textContent?.replace(/\\s+/g, ' ').trim() || '';
264
+ if (text && text !== 'Name cleared') return text;
265
+ }
266
+ return '';
267
+ };
268
+
269
+ // WeChat has multiple article templates. Newer pages use #js_text_title.
270
+ result.title = pickFirstText(
271
+ '#activity-name',
272
+ '#js_text_title',
273
+ '.rich_media_title',
274
+ );
207
275
 
208
- // Author (WeChat Official Account name): #js_name
209
- const authorEl = document.querySelector('#js_name');
210
- result.author = authorEl ? authorEl.textContent.trim() : '';
276
+ result.author = pickFirstText(
277
+ '#js_name',
278
+ '.wx_follow_nickname',
279
+ '#profileBt .profile_nickname',
280
+ '.rich_media_meta.rich_media_meta_nickname',
281
+ '.rich_media_meta_nickname',
282
+ );
211
283
 
212
284
  // Publish time: prefer the rendered DOM text, then fall back to numeric create_time values.
213
285
  const publishTimeEl = document.querySelector('#publish_time');
@@ -217,6 +289,13 @@ cli({
217
289
  document.documentElement.innerHTML,
218
290
  );
219
291
 
292
+ const detectWechatAccessIssue = ${buildDetectWechatAccessIssueJs()};
293
+ result.errorHint = detectWechatAccessIssue(
294
+ document.body ? document.body.innerText : '',
295
+ document.documentElement.innerHTML,
296
+ );
297
+ if (result.errorHint) return result;
298
+
220
299
  // Content processing
221
300
  const contentEl = document.querySelector('#js_content');
222
301
  if (!contentEl) return result;
@@ -268,6 +347,16 @@ cli({
268
347
  })()
269
348
  `);
270
349
 
350
+ if (data?.errorHint === 'environment verification required') {
351
+ return [{
352
+ title: 'Error',
353
+ author: '-',
354
+ publish_time: '-',
355
+ status: 'failed — verification required in WeChat browser page',
356
+ size: '-',
357
+ }];
358
+ }
359
+
271
360
  return downloadArticle(
272
361
  {
273
362
  title: data?.title || '',