@jackwener/opencli 1.5.6 → 1.5.8

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 (338) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +4 -2
  3. package/README.zh-CN.md +4 -1
  4. package/SKILL.md +879 -0
  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 +7 -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.js +22 -2
  13. package/dist/browser/page.test.d.ts +1 -0
  14. package/dist/browser/page.test.js +44 -0
  15. package/dist/browser/stealth.js +198 -0
  16. package/dist/browser/stealth.test.d.ts +1 -0
  17. package/dist/browser/stealth.test.js +134 -0
  18. package/dist/browser.test.js +1 -1
  19. package/dist/build-manifest.d.ts +1 -0
  20. package/dist/build-manifest.js +5 -1
  21. package/dist/build-manifest.test.js +2 -0
  22. package/dist/cli-manifest.json +544 -137
  23. package/dist/cli.js +20 -3
  24. package/dist/clis/antigravity/serve.d.ts +1 -1
  25. package/dist/clis/antigravity/serve.js +5 -8
  26. package/dist/clis/bilibili/subtitle.js +4 -0
  27. package/dist/clis/bilibili/subtitle.test.d.ts +1 -0
  28. package/dist/clis/bilibili/subtitle.test.js +48 -0
  29. package/dist/clis/chatwise/ask.js +0 -2
  30. package/dist/clis/chatwise/export.js +0 -2
  31. package/dist/clis/chatwise/history.js +0 -2
  32. package/dist/clis/chatwise/model.js +0 -2
  33. package/dist/clis/chatwise/new.js +1 -2
  34. package/dist/clis/chatwise/read.js +0 -2
  35. package/dist/clis/chatwise/screenshot.js +1 -2
  36. package/dist/clis/chatwise/send.js +0 -2
  37. package/dist/clis/chatwise/status.js +1 -2
  38. package/dist/clis/ctrip/search.d.ts +13 -0
  39. package/dist/clis/ctrip/search.js +73 -48
  40. package/dist/clis/ctrip/search.test.d.ts +1 -0
  41. package/dist/clis/ctrip/search.test.js +64 -0
  42. package/dist/clis/douyin/_shared/sts2.js +8 -2
  43. package/dist/clis/douyin/_shared/sts2.test.d.ts +1 -0
  44. package/dist/clis/douyin/_shared/sts2.test.js +27 -0
  45. package/dist/clis/douyin/activities.js +4 -2
  46. package/dist/clis/douyin/activities.test.js +34 -1
  47. package/dist/clis/douyin/collections.js +1 -1
  48. package/dist/clis/douyin/collections.test.js +24 -2
  49. package/dist/clis/douyin/draft.d.ts +8 -11
  50. package/dist/clis/douyin/draft.js +302 -185
  51. package/dist/clis/douyin/draft.test.d.ts +1 -1
  52. package/dist/clis/douyin/draft.test.js +357 -2
  53. package/dist/clis/douyin/hashtag.js +9 -2
  54. package/dist/clis/douyin/hashtag.test.js +35 -2
  55. package/dist/clis/douyin/profile.js +1 -1
  56. package/dist/clis/douyin/profile.test.js +36 -1
  57. package/dist/clis/douyin/videos.js +22 -5
  58. package/dist/clis/douyin/videos.test.js +45 -2
  59. package/dist/clis/facebook/search.test.d.ts +5 -0
  60. package/dist/clis/facebook/search.test.js +60 -0
  61. package/dist/clis/facebook/search.yaml +4 -3
  62. package/dist/clis/instagram/download.d.ts +16 -0
  63. package/dist/clis/instagram/download.js +225 -0
  64. package/dist/clis/instagram/download.test.d.ts +1 -0
  65. package/dist/clis/instagram/download.test.js +118 -0
  66. package/dist/clis/notebooklm/bind-current.d.ts +1 -0
  67. package/dist/clis/notebooklm/bind-current.js +29 -0
  68. package/dist/clis/notebooklm/bind-current.test.d.ts +1 -0
  69. package/dist/clis/notebooklm/bind-current.test.js +35 -0
  70. package/dist/clis/notebooklm/binding.test.d.ts +1 -0
  71. package/dist/clis/notebooklm/binding.test.js +44 -0
  72. package/dist/clis/notebooklm/compat.test.d.ts +3 -0
  73. package/dist/clis/notebooklm/compat.test.js +16 -0
  74. package/dist/clis/notebooklm/current.d.ts +1 -0
  75. package/dist/clis/notebooklm/current.js +28 -0
  76. package/dist/clis/notebooklm/get.d.ts +1 -0
  77. package/dist/clis/notebooklm/get.js +37 -0
  78. package/dist/clis/notebooklm/history.d.ts +1 -0
  79. package/dist/clis/notebooklm/history.js +25 -0
  80. package/dist/clis/notebooklm/history.test.d.ts +1 -0
  81. package/dist/clis/notebooklm/history.test.js +58 -0
  82. package/dist/clis/notebooklm/list.d.ts +1 -0
  83. package/dist/clis/notebooklm/list.js +35 -0
  84. package/dist/clis/notebooklm/note-list.d.ts +1 -0
  85. package/dist/clis/notebooklm/note-list.js +28 -0
  86. package/dist/clis/notebooklm/note-list.test.d.ts +1 -0
  87. package/dist/clis/notebooklm/note-list.test.js +56 -0
  88. package/dist/clis/notebooklm/notes-get.d.ts +1 -0
  89. package/dist/clis/notebooklm/notes-get.js +47 -0
  90. package/dist/clis/notebooklm/notes-get.test.d.ts +1 -0
  91. package/dist/clis/notebooklm/notes-get.test.js +72 -0
  92. package/dist/clis/notebooklm/rpc.d.ts +36 -0
  93. package/dist/clis/notebooklm/rpc.js +189 -0
  94. package/dist/clis/notebooklm/rpc.test.d.ts +1 -0
  95. package/dist/clis/notebooklm/rpc.test.js +105 -0
  96. package/dist/clis/notebooklm/shared.d.ts +87 -0
  97. package/dist/clis/notebooklm/shared.js +3 -0
  98. package/dist/clis/notebooklm/source-fulltext.d.ts +1 -0
  99. package/dist/clis/notebooklm/source-fulltext.js +44 -0
  100. package/dist/clis/notebooklm/source-fulltext.test.d.ts +1 -0
  101. package/dist/clis/notebooklm/source-fulltext.test.js +106 -0
  102. package/dist/clis/notebooklm/source-get.d.ts +1 -0
  103. package/dist/clis/notebooklm/source-get.js +40 -0
  104. package/dist/clis/notebooklm/source-get.test.d.ts +1 -0
  105. package/dist/clis/notebooklm/source-get.test.js +84 -0
  106. package/dist/clis/notebooklm/source-guide.d.ts +1 -0
  107. package/dist/clis/notebooklm/source-guide.js +44 -0
  108. package/dist/clis/notebooklm/source-guide.test.d.ts +1 -0
  109. package/dist/clis/notebooklm/source-guide.test.js +104 -0
  110. package/dist/clis/notebooklm/source-list.d.ts +1 -0
  111. package/dist/clis/notebooklm/source-list.js +30 -0
  112. package/dist/clis/notebooklm/status.d.ts +1 -0
  113. package/dist/clis/notebooklm/status.js +31 -0
  114. package/dist/clis/notebooklm/summary.d.ts +1 -0
  115. package/dist/clis/notebooklm/summary.js +30 -0
  116. package/dist/clis/notebooklm/summary.test.d.ts +1 -0
  117. package/dist/clis/notebooklm/summary.test.js +78 -0
  118. package/dist/clis/notebooklm/utils.d.ts +37 -0
  119. package/dist/clis/notebooklm/utils.js +739 -0
  120. package/dist/clis/notebooklm/utils.test.d.ts +1 -0
  121. package/dist/clis/notebooklm/utils.test.js +390 -0
  122. package/dist/clis/substack/utils.d.ts +4 -0
  123. package/dist/clis/substack/utils.js +8 -2
  124. package/dist/clis/substack/utils.test.d.ts +1 -0
  125. package/dist/clis/substack/utils.test.js +46 -0
  126. package/dist/clis/v2ex/hot.yaml +4 -1
  127. package/dist/clis/v2ex/latest.yaml +4 -1
  128. package/dist/clis/v2ex/topic.yaml +6 -1
  129. package/dist/clis/weixin/download.d.ts +9 -0
  130. package/dist/clis/weixin/download.js +76 -6
  131. package/dist/clis/weread/book.js +108 -2
  132. package/dist/clis/weread/commands.test.js +262 -152
  133. package/dist/clis/weread/utils.d.ts +10 -0
  134. package/dist/clis/weread/utils.js +27 -7
  135. package/dist/clis/xiaohongshu/comments.d.ts +3 -0
  136. package/dist/clis/xiaohongshu/comments.js +76 -17
  137. package/dist/clis/xiaohongshu/comments.test.js +70 -9
  138. package/dist/clis/xiaohongshu/download.d.ts +4 -1
  139. package/dist/clis/xiaohongshu/download.js +83 -22
  140. package/dist/clis/xiaohongshu/download.test.d.ts +1 -0
  141. package/dist/clis/xiaohongshu/download.test.js +75 -0
  142. package/dist/clis/xiaohongshu/note-helpers.d.ts +12 -0
  143. package/dist/clis/xiaohongshu/note-helpers.js +23 -0
  144. package/dist/clis/xiaohongshu/note.d.ts +7 -0
  145. package/dist/clis/xiaohongshu/note.js +76 -0
  146. package/dist/clis/xiaohongshu/note.test.d.ts +1 -0
  147. package/dist/clis/xiaohongshu/note.test.js +136 -0
  148. package/dist/clis/xiaohongshu/search.js +9 -0
  149. package/dist/clis/xiaohongshu/search.test.js +10 -4
  150. package/dist/clis/youtube/search.js +57 -17
  151. package/dist/clis/zhihu/question.js +19 -17
  152. package/dist/clis/zhihu/question.test.d.ts +1 -0
  153. package/dist/clis/zhihu/question.test.js +54 -0
  154. package/dist/commanderAdapter.js +9 -0
  155. package/dist/commanderAdapter.test.js +25 -0
  156. package/dist/commands/daemon.d.ts +9 -0
  157. package/dist/commands/daemon.js +124 -0
  158. package/dist/commands/daemon.test.d.ts +1 -0
  159. package/dist/commands/daemon.test.js +185 -0
  160. package/dist/completion.js +3 -1
  161. package/dist/constants.d.ts +2 -0
  162. package/dist/constants.js +2 -0
  163. package/dist/daemon.d.ts +1 -1
  164. package/dist/daemon.js +25 -14
  165. package/dist/daemon.test.d.ts +1 -0
  166. package/dist/daemon.test.js +65 -0
  167. package/dist/discovery.d.ts +9 -0
  168. package/dist/discovery.js +47 -2
  169. package/dist/electron-apps.d.ts +29 -0
  170. package/dist/electron-apps.js +65 -0
  171. package/dist/electron-apps.test.d.ts +1 -0
  172. package/dist/electron-apps.test.js +43 -0
  173. package/dist/engine.test.js +41 -9
  174. package/dist/execution.js +20 -16
  175. package/dist/extension-manifest-regression.test.js +1 -0
  176. package/dist/idle-manager.d.ts +19 -0
  177. package/dist/idle-manager.js +54 -0
  178. package/dist/launcher.d.ts +36 -0
  179. package/dist/launcher.js +152 -0
  180. package/dist/launcher.test.d.ts +1 -0
  181. package/dist/launcher.test.js +57 -0
  182. package/dist/main.js +3 -3
  183. package/dist/registry.d.ts +1 -0
  184. package/dist/registry.js +31 -3
  185. package/dist/registry.test.js +13 -0
  186. package/dist/runtime.d.ts +5 -3
  187. package/dist/runtime.js +12 -5
  188. package/dist/serialization.d.ts +1 -0
  189. package/dist/serialization.js +3 -0
  190. package/dist/serialization.test.js +17 -1
  191. package/dist/tui.d.ts +7 -0
  192. package/dist/tui.js +52 -0
  193. package/dist/tui.test.d.ts +1 -0
  194. package/dist/tui.test.js +19 -0
  195. package/dist/weixin-download.test.js +14 -0
  196. package/docs/.vitepress/config.mts +1 -0
  197. package/docs/adapters/browser/notebooklm.md +69 -0
  198. package/docs/adapters/browser/xiaohongshu.md +19 -10
  199. package/docs/adapters/index.md +67 -66
  200. package/docs/guide/browser-bridge.md +12 -0
  201. package/docs/guide/troubleshooting.md +9 -4
  202. package/docs/superpowers/plans/2026-03-31-daemon-lifecycle-redesign.md +857 -0
  203. package/docs/superpowers/specs/2026-03-31-daemon-lifecycle-redesign.md +208 -0
  204. package/docs/zh/guide/browser-bridge.md +12 -0
  205. package/extension/dist/background.js +250 -11
  206. package/extension/manifest.json +2 -1
  207. package/extension/src/background.test.ts +202 -2
  208. package/extension/src/background.ts +175 -10
  209. package/extension/src/cdp.test.ts +75 -0
  210. package/extension/src/cdp.ts +89 -3
  211. package/extension/src/protocol.ts +7 -5
  212. package/package.json +1 -1
  213. package/src/browser/cdp.ts +24 -17
  214. package/src/browser/daemon-client.ts +7 -1
  215. package/src/browser/dom-helpers.test.ts +15 -1
  216. package/src/browser/dom-helpers.ts +1 -0
  217. package/src/browser/mcp.ts +18 -13
  218. package/src/browser/page.test.ts +58 -0
  219. package/src/browser/page.ts +18 -2
  220. package/src/browser/stealth.test.ts +153 -0
  221. package/src/browser/stealth.ts +198 -0
  222. package/src/browser.test.ts +1 -1
  223. package/src/build-manifest.test.ts +2 -0
  224. package/src/build-manifest.ts +6 -1
  225. package/src/cli.ts +21 -3
  226. package/src/clis/antigravity/SKILL.md +3 -12
  227. package/src/clis/antigravity/serve.ts +5 -10
  228. package/src/clis/bilibili/subtitle.test.ts +60 -0
  229. package/src/clis/bilibili/subtitle.ts +4 -0
  230. package/src/clis/chatwise/ask.ts +0 -2
  231. package/src/clis/chatwise/export.ts +0 -2
  232. package/src/clis/chatwise/history.ts +0 -2
  233. package/src/clis/chatwise/model.ts +0 -2
  234. package/src/clis/chatwise/new.ts +1 -2
  235. package/src/clis/chatwise/read.ts +0 -2
  236. package/src/clis/chatwise/screenshot.ts +1 -2
  237. package/src/clis/chatwise/send.ts +0 -2
  238. package/src/clis/chatwise/status.ts +1 -2
  239. package/src/clis/ctrip/search.test.ts +73 -0
  240. package/src/clis/ctrip/search.ts +97 -47
  241. package/src/clis/douyin/_shared/sts2.test.ts +31 -0
  242. package/src/clis/douyin/_shared/sts2.ts +11 -3
  243. package/src/clis/douyin/activities.test.ts +41 -1
  244. package/src/clis/douyin/activities.ts +12 -3
  245. package/src/clis/douyin/collections.test.ts +35 -2
  246. package/src/clis/douyin/collections.ts +1 -1
  247. package/src/clis/douyin/draft.test.ts +444 -2
  248. package/src/clis/douyin/draft.ts +382 -218
  249. package/src/clis/douyin/hashtag.test.ts +42 -2
  250. package/src/clis/douyin/hashtag.ts +11 -3
  251. package/src/clis/douyin/profile.test.ts +43 -1
  252. package/src/clis/douyin/profile.ts +9 -2
  253. package/src/clis/douyin/videos.test.ts +52 -2
  254. package/src/clis/douyin/videos.ts +49 -15
  255. package/src/clis/facebook/search.test.ts +70 -0
  256. package/src/clis/facebook/search.yaml +4 -3
  257. package/src/clis/instagram/download.test.ts +159 -0
  258. package/src/clis/instagram/download.ts +286 -0
  259. package/src/clis/notebooklm/bind-current.test.ts +43 -0
  260. package/src/clis/notebooklm/bind-current.ts +36 -0
  261. package/src/clis/notebooklm/binding.test.ts +53 -0
  262. package/src/clis/notebooklm/compat.test.ts +19 -0
  263. package/src/clis/notebooklm/current.ts +38 -0
  264. package/src/clis/notebooklm/get.ts +53 -0
  265. package/src/clis/notebooklm/history.test.ts +70 -0
  266. package/src/clis/notebooklm/history.ts +36 -0
  267. package/src/clis/notebooklm/list.ts +40 -0
  268. package/src/clis/notebooklm/note-list.test.ts +64 -0
  269. package/src/clis/notebooklm/note-list.ts +42 -0
  270. package/src/clis/notebooklm/notes-get.test.ts +88 -0
  271. package/src/clis/notebooklm/notes-get.ts +67 -0
  272. package/src/clis/notebooklm/rpc.test.ts +126 -0
  273. package/src/clis/notebooklm/rpc.ts +286 -0
  274. package/src/clis/notebooklm/shared.ts +98 -0
  275. package/src/clis/notebooklm/source-fulltext.test.ts +123 -0
  276. package/src/clis/notebooklm/source-fulltext.ts +69 -0
  277. package/src/clis/notebooklm/source-get.test.ts +100 -0
  278. package/src/clis/notebooklm/source-get.ts +60 -0
  279. package/src/clis/notebooklm/source-guide.test.ts +121 -0
  280. package/src/clis/notebooklm/source-guide.ts +69 -0
  281. package/src/clis/notebooklm/source-list.ts +45 -0
  282. package/src/clis/notebooklm/status.ts +34 -0
  283. package/src/clis/notebooklm/summary.test.ts +94 -0
  284. package/src/clis/notebooklm/summary.ts +45 -0
  285. package/src/clis/notebooklm/utils.test.ts +446 -0
  286. package/src/clis/notebooklm/utils.ts +893 -0
  287. package/src/clis/substack/utils.test.ts +54 -0
  288. package/src/clis/substack/utils.ts +10 -2
  289. package/src/clis/v2ex/hot.yaml +4 -1
  290. package/src/clis/v2ex/latest.yaml +4 -1
  291. package/src/clis/v2ex/topic.yaml +6 -1
  292. package/src/clis/weixin/download.ts +95 -6
  293. package/src/clis/weread/book.ts +142 -2
  294. package/src/clis/weread/commands.test.ts +314 -154
  295. package/src/clis/weread/utils.ts +33 -4
  296. package/src/clis/xiaohongshu/comments.test.ts +85 -9
  297. package/src/clis/xiaohongshu/comments.ts +76 -17
  298. package/src/clis/xiaohongshu/download.test.ts +96 -0
  299. package/src/clis/xiaohongshu/download.ts +83 -22
  300. package/src/clis/xiaohongshu/note-helpers.ts +25 -0
  301. package/src/clis/xiaohongshu/note.test.ts +164 -0
  302. package/src/clis/xiaohongshu/note.ts +86 -0
  303. package/src/clis/xiaohongshu/search.test.ts +11 -4
  304. package/src/clis/xiaohongshu/search.ts +13 -0
  305. package/src/clis/youtube/search.ts +57 -17
  306. package/src/clis/zhihu/question.test.ts +71 -0
  307. package/src/clis/zhihu/question.ts +27 -15
  308. package/src/commanderAdapter.test.ts +30 -0
  309. package/src/commanderAdapter.ts +7 -0
  310. package/src/commands/daemon.test.ts +238 -0
  311. package/src/commands/daemon.ts +135 -0
  312. package/src/completion.ts +2 -1
  313. package/src/constants.ts +3 -0
  314. package/src/daemon.test.ts +88 -0
  315. package/src/daemon.ts +26 -14
  316. package/src/discovery.ts +52 -2
  317. package/src/electron-apps.test.ts +50 -0
  318. package/src/electron-apps.ts +89 -0
  319. package/src/engine.test.ts +45 -9
  320. package/src/execution.ts +24 -19
  321. package/src/extension-manifest-regression.test.ts +1 -0
  322. package/src/idle-manager.ts +60 -0
  323. package/src/launcher.test.ts +67 -0
  324. package/src/launcher.ts +185 -0
  325. package/src/main.ts +3 -2
  326. package/src/registry.test.ts +15 -0
  327. package/src/registry.ts +32 -3
  328. package/src/runtime.ts +13 -7
  329. package/src/serialization.test.ts +19 -1
  330. package/src/serialization.ts +2 -0
  331. package/src/tui.test.ts +23 -0
  332. package/src/tui.ts +65 -0
  333. package/src/weixin-download.test.ts +27 -0
  334. package/tests/e2e/browser-public-extended.test.ts +6 -2
  335. package/chatwise-opencli.ps1 +0 -82
  336. package/dist/clis/chatwise/shared.d.ts +0 -2
  337. package/dist/clis/chatwise/shared.js +0 -6
  338. package/src/clis/chatwise/shared.ts +0 -8
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.');
@@ -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';
@@ -0,0 +1,48 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { AuthRequiredError, EmptyResultError } from '../../errors.js';
3
+ const { mockApiGet } = vi.hoisted(() => ({
4
+ mockApiGet: vi.fn(),
5
+ }));
6
+ vi.mock('./utils.js', () => ({
7
+ apiGet: mockApiGet,
8
+ }));
9
+ import { getRegistry } from '../../registry.js';
10
+ import './subtitle.js';
11
+ describe('bilibili subtitle', () => {
12
+ const command = getRegistry().get('bilibili/subtitle');
13
+ const page = {
14
+ goto: vi.fn().mockResolvedValue(undefined),
15
+ evaluate: vi.fn(),
16
+ };
17
+ beforeEach(() => {
18
+ mockApiGet.mockReset();
19
+ page.goto.mockClear();
20
+ page.evaluate.mockReset();
21
+ });
22
+ it('throws AuthRequiredError when bilibili hides subtitles behind login', async () => {
23
+ page.evaluate.mockResolvedValueOnce(123456);
24
+ mockApiGet.mockResolvedValueOnce({
25
+ code: 0,
26
+ data: {
27
+ need_login_subtitle: true,
28
+ subtitle: {
29
+ subtitles: [],
30
+ },
31
+ },
32
+ });
33
+ await expect(command.func(page, { bvid: 'BV1GbXPBeEZm' })).rejects.toSatisfy((err) => err instanceof AuthRequiredError && /login|登录/i.test(err.message));
34
+ });
35
+ it('throws EmptyResultError when a video truly has no subtitles', async () => {
36
+ page.evaluate.mockResolvedValueOnce(123456);
37
+ mockApiGet.mockResolvedValueOnce({
38
+ code: 0,
39
+ data: {
40
+ need_login_subtitle: false,
41
+ subtitle: {
42
+ subtitles: [],
43
+ },
44
+ },
45
+ });
46
+ await expect(command.func(page, { bvid: 'BV1GbXPBeEZm' })).rejects.toThrow(EmptyResultError);
47
+ });
48
+ });
@@ -1,6 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
2
  import { SelectorError } from '../../errors.js';
3
- import { chatwiseRequiredEnv } from './shared.js';
4
3
  export const askCommand = cli({
5
4
  site: 'chatwise',
6
5
  name: 'ask',
@@ -8,7 +7,6 @@ export const askCommand = cli({
8
7
  domain: 'localhost',
9
8
  strategy: Strategy.UI,
10
9
  browser: true,
11
- requiredEnv: chatwiseRequiredEnv,
12
10
  args: [
13
11
  { name: 'text', required: true, positional: true, help: 'Prompt to send' },
14
12
  { name: 'timeout', required: false, help: 'Max seconds to wait (default: 30)', default: '30' },
@@ -1,6 +1,5 @@
1
1
  import * as fs from 'node:fs';
2
2
  import { cli, Strategy } from '../../registry.js';
3
- import { chatwiseRequiredEnv } from './shared.js';
4
3
  export const exportCommand = cli({
5
4
  site: 'chatwise',
6
5
  name: 'export',
@@ -8,7 +7,6 @@ export const exportCommand = cli({
8
7
  domain: 'localhost',
9
8
  strategy: Strategy.UI,
10
9
  browser: true,
11
- requiredEnv: chatwiseRequiredEnv,
12
10
  args: [
13
11
  { name: 'output', required: false, help: 'Output file (default: /tmp/chatwise-export.md)' },
14
12
  ],
@@ -1,5 +1,4 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { chatwiseRequiredEnv } from './shared.js';
3
2
  export const historyCommand = cli({
4
3
  site: 'chatwise',
5
4
  name: 'history',
@@ -7,7 +6,6 @@ export const historyCommand = cli({
7
6
  domain: 'localhost',
8
7
  strategy: Strategy.UI,
9
8
  browser: true,
10
- requiredEnv: chatwiseRequiredEnv,
11
9
  args: [],
12
10
  columns: ['Index', 'Title'],
13
11
  func: async (page) => {
@@ -1,6 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
2
  import { SelectorError } from '../../errors.js';
3
- import { chatwiseRequiredEnv } from './shared.js';
4
3
  export const modelCommand = cli({
5
4
  site: 'chatwise',
6
5
  name: 'model',
@@ -8,7 +7,6 @@ export const modelCommand = cli({
8
7
  domain: 'localhost',
9
8
  strategy: Strategy.UI,
10
9
  browser: true,
11
- requiredEnv: chatwiseRequiredEnv,
12
10
  args: [
13
11
  { name: 'model-name', required: false, positional: true, help: 'Model to switch to (e.g. gpt-4, claude-3)' },
14
12
  ],
@@ -1,3 +1,2 @@
1
1
  import { makeNewCommand } from '../_shared/desktop-commands.js';
2
- import { chatwiseRequiredEnv } from './shared.js';
3
- export const newCommand = makeNewCommand('chatwise', 'ChatWise conversation', { requiredEnv: chatwiseRequiredEnv });
2
+ export const newCommand = makeNewCommand('chatwise', 'ChatWise conversation');
@@ -1,5 +1,4 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { chatwiseRequiredEnv } from './shared.js';
3
2
  export const readCommand = cli({
4
3
  site: 'chatwise',
5
4
  name: 'read',
@@ -7,7 +6,6 @@ export const readCommand = cli({
7
6
  domain: 'localhost',
8
7
  strategy: Strategy.UI,
9
8
  browser: true,
10
- requiredEnv: chatwiseRequiredEnv,
11
9
  args: [],
12
10
  columns: ['Content'],
13
11
  func: async (page) => {
@@ -1,3 +1,2 @@
1
1
  import { makeScreenshotCommand } from '../_shared/desktop-commands.js';
2
- import { chatwiseRequiredEnv } from './shared.js';
3
- export const screenshotCommand = makeScreenshotCommand('chatwise', 'ChatWise', { requiredEnv: chatwiseRequiredEnv });
2
+ export const screenshotCommand = makeScreenshotCommand('chatwise', 'ChatWise');
@@ -1,6 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
2
  import { SelectorError } from '../../errors.js';
3
- import { chatwiseRequiredEnv } from './shared.js';
4
3
  export const sendCommand = cli({
5
4
  site: 'chatwise',
6
5
  name: 'send',
@@ -8,7 +7,6 @@ export const sendCommand = cli({
8
7
  domain: 'localhost',
9
8
  strategy: Strategy.UI,
10
9
  browser: true,
11
- requiredEnv: chatwiseRequiredEnv,
12
10
  args: [{ name: 'text', required: true, positional: true, help: 'Message to send' }],
13
11
  columns: ['Status', 'InjectedText'],
14
12
  func: async (page, kwargs) => {
@@ -1,3 +1,2 @@
1
1
  import { makeStatusCommand } from '../_shared/desktop-commands.js';
2
- import { chatwiseRequiredEnv } from './shared.js';
3
- export const statusCommand = makeStatusCommand('chatwise', 'ChatWise Desktop', { requiredEnv: chatwiseRequiredEnv });
2
+ export const statusCommand = makeStatusCommand('chatwise', 'ChatWise Desktop');
@@ -1 +1,14 @@
1
+ declare function clampLimit(raw: unknown, fallback?: number): number;
2
+ declare function mapSearchResults(results: unknown[], limit: number): {
3
+ rank: number;
4
+ name: string;
5
+ type: string;
6
+ score: string | number;
7
+ price: string | number;
8
+ url: string;
9
+ }[];
10
+ export declare const __test__: {
11
+ clampLimit: typeof clampLimit;
12
+ mapSearchResults: typeof mapSearchResults;
13
+ };
1
14
  export {};
@@ -1,61 +1,86 @@
1
1
  /**
2
- * 携程旅行搜索 — browser cookie, multi-strategy.
2
+ * 携程旅行搜索 — public destination and hotel suggestion lookup.
3
3
  */
4
+ import { ArgumentError, CliError, EmptyResultError } from '../../errors.js';
4
5
  import { cli, Strategy } from '../../registry.js';
6
+ function clampLimit(raw, fallback = 15) {
7
+ const parsed = Number(raw);
8
+ if (!Number.isFinite(parsed))
9
+ return fallback;
10
+ return Math.max(1, Math.min(Math.floor(parsed), 50));
11
+ }
12
+ function mapSearchResults(results, limit) {
13
+ return results
14
+ .filter((item) => !!item && typeof item === 'object')
15
+ .slice(0, limit)
16
+ .map((item, index) => ({
17
+ rank: index + 1,
18
+ name: String(item.displayName || item.word || item.cityName || '').replace(/\s+/g, ' ').trim(),
19
+ type: String(item.displayType || item.type || '').replace(/\s+/g, ' ').trim(),
20
+ score: item.commentScore ?? item.cStar ?? '',
21
+ price: item.price ?? item.minPrice ?? '',
22
+ url: '',
23
+ }))
24
+ .filter((item) => item.name);
25
+ }
5
26
  cli({
6
27
  site: 'ctrip',
7
28
  name: 'search',
8
- description: '携程旅行搜索',
9
- domain: 'www.ctrip.com',
10
- strategy: Strategy.COOKIE,
29
+ description: '搜索携程目的地、景区和酒店联想结果',
30
+ strategy: Strategy.PUBLIC,
31
+ browser: false,
11
32
  args: [
12
33
  { name: 'query', required: true, positional: true, help: 'Search keyword (city or attraction)' },
13
34
  { name: 'limit', type: 'int', default: 15, help: 'Number of results' },
14
35
  ],
15
36
  columns: ['rank', 'name', 'type', 'score', 'price', 'url'],
16
- func: async (page, kwargs) => {
17
- const limit = kwargs.limit || 15;
18
- await page.goto('https://www.ctrip.com');
19
- await page.wait(2);
20
- const data = await page.evaluate(`
21
- (async () => {
22
- const query = '${kwargs.query.replace(/'/g, "\\'")}';
23
- const limit = ${limit};
24
-
25
- // Strategy 1: Suggestion API
26
- try {
27
- const suggestUrl = 'https://m.ctrip.com/restapi/h5api/searchapp/search?action=onekeyali&keyword=' + encodeURIComponent(query);
28
- const resp = await fetch(suggestUrl, {credentials: 'include'});
29
- if (resp.ok) {
30
- const d = await resp.json();
31
- const raw = d.data || d.result || d;
32
- if (raw && typeof raw === 'object') {
33
- // Flatten all result categories
34
- const items = [];
35
- for (const key of Object.keys(raw)) {
36
- const list = Array.isArray(raw[key]) ? raw[key] : [];
37
- for (const item of list) {
38
- if (items.length >= limit) break;
39
- items.push({
40
- rank: items.length + 1,
41
- name: item.word || item.name || item.title || '',
42
- type: item.type || item.tpName || key,
43
- score: item.score || '',
44
- price: item.price || item.minPrice || '',
45
- url: item.url || item.surl || '',
46
- });
47
- }
48
- }
49
- if (items.length > 0) return items;
50
- }
51
- }
52
- } catch(e) {}
53
-
54
- return {error: 'No results for: ' + query};
55
- })()
56
- `);
57
- if (!Array.isArray(data))
58
- return [];
59
- return data;
37
+ func: async (_page, kwargs) => {
38
+ const query = String(kwargs.query || '').trim();
39
+ if (!query) {
40
+ throw new ArgumentError('Search keyword cannot be empty');
41
+ }
42
+ const limit = clampLimit(kwargs.limit);
43
+ const response = await fetch('https://m.ctrip.com/restapi/soa2/21881/json/gaHotelSearchEngine', {
44
+ method: 'POST',
45
+ headers: {
46
+ 'content-type': 'application/json',
47
+ },
48
+ body: JSON.stringify({
49
+ keyword: query,
50
+ searchType: 'D',
51
+ platform: 'online',
52
+ pageID: '102001',
53
+ head: {
54
+ Locale: 'zh-CN',
55
+ LocaleController: 'zh_cn',
56
+ Currency: 'CNY',
57
+ PageId: '102001',
58
+ clientID: 'opencli-ctrip-search',
59
+ group: 'ctrip',
60
+ Frontend: {
61
+ sessionID: 1,
62
+ pvid: 1,
63
+ },
64
+ HotelExtension: {
65
+ group: 'CTRIP',
66
+ WebpSupport: false,
67
+ },
68
+ },
69
+ }),
70
+ });
71
+ if (!response.ok) {
72
+ throw new CliError('FETCH_ERROR', `ctrip search failed with status ${response.status}`, 'Retry the command or verify ctrip.com is reachable');
73
+ }
74
+ const payload = await response.json();
75
+ const rawResults = Array.isArray(payload?.Response?.searchResults) ? payload.Response.searchResults : [];
76
+ const results = mapSearchResults(rawResults, limit);
77
+ if (!results.length) {
78
+ throw new EmptyResultError('ctrip search', 'Try a destination, scenic spot, or hotel keyword such as "苏州" or "朱家尖"');
79
+ }
80
+ return results;
60
81
  },
61
82
  });
83
+ export const __test__ = {
84
+ clampLimit,
85
+ mapSearchResults,
86
+ };
@@ -0,0 +1 @@
1
+ import './search.js';
@@ -0,0 +1,64 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { getRegistry } from '../../registry.js';
3
+ import './search.js';
4
+ describe('ctrip search', () => {
5
+ const command = getRegistry().get('ctrip/search');
6
+ beforeEach(() => {
7
+ vi.unstubAllGlobals();
8
+ });
9
+ it('maps live endpoint results into ranked rows', async () => {
10
+ vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(JSON.stringify({
11
+ Response: {
12
+ searchResults: [
13
+ {
14
+ displayName: '苏州, 江苏, 中国',
15
+ displayType: '城市',
16
+ commentScore: 0,
17
+ price: '',
18
+ },
19
+ {
20
+ word: '姑苏区',
21
+ type: '行政区',
22
+ cStar: 4.8,
23
+ minPrice: 320,
24
+ },
25
+ ],
26
+ },
27
+ }), { status: 200 })));
28
+ const result = await command.func(null, { query: '苏州', limit: 3 });
29
+ expect(result).toEqual([
30
+ {
31
+ rank: 1,
32
+ name: '苏州, 江苏, 中国',
33
+ type: '城市',
34
+ score: 0,
35
+ price: '',
36
+ url: '',
37
+ },
38
+ {
39
+ rank: 2,
40
+ name: '姑苏区',
41
+ type: '行政区',
42
+ score: 4.8,
43
+ price: 320,
44
+ url: '',
45
+ },
46
+ ]);
47
+ });
48
+ it('rejects empty queries', async () => {
49
+ await expect(command.func(null, { query: ' ', limit: 3 })).rejects.toThrow('Search keyword cannot be empty');
50
+ });
51
+ it('surfaces fetch failures as CliError', async () => {
52
+ vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response('{}', { status: 503 })));
53
+ await expect(command.func(null, { query: '苏州', limit: 3 })).rejects.toMatchObject({
54
+ code: 'FETCH_ERROR',
55
+ message: 'ctrip search failed with status 503',
56
+ });
57
+ });
58
+ it('surfaces empty results as EmptyResultError', async () => {
59
+ vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(JSON.stringify({
60
+ Response: { searchResults: [] },
61
+ }), { status: 200 })));
62
+ await expect(command.func(null, { query: '苏州', limit: 3 })).rejects.toThrow('ctrip search returned no data');
63
+ });
64
+ });
@@ -8,8 +8,14 @@ const STS2_URL = 'https://creator.douyin.com/aweme/mid/video/sts2/?scene=web&aid
8
8
  export async function getSts2Credentials(page) {
9
9
  const js = `fetch(${JSON.stringify(STS2_URL)}, { credentials: 'include' }).then(r => r.json())`;
10
10
  const res = await page.evaluate(js);
11
- if (!res?.data?.access_key_id) {
11
+ const credentials = (typeof res === 'object' &&
12
+ res !== null &&
13
+ 'data' in res &&
14
+ res.data)
15
+ ? res.data
16
+ : res;
17
+ if (!credentials?.access_key_id) {
12
18
  throw new AuthRequiredError('creator.douyin.com', 'STS2 credentials missing');
13
19
  }
14
- return res.data;
20
+ return credentials;
15
21
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { AuthRequiredError } from '../../../errors.js';
3
+ import { getSts2Credentials } from './sts2.js';
4
+ describe('douyin sts2 credentials', () => {
5
+ it('accepts top-level credential fields returned by creator center', async () => {
6
+ const page = {
7
+ evaluate: async () => ({
8
+ access_key_id: 'ak',
9
+ secret_access_key: 'sk',
10
+ session_token: 'token',
11
+ expired_time: 1_234_567_890,
12
+ }),
13
+ };
14
+ await expect(getSts2Credentials(page)).resolves.toEqual({
15
+ access_key_id: 'ak',
16
+ secret_access_key: 'sk',
17
+ session_token: 'token',
18
+ expired_time: 1_234_567_890,
19
+ });
20
+ });
21
+ it('still rejects responses without credential fields', async () => {
22
+ const page = {
23
+ evaluate: async () => ({ status_code: 8 }),
24
+ };
25
+ await expect(getSts2Credentials(page)).rejects.toBeInstanceOf(AuthRequiredError);
26
+ });
27
+ });
@@ -13,8 +13,10 @@ cli({
13
13
  const res = await browserFetch(page, 'GET', url);
14
14
  return (res.activity_list ?? []).map(a => ({
15
15
  activity_id: a.activity_id,
16
- title: a.title,
17
- end_time: new Date(a.end_time * 1000).toLocaleString('zh-CN', { timeZone: 'Asia/Tokyo' }),
16
+ title: a.title ?? a.activity_name ?? '',
17
+ end_time: typeof a.end_time === 'number'
18
+ ? new Date(a.end_time * 1000).toLocaleString('zh-CN', { timeZone: 'Asia/Tokyo' })
19
+ : (a.show_end_time ?? ''),
18
20
  }));
19
21
  },
20
22
  });
@@ -1,7 +1,16 @@
1
- import { describe, expect, it } from 'vitest';
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ const { browserFetchMock } = vi.hoisted(() => ({
3
+ browserFetchMock: vi.fn(),
4
+ }));
5
+ vi.mock('./_shared/browser-fetch.js', () => ({
6
+ browserFetch: browserFetchMock,
7
+ }));
2
8
  import { getRegistry } from '../../registry.js';
3
9
  import './activities.js';
4
10
  describe('douyin activities registration', () => {
11
+ beforeEach(() => {
12
+ browserFetchMock.mockReset();
13
+ });
5
14
  it('registers the activities command', () => {
6
15
  const registry = getRegistry();
7
16
  const cmd = [...registry.values()].find(c => c.site === 'douyin' && c.name === 'activities');
@@ -19,4 +28,28 @@ describe('douyin activities registration', () => {
19
28
  const cmd = [...registry.values()].find(c => c.site === 'douyin' && c.name === 'activities');
20
29
  expect(cmd?.strategy).toBe('cookie');
21
30
  });
31
+ it('maps the current activity payload shape returned by creator center', async () => {
32
+ const registry = getRegistry();
33
+ const cmd = [...registry.values()].find(c => c.site === 'douyin' && c.name === 'activities');
34
+ expect(cmd?.func).toBeDefined();
35
+ if (!cmd?.func)
36
+ throw new Error('douyin activities command not registered');
37
+ browserFetchMock.mockResolvedValueOnce({
38
+ activity_list: [
39
+ {
40
+ activity_id: '200',
41
+ activity_name: '超会玩派对',
42
+ show_end_time: '2026.05.31',
43
+ },
44
+ ],
45
+ });
46
+ const rows = await cmd.func({}, {});
47
+ expect(rows).toEqual([
48
+ {
49
+ activity_id: '200',
50
+ title: '超会玩派对',
51
+ end_time: '2026.05.31',
52
+ },
53
+ ]);
54
+ });
22
55
  });
@@ -11,7 +11,7 @@ cli({
11
11
  ],
12
12
  columns: ['mix_id', 'name', 'item_count'],
13
13
  func: async (page, kwargs) => {
14
- const url = `https://creator.douyin.com/web/api/mix/list/?aid=1128&count=${kwargs.limit}`;
14
+ const url = `https://creator.douyin.com/web/api/mix/list/?status=0,1,2,3,6&count=${kwargs.limit}&cursor=0&should_query_new_mix=1&device_platform=web&aid=1128`;
15
15
  const res = await browserFetch(page, 'GET', url);
16
16
  return (res.mix_list ?? []).map(m => ({
17
17
  mix_id: m.mix_id,