@jackwener/opencli 1.3.2 → 1.4.0

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 (508) hide show
  1. package/.github/pull_request_template.md +3 -1
  2. package/.github/workflows/build-extension.yml +7 -1
  3. package/.github/workflows/ci.yml +29 -3
  4. package/.github/workflows/docs.yml +1 -1
  5. package/.github/workflows/e2e-headed.yml +20 -0
  6. package/.github/workflows/release.yml +1 -1
  7. package/.github/workflows/security.yml +0 -3
  8. package/CHANGELOG.md +55 -0
  9. package/CONTRIBUTING.md +6 -3
  10. package/README.md +37 -10
  11. package/README.zh-CN.md +37 -10
  12. package/SKILL.md +7 -2
  13. package/TESTING.md +1 -0
  14. package/chatwise-opencli.ps1 +82 -0
  15. package/dist/analysis.d.ts +38 -0
  16. package/dist/analysis.js +166 -0
  17. package/dist/browser/cdp.d.ts +0 -4
  18. package/dist/browser/cdp.js +59 -38
  19. package/dist/browser/cdp.test.d.ts +1 -0
  20. package/dist/browser/cdp.test.js +52 -0
  21. package/dist/browser/daemon-client.js +2 -1
  22. package/dist/browser/discover.js +2 -1
  23. package/dist/browser/dom-snapshot.d.ts +2 -2
  24. package/dist/browser/dom-snapshot.js +54 -1
  25. package/dist/browser/dom-snapshot.test.js +36 -0
  26. package/dist/browser/errors.js +2 -1
  27. package/dist/browser/index.d.ts +3 -2
  28. package/dist/browser/index.js +2 -1
  29. package/dist/browser/mcp.d.ts +0 -2
  30. package/dist/browser/mcp.js +2 -3
  31. package/dist/browser/page.d.ts +4 -3
  32. package/dist/browser/page.js +44 -35
  33. package/dist/browser/stealth.d.ts +16 -0
  34. package/dist/browser/stealth.js +155 -0
  35. package/dist/browser.test.js +47 -1
  36. package/dist/build-manifest.js +15 -9
  37. package/dist/build-manifest.test.js +12 -0
  38. package/dist/cascade.js +4 -2
  39. package/dist/cli-manifest.json +639 -258
  40. package/dist/cli.js +57 -29
  41. package/dist/clis/_shared/desktop-commands.d.ts +22 -0
  42. package/dist/clis/_shared/desktop-commands.js +108 -0
  43. package/dist/clis/antigravity/serve.js +5 -2
  44. package/dist/clis/arxiv/search.js +1 -1
  45. package/dist/clis/bilibili/dynamic.test.d.ts +1 -0
  46. package/dist/clis/bilibili/dynamic.test.js +68 -0
  47. package/dist/clis/bilibili/favorite.js +4 -2
  48. package/dist/clis/bilibili/following.js +3 -2
  49. package/dist/clis/bilibili/subtitle.js +8 -7
  50. package/dist/clis/bilibili/utils.js +2 -2
  51. package/dist/clis/boss/batchgreet.js +1 -1
  52. package/dist/clis/boss/chatlist.js +1 -1
  53. package/dist/clis/boss/chatmsg.js +1 -1
  54. package/dist/clis/boss/detail.js +1 -1
  55. package/dist/clis/boss/exchange.js +1 -1
  56. package/dist/clis/boss/greet.js +1 -1
  57. package/dist/clis/boss/invite.js +1 -1
  58. package/dist/clis/boss/joblist.js +1 -1
  59. package/dist/clis/boss/mark.js +4 -3
  60. package/dist/clis/boss/recommend.js +1 -1
  61. package/dist/clis/boss/resume.js +1 -1
  62. package/dist/clis/boss/search.js +1 -1
  63. package/dist/clis/boss/send.js +5 -4
  64. package/dist/clis/boss/stats.js +1 -1
  65. package/dist/clis/chatgpt/ask.js +4 -0
  66. package/dist/clis/chatgpt/new.js +5 -1
  67. package/dist/clis/chatgpt/read.js +5 -1
  68. package/dist/clis/chatgpt/send.js +2 -1
  69. package/dist/clis/chatgpt/status.js +5 -1
  70. package/dist/clis/chatwise/ask.js +8 -2
  71. package/dist/clis/chatwise/export.js +2 -0
  72. package/dist/clis/chatwise/history.js +2 -0
  73. package/dist/clis/chatwise/model.js +8 -3
  74. package/dist/clis/chatwise/new.js +3 -18
  75. package/dist/clis/chatwise/read.js +2 -0
  76. package/dist/clis/chatwise/screenshot.js +3 -27
  77. package/dist/clis/chatwise/send.js +8 -2
  78. package/dist/clis/chatwise/shared.d.ts +2 -0
  79. package/dist/clis/chatwise/shared.js +6 -0
  80. package/dist/clis/chatwise/status.js +3 -22
  81. package/dist/clis/codex/ask.js +6 -2
  82. package/dist/clis/codex/dump.js +2 -25
  83. package/dist/clis/codex/new.js +2 -25
  84. package/dist/clis/codex/screenshot.js +2 -27
  85. package/dist/clis/codex/send.js +6 -4
  86. package/dist/clis/codex/status.js +2 -22
  87. package/dist/clis/cursor/ask.js +2 -1
  88. package/dist/clis/cursor/composer.js +2 -1
  89. package/dist/clis/cursor/dump.js +2 -25
  90. package/dist/clis/cursor/new.js +2 -18
  91. package/dist/clis/cursor/read.js +2 -1
  92. package/dist/clis/cursor/screenshot.js +1 -30
  93. package/dist/clis/cursor/send.js +2 -1
  94. package/dist/clis/cursor/status.js +2 -21
  95. package/dist/clis/dictionary/examples.yaml +25 -0
  96. package/dist/clis/dictionary/search.yaml +27 -0
  97. package/dist/clis/dictionary/synonyms.yaml +25 -0
  98. package/dist/clis/douban/book-hot.js +1 -1
  99. package/dist/clis/douban/movie-hot.js +1 -1
  100. package/dist/clis/douban/search.js +1 -1
  101. package/dist/clis/douban/utils.d.ts +4 -1
  102. package/dist/clis/douban/utils.js +156 -1
  103. package/dist/clis/doubao/ask.js +1 -1
  104. package/dist/clis/doubao/new.js +1 -1
  105. package/dist/clis/doubao/read.js +1 -1
  106. package/dist/clis/doubao/send.js +1 -1
  107. package/dist/clis/doubao/status.js +1 -1
  108. package/dist/clis/doubao-app/ask.js +1 -1
  109. package/dist/clis/doubao-app/new.js +1 -1
  110. package/dist/clis/doubao-app/read.js +1 -1
  111. package/dist/clis/doubao-app/send.js +1 -1
  112. package/dist/clis/grok/ask.d.ts +4 -0
  113. package/dist/clis/grok/ask.js +28 -10
  114. package/dist/clis/grok/ask.test.js +18 -0
  115. package/dist/clis/jd/item.d.ts +1 -0
  116. package/dist/clis/jd/item.js +96 -0
  117. package/dist/clis/jd/item.test.d.ts +1 -0
  118. package/dist/clis/jd/item.test.js +28 -0
  119. package/dist/clis/jike/feed.js +1 -1
  120. package/dist/clis/jike/search.js +1 -1
  121. package/dist/clis/linkedin/search.js +5 -4
  122. package/dist/clis/linkedin/timeline.d.ts +21 -0
  123. package/dist/clis/linkedin/timeline.js +503 -0
  124. package/dist/clis/linkedin/timeline.test.d.ts +1 -0
  125. package/dist/clis/linkedin/timeline.test.js +81 -0
  126. package/dist/clis/medium/feed.js +1 -1
  127. package/dist/clis/medium/search.js +1 -1
  128. package/dist/clis/medium/user.js +1 -1
  129. package/dist/clis/medium/{shared.js → utils.js} +2 -1
  130. package/dist/clis/pixiv/detail.yaml +49 -0
  131. package/dist/clis/pixiv/download.d.ts +7 -0
  132. package/dist/clis/pixiv/download.js +78 -0
  133. package/dist/clis/pixiv/download.test.d.ts +1 -0
  134. package/dist/clis/pixiv/download.test.js +87 -0
  135. package/dist/clis/pixiv/illusts.d.ts +8 -0
  136. package/dist/clis/pixiv/illusts.js +65 -0
  137. package/dist/clis/pixiv/illusts.test.d.ts +1 -0
  138. package/dist/clis/pixiv/illusts.test.js +99 -0
  139. package/dist/clis/pixiv/ranking.yaml +53 -0
  140. package/dist/clis/pixiv/search.d.ts +6 -0
  141. package/dist/clis/pixiv/search.js +43 -0
  142. package/dist/clis/pixiv/search.test.d.ts +1 -0
  143. package/dist/clis/pixiv/search.test.js +83 -0
  144. package/dist/clis/pixiv/test-utils.d.ts +12 -0
  145. package/dist/clis/pixiv/test-utils.js +23 -0
  146. package/dist/clis/pixiv/user.yaml +46 -0
  147. package/dist/clis/pixiv/utils.d.ts +27 -0
  148. package/dist/clis/pixiv/utils.js +49 -0
  149. package/dist/clis/reddit/comment.js +2 -1
  150. package/dist/clis/reddit/read.js +4 -3
  151. package/dist/clis/reddit/read.test.d.ts +1 -0
  152. package/dist/clis/reddit/read.test.js +28 -0
  153. package/dist/clis/reddit/save.js +2 -1
  154. package/dist/clis/reddit/saved.js +7 -3
  155. package/dist/clis/reddit/subscribe.js +2 -1
  156. package/dist/clis/reddit/upvote.js +2 -1
  157. package/dist/clis/reddit/upvoted.js +7 -3
  158. package/dist/clis/sinablog/article.js +1 -1
  159. package/dist/clis/sinablog/hot.js +1 -1
  160. package/dist/clis/sinablog/user.js +1 -1
  161. package/dist/clis/substack/feed.js +1 -1
  162. package/dist/clis/substack/publication.js +1 -1
  163. package/dist/clis/substack/search.js +3 -2
  164. package/dist/clis/substack/{shared.js → utils.js} +3 -2
  165. package/dist/clis/tiktok/search.yaml +2 -1
  166. package/dist/clis/twitter/accept.js +2 -1
  167. package/dist/clis/twitter/article.js +4 -1
  168. package/dist/clis/twitter/block.js +2 -1
  169. package/dist/clis/twitter/bookmark.js +2 -1
  170. package/dist/clis/twitter/bookmarks.js +3 -2
  171. package/dist/clis/twitter/delete.js +2 -1
  172. package/dist/clis/twitter/follow.js +2 -1
  173. package/dist/clis/twitter/followers.js +3 -2
  174. package/dist/clis/twitter/following.js +3 -2
  175. package/dist/clis/twitter/hide-reply.js +2 -1
  176. package/dist/clis/twitter/like.js +2 -1
  177. package/dist/clis/twitter/notifications.js +2 -1
  178. package/dist/clis/twitter/post.js +2 -1
  179. package/dist/clis/twitter/profile.js +5 -2
  180. package/dist/clis/twitter/reply-dm.js +2 -1
  181. package/dist/clis/twitter/reply.js +2 -1
  182. package/dist/clis/twitter/search.js +30 -13
  183. package/dist/clis/twitter/search.test.d.ts +1 -0
  184. package/dist/clis/twitter/search.test.js +104 -0
  185. package/dist/clis/twitter/thread.js +2 -2
  186. package/dist/clis/twitter/timeline.js +3 -2
  187. package/dist/clis/twitter/trending.js +3 -2
  188. package/dist/clis/twitter/unblock.js +2 -1
  189. package/dist/clis/twitter/unbookmark.js +2 -1
  190. package/dist/clis/twitter/unfollow.js +2 -1
  191. package/dist/clis/v2ex/daily.js +3 -2
  192. package/dist/clis/v2ex/me.js +3 -2
  193. package/dist/clis/v2ex/notifications.js +4 -4
  194. package/dist/clis/web/read.d.ts +16 -0
  195. package/dist/clis/web/read.js +202 -0
  196. package/dist/clis/xueqiu/danjuan-utils.d.ts +55 -0
  197. package/dist/clis/xueqiu/danjuan-utils.js +126 -0
  198. package/dist/clis/xueqiu/danjuan-utils.test.d.ts +1 -0
  199. package/dist/clis/xueqiu/danjuan-utils.test.js +41 -0
  200. package/dist/clis/xueqiu/fund-holdings.d.ts +1 -0
  201. package/dist/clis/xueqiu/fund-holdings.js +28 -0
  202. package/dist/clis/xueqiu/fund-snapshot.d.ts +1 -0
  203. package/dist/clis/xueqiu/fund-snapshot.js +25 -0
  204. package/dist/clis/youtube/transcript.js +5 -4
  205. package/dist/clis/youtube/video.js +3 -2
  206. package/dist/constants.d.ts +2 -0
  207. package/dist/constants.js +2 -0
  208. package/dist/daemon.js +9 -4
  209. package/dist/discovery.js +11 -10
  210. package/dist/doctor.js +4 -2
  211. package/dist/download/index.d.ts +4 -12
  212. package/dist/download/index.js +33 -12
  213. package/dist/download/index.test.js +79 -2
  214. package/dist/download/media-download.js +4 -2
  215. package/dist/engine.test.js +76 -4
  216. package/dist/execution.d.ts +1 -9
  217. package/dist/execution.js +56 -46
  218. package/dist/explore.js +12 -111
  219. package/dist/external-clis.yaml +0 -8
  220. package/dist/external.js +7 -5
  221. package/dist/external.test.js +4 -0
  222. package/dist/generate.d.ts +0 -9
  223. package/dist/generate.js +4 -20
  224. package/dist/hooks.d.ts +46 -0
  225. package/dist/hooks.js +56 -0
  226. package/dist/hooks.test.d.ts +4 -0
  227. package/dist/hooks.test.js +92 -0
  228. package/dist/interceptor.js +70 -23
  229. package/dist/main.js +2 -0
  230. package/dist/output.js +12 -6
  231. package/dist/pipeline/executor.js +1 -1
  232. package/dist/pipeline/steps/browser.js +1 -3
  233. package/dist/pipeline/steps/download.js +42 -26
  234. package/dist/pipeline/steps/download.test.d.ts +1 -0
  235. package/dist/pipeline/steps/download.test.js +101 -0
  236. package/dist/pipeline/steps/fetch.js +40 -22
  237. package/dist/pipeline/steps/fetch.test.d.ts +1 -0
  238. package/dist/pipeline/steps/fetch.test.js +123 -0
  239. package/dist/pipeline/steps/transform.js +2 -6
  240. package/dist/pipeline/template.js +66 -52
  241. package/dist/pipeline/template.test.js +28 -0
  242. package/dist/pipeline/transform.test.js +18 -0
  243. package/dist/plugin.d.ts +40 -1
  244. package/dist/plugin.js +214 -17
  245. package/dist/plugin.test.d.ts +1 -1
  246. package/dist/plugin.test.js +219 -3
  247. package/dist/record.js +6 -98
  248. package/dist/registry-api.d.ts +2 -0
  249. package/dist/registry-api.js +1 -0
  250. package/dist/registry.d.ts +5 -2
  251. package/dist/registry.js +1 -2
  252. package/dist/runtime.d.ts +0 -1
  253. package/dist/runtime.js +14 -4
  254. package/dist/snapshotFormatter.d.ts +7 -14
  255. package/dist/snapshotFormatter.js +38 -78
  256. package/dist/utils.d.ts +9 -0
  257. package/dist/utils.js +29 -0
  258. package/dist/validate.js +3 -5
  259. package/dist/yaml-schema.d.ts +26 -0
  260. package/dist/yaml-schema.js +5 -0
  261. package/docs/.vitepress/config.mts +3 -0
  262. package/docs/adapters/browser/dictionary.md +27 -0
  263. package/docs/adapters/browser/douban.md +18 -8
  264. package/docs/adapters/browser/jd.md +27 -0
  265. package/docs/adapters/browser/linkedin.md +6 -0
  266. package/docs/adapters/browser/pixiv.md +92 -0
  267. package/docs/adapters/browser/web.md +30 -0
  268. package/docs/adapters/browser/wikipedia.md +0 -9
  269. package/docs/adapters/browser/xueqiu.md +27 -9
  270. package/docs/adapters/desktop/antigravity.md +0 -3
  271. package/docs/adapters/index.md +11 -9
  272. package/docs/comparison.md +125 -0
  273. package/docs/developer/contributing.md +21 -2
  274. package/docs/developer/testing.md +14 -8
  275. package/docs/developer/ts-adapter.md +18 -0
  276. package/docs/developer/yaml-adapter.md +16 -0
  277. package/docs/guide/plugins.md +10 -0
  278. package/docs/zh/guide/plugins.md +10 -0
  279. package/extension/dist/background.js +519 -444
  280. package/extension/manifest.json +1 -1
  281. package/extension/package.json +1 -1
  282. package/extension/src/background.test.ts +46 -1
  283. package/extension/src/background.ts +108 -33
  284. package/extension/src/cdp.ts +9 -9
  285. package/package.json +3 -2
  286. package/scripts/check-doc-coverage.sh +2 -0
  287. package/src/analysis.ts +170 -0
  288. package/src/browser/cdp.test.ts +66 -0
  289. package/src/browser/cdp.ts +64 -41
  290. package/src/browser/daemon-client.ts +4 -3
  291. package/src/browser/discover.ts +2 -1
  292. package/src/browser/dom-snapshot.test.ts +42 -0
  293. package/src/browser/dom-snapshot.ts +56 -3
  294. package/src/browser/errors.ts +2 -1
  295. package/src/browser/index.ts +3 -2
  296. package/src/browser/mcp.ts +2 -4
  297. package/src/browser/page.ts +43 -35
  298. package/src/browser/stealth.ts +156 -0
  299. package/src/browser.test.ts +51 -1
  300. package/src/build-manifest.test.ts +14 -0
  301. package/src/build-manifest.ts +13 -32
  302. package/src/cascade.ts +5 -3
  303. package/src/cli.ts +66 -34
  304. package/src/clis/_shared/desktop-commands.ts +121 -0
  305. package/src/clis/antigravity/serve.ts +6 -3
  306. package/src/clis/arxiv/search.ts +1 -1
  307. package/src/clis/bilibili/dynamic.test.ts +79 -0
  308. package/src/clis/bilibili/favorite.ts +5 -2
  309. package/src/clis/bilibili/following.ts +3 -2
  310. package/src/clis/bilibili/subtitle.ts +8 -7
  311. package/src/clis/bilibili/utils.ts +2 -2
  312. package/src/clis/boss/batchgreet.ts +1 -1
  313. package/src/clis/boss/chatlist.ts +1 -1
  314. package/src/clis/boss/chatmsg.ts +1 -1
  315. package/src/clis/boss/detail.ts +1 -1
  316. package/src/clis/boss/exchange.ts +1 -1
  317. package/src/clis/boss/greet.ts +1 -1
  318. package/src/clis/boss/invite.ts +1 -1
  319. package/src/clis/boss/joblist.ts +1 -1
  320. package/src/clis/boss/mark.ts +4 -3
  321. package/src/clis/boss/recommend.ts +1 -1
  322. package/src/clis/boss/resume.ts +1 -1
  323. package/src/clis/boss/search.ts +1 -1
  324. package/src/clis/boss/send.ts +5 -4
  325. package/src/clis/boss/stats.ts +1 -1
  326. package/src/clis/chatgpt/ask.ts +5 -0
  327. package/src/clis/chatgpt/new.ts +7 -2
  328. package/src/clis/chatgpt/read.ts +7 -2
  329. package/src/clis/chatgpt/send.ts +3 -2
  330. package/src/clis/chatgpt/status.ts +6 -1
  331. package/src/clis/chatwise/ask.ts +7 -2
  332. package/src/clis/chatwise/export.ts +2 -0
  333. package/src/clis/chatwise/history.ts +2 -0
  334. package/src/clis/chatwise/model.ts +7 -3
  335. package/src/clis/chatwise/new.ts +3 -20
  336. package/src/clis/chatwise/read.ts +2 -0
  337. package/src/clis/chatwise/screenshot.ts +3 -32
  338. package/src/clis/chatwise/send.ts +7 -2
  339. package/src/clis/chatwise/shared.ts +8 -0
  340. package/src/clis/chatwise/status.ts +3 -24
  341. package/src/clis/codex/ask.ts +5 -2
  342. package/src/clis/codex/dump.ts +2 -27
  343. package/src/clis/codex/new.ts +2 -28
  344. package/src/clis/codex/screenshot.ts +2 -32
  345. package/src/clis/codex/send.ts +5 -4
  346. package/src/clis/codex/status.ts +2 -24
  347. package/src/clis/cursor/ask.ts +2 -1
  348. package/src/clis/cursor/composer.ts +2 -1
  349. package/src/clis/cursor/dump.ts +2 -27
  350. package/src/clis/cursor/new.ts +2 -20
  351. package/src/clis/cursor/read.ts +2 -1
  352. package/src/clis/cursor/screenshot.ts +1 -36
  353. package/src/clis/cursor/send.ts +2 -1
  354. package/src/clis/cursor/status.ts +2 -22
  355. package/src/clis/dictionary/examples.yaml +25 -0
  356. package/src/clis/dictionary/search.yaml +27 -0
  357. package/src/clis/dictionary/synonyms.yaml +25 -0
  358. package/src/clis/douban/book-hot.ts +1 -1
  359. package/src/clis/douban/movie-hot.ts +1 -1
  360. package/src/clis/douban/search.ts +1 -1
  361. package/src/clis/douban/utils.ts +165 -1
  362. package/src/clis/doubao/ask.ts +1 -1
  363. package/src/clis/doubao/new.ts +1 -1
  364. package/src/clis/doubao/read.ts +1 -1
  365. package/src/clis/doubao/send.ts +1 -1
  366. package/src/clis/doubao/status.ts +1 -1
  367. package/src/clis/doubao-app/ask.ts +1 -1
  368. package/src/clis/doubao-app/new.ts +1 -1
  369. package/src/clis/doubao-app/read.ts +1 -1
  370. package/src/clis/doubao-app/send.ts +1 -1
  371. package/src/clis/grok/ask.test.ts +25 -0
  372. package/src/clis/grok/ask.ts +25 -12
  373. package/src/clis/jd/item.test.ts +35 -0
  374. package/src/clis/jd/item.ts +101 -0
  375. package/src/clis/jike/feed.ts +1 -1
  376. package/src/clis/jike/search.ts +1 -1
  377. package/src/clis/linkedin/search.ts +5 -4
  378. package/src/clis/linkedin/timeline.test.ts +99 -0
  379. package/src/clis/linkedin/timeline.ts +532 -0
  380. package/src/clis/medium/feed.ts +1 -1
  381. package/src/clis/medium/search.ts +1 -1
  382. package/src/clis/medium/user.ts +1 -1
  383. package/src/clis/medium/{shared.ts → utils.ts} +2 -1
  384. package/src/clis/pixiv/detail.yaml +49 -0
  385. package/src/clis/pixiv/download.test.ts +114 -0
  386. package/src/clis/pixiv/download.ts +91 -0
  387. package/src/clis/pixiv/illusts.test.ts +115 -0
  388. package/src/clis/pixiv/illusts.ts +78 -0
  389. package/src/clis/pixiv/ranking.yaml +53 -0
  390. package/src/clis/pixiv/search.test.ts +97 -0
  391. package/src/clis/pixiv/search.ts +53 -0
  392. package/src/clis/pixiv/test-utils.ts +29 -0
  393. package/src/clis/pixiv/user.yaml +46 -0
  394. package/src/clis/pixiv/utils.ts +62 -0
  395. package/src/clis/reddit/comment.ts +2 -1
  396. package/src/clis/reddit/read.test.ts +34 -0
  397. package/src/clis/reddit/read.ts +4 -3
  398. package/src/clis/reddit/save.ts +2 -1
  399. package/src/clis/reddit/saved.ts +6 -2
  400. package/src/clis/reddit/subscribe.ts +2 -1
  401. package/src/clis/reddit/upvote.ts +2 -1
  402. package/src/clis/reddit/upvoted.ts +6 -2
  403. package/src/clis/sinablog/article.ts +1 -1
  404. package/src/clis/sinablog/hot.ts +1 -1
  405. package/src/clis/sinablog/user.ts +1 -1
  406. package/src/clis/substack/feed.ts +1 -1
  407. package/src/clis/substack/publication.ts +1 -1
  408. package/src/clis/substack/search.ts +3 -2
  409. package/src/clis/substack/{shared.ts → utils.ts} +3 -2
  410. package/src/clis/tiktok/search.yaml +2 -1
  411. package/src/clis/twitter/accept.ts +2 -1
  412. package/src/clis/twitter/article.ts +3 -1
  413. package/src/clis/twitter/block.ts +2 -1
  414. package/src/clis/twitter/bookmark.ts +2 -1
  415. package/src/clis/twitter/bookmarks.ts +3 -2
  416. package/src/clis/twitter/delete.ts +2 -1
  417. package/src/clis/twitter/follow.ts +2 -1
  418. package/src/clis/twitter/followers.ts +3 -2
  419. package/src/clis/twitter/following.ts +3 -2
  420. package/src/clis/twitter/hide-reply.ts +2 -1
  421. package/src/clis/twitter/like.ts +2 -1
  422. package/src/clis/twitter/notifications.ts +2 -1
  423. package/src/clis/twitter/post.ts +2 -1
  424. package/src/clis/twitter/profile.ts +4 -2
  425. package/src/clis/twitter/reply-dm.ts +2 -1
  426. package/src/clis/twitter/reply.ts +2 -1
  427. package/src/clis/twitter/search.test.ts +113 -0
  428. package/src/clis/twitter/search.ts +38 -14
  429. package/src/clis/twitter/thread.ts +2 -2
  430. package/src/clis/twitter/timeline.ts +3 -2
  431. package/src/clis/twitter/trending.ts +3 -2
  432. package/src/clis/twitter/unblock.ts +2 -1
  433. package/src/clis/twitter/unbookmark.ts +2 -1
  434. package/src/clis/twitter/unfollow.ts +2 -1
  435. package/src/clis/v2ex/daily.ts +3 -2
  436. package/src/clis/v2ex/me.ts +3 -2
  437. package/src/clis/v2ex/notifications.ts +3 -4
  438. package/src/clis/web/read.ts +210 -0
  439. package/src/clis/xueqiu/danjuan-utils.test.ts +49 -0
  440. package/src/clis/xueqiu/danjuan-utils.ts +176 -0
  441. package/src/clis/xueqiu/fund-holdings.ts +32 -0
  442. package/src/clis/xueqiu/fund-snapshot.ts +27 -0
  443. package/src/clis/youtube/transcript.ts +5 -4
  444. package/src/clis/youtube/video.ts +3 -2
  445. package/src/constants.ts +3 -0
  446. package/src/daemon.ts +7 -5
  447. package/src/discovery.ts +12 -34
  448. package/src/doctor.ts +5 -3
  449. package/src/download/index.test.ts +93 -2
  450. package/src/download/index.ts +44 -23
  451. package/src/download/media-download.ts +5 -3
  452. package/src/engine.test.ts +84 -3
  453. package/src/execution.ts +62 -46
  454. package/src/explore.ts +21 -90
  455. package/src/external-clis.yaml +0 -8
  456. package/src/external.test.ts +9 -0
  457. package/src/external.ts +12 -10
  458. package/src/generate.ts +4 -41
  459. package/src/hooks.test.ts +126 -0
  460. package/src/hooks.ts +90 -0
  461. package/src/interceptor.ts +73 -23
  462. package/src/main.ts +2 -0
  463. package/src/output.ts +14 -6
  464. package/src/pipeline/executor.ts +1 -1
  465. package/src/pipeline/steps/browser.ts +1 -3
  466. package/src/pipeline/steps/download.test.ts +136 -0
  467. package/src/pipeline/steps/download.ts +47 -34
  468. package/src/pipeline/steps/fetch.test.ts +179 -0
  469. package/src/pipeline/steps/fetch.ts +39 -23
  470. package/src/pipeline/steps/transform.ts +2 -6
  471. package/src/pipeline/template.test.ts +28 -0
  472. package/src/pipeline/template.ts +67 -79
  473. package/src/pipeline/transform.test.ts +20 -0
  474. package/src/plugin.test.ts +251 -3
  475. package/src/plugin.ts +265 -21
  476. package/src/record.ts +12 -84
  477. package/src/registry-api.ts +2 -0
  478. package/src/registry.ts +7 -4
  479. package/src/runtime.ts +14 -4
  480. package/src/snapshotFormatter.ts +43 -121
  481. package/src/utils.ts +39 -0
  482. package/src/validate.ts +3 -6
  483. package/src/yaml-schema.ts +28 -0
  484. package/tests/e2e/browser-auth.test.ts +25 -0
  485. package/tests/e2e/plugin-management.test.ts +137 -0
  486. package/tests/e2e/public-commands.test.ts +34 -1
  487. package/vitest.config.ts +19 -1
  488. package/.github/workflows/pkg-pr-new.yml +0 -30
  489. package/dist/clis/douban/shared.d.ts +0 -4
  490. package/dist/clis/douban/shared.js +0 -155
  491. package/src/clis/douban/shared.ts +0 -165
  492. /package/dist/clis/boss/{common.d.ts → utils.d.ts} +0 -0
  493. /package/dist/clis/boss/{common.js → utils.js} +0 -0
  494. /package/dist/clis/doubao/{common.d.ts → utils.d.ts} +0 -0
  495. /package/dist/clis/doubao/{common.js → utils.js} +0 -0
  496. /package/dist/clis/doubao-app/{common.d.ts → utils.d.ts} +0 -0
  497. /package/dist/clis/doubao-app/{common.js → utils.js} +0 -0
  498. /package/dist/clis/jike/{shared.d.ts → utils.d.ts} +0 -0
  499. /package/dist/clis/jike/{shared.js → utils.js} +0 -0
  500. /package/dist/clis/medium/{shared.d.ts → utils.d.ts} +0 -0
  501. /package/dist/clis/sinablog/{shared.d.ts → utils.d.ts} +0 -0
  502. /package/dist/clis/sinablog/{shared.js → utils.js} +0 -0
  503. /package/dist/clis/substack/{shared.d.ts → utils.d.ts} +0 -0
  504. /package/src/clis/boss/{common.ts → utils.ts} +0 -0
  505. /package/src/clis/doubao/{common.ts → utils.ts} +0 -0
  506. /package/src/clis/doubao-app/{common.ts → utils.ts} +0 -0
  507. /package/src/clis/jike/{shared.ts → utils.ts} +0 -0
  508. /package/src/clis/sinablog/{shared.ts → utils.ts} +0 -0
package/src/cli.ts CHANGED
@@ -15,6 +15,7 @@ import { PKG_VERSION } from './version.js';
15
15
  import { printCompletionScript } from './completion.js';
16
16
  import { loadExternalClis, executeExternalCli, installExternalCli, registerExternalCli, isBinaryInstalled } from './external.js';
17
17
  import { registerAllCommands } from './commanderAdapter.js';
18
+ import { getErrorMessage } from './errors.js';
18
19
 
19
20
  export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
20
21
  const program = new Command();
@@ -173,8 +174,6 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
173
174
  const r = await generateCliFromUrl({
174
175
  url,
175
176
  BrowserFactory: getBrowserFactory(),
176
- builtinClis: BUILTIN_CLIS,
177
- userClis: USER_CLIS,
178
177
  goal: opts.goal,
179
178
  site: opts.site,
180
179
  workspace,
@@ -262,8 +261,8 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
262
261
  const name = installPlugin(source);
263
262
  await discoverPlugins();
264
263
  console.log(chalk.green(`✅ Plugin "${name}" installed successfully. Commands are ready to use.`));
265
- } catch (err: any) {
266
- console.error(chalk.red(`Error: ${err.message}`));
264
+ } catch (err) {
265
+ console.error(chalk.red(`Error: ${getErrorMessage(err)}`));
267
266
  process.exitCode = 1;
268
267
  }
269
268
  });
@@ -277,25 +276,69 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
277
276
  try {
278
277
  uninstallPlugin(name);
279
278
  console.log(chalk.green(`✅ Plugin "${name}" uninstalled.`));
280
- } catch (err: any) {
281
- console.error(chalk.red(`Error: ${err.message}`));
279
+ } catch (err) {
280
+ console.error(chalk.red(`Error: ${getErrorMessage(err)}`));
282
281
  process.exitCode = 1;
283
282
  }
284
283
  });
285
284
 
286
285
  pluginCmd
287
286
  .command('update')
288
- .description('Update a plugin to the latest version')
289
- .argument('<name>', 'Plugin name')
290
- .action(async (name: string) => {
291
- const { updatePlugin } = await import('./plugin.js');
287
+ .description('Update a plugin (or all plugins) to the latest version')
288
+ .argument('[name]', 'Plugin name (required unless --all is passed)')
289
+ .option('--all', 'Update all installed plugins')
290
+ .action(async (name: string | undefined, opts: { all?: boolean }) => {
291
+ if (!name && !opts.all) {
292
+ console.error(chalk.red('Error: Please specify a plugin name or use the --all flag.'));
293
+ process.exitCode = 1;
294
+ return;
295
+ }
296
+ if (name && opts.all) {
297
+ console.error(chalk.red('Error: Cannot specify both a plugin name and --all.'));
298
+ process.exitCode = 1;
299
+ return;
300
+ }
301
+
302
+ const { updatePlugin, updateAllPlugins } = await import('./plugin.js');
292
303
  const { discoverPlugins } = await import('./discovery.js');
304
+ if (opts.all) {
305
+ const results = updateAllPlugins();
306
+ if (results.length > 0) {
307
+ await discoverPlugins();
308
+ }
309
+
310
+ let hasErrors = false;
311
+ console.log(chalk.bold(' Update Results:'));
312
+ for (const result of results) {
313
+ if (result.success) {
314
+ console.log(` ${chalk.green('✓')} ${result.name}`);
315
+ continue;
316
+ }
317
+ hasErrors = true;
318
+ console.log(` ${chalk.red('✗')} ${result.name} — ${chalk.dim(result.error)}`);
319
+ }
320
+
321
+ if (results.length === 0) {
322
+ console.log(chalk.dim(' No plugins installed.'));
323
+ return;
324
+ }
325
+
326
+ console.log();
327
+ if (hasErrors) {
328
+ console.error(chalk.red('Completed with some errors.'));
329
+ process.exitCode = 1;
330
+ } else {
331
+ console.log(chalk.green('✅ All plugins updated successfully.'));
332
+ }
333
+ return;
334
+ }
335
+
293
336
  try {
294
- updatePlugin(name);
337
+ updatePlugin(name!);
295
338
  await discoverPlugins();
296
339
  console.log(chalk.green(`✅ Plugin "${name}" updated successfully.`));
297
- } catch (err: any) {
298
- console.error(chalk.red(`Error: ${err.message}`));
340
+ } catch (err) {
341
+ console.error(chalk.red(`Error: ${getErrorMessage(err)}`));
299
342
  process.exitCode = 1;
300
343
  }
301
344
  });
@@ -326,9 +369,10 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
326
369
  console.log(chalk.bold(' Installed plugins'));
327
370
  console.log();
328
371
  for (const p of plugins) {
372
+ const version = p.version ? chalk.green(` @${p.version}`) : '';
329
373
  const cmds = p.commands.length > 0 ? chalk.dim(` (${p.commands.join(', ')})`) : '';
330
374
  const src = p.source ? chalk.dim(` ← ${p.source}`) : '';
331
- console.log(` ${chalk.cyan(p.name)}${cmds}${src}`);
375
+ console.log(` ${chalk.cyan(p.name)}${version}${cmds}${src}`);
332
376
  }
333
377
  console.log();
334
378
  console.log(chalk.dim(` ${plugins.length} plugin(s) installed`));
@@ -371,8 +415,8 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
371
415
  })();
372
416
  try {
373
417
  executeExternalCli(name, args, externalClis);
374
- } catch (err: any) {
375
- console.error(chalk.red(`Error: ${err.message}`));
418
+ } catch (err) {
419
+ console.error(chalk.red(`Error: ${getErrorMessage(err)}`));
376
420
  process.exitCode = 1;
377
421
  }
378
422
  }
@@ -408,29 +452,17 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
408
452
  registerAllCommands(program, siteGroups);
409
453
 
410
454
  // ── Unknown command fallback ──────────────────────────────────────────────
411
-
412
- const DENY_LIST = new Set([
413
- 'rm', 'sudo', 'dd', 'mkfs', 'fdisk', 'shutdown', 'reboot',
414
- 'kill', 'killall', 'chmod', 'chown', 'passwd', 'su', 'mount',
415
- 'umount', 'format', 'diskutil',
416
- ]);
455
+ // Security: do NOT auto-discover and register arbitrary system binaries.
456
+ // Only explicitly registered external CLIs (via `opencli register`) are allowed.
417
457
 
418
458
  program.on('command:*', (operands: string[]) => {
419
459
  const binary = operands[0];
420
- if (DENY_LIST.has(binary)) {
421
- console.error(chalk.red(`Refusing to register system command '${binary}'.`));
422
- process.exitCode = 1;
423
- return;
424
- }
460
+ console.error(chalk.red(`error: unknown command '${binary}'`));
425
461
  if (isBinaryInstalled(binary)) {
426
- console.log(chalk.cyan(`🔹 Auto-discovered local CLI '${binary}'. Registering...`));
427
- registerExternalCli(binary);
428
- passthroughExternal(binary);
429
- } else {
430
- console.error(chalk.red(`error: unknown command '${binary}'`));
431
- program.outputHelp();
432
- process.exitCode = 1;
462
+ console.error(chalk.dim(` Tip: '${binary}' exists on your PATH. Use 'opencli register ${binary}' to add it as an external CLI.`));
433
463
  }
464
+ program.outputHelp();
465
+ process.exitCode = 1;
434
466
  });
435
467
 
436
468
  program.parse();
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Shared command factories for Electron/desktop app adapters.
3
+ * Eliminates duplicate screenshot/status/new/dump implementations
4
+ * across cursor, codex, chatwise, etc.
5
+ */
6
+
7
+ import * as fs from 'node:fs';
8
+ import { cli, Strategy } from '../../registry.js';
9
+ import type { IPage } from '../../types.js';
10
+ import type { CliOptions } from '../../registry.js';
11
+
12
+ /**
13
+ * Factory: capture DOM HTML + accessibility snapshot.
14
+ */
15
+ export function makeScreenshotCommand(site: string, displayName?: string, extra: Partial<CliOptions> = {}) {
16
+ const label = displayName ?? site;
17
+ return cli({
18
+ ...extra,
19
+ site,
20
+ name: 'screenshot',
21
+ description: `Capture a snapshot of the current ${label} window (DOM + Accessibility tree)`,
22
+ domain: 'localhost',
23
+ strategy: Strategy.UI,
24
+ browser: true,
25
+ args: [
26
+ { name: 'output', required: false, help: `Output file path (default: /tmp/${site}-snapshot.txt)` },
27
+ ],
28
+ columns: ['Status', 'File'],
29
+ func: async (page: IPage, kwargs: any) => {
30
+ const outputPath = (kwargs.output as string) || `/tmp/${site}-snapshot.txt`;
31
+
32
+ const snap = await page.snapshot({ compact: true });
33
+ const html = await page.evaluate('document.documentElement.outerHTML');
34
+
35
+ const htmlPath = outputPath.replace(/\.\w+$/, '') + '-dom.html';
36
+ const snapPath = outputPath.replace(/\.\w+$/, '') + '-a11y.txt';
37
+
38
+ fs.writeFileSync(htmlPath, html);
39
+ fs.writeFileSync(snapPath, typeof snap === 'string' ? snap : JSON.stringify(snap, null, 2));
40
+
41
+ return [
42
+ { Status: 'Success', File: htmlPath },
43
+ { Status: 'Success', File: snapPath },
44
+ ];
45
+ },
46
+ });
47
+ }
48
+
49
+ /**
50
+ * Factory: check CDP connection status.
51
+ */
52
+ export function makeStatusCommand(site: string, displayName?: string, extra: Partial<CliOptions> = {}) {
53
+ const label = displayName ?? site;
54
+ return cli({
55
+ ...extra,
56
+ site,
57
+ name: 'status',
58
+ description: `Check active CDP connection to ${label}`,
59
+ domain: 'localhost',
60
+ strategy: Strategy.UI,
61
+ browser: true,
62
+ columns: ['Status', 'Url', 'Title'],
63
+ func: async (page: IPage) => {
64
+ const url = await page.evaluate('window.location.href');
65
+ const title = await page.evaluate('document.title');
66
+ return [{ Status: 'Connected', Url: url, Title: title }];
67
+ },
68
+ });
69
+ }
70
+
71
+ /**
72
+ * Factory: start a new session via Cmd/Ctrl+N.
73
+ */
74
+ export function makeNewCommand(site: string, displayName?: string, extra: Partial<CliOptions> = {}) {
75
+ const label = displayName ?? site;
76
+ return cli({
77
+ ...extra,
78
+ site,
79
+ name: 'new',
80
+ description: `Start a new ${label} session`,
81
+ domain: 'localhost',
82
+ strategy: Strategy.UI,
83
+ browser: true,
84
+ columns: ['Status'],
85
+ func: async (page: IPage) => {
86
+ const isMac = process.platform === 'darwin';
87
+ await page.pressKey(isMac ? 'Meta+N' : 'Control+N');
88
+ await page.wait(1);
89
+ return [{ Status: 'Success' }];
90
+ },
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Factory: dump DOM + snapshot for reverse-engineering.
96
+ */
97
+ export function makeDumpCommand(site: string) {
98
+ return cli({
99
+ site,
100
+ name: 'dump',
101
+ description: `Dump the DOM and Accessibility tree of ${site} for reverse-engineering`,
102
+ domain: 'localhost',
103
+ strategy: Strategy.UI,
104
+ browser: true,
105
+ columns: ['action', 'files'],
106
+ func: async (page: IPage) => {
107
+ const dom = await page.evaluate('document.body.innerHTML');
108
+ fs.writeFileSync(`/tmp/${site}-dom.html`, dom);
109
+
110
+ const snap = await page.snapshot({ interactive: false });
111
+ fs.writeFileSync(`/tmp/${site}-snapshot.json`, JSON.stringify(snap, null, 2));
112
+
113
+ return [
114
+ {
115
+ action: 'Dom extraction finished',
116
+ files: `/tmp/${site}-dom.html, /tmp/${site}-snapshot.json`,
117
+ },
118
+ ];
119
+ },
120
+ });
121
+ }
@@ -13,6 +13,7 @@
13
13
  import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
14
14
  import { CDPBridge } from '../../browser/cdp.js';
15
15
  import type { IPage } from '../../types.js';
16
+ import { getErrorMessage } from '../../errors.js';
16
17
 
17
18
  // ─── Types ───────────────────────────────────────────────────────────
18
19
 
@@ -461,15 +462,17 @@ export async function startServe(opts: { port?: number } = {}): Promise<void> {
461
462
  cdp = new CDPBridge();
462
463
  try {
463
464
  page = await cdp.connect({ timeout: 15_000 });
464
- } catch (err: any) {
465
+ } catch (err: unknown) {
465
466
  cdp = null;
466
- const isRefused = err?.cause?.code === 'ECONNREFUSED' || err?.message?.includes('ECONNREFUSED');
467
+ const errMsg = getErrorMessage(err);
468
+ const cause = err instanceof Error ? (err.cause as Record<string, unknown> | undefined) : undefined;
469
+ const isRefused = cause?.code === 'ECONNREFUSED' || errMsg.includes('ECONNREFUSED');
467
470
  throw new Error(
468
471
  isRefused
469
472
  ? `Cannot connect to Antigravity at ${endpoint}.\n` +
470
473
  ' 1. Make sure Antigravity is running\n' +
471
474
  ' 2. Launch with: --remote-debugging-port=9224'
472
- : `CDP connection failed: ${err.message}`
475
+ : `CDP connection failed: ${errMsg}`
473
476
  );
474
477
  }
475
478
 
@@ -15,7 +15,7 @@ cli({
15
15
  columns: ['id', 'title', 'authors', 'published'],
16
16
  func: async (_page, args) => {
17
17
  const limit = Math.max(1, Math.min(Number(args.limit), 25));
18
- const query = encodeURIComponent(`all:${args.keyword}`);
18
+ const query = encodeURIComponent(`all:${args.query}`);
19
19
  const xml = await arxivFetch(`search_query=${query}&max_results=${limit}&sortBy=relevance`);
20
20
  const entries = parseEntries(xml);
21
21
  if (!entries.length) throw new CliError('NOT_FOUND', 'No papers found', 'Try a different keyword');
@@ -0,0 +1,79 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+
3
+ const { mockApiGet } = vi.hoisted(() => ({
4
+ mockApiGet: vi.fn(),
5
+ }));
6
+
7
+ vi.mock('./utils.js', () => ({
8
+ apiGet: mockApiGet,
9
+ }));
10
+
11
+ import { getRegistry } from '../../registry.js';
12
+ import './dynamic.js';
13
+
14
+ describe('bilibili dynamic adapter', () => {
15
+ const command = getRegistry().get('bilibili/dynamic');
16
+
17
+ beforeEach(() => {
18
+ mockApiGet.mockReset();
19
+ });
20
+
21
+ it('maps desc text rows from the dynamic feed payload', async () => {
22
+ mockApiGet.mockResolvedValue({
23
+ data: {
24
+ items: [
25
+ {
26
+ id_str: '123',
27
+ modules: {
28
+ module_author: { name: 'Alice' },
29
+ module_dynamic: { desc: { text: 'hello world' } },
30
+ module_stat: { like: { count: 9 } },
31
+ },
32
+ },
33
+ ],
34
+ },
35
+ });
36
+
37
+ const result = await command!.func!({} as any, { limit: 5 });
38
+
39
+ expect(mockApiGet).toHaveBeenCalledWith({}, '/x/polymer/web-dynamic/v1/feed/all', { params: {}, signed: false });
40
+ expect(result).toEqual([
41
+ {
42
+ id: '123',
43
+ author: 'Alice',
44
+ text: 'hello world',
45
+ likes: 9,
46
+ url: 'https://t.bilibili.com/123',
47
+ },
48
+ ]);
49
+ });
50
+
51
+ it('falls back to archive title when desc text is absent', async () => {
52
+ mockApiGet.mockResolvedValue({
53
+ data: {
54
+ items: [
55
+ {
56
+ id_str: '456',
57
+ modules: {
58
+ module_author: { name: 'Bob' },
59
+ module_dynamic: { major: { archive: { title: 'Video title' } } },
60
+ module_stat: { like: { count: 3 } },
61
+ },
62
+ },
63
+ ],
64
+ },
65
+ });
66
+
67
+ const result = await command!.func!({} as any, { limit: 5 });
68
+
69
+ expect(result).toEqual([
70
+ {
71
+ id: '456',
72
+ author: 'Bob',
73
+ text: 'Video title',
74
+ likes: 3,
75
+ url: 'https://t.bilibili.com/456',
76
+ },
77
+ ]);
78
+ });
79
+ });
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { apiGet, payloadData } from './utils.js';
2
+ import { apiGet, payloadData, getSelfUid } from './utils.js';
3
3
 
4
4
  cli({
5
5
  site: 'bilibili',
@@ -15,9 +15,12 @@ cli({
15
15
  func: async (page, kwargs) => {
16
16
  const { limit = 20, page: pageNum = 1 } = kwargs;
17
17
 
18
+ // Get current user's UID
19
+ const uid = await getSelfUid(page);
20
+
18
21
  // Get default favorite folder ID
19
22
  const foldersPayload = await apiGet(page, '/x/v3/fav/folder/created/list-all', {
20
- params: { up_mid: 0 },
23
+ params: { up_mid: uid },
21
24
  signed: true,
22
25
  });
23
26
  const folders = payloadData(foldersPayload)?.list ?? [];
@@ -1,4 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
+ import { CommandExecutionError } from '../../errors.js';
2
3
  import type { IPage } from '../../types.js';
3
4
  import { fetchJson, getSelfUid, resolveUid } from './utils.js';
4
5
 
@@ -14,7 +15,7 @@ cli({
14
15
  ],
15
16
  columns: ['mid', 'name', 'sign', 'following', 'fans'],
16
17
  func: async (page: IPage | null, kwargs: any) => {
17
- if (!page) throw new Error('Requires browser');
18
+ if (!page) throw new CommandExecutionError('Browser session required for bilibili following');
18
19
 
19
20
  // 1. Resolve UID (default to self)
20
21
  const uid = kwargs.uid
@@ -30,7 +31,7 @@ cli({
30
31
  );
31
32
 
32
33
  if (payload.code !== 0) {
33
- throw new Error(`获取关注列表失败: ${payload.message} (${payload.code})`);
34
+ throw new CommandExecutionError(`获取关注列表失败: ${payload.message} (${payload.code})`);
34
35
  }
35
36
 
36
37
  const list = payload.data?.list || [];
@@ -1,4 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
+ import { AuthRequiredError, CommandExecutionError, EmptyResultError, SelectorError } from '../../errors.js';
2
3
  import type { IPage } from '../../types.js';
3
4
  import { apiGet } from './utils.js';
4
5
 
@@ -13,7 +14,7 @@ cli({
13
14
  ],
14
15
  columns: ['index', 'from', 'to', 'content'],
15
16
  func: async (page: IPage | null, kwargs: any) => {
16
- if (!page) throw new Error('Requires browser');
17
+ if (!page) throw new CommandExecutionError('Browser session required for bilibili subtitle');
17
18
  // 1. 先前往视频详情页 (建立有鉴权的 Session,且这里不需要加载完整个视频)
18
19
  await page.goto(`https://www.bilibili.com/video/${kwargs.bvid}/`);
19
20
 
@@ -24,7 +25,7 @@ cli({
24
25
  })()`);
25
26
 
26
27
  if (!cid) {
27
- throw new Error('无法在页面中提取到当前视频的 CID,请检查页面是否正常加载。');
28
+ throw new SelectorError('videoData.cid', '无法在页面中提取到当前视频的 CID,请检查页面是否正常加载。');
28
29
  }
29
30
 
30
31
  // 3. 在 Node 端使用 apiGet 获取带 Wbi 签名的字幕列表
@@ -35,12 +36,12 @@ cli({
35
36
  });
36
37
 
37
38
  if (payload.code !== 0) {
38
- throw new Error(`获取视频播放信息失败: ${payload.message} (${payload.code})`);
39
+ throw new CommandExecutionError(`获取视频播放信息失败: ${payload.message} (${payload.code})`);
39
40
  }
40
41
 
41
42
  const subtitles = payload.data?.subtitle?.subtitles || [];
42
43
  if (subtitles.length === 0) {
43
- throw new Error('此视频没有发现外挂或智能字幕。');
44
+ throw new EmptyResultError('bilibili subtitle', '此视频没有发现外挂或智能字幕。');
44
45
  }
45
46
 
46
47
  // 4. 选择目标字幕语言
@@ -50,7 +51,7 @@ cli({
50
51
 
51
52
  const targetSubUrl = target.subtitle_url;
52
53
  if (!targetSubUrl || targetSubUrl === '') {
53
- throw new Error('[风控拦截/未登录] 获取到的 subtitle_url 为空!请确保 CLI 已成功登录且风控未封锁此账号。');
54
+ throw new AuthRequiredError('bilibili.com', '[风控拦截/未登录] 获取到的 subtitle_url 为空!请确保 CLI 已成功登录且风控未封锁此账号。');
54
55
  }
55
56
 
56
57
  const finalUrl = targetSubUrl.startsWith('//') ? 'https:' + targetSubUrl : targetSubUrl;
@@ -81,12 +82,12 @@ cli({
81
82
  const items = await page.evaluate(fetchJs);
82
83
 
83
84
  if (items?.error) {
84
- throw new Error(`字幕获取失败: ${items.error}${items.text ? ' — ' + items.text : ''}`);
85
+ throw new CommandExecutionError(`字幕获取失败: ${items.error}${items.text ? ' — ' + items.text : ''}`);
85
86
  }
86
87
 
87
88
  const finalItems = items?.data || [];
88
89
  if (!Array.isArray(finalItems)) {
89
- throw new Error('解析到的字幕列表对象不符合数组格式');
90
+ throw new CommandExecutionError('解析到的字幕列表对象不符合数组格式');
90
91
  }
91
92
 
92
93
  // 6. 数据映射
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import type { IPage } from '../../types.js';
6
- import { AuthRequiredError } from '../../errors.js';
6
+ import { AuthRequiredError, EmptyResultError } from '../../errors.js';
7
7
 
8
8
  const MIXIN_KEY_ENC_TAB = [
9
9
  46,47,18,2,53,8,23,32,15,50,10,31,58,3,45,35,27,43,5,49,
@@ -112,5 +112,5 @@ export async function resolveUid(page: IPage, input: string): Promise<string> {
112
112
  });
113
113
  const results = payload?.data?.result ?? [];
114
114
  if (results.length > 0) return String(results[0].mid);
115
- throw new Error(`Cannot resolve UID for: ${input}`);
115
+ throw new EmptyResultError(`bilibili user search: ${input}`, 'User may not exist or username may have changed.');
116
116
  }
@@ -5,7 +5,7 @@ import { cli, Strategy } from '../../registry.js';
5
5
  import {
6
6
  requirePage, navigateToChat, fetchRecommendList,
7
7
  clickCandidateInList, typeAndSendMessage, verbose,
8
- } from './common.js';
8
+ } from './utils.js';
9
9
 
10
10
  cli({
11
11
  site: 'boss',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { requirePage, navigateToChat, fetchFriendList } from './common.js';
2
+ import { requirePage, navigateToChat, fetchFriendList } from './utils.js';
3
3
 
4
4
  cli({
5
5
  site: 'boss',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { requirePage, navigateToChat, bossFetch, findFriendByUid } from './common.js';
2
+ import { requirePage, navigateToChat, bossFetch, findFriendByUid } from './utils.js';
3
3
 
4
4
  cli({
5
5
  site: 'boss',
@@ -2,7 +2,7 @@
2
2
  * BOSS直聘 job detail — fetch full job posting details via browser cookie API.
3
3
  */
4
4
  import { cli, Strategy } from '../../registry.js';
5
- import { requirePage, navigateTo, bossFetch, verbose } from './common.js';
5
+ import { requirePage, navigateTo, bossFetch, verbose } from './utils.js';
6
6
 
7
7
  cli({
8
8
  site: 'boss',
@@ -2,7 +2,7 @@
2
2
  * BOSS直聘 exchange — request phone/wechat exchange with a candidate.
3
3
  */
4
4
  import { cli, Strategy } from '../../registry.js';
5
- import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './common.js';
5
+ import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './utils.js';
6
6
 
7
7
  cli({
8
8
  site: 'boss',
@@ -5,7 +5,7 @@ import { cli, Strategy } from '../../registry.js';
5
5
  import {
6
6
  requirePage, navigateToChat, findFriendByUid,
7
7
  clickCandidateInList, typeAndSendMessage, verbose,
8
- } from './common.js';
8
+ } from './utils.js';
9
9
 
10
10
  cli({
11
11
  site: 'boss',
@@ -2,7 +2,7 @@
2
2
  * BOSS直聘 invite — send interview invitation to a candidate.
3
3
  */
4
4
  import { cli, Strategy } from '../../registry.js';
5
- import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './common.js';
5
+ import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './utils.js';
6
6
 
7
7
  cli({
8
8
  site: 'boss',
@@ -2,7 +2,7 @@
2
2
  * BOSS直聘 job list — list my published jobs via boss API.
3
3
  */
4
4
  import { cli, Strategy } from '../../registry.js';
5
- import { requirePage, navigateToChat, bossFetch, verbose } from './common.js';
5
+ import { requirePage, navigateToChat, bossFetch, verbose } from './utils.js';
6
6
 
7
7
  cli({
8
8
  site: 'boss',
@@ -6,7 +6,8 @@
6
6
  * 6=已交换微信, 7=不合适, 8=牛人发起, 11=收藏
7
7
  */
8
8
  import { cli, Strategy } from '../../registry.js';
9
- import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './common.js';
9
+ import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './utils.js';
10
+ import { ArgumentError, EmptyResultError } from '../../errors.js';
10
11
 
11
12
  const LABEL_MAP: Record<string, number> = {
12
13
  '新招呼': 1, '沟通中': 2, '已约面': 3, '已获取简历': 4,
@@ -44,7 +45,7 @@ cli({
44
45
  if (entry) {
45
46
  labelId = entry[1];
46
47
  } else {
47
- throw new Error(`未知标签: ${labelInput}。可用标签: ${Object.keys(LABEL_MAP).join(', ')}`);
48
+ throw new ArgumentError(`未知标签: ${labelInput}。可用标签: ${Object.keys(LABEL_MAP).join(', ')}`);
48
49
  }
49
50
  }
50
51
 
@@ -53,7 +54,7 @@ cli({
53
54
  await navigateToChat(page);
54
55
 
55
56
  const friend = await findFriendByUid(page, kwargs.uid, { checkGreetList: true });
56
- if (!friend) throw new Error('未找到该候选人');
57
+ if (!friend) throw new EmptyResultError('boss candidate search');
57
58
 
58
59
  const friendName = friend.name || '候选人';
59
60
  const action = remove ? 'deleteMark' : 'addMark';
@@ -2,7 +2,7 @@
2
2
  * BOSS直聘 recommend — view recommended candidates (新招呼/greet sort list).
3
3
  */
4
4
  import { cli, Strategy } from '../../registry.js';
5
- import { requirePage, navigateToChat, bossFetch, fetchRecommendList, verbose } from './common.js';
5
+ import { requirePage, navigateToChat, bossFetch, fetchRecommendList, verbose } from './utils.js';
6
6
 
7
7
  cli({
8
8
  site: 'boss',
@@ -10,7 +10,7 @@
10
10
  * .position-content → job being discussed + expectation
11
11
  */
12
12
  import { cli, Strategy } from '../../registry.js';
13
- import { requirePage, navigateToChat, findFriendByUid, clickCandidateInList } from './common.js';
13
+ import { requirePage, navigateToChat, findFriendByUid, clickCandidateInList } from './utils.js';
14
14
 
15
15
  cli({
16
16
  site: 'boss',
@@ -2,7 +2,7 @@
2
2
  * BOSS直聘 job search — browser cookie API.
3
3
  */
4
4
  import { cli, Strategy } from '../../registry.js';
5
- import { requirePage, navigateTo, bossFetch, assertOk, verbose } from './common.js';
5
+ import { requirePage, navigateTo, bossFetch, assertOk, verbose } from './utils.js';
6
6
 
7
7
  /** City name → BOSS Zhipin city code mapping */
8
8
  const CITY_CODES: Record<string, string> = {