@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
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Pixiv shared helpers: authenticated Ajax fetch with standard error handling.
3
+ *
4
+ * All Pixiv Ajax APIs return `{ error: false, body: ... }` on success.
5
+ * On failure the HTTP status code is used to distinguish auth (401/403),
6
+ * not-found (404), and other errors.
7
+ */
8
+
9
+ import type { IPage } from '../../types.js';
10
+ import { AuthRequiredError, CommandExecutionError } from '../../errors.js';
11
+
12
+ const PIXIV_DOMAIN = 'www.pixiv.net';
13
+
14
+ /**
15
+ * Navigate to Pixiv (to attach cookies) then fetch a Pixiv Ajax API endpoint.
16
+ *
17
+ * Handles the common navigate → evaluate(fetch) → error-check pattern used
18
+ * by every Pixiv TS adapter.
19
+ *
20
+ * @param page - Browser page instance
21
+ * @param path - API path, e.g. '/ajax/illust/12345'
22
+ * @param opts - Optional query params
23
+ * @returns - The parsed `body` from the JSON response
24
+ * @throws AuthRequiredError on 401/403
25
+ * @throws CommandExecutionError on 404 or other HTTP errors
26
+ */
27
+ export async function pixivFetch(
28
+ page: IPage,
29
+ path: string,
30
+ opts: { params?: Record<string, string | number>; notFoundMsg?: string } = {},
31
+ ): Promise<any> {
32
+ await page.goto(`https://${PIXIV_DOMAIN}`);
33
+
34
+ const qs = opts.params
35
+ ? '?' + Object.entries(opts.params).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&')
36
+ : '';
37
+ const url = `https://${PIXIV_DOMAIN}${path}${qs}`;
38
+
39
+ const data: any = await page.evaluate(`
40
+ (async () => {
41
+ const res = await fetch(${JSON.stringify(url)}, { credentials: 'include' });
42
+ if (!res.ok) return { __httpError: res.status };
43
+ return await res.json();
44
+ })()
45
+ `);
46
+
47
+ if (data?.__httpError) {
48
+ const status = data.__httpError;
49
+ if (status === 401 || status === 403) {
50
+ throw new AuthRequiredError(PIXIV_DOMAIN, 'Authentication required — please log in to Pixiv in Chrome');
51
+ }
52
+ if (status === 404) {
53
+ throw new CommandExecutionError(opts.notFoundMsg || `Pixiv resource not found (HTTP 404)`);
54
+ }
55
+ throw new CommandExecutionError(`Pixiv request failed (HTTP ${status})`);
56
+ }
57
+
58
+ return data?.body;
59
+ }
60
+
61
+ /** Maximum number of illust IDs per batch detail request (Pixiv server limit). */
62
+ export const BATCH_SIZE = 48;
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
 
3
4
  cli({
@@ -13,7 +14,7 @@ cli({
13
14
  ],
14
15
  columns: ['status', 'message'],
15
16
  func: async (page, kwargs) => {
16
- if (!page) throw new Error('Requires browser');
17
+ if (!page) throw new CommandExecutionError('Browser session required');
17
18
 
18
19
  await page.goto('https://www.reddit.com');
19
20
 
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { getRegistry } from '../../registry.js';
3
+ import './read.js';
4
+
5
+ describe('reddit read adapter', () => {
6
+ const command = getRegistry().get('reddit/read');
7
+
8
+ it('returns threaded rows from the browser-evaluated payload', async () => {
9
+ const page = {
10
+ goto: vi.fn().mockResolvedValue(undefined),
11
+ evaluate: vi.fn().mockResolvedValue([
12
+ { type: 'POST', author: 'alice', score: 10, text: 'Title' },
13
+ { type: 'L0', author: 'bob', score: 5, text: 'Comment' },
14
+ ]),
15
+ } as any;
16
+
17
+ const result = await command!.func!(page, { 'post-id': 'abc123', limit: 5 });
18
+
19
+ expect(page.goto).toHaveBeenCalledWith('https://www.reddit.com');
20
+ expect(result).toEqual([
21
+ { type: 'POST', author: 'alice', score: 10, text: 'Title' },
22
+ { type: 'L0', author: 'bob', score: 5, text: 'Comment' },
23
+ ]);
24
+ });
25
+
26
+ it('surfaces adapter-level API errors clearly', async () => {
27
+ const page = {
28
+ goto: vi.fn().mockResolvedValue(undefined),
29
+ evaluate: vi.fn().mockResolvedValue({ error: 'Reddit API returned HTTP 403' }),
30
+ } as any;
31
+
32
+ await expect(command!.func!(page, { 'post-id': 'abc123' })).rejects.toThrow('Reddit API returned HTTP 403');
33
+ });
34
+ });
@@ -7,6 +7,7 @@
7
7
  * - Indented output showing conversation threads
8
8
  */
9
9
  import { cli, Strategy } from '../../registry.js';
10
+ import { CommandExecutionError } from '../../errors.js';
10
11
 
11
12
  cli({
12
13
  site: 'reddit',
@@ -176,9 +177,9 @@ cli({
176
177
  })()
177
178
  `);
178
179
 
179
- if (!data || typeof data !== 'object') throw new Error('Failed to fetch post data');
180
- if (!Array.isArray(data) && data.error) throw new Error(data.error);
181
- if (!Array.isArray(data)) throw new Error('Unexpected response');
180
+ if (!data || typeof data !== 'object') throw new CommandExecutionError('Failed to fetch post data');
181
+ if (!Array.isArray(data) && data.error) throw new CommandExecutionError(data.error);
182
+ if (!Array.isArray(data)) throw new CommandExecutionError('Unexpected response');
182
183
 
183
184
  return data;
184
185
  },
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
 
3
4
  cli({
@@ -13,7 +14,7 @@ cli({
13
14
  ],
14
15
  columns: ['status', 'message'],
15
16
  func: async (page, kwargs) => {
16
- if (!page) throw new Error('Requires browser');
17
+ if (!page) throw new CommandExecutionError('Browser session required');
17
18
 
18
19
  await page.goto('https://www.reddit.com');
19
20
 
@@ -1,3 +1,4 @@
1
+ import { AuthRequiredError, CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
 
3
4
  cli({
@@ -12,7 +13,7 @@ cli({
12
13
  ],
13
14
  columns: ['title', 'subreddit', 'score', 'comments', 'url'],
14
15
  func: async (page, kwargs) => {
15
- if (!page) throw new Error('Requires browser');
16
+ if (!page) throw new CommandExecutionError('Browser session required');
16
17
 
17
18
  await page.goto('https://www.reddit.com');
18
19
 
@@ -41,7 +42,10 @@ cli({
41
42
  }
42
43
  })()`);
43
44
 
44
- if (result?.error) throw new Error(result.error);
45
+ if (result?.error) {
46
+ if (String(result.error).includes('Not logged in')) throw new AuthRequiredError('reddit.com', result.error);
47
+ throw new CommandExecutionError(result.error);
48
+ }
45
49
  return (result || []).slice(0, kwargs.limit);
46
50
  }
47
51
  });
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
 
3
4
  cli({
@@ -13,7 +14,7 @@ cli({
13
14
  ],
14
15
  columns: ['status', 'message'],
15
16
  func: async (page, kwargs) => {
16
- if (!page) throw new Error('Requires browser');
17
+ if (!page) throw new CommandExecutionError('Browser session required');
17
18
 
18
19
  await page.goto('https://www.reddit.com');
19
20
 
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
 
3
4
  cli({
@@ -13,7 +14,7 @@ cli({
13
14
  ],
14
15
  columns: ['status', 'message'],
15
16
  func: async (page, kwargs) => {
16
- if (!page) throw new Error('Requires browser');
17
+ if (!page) throw new CommandExecutionError('Browser session required');
17
18
 
18
19
  await page.goto('https://www.reddit.com');
19
20
 
@@ -1,3 +1,4 @@
1
+ import { AuthRequiredError, CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
 
3
4
  cli({
@@ -12,7 +13,7 @@ cli({
12
13
  ],
13
14
  columns: ['title', 'subreddit', 'score', 'comments', 'url'],
14
15
  func: async (page, kwargs) => {
15
- if (!page) throw new Error('Requires browser');
16
+ if (!page) throw new CommandExecutionError('Browser session required');
16
17
 
17
18
  await page.goto('https://www.reddit.com');
18
19
 
@@ -41,7 +42,10 @@ cli({
41
42
  }
42
43
  })()`);
43
44
 
44
- if (result?.error) throw new Error(result.error);
45
+ if (result?.error) {
46
+ if (String(result.error).includes('Not logged in')) throw new AuthRequiredError('reddit.com', result.error);
47
+ throw new CommandExecutionError(result.error);
48
+ }
45
49
  return (result || []).slice(0, kwargs.limit);
46
50
  }
47
51
  });
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { loadSinaBlogArticle } from './shared.js';
2
+ import { loadSinaBlogArticle } from './utils.js';
3
3
 
4
4
  cli({
5
5
  site: 'sinablog',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { loadSinaBlogHot } from './shared.js';
2
+ import { loadSinaBlogHot } from './utils.js';
3
3
 
4
4
  cli({
5
5
  site: 'sinablog',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { loadSinaBlogUser } from './shared.js';
2
+ import { loadSinaBlogUser } from './utils.js';
3
3
 
4
4
  cli({
5
5
  site: 'sinablog',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { buildSubstackBrowseUrl, loadSubstackFeed } from './shared.js';
2
+ import { buildSubstackBrowseUrl, loadSubstackFeed } from './utils.js';
3
3
 
4
4
  cli({
5
5
  site: 'substack',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { loadSubstackArchive } from './shared.js';
2
+ import { loadSubstackArchive } from './utils.js';
3
3
 
4
4
  cli({
5
5
  site: 'substack',
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
 
3
4
  type SubstackPostResult = {
@@ -32,7 +33,7 @@ async function searchPosts(keyword: string, limit: number): Promise<SubstackPost
32
33
  url.searchParams.set('includePlatformResults', 'true');
33
34
 
34
35
  const resp = await fetch(url, { headers: headers() });
35
- if (!resp.ok) throw new Error(`Substack post search failed: HTTP ${resp.status}`);
36
+ if (!resp.ok) throw new CommandExecutionError(`Substack post search failed: HTTP ${resp.status}`);
36
37
 
37
38
  const data = await resp.json() as { results?: any[] };
38
39
  const results = Array.isArray(data?.results) ? data.results : [];
@@ -52,7 +53,7 @@ async function searchPublications(keyword: string, limit: number): Promise<Subst
52
53
  url.searchParams.set('page', '0');
53
54
 
54
55
  const resp = await fetch(url, { headers: headers() });
55
- if (!resp.ok) throw new Error(`Substack publication search failed: HTTP ${resp.status}`);
56
+ if (!resp.ok) throw new CommandExecutionError(`Substack publication search failed: HTTP ${resp.status}`);
56
57
 
57
58
  const data = await resp.json() as { results?: any[] };
58
59
  const results = Array.isArray(data?.results) ? data.results : [];
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import type { IPage } from '../../types.js';
2
3
 
3
4
  export function buildSubstackBrowseUrl(category?: string): string {
@@ -7,7 +8,7 @@ export function buildSubstackBrowseUrl(category?: string): string {
7
8
  }
8
9
 
9
10
  export async function loadSubstackFeed(page: IPage, url: string, limit: number): Promise<any[]> {
10
- if (!page) throw new Error('Requires browser session');
11
+ if (!page) throw new CommandExecutionError('Browser session required for substack feed');
11
12
  await page.goto(url);
12
13
  await page.wait(5);
13
14
  const data = await page.evaluate(`
@@ -76,7 +77,7 @@ export async function loadSubstackFeed(page: IPage, url: string, limit: number):
76
77
  }
77
78
 
78
79
  export async function loadSubstackArchive(page: IPage, baseUrl: string, limit: number): Promise<any[]> {
79
- if (!page) throw new Error('Requires browser session');
80
+ if (!page) throw new CommandExecutionError('Browser session required for substack archive');
80
81
  await page.goto(`${baseUrl}/archive`);
81
82
  await page.wait(5);
82
83
  const data = await page.evaluate(`
@@ -35,6 +35,7 @@ pipeline:
35
35
  rank: idx + 1,
36
36
  desc: (v.desc || '').replace(/\n/g, ' ').substring(0, 100),
37
37
  author: a.uniqueId || '',
38
+ url: (a.uniqueId && v.id) ? 'https://www.tiktok.com/@' + a.uniqueId + '/video/' + v.id : '',
38
39
  plays: s.playCount || 0,
39
40
  likes: s.diggCount || 0,
40
41
  comments: s.commentCount || 0,
@@ -43,4 +44,4 @@ pipeline:
43
44
  });
44
45
  })()
45
46
 
46
- columns: [rank, desc, author, plays, likes, comments, shares]
47
+ columns: [rank, desc, author, url, plays, likes, comments, shares]
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
  import type { IPage } from '../../types.js';
3
4
 
@@ -15,7 +16,7 @@ cli({
15
16
  ],
16
17
  columns: ['index', 'status', 'user', 'message'],
17
18
  func: async (page: IPage | null, kwargs: any) => {
18
- if (!page) throw new Error('Requires browser');
19
+ if (!page) throw new CommandExecutionError('Browser session required for twitter accept');
19
20
 
20
21
  const keywords: string[] = kwargs.query.split(',').map((k: string) => k.trim()).filter(Boolean);
21
22
  const maxAccepts: number = kwargs.max ?? 20;
@@ -1,3 +1,4 @@
1
+ import { AuthRequiredError, CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
 
3
4
  cli({
@@ -153,7 +154,8 @@ cli({
153
154
  `);
154
155
 
155
156
  if (result?.error) {
156
- throw new Error(result.error + (result.hint ? ` (${result.hint})` : ''));
157
+ if (String(result.error).includes('No ct0 cookie')) throw new AuthRequiredError('x.com', result.error);
158
+ throw new CommandExecutionError(result.error + (result.hint ? ` (${result.hint})` : ''));
157
159
  }
158
160
 
159
161
  return result || [];
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
  import type { IPage } from '../../types.js';
3
4
 
@@ -13,7 +14,7 @@ cli({
13
14
  ],
14
15
  columns: ['status', 'message'],
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 twitter block');
17
18
  const username = kwargs.username.replace(/^@/, '');
18
19
 
19
20
  await page.goto(`https://x.com/${username}`);
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
  import type { IPage } from '../../types.js';
3
4
 
@@ -13,7 +14,7 @@ cli({
13
14
  ],
14
15
  columns: ['status', 'message'],
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 twitter bookmark');
17
18
 
18
19
  await page.goto(kwargs.url);
19
20
  await page.wait(5);
@@ -1,4 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
+ import { AuthRequiredError, CommandExecutionError } from '../../errors.js';
2
3
 
3
4
  const BEARER_TOKEN = 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';
4
5
  const BOOKMARKS_QUERY_ID = 'Fy0QMy4q_aZCpkO0PnyLYw';
@@ -137,7 +138,7 @@ cli({
137
138
  const ct0 = await page.evaluate(`() => {
138
139
  return document.cookie.split(';').map(c => c.trim()).find(c => c.startsWith('ct0='))?.split('=')[1] || null;
139
140
  }`);
140
- if (!ct0) throw new Error('Not logged into x.com (no ct0 cookie)');
141
+ if (!ct0) throw new AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)');
141
142
 
142
143
  const queryId = await page.evaluate(`async () => {
143
144
  try {
@@ -185,7 +186,7 @@ cli({
185
186
  }`);
186
187
 
187
188
  if (data?.error) {
188
- if (allTweets.length === 0) throw new Error(`HTTP ${data.error}: Failed to fetch bookmarks. queryId may have expired.`);
189
+ if (allTweets.length === 0) throw new CommandExecutionError(`HTTP ${data.error}: Failed to fetch bookmarks. queryId may have expired.`);
189
190
  break;
190
191
  }
191
192
 
@@ -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
 
4
5
  cli({
@@ -13,7 +14,7 @@ cli({
13
14
  ],
14
15
  columns: ['status', 'message'],
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 twitter delete');
17
18
 
18
19
  await page.goto(kwargs.url);
19
20
  await page.wait(5); // Wait for tweet to load completely
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
  import type { IPage } from '../../types.js';
3
4
 
@@ -13,7 +14,7 @@ cli({
13
14
  ],
14
15
  columns: ['status', 'message'],
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 twitter follow');
17
18
  const username = kwargs.username.replace(/^@/, '');
18
19
 
19
20
  await page.goto(`https://x.com/${username}`);
@@ -1,3 +1,4 @@
1
+ import { AuthRequiredError, SelectorError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
 
3
4
  cli({
@@ -26,7 +27,7 @@ cli({
26
27
  }`);
27
28
 
28
29
  if (!href) {
29
- throw new Error('Could not find logged-in user profile link. Are you logged in?');
30
+ throw new AuthRequiredError('x.com', 'Could not find logged-in user profile link. Are you logged in?');
30
31
  }
31
32
  targetUser = href.replace('/', '');
32
33
  }
@@ -55,7 +56,7 @@ cli({
55
56
  return false;
56
57
  }`);
57
58
  if (!clicked) {
58
- throw new Error('Could not find followers link on profile page. Twitter may have changed the layout.');
59
+ throw new SelectorError('Twitter followers link', 'Twitter may have changed the layout.');
59
60
  }
60
61
  await page.wait(5);
61
62
 
@@ -1,3 +1,4 @@
1
+ import { AuthRequiredError, SelectorError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
 
3
4
  cli({
@@ -26,7 +27,7 @@ cli({
26
27
  }`);
27
28
 
28
29
  if (!href) {
29
- throw new Error('Could not find logged-in user profile link. Are you logged in?');
30
+ throw new AuthRequiredError('x.com', 'Could not find logged-in user profile link. Are you logged in?');
30
31
  }
31
32
  targetUser = href.replace('/', '');
32
33
  }
@@ -48,7 +49,7 @@ cli({
48
49
  return false;
49
50
  }`);
50
51
  if (!clicked) {
51
- throw new Error('Could not find following link on profile page. Twitter may have changed the layout.');
52
+ throw new SelectorError('Twitter following link', 'Twitter may have changed the layout.');
52
53
  }
53
54
  await page.wait(5);
54
55
 
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
  import type { IPage } from '../../types.js';
3
4
 
@@ -13,7 +14,7 @@ cli({
13
14
  ],
14
15
  columns: ['status', 'message'],
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 twitter hide-reply');
17
18
 
18
19
  await page.goto(kwargs.url);
19
20
  await page.wait(5);
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
  import type { IPage } from '../../types.js';
3
4
 
@@ -13,7 +14,7 @@ cli({
13
14
  ],
14
15
  columns: ['status', 'message'],
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 twitter like');
17
18
 
18
19
  await page.goto(kwargs.url);
19
20
  await page.wait(5); // Wait for tweet to load completely
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
 
3
4
  cli({
@@ -29,7 +30,7 @@ cli({
29
30
  // Verify SPA navigation succeeded
30
31
  const currentUrl = await page.evaluate('() => window.location.pathname');
31
32
  if (currentUrl !== '/notifications') {
32
- throw new Error('SPA navigation to notifications failed. Twitter may have changed its routing.');
33
+ throw new CommandExecutionError('SPA navigation to notifications failed. Twitter may have changed its routing.');
33
34
  }
34
35
 
35
36
  // 4. Scroll to trigger pagination
@@ -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
 
4
5
  cli({
@@ -13,7 +14,7 @@ cli({
13
14
  ],
14
15
  columns: ['status', 'message', 'text'],
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 twitter post');
17
18
 
18
19
  // 1. Navigate directly to the compose tweet modal
19
20
  await page.goto('https://x.com/compose/tweet');
@@ -1,3 +1,4 @@
1
+ import { AuthRequiredError, CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
 
3
4
  cli({
@@ -22,7 +23,7 @@ cli({
22
23
  const link = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]');
23
24
  return link ? link.getAttribute('href') : null;
24
25
  }`);
25
- if (!href) throw new Error('Could not detect logged-in user. Are you logged in?');
26
+ if (!href) throw new AuthRequiredError('x.com', 'Could not detect logged-in user. Are you logged in?');
26
27
  username = href.replace('/', '');
27
28
  }
28
29
 
@@ -121,7 +122,8 @@ cli({
121
122
  `);
122
123
 
123
124
  if (result?.error) {
124
- throw new Error(result.error + (result.hint ? ` (${result.hint})` : ''));
125
+ if (String(result.error).includes('No ct0 cookie')) throw new AuthRequiredError('x.com', result.error);
126
+ throw new CommandExecutionError(result.error + (result.hint ? ` (${result.hint})` : ''));
125
127
  }
126
128
 
127
129
  return result || [];
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
  import type { IPage } from '../../types.js';
3
4
 
@@ -16,7 +17,7 @@ cli({
16
17
  ],
17
18
  columns: ['index', 'status', 'user', 'message'],
18
19
  func: async (page: IPage | null, kwargs: any) => {
19
- if (!page) throw new Error('Requires browser');
20
+ if (!page) throw new CommandExecutionError('Browser session required for twitter reply-dm');
20
21
 
21
22
  const messageText: string = kwargs.text;
22
23
  const maxSend: number = kwargs.max ?? 20;
@@ -1,3 +1,4 @@
1
+ import { CommandExecutionError } from '../../errors.js';
1
2
  import { cli, Strategy } from '../../registry.js';
2
3
  import type { IPage } from '../../types.js';
3
4
 
@@ -14,7 +15,7 @@ cli({
14
15
  ],
15
16
  columns: ['status', 'message', 'text'],
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 twitter reply');
18
19
 
19
20
  // 1. Navigate to the tweet page
20
21
  await page.goto(kwargs.url);