@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
package/dist/cli.js CHANGED
@@ -15,6 +15,7 @@ import { printCompletionScript } from './completion.js';
15
15
  import { loadExternalClis, executeExternalCli, installExternalCli, registerExternalCli, isBinaryInstalled } from './external.js';
16
16
  import { registerAllCommands } from './commanderAdapter.js';
17
17
  import { EXIT_CODES, getErrorMessage } from './errors.js';
18
+ import { daemonStatus, daemonStop, daemonRestart } from './commands/daemon.js';
18
19
  export function runCli(BUILTIN_CLIS, USER_CLIS) {
19
20
  const program = new Command();
20
21
  // enablePositionalOptions: prevents parent from consuming flags meant for subcommands;
@@ -32,7 +33,7 @@ export function runCli(BUILTIN_CLIS, USER_CLIS) {
32
33
  .option('--json', 'JSON output (deprecated)')
33
34
  .action((opts) => {
34
35
  const registry = getRegistry();
35
- const commands = [...registry.values()].sort((a, b) => fullName(a).localeCompare(fullName(b)));
36
+ const commands = [...new Set(registry.values())].sort((a, b) => fullName(a).localeCompare(fullName(b)));
36
37
  const fmt = opts.json && opts.format === 'table' ? 'json' : opts.format;
37
38
  const isStructured = fmt === 'json' || fmt === 'yaml';
38
39
  if (fmt !== 'table') {
@@ -42,6 +43,7 @@ export function runCli(BUILTIN_CLIS, USER_CLIS) {
42
43
  command: fullName(c),
43
44
  site: c.site,
44
45
  name: c.name,
46
+ aliases: c.aliases?.join(', ') ?? '',
45
47
  description: c.description,
46
48
  strategy: strategyLabel(c),
47
49
  browser: !!c.browser,
@@ -49,7 +51,7 @@ export function runCli(BUILTIN_CLIS, USER_CLIS) {
49
51
  }));
50
52
  renderOutput(rows, {
51
53
  fmt,
52
- columns: ['command', 'site', 'name', 'description', 'strategy', 'browser', 'args',
54
+ columns: ['command', 'site', 'name', 'aliases', 'description', 'strategy', 'browser', 'args',
53
55
  ...(isStructured ? ['columns', 'domain'] : [])],
54
56
  title: 'opencli/list',
55
57
  source: 'opencli list',
@@ -73,7 +75,8 @@ export function runCli(BUILTIN_CLIS, USER_CLIS) {
73
75
  const tag = label === 'public'
74
76
  ? chalk.green('[public]')
75
77
  : chalk.yellow(`[${label}]`);
76
- console.log(` ${cmd.name} ${tag}${cmd.description ? chalk.dim(` ${cmd.description}`) : ''}`);
78
+ const aliases = cmd.aliases?.length ? chalk.dim(` (aliases: ${cmd.aliases.join(', ')})`) : '';
79
+ console.log(` ${cmd.name} ${tag}${aliases}${cmd.description ? chalk.dim(` — ${cmd.description}`) : ''}`);
77
80
  }
78
81
  console.log();
79
82
  }
@@ -411,6 +414,20 @@ export function runCli(BUILTIN_CLIS, USER_CLIS) {
411
414
  process.exitCode = EXIT_CODES.GENERIC_ERROR;
412
415
  }
413
416
  });
417
+ // ── Built-in: daemon ──────────────────────────────────────────────────────
418
+ const daemonCmd = program.command('daemon').description('Manage the opencli daemon');
419
+ daemonCmd
420
+ .command('status')
421
+ .description('Show daemon status')
422
+ .action(async () => { await daemonStatus(); });
423
+ daemonCmd
424
+ .command('stop')
425
+ .description('Stop the daemon')
426
+ .action(async () => { await daemonStop(); });
427
+ daemonCmd
428
+ .command('restart')
429
+ .description('Restart the daemon')
430
+ .action(async () => { await daemonRestart(); });
414
431
  // ── External CLIs ─────────────────────────────────────────────────────────
415
432
  const externalClis = loadExternalClis();
416
433
  program
@@ -6,7 +6,7 @@
6
6
  * and returns it in Anthropic format.
7
7
  *
8
8
  * Usage:
9
- * OPENCLI_CDP_ENDPOINT=http://127.0.0.1:9224 opencli antigravity serve --port 8082
9
+ * opencli antigravity serve --port 8082
10
10
  * ANTHROPIC_BASE_URL=http://localhost:8082 claude
11
11
  */
12
12
  export declare function startServe(opts?: {
@@ -6,11 +6,12 @@
6
6
  * and returns it in Anthropic format.
7
7
  *
8
8
  * Usage:
9
- * OPENCLI_CDP_ENDPOINT=http://127.0.0.1:9224 opencli antigravity serve --port 8082
9
+ * opencli antigravity serve --port 8082
10
10
  * ANTHROPIC_BASE_URL=http://localhost:8082 claude
11
11
  */
12
12
  import { createServer } from 'node:http';
13
13
  import { CDPBridge } from '../../browser/cdp.js';
14
+ import { resolveElectronEndpoint } from '../../launcher.js';
14
15
  import { EXIT_CODES, getErrorMessage } from '../../errors.js';
15
16
  // ─── Helpers ─────────────────────────────────────────────────────────
16
17
  function generateMsgId() {
@@ -365,11 +366,7 @@ export async function startServe(opts = {}) {
365
366
  page = null;
366
367
  }
367
368
  }
368
- const endpoint = process.env.OPENCLI_CDP_ENDPOINT;
369
- if (!endpoint) {
370
- throw new Error('OPENCLI_CDP_ENDPOINT is not set.\n' +
371
- 'Usage: OPENCLI_CDP_ENDPOINT=http://127.0.0.1:9224 opencli antigravity serve');
372
- }
369
+ const endpoint = await resolveElectronEndpoint('antigravity');
373
370
  // Note: Antigravity chat panel lives inside editor windows, not in Launchpad.
374
371
  // If multiple editor windows are open, set OPENCLI_CDP_TARGET to the window title.
375
372
  if (process.env.OPENCLI_CDP_TARGET) {
@@ -386,7 +383,7 @@ export async function startServe(opts = {}) {
386
383
  console.error(`[serve] Connecting via CDP (target pattern: "${process.env.OPENCLI_CDP_TARGET}")...`);
387
384
  cdp = new CDPBridge();
388
385
  try {
389
- page = await cdp.connect({ timeout: 15_000 });
386
+ page = await cdp.connect({ timeout: 15_000, cdpEndpoint: endpoint });
390
387
  }
391
388
  catch (err) {
392
389
  cdp = null;
@@ -396,7 +393,7 @@ export async function startServe(opts = {}) {
396
393
  throw new Error(isRefused
397
394
  ? `Cannot connect to Antigravity at ${endpoint}.\n` +
398
395
  ' 1. Make sure Antigravity is running\n' +
399
- ' 2. Launch with: --remote-debugging-port=9224'
396
+ ' 2. Launch with: --remote-debugging-port=9234'
400
397
  : `CDP connection failed: ${errMsg}`);
401
398
  }
402
399
  console.error('[serve] ✅ CDP connected.');
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,72 @@
1
+ import { AuthRequiredError, EmptyResultError } from '../../errors.js';
2
+ import { cli, Strategy } from '../../registry.js';
3
+ /**
4
+ * band bands — List all Bands you belong to.
5
+ *
6
+ * Band.us renders the full band list in the left sidebar of the home page for
7
+ * logged-in users, so we can extract everything we need from the DOM without
8
+ * XHR interception or any secondary navigation.
9
+ *
10
+ * Each sidebar item is an <a href="/band/{band_no}/..."> link whose text and
11
+ * data attributes carry the band name and member count.
12
+ */
13
+ cli({
14
+ site: 'band',
15
+ name: 'bands',
16
+ description: 'List all Bands you belong to',
17
+ domain: 'www.band.us',
18
+ strategy: Strategy.COOKIE,
19
+ browser: true,
20
+ args: [],
21
+ columns: ['band_no', 'name', 'members'],
22
+ func: async (page, _kwargs) => {
23
+ const cookies = await page.getCookies({ domain: 'band.us' });
24
+ const isLoggedIn = cookies.some(c => c.name === 'band_session');
25
+ if (!isLoggedIn)
26
+ throw new AuthRequiredError('band.us', 'Not logged in to Band');
27
+ // Extract the band list from the sidebar. Poll until at least one band card
28
+ // appears (React hydration may take a moment after navigation).
29
+ // Sidebar band cards use class "bandCover _link" with hrefs like /band/{id}/post.
30
+ const bands = await page.evaluate(`
31
+ (async () => {
32
+ const sleep = ms => new Promise(r => setTimeout(r, ms));
33
+
34
+ // Wait up to 9 s for sidebar band cards to render.
35
+ for (let i = 0; i < 30; i++) {
36
+ if (document.querySelector('a.bandCover._link')) break;
37
+ await sleep(300);
38
+ }
39
+
40
+ const norm = s => (s || '').replace(/\\s+/g, ' ').trim();
41
+ const seen = new Set();
42
+ const results = [];
43
+
44
+ for (const a of Array.from(document.querySelectorAll('a.bandCover._link'))) {
45
+ // Extract band_no from href: /band/{id} or /band/{id}/post only.
46
+ const m = (a.getAttribute('href') || '').match(/^\\/band\\/(\\d+)(?:\\/post)?\\/?$/);
47
+ if (!m) continue;
48
+ const bandNo = Number(m[1]);
49
+ if (seen.has(bandNo)) continue;
50
+ seen.add(bandNo);
51
+
52
+ // Band name lives in p.uriText inside div.bandName.
53
+ const nameEl = a.querySelector('p.uriText');
54
+ const name = nameEl ? norm(nameEl.textContent) : '';
55
+ if (!name) continue;
56
+
57
+ // Member count is the <em> inside span.member.
58
+ const memberEl = a.querySelector('span.member em');
59
+ const members = memberEl ? parseInt((memberEl.textContent || '').replace(/[^0-9]/g, ''), 10) || 0 : 0;
60
+
61
+ results.push({ band_no: bandNo, name, members });
62
+ }
63
+
64
+ return results;
65
+ })()
66
+ `);
67
+ if (!bands || bands.length === 0) {
68
+ throw new EmptyResultError('band bands', 'No bands found in sidebar — are you logged in?');
69
+ }
70
+ return bands;
71
+ },
72
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,127 @@
1
+ import { AuthRequiredError, EmptyResultError, SelectorError } from '../../errors.js';
2
+ import { cli, Strategy } from '../../registry.js';
3
+ /**
4
+ * band mentions — Show Band notifications where you were @mentioned.
5
+ *
6
+ * Band.us signs every API request with a per-request HMAC (`md` header) generated
7
+ * by its own JavaScript, so we cannot replicate it externally. Instead we use
8
+ * Strategy.INTERCEPT: install an XHR interceptor, open the notification panel by
9
+ * clicking the bell to trigger the get_news XHR call, then apply client-side
10
+ * filtering to extract notifications matching the requested filter/unread options.
11
+ */
12
+ cli({
13
+ site: 'band',
14
+ name: 'mentions',
15
+ description: 'Show Band notifications where you are @mentioned',
16
+ domain: 'www.band.us',
17
+ strategy: Strategy.INTERCEPT,
18
+ browser: true,
19
+ args: [
20
+ {
21
+ name: 'filter',
22
+ default: 'mentioned',
23
+ choices: ['mentioned', 'all', 'post', 'comment'],
24
+ help: 'Filter: mentioned (default) | all | post | comment',
25
+ },
26
+ { name: 'limit', type: 'int', default: 20, help: 'Max results' },
27
+ { name: 'unread', type: 'bool', default: false, help: 'Show only unread notifications' },
28
+ ],
29
+ columns: ['time', 'band', 'type', 'from', 'text', 'url'],
30
+ func: async (page, kwargs) => {
31
+ const filter = kwargs.filter;
32
+ const limit = kwargs.limit;
33
+ const unreadOnly = kwargs.unread;
34
+ // Navigate with a timestamp param to force a fresh page load each run.
35
+ // Without this, same-URL navigation may skip the reload (preserving the JS context
36
+ // and leaving the notification panel open from a previous run).
37
+ await page.goto(`https://www.band.us/?_=${Date.now()}`);
38
+ const cookies = await page.getCookies({ domain: 'band.us' });
39
+ const isLoggedIn = cookies.some(c => c.name === 'band_session');
40
+ if (!isLoggedIn)
41
+ throw new AuthRequiredError('band.us', 'Not logged in to Band');
42
+ // Install XHR interceptor before any clicks so all get_news responses are captured.
43
+ await page.installInterceptor('get_news');
44
+ // Wait for the bell button to appear (React hydration) instead of a fixed sleep.
45
+ let bellReady = false;
46
+ for (let i = 0; i < 20; i++) {
47
+ const exists = await page.evaluate(`() => !!document.querySelector('button._btnWidgetIcon')`);
48
+ if (exists) {
49
+ bellReady = true;
50
+ break;
51
+ }
52
+ await page.wait(0.5);
53
+ }
54
+ if (!bellReady) {
55
+ throw new SelectorError('button._btnWidgetIcon', 'Notification bell not found. The Band.us UI may have changed.');
56
+ }
57
+ // Poll until a capture containing result_data.news arrives, up to maxSecs seconds.
58
+ // getInterceptedRequests() clears the array on each call, so captures are accumulated
59
+ // locally. The interceptor pattern 'get_news' also matches 'get_news_count' responses
60
+ // which don't have result_data.news — keep polling until the real news response arrives.
61
+ const waitForOneCapture = async (maxSecs = 8) => {
62
+ const captures = [];
63
+ for (let i = 0; i < maxSecs * 2; i++) {
64
+ await page.wait(0.5); // 0.5 seconds per iteration (page.wait takes seconds)
65
+ const reqs = await page.getInterceptedRequests();
66
+ if (reqs.length > 0) {
67
+ captures.push(...reqs);
68
+ if (captures.some((r) => Array.isArray(r?.result_data?.news)))
69
+ return captures;
70
+ }
71
+ }
72
+ return captures;
73
+ };
74
+ // Click the bell. Guard against the element disappearing between the readiness
75
+ // check and the click (e.g. due to a React re-render) to surface a clear error.
76
+ const bellClicked = await page.evaluate(`() => {
77
+ const el = document.querySelector('button._btnWidgetIcon');
78
+ if (!el) return false;
79
+ el.click();
80
+ return true;
81
+ }`);
82
+ if (!bellClicked) {
83
+ throw new SelectorError('button._btnWidgetIcon', 'Notification bell disappeared before click. The Band.us UI may have changed.');
84
+ }
85
+ const requests = await waitForOneCapture();
86
+ // Find the get_news response (has result_data.news); get_news_count responses do not.
87
+ const newsReq = requests.find((r) => Array.isArray(r?.result_data?.news));
88
+ if (!newsReq) {
89
+ throw new EmptyResultError('band mentions', 'Failed to capture get_news response from Band.us. Try running the command again.');
90
+ }
91
+ let items = newsReq.result_data.news ?? [];
92
+ if (items.length === 0) {
93
+ throw new EmptyResultError('band mentions', 'No notifications found');
94
+ }
95
+ // Apply filters client-side from the full notification list.
96
+ if (unreadOnly) {
97
+ items = items.filter((n) => n.is_new === true);
98
+ }
99
+ if (filter === 'mentioned') {
100
+ // 'filters' is Band's server-side tag array; 'referred' means you were @mentioned.
101
+ items = items.filter((n) => n.filters?.includes('referred'));
102
+ }
103
+ else if (filter === 'post') {
104
+ items = items.filter((n) => n.category === 'post');
105
+ }
106
+ else if (filter === 'comment') {
107
+ items = items.filter((n) => n.category === 'comment');
108
+ }
109
+ // Band markup tags (<band:mention uid="...">, <band:sticker>, etc.) appear in
110
+ // notification text; strip them to get plain readable content.
111
+ const stripBandTags = (s) => s.replace(/<\/?band:[^>]+>/g, '');
112
+ return items.slice(0, limit).map((n) => {
113
+ const ts = n.created_at ? new Date(n.created_at) : null;
114
+ return {
115
+ time: ts
116
+ ? ts.toLocaleString('ja-JP', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })
117
+ : '',
118
+ band: n.band?.name ?? '',
119
+ // 'filters' is Band's server-side tag array; 'referred' means you were @mentioned.
120
+ type: n.filters?.includes('referred') ? '@mention' : n.category ?? '',
121
+ from: n.actor?.name ?? '',
122
+ text: stripBandTags(n.subtext ?? '').slice(0, 100),
123
+ url: n.action?.pc ?? '',
124
+ };
125
+ });
126
+ },
127
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,175 @@
1
+ import { AuthRequiredError, EmptyResultError } from '../../errors.js';
2
+ import { formatCookieHeader } from '../../download/index.js';
3
+ import { downloadMedia } from '../../download/media-download.js';
4
+ import { cli, Strategy } from '../../registry.js';
5
+ /**
6
+ * band post — Export full content of a Band post: body, comments, and optional photo download.
7
+ *
8
+ * Navigates directly to the post URL and extracts everything from the DOM.
9
+ * No XHR interception needed — Band renders the full post for logged-in users.
10
+ *
11
+ * Output rows:
12
+ * type=post → the post itself (author, date, body text)
13
+ * type=comment → top-level comment
14
+ * type=reply → reply to a comment (nested under its parent)
15
+ *
16
+ * Photo thumbnail URLs carry a ?type=sNNN suffix; stripping it yields full-res.
17
+ */
18
+ cli({
19
+ site: 'band',
20
+ name: 'post',
21
+ description: 'Export full content of a post including comments',
22
+ domain: 'www.band.us',
23
+ strategy: Strategy.COOKIE,
24
+ navigateBefore: false,
25
+ browser: true,
26
+ args: [
27
+ { name: 'band_no', positional: true, required: true, type: 'int', help: 'Band number' },
28
+ { name: 'post_no', positional: true, required: true, type: 'int', help: 'Post number' },
29
+ { name: 'output', type: 'str', default: '', help: 'Directory to save attached photos' },
30
+ { name: 'comments', type: 'bool', default: true, help: 'Include comments (default: true)' },
31
+ ],
32
+ columns: ['type', 'author', 'date', 'text'],
33
+ func: async (page, kwargs) => {
34
+ const bandNo = Number(kwargs.band_no);
35
+ const postNo = Number(kwargs.post_no);
36
+ const outputDir = kwargs.output;
37
+ const withComments = kwargs.comments;
38
+ await page.goto(`https://www.band.us/band/${bandNo}/post/${postNo}`);
39
+ const cookies = await page.getCookies({ domain: 'band.us' });
40
+ const isLoggedIn = cookies.some(c => c.name === 'band_session');
41
+ if (!isLoggedIn)
42
+ throw new AuthRequiredError('band.us', 'Not logged in to Band');
43
+ const data = await page.evaluate(`
44
+ (async () => {
45
+ const withComments = ${withComments};
46
+ const sleep = ms => new Promise(r => setTimeout(r, ms));
47
+ const norm = s => (s || '').replace(/\\s+/g, ' ').trim();
48
+ // Band embeds <band:mention>, <band:sticker>, etc. in content — strip to plain text.
49
+ const stripTags = s => s.replace(/<\\/?band:[^>]+>/g, '');
50
+
51
+ // Wait up to 9 s for the post content to render (poll for the author link,
52
+ // which appears after React hydration fills the post header).
53
+ for (let i = 0; i < 30; i++) {
54
+ if (document.querySelector('._postWrapper a.text')) break;
55
+ await sleep(300);
56
+ }
57
+
58
+ const postCard = document.querySelector('._postWrapper');
59
+ const commentSection = postCard?.querySelector('.dPostCommentMainView');
60
+
61
+ // Author and date live in the post header, above the comment section.
62
+ // Exclude any matches inside the comment section to avoid picking up comment authors.
63
+ let author = '', date = '';
64
+ for (const el of (postCard?.querySelectorAll('a.text') || [])) {
65
+ if (!commentSection?.contains(el)) { author = norm(el.textContent); break; }
66
+ }
67
+ for (const el of (postCard?.querySelectorAll('time.time') || [])) {
68
+ if (!commentSection?.contains(el)) { date = norm(el.textContent); break; }
69
+ }
70
+
71
+ const bodyEl = postCard?.querySelector('.postText._postText');
72
+ const text = bodyEl ? stripTags(norm(bodyEl.innerText || bodyEl.textContent)) : '';
73
+
74
+ // Photo thumbnails have a ?type=sNNN query param; strip it for full-res URL.
75
+ // Use location.href as base so protocol-relative or relative URLs resolve correctly.
76
+ const photos = Array.from(postCard?.querySelectorAll('img._imgRecentPhoto, img._imgPhoto') || [])
77
+ .map(img => {
78
+ const src = img.getAttribute('src') || '';
79
+ if (!src) return '';
80
+ try { const u = new URL(src, location.href); return u.origin + u.pathname; }
81
+ catch { return ''; }
82
+ })
83
+ .filter(Boolean);
84
+
85
+ if (!withComments) return { author, date, text, photos, comments: [] };
86
+
87
+ // Wait up to 6 s for the comment list container to render.
88
+ // Wait for the container itself (not .cComment) so posts with zero comments
89
+ // don't incur a fixed 6s delay waiting for an element that never appears.
90
+ for (let i = 0; i < 20; i++) {
91
+ if (postCard?.querySelector('.sCommentList._heightDetectAreaForComment')) break;
92
+ await sleep(300);
93
+ }
94
+
95
+ // Recursively collect comments and their replies.
96
+ // Replies live in .sReplyList > .sCommentList, not in ._replyRegion.
97
+ function extractComments(container, depth) {
98
+ const results = [];
99
+ for (const el of container.querySelectorAll(':scope > .cComment')) {
100
+ results.push({
101
+ depth,
102
+ author: norm(el.querySelector('strong.name')?.textContent),
103
+ date: norm(el.querySelector('time.time')?.textContent),
104
+ text: stripTags(norm(el.querySelector('p.txt._commentContent')?.innerText || '')),
105
+ });
106
+ const replyList = el.querySelector('.sReplyList .sCommentList._heightDetectAreaForComment');
107
+ if (replyList) results.push(...extractComments(replyList, depth + 1));
108
+ }
109
+ return results;
110
+ }
111
+
112
+ const commentList = postCard?.querySelector('.sCommentList._heightDetectAreaForComment');
113
+ const comments = commentList ? extractComments(commentList, 0) : [];
114
+
115
+ return { author, date, text, photos, comments };
116
+ })()
117
+ `);
118
+ if (!data?.text && !data?.comments?.length && !data?.photos?.length) {
119
+ throw new EmptyResultError('band post', 'Post not found or not accessible');
120
+ }
121
+ const photos = data.photos ?? [];
122
+ // Download photos when --output is specified, using the shared downloadMedia utility
123
+ // which handles redirects, timeouts, and stream errors correctly.
124
+ // Pass browser cookies so Band's login-protected photo URLs don't fail with 401/403.
125
+ if (outputDir && photos.length > 0) {
126
+ // Only send Band cookies to Band-hosted URLs; avoid leaking auth cookies to third-party CDNs.
127
+ // Use a global index across both batches so filenames don't collide (photo_1, photo_2, ...).
128
+ const cookieHeader = formatCookieHeader(await page.getCookies({ url: 'https://www.band.us' }));
129
+ const isBandUrl = (u) => { try {
130
+ const h = new URL(u).hostname;
131
+ return h === 'band.us' || h.endsWith('.band.us');
132
+ }
133
+ catch {
134
+ return false;
135
+ } };
136
+ // Derive extension from URL path so downloaded files have correct extensions (e.g. photo_1.jpg).
137
+ const urlExt = (u) => { try {
138
+ return new URL(u).pathname.match(/\.(\w+)$/)?.[1] ?? 'jpg';
139
+ }
140
+ catch {
141
+ return 'jpg';
142
+ } };
143
+ let globalIndex = 1;
144
+ const bandPhotos = photos.filter(isBandUrl);
145
+ const otherPhotos = photos.filter(u => !isBandUrl(u));
146
+ if (bandPhotos.length > 0) {
147
+ await downloadMedia(bandPhotos.map(url => ({ type: 'image', url, filename: `photo_${globalIndex++}.${urlExt(url)}` })), { output: outputDir, verbose: false, cookies: cookieHeader });
148
+ }
149
+ if (otherPhotos.length > 0) {
150
+ await downloadMedia(otherPhotos.map(url => ({ type: 'image', url, filename: `photo_${globalIndex++}.${urlExt(url)}` })), { output: outputDir, verbose: false });
151
+ }
152
+ }
153
+ const rows = [];
154
+ // Post row — append photo URLs inline when not downloading to disk.
155
+ rows.push({
156
+ type: 'post',
157
+ author: data.author ?? '',
158
+ date: data.date ?? '',
159
+ text: [
160
+ data.text ?? '',
161
+ ...(outputDir ? [] : photos.map((u, i) => `[photo${i + 1}] ${u}`)),
162
+ ].filter(Boolean).join('\n'),
163
+ });
164
+ // Comment rows — depth=0 → type 'comment', depth≥1 → type 'reply'.
165
+ for (const c of data.comments ?? []) {
166
+ rows.push({
167
+ type: c.depth === 0 ? 'comment' : 'reply',
168
+ author: c.author ?? '',
169
+ date: c.date ?? '',
170
+ text: c.depth > 0 ? ' '.repeat(c.depth) + '└ ' + (c.text ?? '') : (c.text ?? ''),
171
+ });
172
+ }
173
+ return rows;
174
+ },
175
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,94 @@
1
+ import { AuthRequiredError, EmptyResultError } from '../../errors.js';
2
+ import { cli, Strategy } from '../../registry.js';
3
+ /**
4
+ * band posts — List posts from a specific Band.
5
+ *
6
+ * Band.us renders the post list in the DOM for logged-in users, so we navigate
7
+ * directly to the band's post page and extract everything from the DOM — no XHR
8
+ * interception or home-page detour required.
9
+ */
10
+ cli({
11
+ site: 'band',
12
+ name: 'posts',
13
+ description: 'List posts from a Band',
14
+ domain: 'www.band.us',
15
+ strategy: Strategy.COOKIE,
16
+ navigateBefore: false,
17
+ browser: true,
18
+ args: [
19
+ {
20
+ name: 'band_no',
21
+ positional: true,
22
+ required: true,
23
+ type: 'int',
24
+ help: 'Band number (get it from: band bands)',
25
+ },
26
+ { name: 'limit', type: 'int', default: 20, help: 'Max results' },
27
+ ],
28
+ columns: ['date', 'author', 'content', 'comments', 'url'],
29
+ func: async (page, kwargs) => {
30
+ const bandNo = Number(kwargs.band_no);
31
+ const limit = Number(kwargs.limit);
32
+ // Navigate directly to the band's post page — no home-page detour needed.
33
+ await page.goto(`https://www.band.us/band/${bandNo}/post`);
34
+ const cookies = await page.getCookies({ domain: 'band.us' });
35
+ const isLoggedIn = cookies.some(c => c.name === 'band_session');
36
+ if (!isLoggedIn)
37
+ throw new AuthRequiredError('band.us', 'Not logged in to Band');
38
+ // Extract post list from the DOM. Poll until post items appear (React hydration).
39
+ const posts = await page.evaluate(`
40
+ (async () => {
41
+ const sleep = ms => new Promise(r => setTimeout(r, ms));
42
+ const norm = s => (s || '').replace(/\\s+/g, ' ').trim();
43
+ const limit = ${limit};
44
+
45
+ // Wait up to 9 s for post items to render.
46
+ for (let i = 0; i < 30; i++) {
47
+ if (document.querySelector('article.cContentsCard._postMainWrap')) break;
48
+ await sleep(300);
49
+ }
50
+
51
+ // Band embeds custom <band:mention>, <band:sticker>, etc. tags in content.
52
+ const stripTags = s => s.replace(/<\\/?band:[^>]+>/g, '');
53
+
54
+ const results = [];
55
+ const postEls = Array.from(
56
+ document.querySelectorAll('article.cContentsCard._postMainWrap')
57
+ );
58
+
59
+ for (const el of postEls) {
60
+ // URL: first post permalink link (absolute or relative).
61
+ const linkEl = el.querySelector('a[href*="/post/"]');
62
+ const href = linkEl?.getAttribute('href') || '';
63
+ if (!href) continue;
64
+ const url = href.startsWith('http') ? href : 'https://www.band.us' + href;
65
+
66
+ // Author name — a.text in the post header area.
67
+ const author = norm(el.querySelector('a.text')?.textContent);
68
+
69
+ // Date / timestamp.
70
+ const date = norm(el.querySelector('time')?.textContent);
71
+
72
+ // Post body text (strip Band markup tags, truncate for listing).
73
+ const bodyEl = el.querySelector('.postText._postText');
74
+ const content = bodyEl
75
+ ? stripTags(norm(bodyEl.innerText || bodyEl.textContent)).slice(0, 120)
76
+ : '';
77
+
78
+ // Comment count is in span.count inside the count area.
79
+ const commentEl = el.querySelector('span.count');
80
+ const comments = commentEl ? parseInt((commentEl.textContent || '').replace(/[^0-9]/g, ''), 10) || 0 : 0;
81
+
82
+ if (results.length >= limit) break;
83
+ results.push({ date, author, content, comments, url });
84
+ }
85
+
86
+ return results;
87
+ })()
88
+ `);
89
+ if (!posts || posts.length === 0) {
90
+ throw new EmptyResultError('band posts', 'No posts found in this Band');
91
+ }
92
+ return posts;
93
+ },
94
+ });
@@ -33,8 +33,12 @@ cli({
33
33
  if (payload.code !== 0) {
34
34
  throw new CommandExecutionError(`获取视频播放信息失败: ${payload.message} (${payload.code})`);
35
35
  }
36
+ const needLoginSubtitle = payload.data?.need_login_subtitle === true;
36
37
  const subtitles = payload.data?.subtitle?.subtitles || [];
37
38
  if (subtitles.length === 0) {
39
+ if (needLoginSubtitle) {
40
+ throw new AuthRequiredError('bilibili.com', 'Bilibili subtitles are hidden behind login for this video. Please log in to bilibili.com in Chrome and retry.');
41
+ }
38
42
  throw new EmptyResultError('bilibili subtitle', '此视频没有发现外挂或智能字幕。');
39
43
  }
40
44
  // 4. 选择目标字幕语言
@@ -0,0 +1 @@
1
+ import './subtitle.js';