@jackwener/opencli 1.3.3 → 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 (496) 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 +30 -3
  11. package/README.zh-CN.md +30 -3
  12. package/SKILL.md +7 -1
  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 +53 -41
  19. package/dist/browser/cdp.test.d.ts +1 -0
  20. package/dist/browser/cdp.test.js +52 -0
  21. package/dist/browser/dom-snapshot.d.ts +2 -2
  22. package/dist/browser/dom-snapshot.js +54 -1
  23. package/dist/browser/dom-snapshot.test.js +36 -0
  24. package/dist/browser/index.d.ts +2 -2
  25. package/dist/browser/index.js +1 -1
  26. package/dist/browser/mcp.d.ts +0 -2
  27. package/dist/browser/mcp.js +2 -3
  28. package/dist/browser/page.d.ts +4 -3
  29. package/dist/browser/page.js +34 -37
  30. package/dist/browser/stealth.d.ts +0 -2
  31. package/dist/browser/stealth.js +24 -9
  32. package/dist/browser.test.js +2 -2
  33. package/dist/build-manifest.js +15 -9
  34. package/dist/build-manifest.test.js +12 -0
  35. package/dist/cascade.js +4 -2
  36. package/dist/cli-manifest.json +639 -258
  37. package/dist/cli.js +57 -29
  38. package/dist/clis/_shared/desktop-commands.d.ts +22 -0
  39. package/dist/clis/_shared/desktop-commands.js +108 -0
  40. package/dist/clis/antigravity/serve.js +5 -2
  41. package/dist/clis/arxiv/search.js +1 -1
  42. package/dist/clis/bilibili/dynamic.test.d.ts +1 -0
  43. package/dist/clis/bilibili/dynamic.test.js +68 -0
  44. package/dist/clis/bilibili/favorite.js +4 -2
  45. package/dist/clis/bilibili/following.js +3 -2
  46. package/dist/clis/bilibili/subtitle.js +8 -7
  47. package/dist/clis/bilibili/utils.js +2 -2
  48. package/dist/clis/boss/batchgreet.js +1 -1
  49. package/dist/clis/boss/chatlist.js +1 -1
  50. package/dist/clis/boss/chatmsg.js +1 -1
  51. package/dist/clis/boss/detail.js +1 -1
  52. package/dist/clis/boss/exchange.js +1 -1
  53. package/dist/clis/boss/greet.js +1 -1
  54. package/dist/clis/boss/invite.js +1 -1
  55. package/dist/clis/boss/joblist.js +1 -1
  56. package/dist/clis/boss/mark.js +4 -3
  57. package/dist/clis/boss/recommend.js +1 -1
  58. package/dist/clis/boss/resume.js +1 -1
  59. package/dist/clis/boss/search.js +1 -1
  60. package/dist/clis/boss/send.js +5 -4
  61. package/dist/clis/boss/stats.js +1 -1
  62. package/dist/clis/chatgpt/ask.js +4 -0
  63. package/dist/clis/chatgpt/new.js +5 -1
  64. package/dist/clis/chatgpt/read.js +5 -1
  65. package/dist/clis/chatgpt/send.js +2 -1
  66. package/dist/clis/chatgpt/status.js +5 -1
  67. package/dist/clis/chatwise/ask.js +8 -2
  68. package/dist/clis/chatwise/export.js +2 -0
  69. package/dist/clis/chatwise/history.js +2 -0
  70. package/dist/clis/chatwise/model.js +8 -3
  71. package/dist/clis/chatwise/new.js +3 -18
  72. package/dist/clis/chatwise/read.js +2 -0
  73. package/dist/clis/chatwise/screenshot.js +3 -27
  74. package/dist/clis/chatwise/send.js +8 -2
  75. package/dist/clis/chatwise/shared.d.ts +2 -0
  76. package/dist/clis/chatwise/shared.js +6 -0
  77. package/dist/clis/chatwise/status.js +3 -22
  78. package/dist/clis/codex/ask.js +6 -2
  79. package/dist/clis/codex/dump.js +2 -25
  80. package/dist/clis/codex/new.js +2 -25
  81. package/dist/clis/codex/screenshot.js +2 -27
  82. package/dist/clis/codex/send.js +6 -4
  83. package/dist/clis/codex/status.js +2 -22
  84. package/dist/clis/cursor/ask.js +2 -1
  85. package/dist/clis/cursor/composer.js +2 -1
  86. package/dist/clis/cursor/dump.js +2 -25
  87. package/dist/clis/cursor/new.js +2 -18
  88. package/dist/clis/cursor/read.js +2 -1
  89. package/dist/clis/cursor/screenshot.js +1 -30
  90. package/dist/clis/cursor/send.js +2 -1
  91. package/dist/clis/cursor/status.js +2 -21
  92. package/dist/clis/dictionary/examples.yaml +25 -0
  93. package/dist/clis/dictionary/search.yaml +27 -0
  94. package/dist/clis/dictionary/synonyms.yaml +25 -0
  95. package/dist/clis/douban/book-hot.js +1 -1
  96. package/dist/clis/douban/movie-hot.js +1 -1
  97. package/dist/clis/douban/search.js +1 -1
  98. package/dist/clis/douban/utils.d.ts +4 -1
  99. package/dist/clis/douban/utils.js +156 -1
  100. package/dist/clis/doubao/ask.js +1 -1
  101. package/dist/clis/doubao/new.js +1 -1
  102. package/dist/clis/doubao/read.js +1 -1
  103. package/dist/clis/doubao/send.js +1 -1
  104. package/dist/clis/doubao/status.js +1 -1
  105. package/dist/clis/doubao-app/ask.js +1 -1
  106. package/dist/clis/doubao-app/new.js +1 -1
  107. package/dist/clis/doubao-app/read.js +1 -1
  108. package/dist/clis/doubao-app/send.js +1 -1
  109. package/dist/clis/grok/ask.d.ts +4 -0
  110. package/dist/clis/grok/ask.js +28 -10
  111. package/dist/clis/grok/ask.test.js +18 -0
  112. package/dist/clis/jd/item.d.ts +1 -0
  113. package/dist/clis/jd/item.js +96 -0
  114. package/dist/clis/jd/item.test.d.ts +1 -0
  115. package/dist/clis/jd/item.test.js +28 -0
  116. package/dist/clis/jike/feed.js +1 -1
  117. package/dist/clis/jike/search.js +1 -1
  118. package/dist/clis/linkedin/search.js +5 -4
  119. package/dist/clis/linkedin/timeline.d.ts +21 -0
  120. package/dist/clis/linkedin/timeline.js +503 -0
  121. package/dist/clis/linkedin/timeline.test.d.ts +1 -0
  122. package/dist/clis/linkedin/timeline.test.js +81 -0
  123. package/dist/clis/medium/feed.js +1 -1
  124. package/dist/clis/medium/search.js +1 -1
  125. package/dist/clis/medium/user.js +1 -1
  126. package/dist/clis/medium/{shared.js → utils.js} +2 -1
  127. package/dist/clis/pixiv/detail.yaml +49 -0
  128. package/dist/clis/pixiv/download.d.ts +7 -0
  129. package/dist/clis/pixiv/download.js +78 -0
  130. package/dist/clis/pixiv/download.test.d.ts +1 -0
  131. package/dist/clis/pixiv/download.test.js +87 -0
  132. package/dist/clis/pixiv/illusts.d.ts +8 -0
  133. package/dist/clis/pixiv/illusts.js +65 -0
  134. package/dist/clis/pixiv/illusts.test.d.ts +1 -0
  135. package/dist/clis/pixiv/illusts.test.js +99 -0
  136. package/dist/clis/pixiv/ranking.yaml +53 -0
  137. package/dist/clis/pixiv/search.d.ts +6 -0
  138. package/dist/clis/pixiv/search.js +43 -0
  139. package/dist/clis/pixiv/search.test.d.ts +1 -0
  140. package/dist/clis/pixiv/search.test.js +83 -0
  141. package/dist/clis/pixiv/test-utils.d.ts +12 -0
  142. package/dist/clis/pixiv/test-utils.js +23 -0
  143. package/dist/clis/pixiv/user.yaml +46 -0
  144. package/dist/clis/pixiv/utils.d.ts +27 -0
  145. package/dist/clis/pixiv/utils.js +49 -0
  146. package/dist/clis/reddit/comment.js +2 -1
  147. package/dist/clis/reddit/read.js +4 -3
  148. package/dist/clis/reddit/read.test.d.ts +1 -0
  149. package/dist/clis/reddit/read.test.js +28 -0
  150. package/dist/clis/reddit/save.js +2 -1
  151. package/dist/clis/reddit/saved.js +7 -3
  152. package/dist/clis/reddit/subscribe.js +2 -1
  153. package/dist/clis/reddit/upvote.js +2 -1
  154. package/dist/clis/reddit/upvoted.js +7 -3
  155. package/dist/clis/sinablog/article.js +1 -1
  156. package/dist/clis/sinablog/hot.js +1 -1
  157. package/dist/clis/sinablog/user.js +1 -1
  158. package/dist/clis/substack/feed.js +1 -1
  159. package/dist/clis/substack/publication.js +1 -1
  160. package/dist/clis/substack/search.js +3 -2
  161. package/dist/clis/substack/{shared.js → utils.js} +3 -2
  162. package/dist/clis/tiktok/search.yaml +2 -1
  163. package/dist/clis/twitter/accept.js +2 -1
  164. package/dist/clis/twitter/article.js +4 -1
  165. package/dist/clis/twitter/block.js +2 -1
  166. package/dist/clis/twitter/bookmark.js +2 -1
  167. package/dist/clis/twitter/bookmarks.js +3 -2
  168. package/dist/clis/twitter/delete.js +2 -1
  169. package/dist/clis/twitter/follow.js +2 -1
  170. package/dist/clis/twitter/followers.js +3 -2
  171. package/dist/clis/twitter/following.js +3 -2
  172. package/dist/clis/twitter/hide-reply.js +2 -1
  173. package/dist/clis/twitter/like.js +2 -1
  174. package/dist/clis/twitter/notifications.js +2 -1
  175. package/dist/clis/twitter/post.js +2 -1
  176. package/dist/clis/twitter/profile.js +5 -2
  177. package/dist/clis/twitter/reply-dm.js +2 -1
  178. package/dist/clis/twitter/reply.js +2 -1
  179. package/dist/clis/twitter/search.js +30 -13
  180. package/dist/clis/twitter/search.test.d.ts +1 -0
  181. package/dist/clis/twitter/search.test.js +104 -0
  182. package/dist/clis/twitter/thread.js +2 -2
  183. package/dist/clis/twitter/timeline.js +3 -2
  184. package/dist/clis/twitter/trending.js +3 -2
  185. package/dist/clis/twitter/unblock.js +2 -1
  186. package/dist/clis/twitter/unbookmark.js +2 -1
  187. package/dist/clis/twitter/unfollow.js +2 -1
  188. package/dist/clis/v2ex/daily.js +3 -2
  189. package/dist/clis/v2ex/me.js +3 -2
  190. package/dist/clis/v2ex/notifications.js +4 -4
  191. package/dist/clis/web/read.d.ts +16 -0
  192. package/dist/clis/web/read.js +202 -0
  193. package/dist/clis/xueqiu/danjuan-utils.d.ts +55 -0
  194. package/dist/clis/xueqiu/danjuan-utils.js +126 -0
  195. package/dist/clis/xueqiu/danjuan-utils.test.d.ts +1 -0
  196. package/dist/clis/xueqiu/danjuan-utils.test.js +41 -0
  197. package/dist/clis/xueqiu/fund-holdings.d.ts +1 -0
  198. package/dist/clis/xueqiu/fund-holdings.js +28 -0
  199. package/dist/clis/xueqiu/fund-snapshot.d.ts +1 -0
  200. package/dist/clis/xueqiu/fund-snapshot.js +25 -0
  201. package/dist/clis/youtube/transcript.js +5 -4
  202. package/dist/clis/youtube/video.js +3 -2
  203. package/dist/daemon.js +7 -3
  204. package/dist/discovery.js +11 -10
  205. package/dist/doctor.js +2 -1
  206. package/dist/download/index.d.ts +4 -12
  207. package/dist/download/index.js +33 -12
  208. package/dist/download/index.test.js +79 -2
  209. package/dist/download/media-download.js +4 -2
  210. package/dist/engine.test.js +76 -4
  211. package/dist/execution.d.ts +1 -9
  212. package/dist/execution.js +56 -46
  213. package/dist/explore.js +12 -111
  214. package/dist/external-clis.yaml +0 -8
  215. package/dist/external.js +7 -5
  216. package/dist/external.test.js +4 -0
  217. package/dist/generate.d.ts +0 -9
  218. package/dist/generate.js +4 -20
  219. package/dist/hooks.d.ts +46 -0
  220. package/dist/hooks.js +56 -0
  221. package/dist/hooks.test.d.ts +4 -0
  222. package/dist/hooks.test.js +92 -0
  223. package/dist/interceptor.js +70 -23
  224. package/dist/main.js +2 -0
  225. package/dist/output.js +12 -6
  226. package/dist/pipeline/executor.js +1 -1
  227. package/dist/pipeline/steps/browser.js +1 -3
  228. package/dist/pipeline/steps/download.js +42 -26
  229. package/dist/pipeline/steps/download.test.d.ts +1 -0
  230. package/dist/pipeline/steps/download.test.js +101 -0
  231. package/dist/pipeline/steps/fetch.js +40 -22
  232. package/dist/pipeline/steps/fetch.test.d.ts +1 -0
  233. package/dist/pipeline/steps/fetch.test.js +123 -0
  234. package/dist/pipeline/steps/transform.js +2 -6
  235. package/dist/pipeline/template.js +66 -52
  236. package/dist/pipeline/template.test.js +28 -0
  237. package/dist/pipeline/transform.test.js +18 -0
  238. package/dist/plugin.d.ts +40 -1
  239. package/dist/plugin.js +214 -17
  240. package/dist/plugin.test.d.ts +1 -1
  241. package/dist/plugin.test.js +219 -3
  242. package/dist/record.js +6 -98
  243. package/dist/registry-api.d.ts +2 -0
  244. package/dist/registry-api.js +1 -0
  245. package/dist/registry.d.ts +5 -2
  246. package/dist/registry.js +1 -2
  247. package/dist/runtime.d.ts +0 -1
  248. package/dist/runtime.js +14 -4
  249. package/dist/snapshotFormatter.d.ts +7 -14
  250. package/dist/snapshotFormatter.js +38 -78
  251. package/dist/utils.d.ts +9 -0
  252. package/dist/utils.js +29 -0
  253. package/dist/validate.js +3 -5
  254. package/dist/yaml-schema.d.ts +26 -0
  255. package/dist/yaml-schema.js +5 -0
  256. package/docs/.vitepress/config.mts +3 -0
  257. package/docs/adapters/browser/dictionary.md +27 -0
  258. package/docs/adapters/browser/jd.md +27 -0
  259. package/docs/adapters/browser/linkedin.md +6 -0
  260. package/docs/adapters/browser/pixiv.md +92 -0
  261. package/docs/adapters/browser/web.md +30 -0
  262. package/docs/adapters/browser/xueqiu.md +27 -9
  263. package/docs/adapters/index.md +3 -1
  264. package/docs/comparison.md +125 -0
  265. package/docs/developer/contributing.md +21 -2
  266. package/docs/developer/testing.md +14 -8
  267. package/docs/developer/ts-adapter.md +18 -0
  268. package/docs/developer/yaml-adapter.md +16 -0
  269. package/docs/guide/plugins.md +10 -0
  270. package/docs/zh/guide/plugins.md +10 -0
  271. package/extension/dist/background.js +519 -444
  272. package/extension/manifest.json +1 -1
  273. package/extension/package.json +1 -1
  274. package/extension/src/background.test.ts +46 -1
  275. package/extension/src/background.ts +108 -33
  276. package/extension/src/cdp.ts +9 -9
  277. package/package.json +3 -2
  278. package/scripts/check-doc-coverage.sh +2 -0
  279. package/src/analysis.ts +170 -0
  280. package/src/browser/cdp.test.ts +66 -0
  281. package/src/browser/cdp.ts +59 -44
  282. package/src/browser/dom-snapshot.test.ts +42 -0
  283. package/src/browser/dom-snapshot.ts +56 -3
  284. package/src/browser/index.ts +2 -2
  285. package/src/browser/mcp.ts +2 -4
  286. package/src/browser/page.ts +34 -37
  287. package/src/browser/stealth.ts +24 -10
  288. package/src/browser.test.ts +2 -2
  289. package/src/build-manifest.test.ts +14 -0
  290. package/src/build-manifest.ts +13 -31
  291. package/src/cascade.ts +5 -3
  292. package/src/cli.ts +66 -34
  293. package/src/clis/_shared/desktop-commands.ts +121 -0
  294. package/src/clis/antigravity/serve.ts +6 -3
  295. package/src/clis/arxiv/search.ts +1 -1
  296. package/src/clis/bilibili/dynamic.test.ts +79 -0
  297. package/src/clis/bilibili/favorite.ts +5 -2
  298. package/src/clis/bilibili/following.ts +3 -2
  299. package/src/clis/bilibili/subtitle.ts +8 -7
  300. package/src/clis/bilibili/utils.ts +2 -2
  301. package/src/clis/boss/batchgreet.ts +1 -1
  302. package/src/clis/boss/chatlist.ts +1 -1
  303. package/src/clis/boss/chatmsg.ts +1 -1
  304. package/src/clis/boss/detail.ts +1 -1
  305. package/src/clis/boss/exchange.ts +1 -1
  306. package/src/clis/boss/greet.ts +1 -1
  307. package/src/clis/boss/invite.ts +1 -1
  308. package/src/clis/boss/joblist.ts +1 -1
  309. package/src/clis/boss/mark.ts +4 -3
  310. package/src/clis/boss/recommend.ts +1 -1
  311. package/src/clis/boss/resume.ts +1 -1
  312. package/src/clis/boss/search.ts +1 -1
  313. package/src/clis/boss/send.ts +5 -4
  314. package/src/clis/boss/stats.ts +1 -1
  315. package/src/clis/chatgpt/ask.ts +5 -0
  316. package/src/clis/chatgpt/new.ts +7 -2
  317. package/src/clis/chatgpt/read.ts +7 -2
  318. package/src/clis/chatgpt/send.ts +3 -2
  319. package/src/clis/chatgpt/status.ts +6 -1
  320. package/src/clis/chatwise/ask.ts +7 -2
  321. package/src/clis/chatwise/export.ts +2 -0
  322. package/src/clis/chatwise/history.ts +2 -0
  323. package/src/clis/chatwise/model.ts +7 -3
  324. package/src/clis/chatwise/new.ts +3 -20
  325. package/src/clis/chatwise/read.ts +2 -0
  326. package/src/clis/chatwise/screenshot.ts +3 -32
  327. package/src/clis/chatwise/send.ts +7 -2
  328. package/src/clis/chatwise/shared.ts +8 -0
  329. package/src/clis/chatwise/status.ts +3 -24
  330. package/src/clis/codex/ask.ts +5 -2
  331. package/src/clis/codex/dump.ts +2 -27
  332. package/src/clis/codex/new.ts +2 -28
  333. package/src/clis/codex/screenshot.ts +2 -32
  334. package/src/clis/codex/send.ts +5 -4
  335. package/src/clis/codex/status.ts +2 -24
  336. package/src/clis/cursor/ask.ts +2 -1
  337. package/src/clis/cursor/composer.ts +2 -1
  338. package/src/clis/cursor/dump.ts +2 -27
  339. package/src/clis/cursor/new.ts +2 -20
  340. package/src/clis/cursor/read.ts +2 -1
  341. package/src/clis/cursor/screenshot.ts +1 -36
  342. package/src/clis/cursor/send.ts +2 -1
  343. package/src/clis/cursor/status.ts +2 -22
  344. package/src/clis/dictionary/examples.yaml +25 -0
  345. package/src/clis/dictionary/search.yaml +27 -0
  346. package/src/clis/dictionary/synonyms.yaml +25 -0
  347. package/src/clis/douban/book-hot.ts +1 -1
  348. package/src/clis/douban/movie-hot.ts +1 -1
  349. package/src/clis/douban/search.ts +1 -1
  350. package/src/clis/douban/utils.ts +165 -1
  351. package/src/clis/doubao/ask.ts +1 -1
  352. package/src/clis/doubao/new.ts +1 -1
  353. package/src/clis/doubao/read.ts +1 -1
  354. package/src/clis/doubao/send.ts +1 -1
  355. package/src/clis/doubao/status.ts +1 -1
  356. package/src/clis/doubao-app/ask.ts +1 -1
  357. package/src/clis/doubao-app/new.ts +1 -1
  358. package/src/clis/doubao-app/read.ts +1 -1
  359. package/src/clis/doubao-app/send.ts +1 -1
  360. package/src/clis/grok/ask.test.ts +25 -0
  361. package/src/clis/grok/ask.ts +25 -12
  362. package/src/clis/jd/item.test.ts +35 -0
  363. package/src/clis/jd/item.ts +101 -0
  364. package/src/clis/jike/feed.ts +1 -1
  365. package/src/clis/jike/search.ts +1 -1
  366. package/src/clis/linkedin/search.ts +5 -4
  367. package/src/clis/linkedin/timeline.test.ts +99 -0
  368. package/src/clis/linkedin/timeline.ts +532 -0
  369. package/src/clis/medium/feed.ts +1 -1
  370. package/src/clis/medium/search.ts +1 -1
  371. package/src/clis/medium/user.ts +1 -1
  372. package/src/clis/medium/{shared.ts → utils.ts} +2 -1
  373. package/src/clis/pixiv/detail.yaml +49 -0
  374. package/src/clis/pixiv/download.test.ts +114 -0
  375. package/src/clis/pixiv/download.ts +91 -0
  376. package/src/clis/pixiv/illusts.test.ts +115 -0
  377. package/src/clis/pixiv/illusts.ts +78 -0
  378. package/src/clis/pixiv/ranking.yaml +53 -0
  379. package/src/clis/pixiv/search.test.ts +97 -0
  380. package/src/clis/pixiv/search.ts +53 -0
  381. package/src/clis/pixiv/test-utils.ts +29 -0
  382. package/src/clis/pixiv/user.yaml +46 -0
  383. package/src/clis/pixiv/utils.ts +62 -0
  384. package/src/clis/reddit/comment.ts +2 -1
  385. package/src/clis/reddit/read.test.ts +34 -0
  386. package/src/clis/reddit/read.ts +4 -3
  387. package/src/clis/reddit/save.ts +2 -1
  388. package/src/clis/reddit/saved.ts +6 -2
  389. package/src/clis/reddit/subscribe.ts +2 -1
  390. package/src/clis/reddit/upvote.ts +2 -1
  391. package/src/clis/reddit/upvoted.ts +6 -2
  392. package/src/clis/sinablog/article.ts +1 -1
  393. package/src/clis/sinablog/hot.ts +1 -1
  394. package/src/clis/sinablog/user.ts +1 -1
  395. package/src/clis/substack/feed.ts +1 -1
  396. package/src/clis/substack/publication.ts +1 -1
  397. package/src/clis/substack/search.ts +3 -2
  398. package/src/clis/substack/{shared.ts → utils.ts} +3 -2
  399. package/src/clis/tiktok/search.yaml +2 -1
  400. package/src/clis/twitter/accept.ts +2 -1
  401. package/src/clis/twitter/article.ts +3 -1
  402. package/src/clis/twitter/block.ts +2 -1
  403. package/src/clis/twitter/bookmark.ts +2 -1
  404. package/src/clis/twitter/bookmarks.ts +3 -2
  405. package/src/clis/twitter/delete.ts +2 -1
  406. package/src/clis/twitter/follow.ts +2 -1
  407. package/src/clis/twitter/followers.ts +3 -2
  408. package/src/clis/twitter/following.ts +3 -2
  409. package/src/clis/twitter/hide-reply.ts +2 -1
  410. package/src/clis/twitter/like.ts +2 -1
  411. package/src/clis/twitter/notifications.ts +2 -1
  412. package/src/clis/twitter/post.ts +2 -1
  413. package/src/clis/twitter/profile.ts +4 -2
  414. package/src/clis/twitter/reply-dm.ts +2 -1
  415. package/src/clis/twitter/reply.ts +2 -1
  416. package/src/clis/twitter/search.test.ts +113 -0
  417. package/src/clis/twitter/search.ts +38 -14
  418. package/src/clis/twitter/thread.ts +2 -2
  419. package/src/clis/twitter/timeline.ts +3 -2
  420. package/src/clis/twitter/trending.ts +3 -2
  421. package/src/clis/twitter/unblock.ts +2 -1
  422. package/src/clis/twitter/unbookmark.ts +2 -1
  423. package/src/clis/twitter/unfollow.ts +2 -1
  424. package/src/clis/v2ex/daily.ts +3 -2
  425. package/src/clis/v2ex/me.ts +3 -2
  426. package/src/clis/v2ex/notifications.ts +3 -4
  427. package/src/clis/web/read.ts +210 -0
  428. package/src/clis/xueqiu/danjuan-utils.test.ts +49 -0
  429. package/src/clis/xueqiu/danjuan-utils.ts +176 -0
  430. package/src/clis/xueqiu/fund-holdings.ts +32 -0
  431. package/src/clis/xueqiu/fund-snapshot.ts +27 -0
  432. package/src/clis/youtube/transcript.ts +5 -4
  433. package/src/clis/youtube/video.ts +3 -2
  434. package/src/daemon.ts +5 -4
  435. package/src/discovery.ts +12 -34
  436. package/src/doctor.ts +3 -2
  437. package/src/download/index.test.ts +93 -2
  438. package/src/download/index.ts +44 -23
  439. package/src/download/media-download.ts +5 -3
  440. package/src/engine.test.ts +84 -3
  441. package/src/execution.ts +62 -46
  442. package/src/explore.ts +21 -90
  443. package/src/external-clis.yaml +0 -8
  444. package/src/external.test.ts +9 -0
  445. package/src/external.ts +12 -10
  446. package/src/generate.ts +4 -41
  447. package/src/hooks.test.ts +126 -0
  448. package/src/hooks.ts +90 -0
  449. package/src/interceptor.ts +73 -23
  450. package/src/main.ts +2 -0
  451. package/src/output.ts +14 -6
  452. package/src/pipeline/executor.ts +1 -1
  453. package/src/pipeline/steps/browser.ts +1 -3
  454. package/src/pipeline/steps/download.test.ts +136 -0
  455. package/src/pipeline/steps/download.ts +47 -34
  456. package/src/pipeline/steps/fetch.test.ts +179 -0
  457. package/src/pipeline/steps/fetch.ts +39 -23
  458. package/src/pipeline/steps/transform.ts +2 -6
  459. package/src/pipeline/template.test.ts +28 -0
  460. package/src/pipeline/template.ts +67 -79
  461. package/src/pipeline/transform.test.ts +20 -0
  462. package/src/plugin.test.ts +251 -3
  463. package/src/plugin.ts +265 -21
  464. package/src/record.ts +12 -84
  465. package/src/registry-api.ts +2 -0
  466. package/src/registry.ts +7 -4
  467. package/src/runtime.ts +14 -4
  468. package/src/snapshotFormatter.ts +43 -121
  469. package/src/utils.ts +39 -0
  470. package/src/validate.ts +3 -5
  471. package/src/yaml-schema.ts +28 -0
  472. package/tests/e2e/browser-auth.test.ts +25 -0
  473. package/tests/e2e/plugin-management.test.ts +137 -0
  474. package/tests/e2e/public-commands.test.ts +34 -1
  475. package/vitest.config.ts +19 -1
  476. package/.github/workflows/pkg-pr-new.yml +0 -30
  477. package/dist/clis/douban/shared.d.ts +0 -4
  478. package/dist/clis/douban/shared.js +0 -155
  479. package/src/clis/douban/shared.ts +0 -165
  480. /package/dist/clis/boss/{common.d.ts → utils.d.ts} +0 -0
  481. /package/dist/clis/boss/{common.js → utils.js} +0 -0
  482. /package/dist/clis/doubao/{common.d.ts → utils.d.ts} +0 -0
  483. /package/dist/clis/doubao/{common.js → utils.js} +0 -0
  484. /package/dist/clis/doubao-app/{common.d.ts → utils.d.ts} +0 -0
  485. /package/dist/clis/doubao-app/{common.js → utils.js} +0 -0
  486. /package/dist/clis/jike/{shared.d.ts → utils.d.ts} +0 -0
  487. /package/dist/clis/jike/{shared.js → utils.js} +0 -0
  488. /package/dist/clis/medium/{shared.d.ts → utils.d.ts} +0 -0
  489. /package/dist/clis/sinablog/{shared.d.ts → utils.d.ts} +0 -0
  490. /package/dist/clis/sinablog/{shared.js → utils.js} +0 -0
  491. /package/dist/clis/substack/{shared.d.ts → utils.d.ts} +0 -0
  492. /package/src/clis/boss/{common.ts → utils.ts} +0 -0
  493. /package/src/clis/doubao/{common.ts → utils.ts} +0 -0
  494. /package/src/clis/doubao-app/{common.ts → utils.ts} +0 -0
  495. /package/src/clis/jike/{shared.ts → utils.ts} +0 -0
  496. /package/src/clis/sinablog/{shared.ts → utils.ts} +0 -0
@@ -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> = {
@@ -8,7 +8,8 @@ import { cli, Strategy } from '../../registry.js';
8
8
  import {
9
9
  requirePage, navigateToChat, findFriendByUid,
10
10
  clickCandidateInList, typeAndSendMessage,
11
- } from './common.js';
11
+ } from './utils.js';
12
+ import { EmptyResultError, SelectorError } from '../../errors.js';
12
13
 
13
14
  cli({
14
15
  site: 'boss',
@@ -29,21 +30,21 @@ cli({
29
30
  await navigateToChat(page, 3);
30
31
 
31
32
  const friend = await findFriendByUid(page, kwargs.uid, { maxPages: 5 });
32
- if (!friend) throw new Error('未找到该候选人,请确认 uid 是否正确');
33
+ if (!friend) throw new EmptyResultError('boss candidate search', '请确认 uid 是否正确');
33
34
 
34
35
  const numericUid = friend.uid;
35
36
  const friendName = friend.name || '候选人';
36
37
 
37
38
  const clicked = await clickCandidateInList(page, numericUid);
38
39
  if (!clicked) {
39
- throw new Error('无法在聊天列表中找到该用户,请确认聊天列表中有此人');
40
+ throw new SelectorError('聊天列表中的用户', '请确认聊天列表中有此人');
40
41
  }
41
42
 
42
43
  await page.wait({ time: 2 });
43
44
 
44
45
  const sent = await typeAndSendMessage(page, kwargs.text);
45
46
  if (!sent) {
46
- throw new Error('找不到消息输入框');
47
+ throw new SelectorError('消息输入框', '聊天页面 UI 可能已改变');
47
48
  }
48
49
 
49
50
  await page.wait({ time: 1 });
@@ -2,7 +2,7 @@
2
2
  * BOSS直聘 stats — job statistics overview.
3
3
  */
4
4
  import { cli, Strategy } from '../../registry.js';
5
- import { requirePage, navigateToChat, bossFetch, fetchFriendList, verbose } from './common.js';
5
+ import { requirePage, navigateToChat, bossFetch, fetchFriendList, verbose } from './utils.js';
6
6
 
7
7
  cli({
8
8
  site: 'boss',
@@ -1,5 +1,6 @@
1
1
  import { execSync, spawnSync } from 'node:child_process';
2
2
  import { cli, Strategy } from '../../registry.js';
3
+ import { ConfigError } from '../../errors.js';
3
4
  import type { IPage } from '../../types.js';
4
5
  import { getVisibleChatMessages } from './ax.js';
5
6
 
@@ -16,6 +17,10 @@ export const askCommand = cli({
16
17
  ],
17
18
  columns: ['Role', 'Text'],
18
19
  func: async (page: IPage | null, kwargs: any) => {
20
+ if (process.platform !== 'darwin') {
21
+ throw new ConfigError('ChatGPT Desktop integration requires macOS (osascript is not available on this platform)');
22
+ }
23
+
19
24
  const text = kwargs.text as string;
20
25
  const timeout = parseInt(kwargs.timeout as string, 10) || 30;
21
26
 
@@ -1,5 +1,6 @@
1
1
  import { execSync } from 'node:child_process';
2
2
  import { cli, Strategy } from '../../registry.js';
3
+ import { ConfigError, getErrorMessage } from '../../errors.js';
3
4
  import type { IPage } from '../../types.js';
4
5
 
5
6
  export const newCommand = cli({
@@ -12,13 +13,17 @@ export const newCommand = cli({
12
13
  args: [],
13
14
  columns: ['Status'],
14
15
  func: async (page: IPage | null) => {
16
+ if (process.platform !== 'darwin') {
17
+ throw new ConfigError('ChatGPT Desktop integration requires macOS (osascript is not available on this platform)');
18
+ }
19
+
15
20
  try {
16
21
  execSync("osascript -e 'tell application \"ChatGPT\" to activate'");
17
22
  execSync("osascript -e 'delay 0.5'");
18
23
  execSync("osascript -e 'tell application \"System Events\" to keystroke \"n\" using command down'");
19
24
  return [{ Status: 'Success' }];
20
- } catch (err: any) {
21
- return [{ Status: "Error: " + err.message }];
25
+ } catch (err) {
26
+ return [{ Status: "Error: " + getErrorMessage(err) }];
22
27
  }
23
28
  },
24
29
  });
@@ -1,5 +1,6 @@
1
1
  import { execSync } from 'node:child_process';
2
2
  import { cli, Strategy } from '../../registry.js';
3
+ import { CommandExecutionError, ConfigError, getErrorMessage } from '../../errors.js';
3
4
  import type { IPage } from '../../types.js';
4
5
  import { getVisibleChatMessages } from './ax.js';
5
6
 
@@ -13,6 +14,10 @@ export const readCommand = cli({
13
14
  args: [],
14
15
  columns: ['Role', 'Text'],
15
16
  func: async (page: IPage | null) => {
17
+ if (process.platform !== 'darwin') {
18
+ throw new ConfigError('ChatGPT Desktop integration requires macOS (osascript is not available on this platform)');
19
+ }
20
+
16
21
  try {
17
22
  execSync("osascript -e 'tell application \"ChatGPT\" to activate'");
18
23
  execSync("osascript -e 'delay 0.3'");
@@ -23,8 +28,8 @@ export const readCommand = cli({
23
28
  }
24
29
 
25
30
  return [{ Role: 'Assistant', Text: messages[messages.length - 1] }];
26
- } catch (err: any) {
27
- throw new Error("Failed to read from ChatGPT: " + err.message);
31
+ } catch (err) {
32
+ throw new CommandExecutionError("Failed to read from ChatGPT: " + getErrorMessage(err));
28
33
  }
29
34
  },
30
35
  });
@@ -1,6 +1,7 @@
1
1
  import { execSync, spawnSync } from 'node:child_process';
2
2
  import { cli, Strategy } from '../../registry.js';
3
3
  import type { IPage } from '../../types.js';
4
+ import { getErrorMessage } from '../../errors.js';
4
5
 
5
6
  export const sendCommand = cli({
6
7
  site: 'chatgpt',
@@ -41,8 +42,8 @@ export const sendCommand = cli({
41
42
  }
42
43
 
43
44
  return [{ Status: 'Success' }];
44
- } catch (err: any) {
45
- return [{ Status: "Error: " + err.message }];
45
+ } catch (err) {
46
+ return [{ Status: "Error: " + getErrorMessage(err) }];
46
47
  }
47
48
  },
48
49
  });
@@ -1,5 +1,6 @@
1
1
  import { execSync } from 'node:child_process';
2
2
  import { cli, Strategy } from '../../registry.js';
3
+ import { CommandExecutionError, ConfigError } from '../../errors.js';
3
4
  import type { IPage } from '../../types.js';
4
5
 
5
6
  export const statusCommand = cli({
@@ -12,11 +13,15 @@ export const statusCommand = cli({
12
13
  args: [],
13
14
  columns: ['Status'],
14
15
  func: async (page: IPage | null) => {
16
+ if (process.platform !== 'darwin') {
17
+ throw new ConfigError('ChatGPT Desktop integration requires macOS (osascript is not available on this platform)');
18
+ }
19
+
15
20
  try {
16
21
  const output = execSync("osascript -e 'application \"ChatGPT\" is running'", { encoding: 'utf-8' }).trim();
17
22
  return [{ Status: output === 'true' ? 'Running' : 'Stopped' }];
18
23
  } catch {
19
- return [{ Status: 'Error querying application state' }];
24
+ throw new CommandExecutionError('Error querying ChatGPT application state');
20
25
  }
21
26
  },
22
27
  });
@@ -1,5 +1,7 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
+ import { SelectorError } from '../../errors.js';
2
3
  import type { IPage } from '../../types.js';
4
+ import { chatwiseRequiredEnv } from './shared.js';
3
5
 
4
6
  export const askCommand = cli({
5
7
  site: 'chatwise',
@@ -8,6 +10,7 @@ export const askCommand = cli({
8
10
  domain: 'localhost',
9
11
  strategy: Strategy.UI,
10
12
  browser: true,
13
+ requiredEnv: chatwiseRequiredEnv,
11
14
  args: [
12
15
  { name: 'text', required: true, positional: true, help: 'Prompt to send' },
13
16
  { name: 'timeout', required: false, help: 'Max seconds to wait (default: 30)', default: '30' },
@@ -26,14 +29,14 @@ export const askCommand = cli({
26
29
  `);
27
30
 
28
31
  // Send message
29
- await page.evaluate(`
32
+ const injected = await page.evaluate(`
30
33
  (function(text) {
31
34
  let composer = document.querySelector('textarea');
32
35
  if (!composer) {
33
36
  const editables = Array.from(document.querySelectorAll('[contenteditable="true"]'));
34
37
  composer = editables.length > 0 ? editables[editables.length - 1] : null;
35
38
  }
36
- if (!composer) throw new Error('Could not find input');
39
+ if (!composer) return false;
37
40
  composer.focus();
38
41
  if (composer.tagName === 'TEXTAREA') {
39
42
  const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
@@ -42,8 +45,10 @@ export const askCommand = cli({
42
45
  } else {
43
46
  document.execCommand('insertText', false, text);
44
47
  }
48
+ return true;
45
49
  })(${JSON.stringify(text)})
46
50
  `);
51
+ if (!injected) throw new SelectorError('ChatWise input element');
47
52
 
48
53
  await page.wait(0.5);
49
54
  await page.pressKey('Enter');
@@ -1,6 +1,7 @@
1
1
  import * as fs from 'node:fs';
2
2
  import { cli, Strategy } from '../../registry.js';
3
3
  import type { IPage } from '../../types.js';
4
+ import { chatwiseRequiredEnv } from './shared.js';
4
5
 
5
6
  export const exportCommand = cli({
6
7
  site: 'chatwise',
@@ -9,6 +10,7 @@ export const exportCommand = cli({
9
10
  domain: 'localhost',
10
11
  strategy: Strategy.UI,
11
12
  browser: true,
13
+ requiredEnv: chatwiseRequiredEnv,
12
14
  args: [
13
15
  { name: 'output', required: false, help: 'Output file (default: /tmp/chatwise-export.md)' },
14
16
  ],
@@ -1,5 +1,6 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
2
  import type { IPage } from '../../types.js';
3
+ import { chatwiseRequiredEnv } from './shared.js';
3
4
 
4
5
  export const historyCommand = cli({
5
6
  site: 'chatwise',
@@ -8,6 +9,7 @@ export const historyCommand = cli({
8
9
  domain: 'localhost',
9
10
  strategy: Strategy.UI,
10
11
  browser: true,
12
+ requiredEnv: chatwiseRequiredEnv,
11
13
  args: [],
12
14
  columns: ['Index', 'Title'],
13
15
  func: async (page: IPage) => {
@@ -1,5 +1,7 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
+ import { SelectorError } from '../../errors.js';
2
3
  import type { IPage } from '../../types.js';
4
+ import { chatwiseRequiredEnv } from './shared.js';
3
5
 
4
6
  export const modelCommand = cli({
5
7
  site: 'chatwise',
@@ -8,6 +10,7 @@ export const modelCommand = cli({
8
10
  domain: 'localhost',
9
11
  strategy: Strategy.UI,
10
12
  browser: true,
13
+ requiredEnv: chatwiseRequiredEnv,
11
14
  args: [
12
15
  { name: 'model-name', required: false, positional: true, help: 'Model to switch to (e.g. gpt-4, claude-3)' },
13
16
  ],
@@ -44,7 +47,7 @@ export const modelCommand = cli({
44
47
  return [{ Status: 'Active', Model: currentModel }];
45
48
  } else {
46
49
  // Try to switch model
47
- await page.evaluate(`
50
+ const opened = await page.evaluate(`
48
51
  (function(target) {
49
52
  const selectors = [
50
53
  '[class*="model"]',
@@ -54,11 +57,12 @@ export const modelCommand = cli({
54
57
 
55
58
  for (const sel of selectors) {
56
59
  const el = document.querySelector(sel);
57
- if (el) { el.click(); return; }
60
+ if (el) { el.click(); return true; }
58
61
  }
59
- throw new Error('Could not find model selector');
62
+ return false;
60
63
  })(${JSON.stringify(desiredModel)})
61
64
  `);
65
+ if (!opened) throw new SelectorError('ChatWise model selector');
62
66
 
63
67
  await page.wait(0.5);
64
68
 
@@ -1,21 +1,4 @@
1
- import { cli, Strategy } from '../../registry.js';
2
- import type { IPage } from '../../types.js';
1
+ import { makeNewCommand } from '../_shared/desktop-commands.js';
2
+ import { chatwiseRequiredEnv } from './shared.js';
3
3
 
4
- export const newCommand = cli({
5
- site: 'chatwise',
6
- name: 'new',
7
- description: 'Start a new conversation in ChatWise',
8
- domain: 'localhost',
9
- strategy: Strategy.UI,
10
- browser: true,
11
- args: [],
12
- columns: ['Status'],
13
- func: async (page: IPage) => {
14
- // ChatWise uses standard Electron shortcuts
15
- const isMac = process.platform === 'darwin';
16
- await page.pressKey(isMac ? 'Meta+N' : 'Control+N');
17
- await page.wait(1);
18
-
19
- return [{ Status: 'Success' }];
20
- },
21
- });
4
+ export const newCommand = makeNewCommand('chatwise', 'ChatWise conversation', { requiredEnv: chatwiseRequiredEnv });
@@ -1,5 +1,6 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
2
  import type { IPage } from '../../types.js';
3
+ import { chatwiseRequiredEnv } from './shared.js';
3
4
 
4
5
  export const readCommand = cli({
5
6
  site: 'chatwise',
@@ -8,6 +9,7 @@ export const readCommand = cli({
8
9
  domain: 'localhost',
9
10
  strategy: Strategy.UI,
10
11
  browser: true,
12
+ requiredEnv: chatwiseRequiredEnv,
11
13
  args: [],
12
14
  columns: ['Content'],
13
15
  func: async (page: IPage) => {
@@ -1,33 +1,4 @@
1
- import * as fs from 'node:fs';
2
- import { cli, Strategy } from '../../registry.js';
3
- import type { IPage } from '../../types.js';
1
+ import { makeScreenshotCommand } from '../_shared/desktop-commands.js';
2
+ import { chatwiseRequiredEnv } from './shared.js';
4
3
 
5
- export const screenshotCommand = cli({
6
- site: 'chatwise',
7
- name: 'screenshot',
8
- description: 'Capture a snapshot of the current ChatWise window (DOM + Accessibility tree)',
9
- domain: 'localhost',
10
- strategy: Strategy.UI,
11
- browser: true,
12
- args: [
13
- { name: 'output', required: false, help: 'Output file path (default: /tmp/chatwise-snapshot)' },
14
- ],
15
- columns: ['Status', 'File'],
16
- func: async (page: IPage, kwargs: any) => {
17
- const basePath = (kwargs.output as string) || '/tmp/chatwise-snapshot';
18
-
19
- const snap = await page.snapshot({ compact: true });
20
- const html = await page.evaluate('document.documentElement.outerHTML');
21
-
22
- const htmlPath = basePath + '-dom.html';
23
- const snapPath = basePath + '-a11y.txt';
24
-
25
- fs.writeFileSync(htmlPath, html);
26
- fs.writeFileSync(snapPath, typeof snap === 'string' ? snap : JSON.stringify(snap, null, 2));
27
-
28
- return [
29
- { Status: 'Success', File: htmlPath },
30
- { Status: 'Success', File: snapPath },
31
- ];
32
- },
33
- });
4
+ export const screenshotCommand = makeScreenshotCommand('chatwise', 'ChatWise', { requiredEnv: chatwiseRequiredEnv });
@@ -1,5 +1,7 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
+ import { SelectorError } from '../../errors.js';
2
3
  import type { IPage } from '../../types.js';
4
+ import { chatwiseRequiredEnv } from './shared.js';
3
5
 
4
6
  export const sendCommand = cli({
5
7
  site: 'chatwise',
@@ -8,12 +10,13 @@ export const sendCommand = cli({
8
10
  domain: 'localhost',
9
11
  strategy: Strategy.UI,
10
12
  browser: true,
13
+ requiredEnv: chatwiseRequiredEnv,
11
14
  args: [{ name: 'text', required: true, positional: true, help: 'Message to send' }],
12
15
  columns: ['Status', 'InjectedText'],
13
16
  func: async (page: IPage, kwargs: any) => {
14
17
  const text = kwargs.text as string;
15
18
 
16
- await page.evaluate(`
19
+ const injected = await page.evaluate(`
17
20
  (function(text) {
18
21
  // ChatWise input can be textarea or contenteditable
19
22
  let composer = document.querySelector('textarea');
@@ -22,7 +25,7 @@ export const sendCommand = cli({
22
25
  composer = editables.length > 0 ? editables[editables.length - 1] : null;
23
26
  }
24
27
 
25
- if (!composer) throw new Error('Could not find ChatWise input element');
28
+ if (!composer) return false;
26
29
 
27
30
  composer.focus();
28
31
 
@@ -34,8 +37,10 @@ export const sendCommand = cli({
34
37
  } else {
35
38
  document.execCommand('insertText', false, text);
36
39
  }
40
+ return true;
37
41
  })(${JSON.stringify(text)})
38
42
  `);
43
+ if (!injected) throw new SelectorError('ChatWise input element');
39
44
 
40
45
  await page.wait(0.5);
41
46
  await page.pressKey('Enter');
@@ -0,0 +1,8 @@
1
+ import type { RequiredEnv } from '../../registry.js';
2
+
3
+ export const chatwiseRequiredEnv: RequiredEnv[] = [
4
+ {
5
+ name: 'OPENCLI_CDP_ENDPOINT',
6
+ help: 'Launch ChatWise with --remote-debugging-port=9228, then run OPENCLI_CDP_ENDPOINT=http://127.0.0.1:9228 opencli chatwise status. If you use a local proxy, also set NO_PROXY=127.0.0.1,localhost.',
7
+ },
8
+ ];