@jackwener/opencli 1.3.3 → 1.4.1

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 (680) hide show
  1. package/.github/actions/setup-chrome/action.yml +5 -4
  2. package/.github/pull_request_template.md +3 -1
  3. package/.github/workflows/build-extension.yml +7 -1
  4. package/.github/workflows/ci.yml +46 -6
  5. package/.github/workflows/docs.yml +1 -1
  6. package/.github/workflows/e2e-headed.yml +36 -3
  7. package/.github/workflows/release.yml +1 -1
  8. package/.github/workflows/security.yml +0 -3
  9. package/CHANGELOG.md +78 -0
  10. package/CONTRIBUTING.md +6 -3
  11. package/PRIVACY.md +57 -0
  12. package/README.md +31 -4
  13. package/README.zh-CN.md +31 -4
  14. package/SKILL.md +107 -2
  15. package/TESTING.md +1 -0
  16. package/chatwise-opencli.ps1 +82 -0
  17. package/dist/analysis.d.ts +38 -0
  18. package/dist/analysis.js +166 -0
  19. package/dist/browser/cdp.d.ts +0 -4
  20. package/dist/browser/cdp.js +53 -41
  21. package/dist/browser/cdp.test.d.ts +1 -0
  22. package/dist/browser/cdp.test.js +52 -0
  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/index.d.ts +2 -2
  27. package/dist/browser/index.js +1 -1
  28. package/dist/browser/mcp.d.ts +0 -2
  29. package/dist/browser/mcp.js +2 -3
  30. package/dist/browser/page.d.ts +4 -3
  31. package/dist/browser/page.js +34 -37
  32. package/dist/browser/stealth.d.ts +0 -2
  33. package/dist/browser/stealth.js +24 -9
  34. package/dist/browser.test.js +2 -2
  35. package/dist/build-manifest.js +15 -9
  36. package/dist/build-manifest.test.js +12 -0
  37. package/dist/cascade.js +4 -2
  38. package/dist/cli-manifest.json +1325 -256
  39. package/dist/cli.js +57 -29
  40. package/dist/clis/_shared/desktop-commands.d.ts +22 -0
  41. package/dist/clis/_shared/desktop-commands.js +108 -0
  42. package/dist/clis/antigravity/serve.js +5 -2
  43. package/dist/clis/apple-podcasts/search.js +2 -1
  44. package/dist/clis/arxiv/search.js +3 -3
  45. package/dist/clis/bbc/news.js +0 -1
  46. package/dist/clis/bilibili/dynamic.test.d.ts +1 -0
  47. package/dist/clis/bilibili/dynamic.test.js +68 -0
  48. package/dist/clis/bilibili/favorite.js +4 -2
  49. package/dist/clis/bilibili/following.js +3 -2
  50. package/dist/clis/bilibili/subtitle.js +8 -7
  51. package/dist/clis/bilibili/utils.js +2 -2
  52. package/dist/clis/boss/batchgreet.js +1 -1
  53. package/dist/clis/boss/chatlist.js +1 -1
  54. package/dist/clis/boss/chatmsg.js +1 -1
  55. package/dist/clis/boss/detail.js +1 -1
  56. package/dist/clis/boss/exchange.js +1 -1
  57. package/dist/clis/boss/greet.js +1 -1
  58. package/dist/clis/boss/invite.js +1 -1
  59. package/dist/clis/boss/joblist.js +1 -1
  60. package/dist/clis/boss/mark.js +4 -3
  61. package/dist/clis/boss/recommend.js +1 -1
  62. package/dist/clis/boss/resume.js +1 -1
  63. package/dist/clis/boss/search.js +1 -1
  64. package/dist/clis/boss/send.js +5 -4
  65. package/dist/clis/boss/stats.js +1 -1
  66. package/dist/clis/chatgpt/ask.js +4 -0
  67. package/dist/clis/chatgpt/new.js +5 -1
  68. package/dist/clis/chatgpt/read.js +5 -1
  69. package/dist/clis/chatgpt/send.js +2 -1
  70. package/dist/clis/chatgpt/status.js +5 -1
  71. package/dist/clis/chatwise/ask.js +8 -2
  72. package/dist/clis/chatwise/export.js +2 -0
  73. package/dist/clis/chatwise/history.js +2 -0
  74. package/dist/clis/chatwise/model.js +8 -3
  75. package/dist/clis/chatwise/new.js +3 -18
  76. package/dist/clis/chatwise/read.js +2 -0
  77. package/dist/clis/chatwise/screenshot.js +3 -27
  78. package/dist/clis/chatwise/send.js +8 -2
  79. package/dist/clis/chatwise/shared.d.ts +2 -0
  80. package/dist/clis/chatwise/shared.js +6 -0
  81. package/dist/clis/chatwise/status.js +3 -22
  82. package/dist/clis/codex/ask.js +6 -2
  83. package/dist/clis/codex/dump.js +2 -25
  84. package/dist/clis/codex/new.js +2 -25
  85. package/dist/clis/codex/screenshot.js +2 -27
  86. package/dist/clis/codex/send.js +6 -4
  87. package/dist/clis/codex/status.js +2 -22
  88. package/dist/clis/ctrip/search.js +0 -1
  89. package/dist/clis/cursor/ask.js +2 -1
  90. package/dist/clis/cursor/composer.js +2 -1
  91. package/dist/clis/cursor/dump.js +2 -25
  92. package/dist/clis/cursor/new.js +2 -18
  93. package/dist/clis/cursor/read.js +2 -1
  94. package/dist/clis/cursor/screenshot.js +1 -30
  95. package/dist/clis/cursor/send.js +2 -1
  96. package/dist/clis/cursor/status.js +2 -21
  97. package/dist/clis/dictionary/examples.yaml +25 -0
  98. package/dist/clis/dictionary/search.yaml +27 -0
  99. package/dist/clis/dictionary/synonyms.yaml +25 -0
  100. package/dist/clis/douban/book-hot.js +1 -1
  101. package/dist/clis/douban/movie-hot.js +1 -1
  102. package/dist/clis/douban/search.js +1 -1
  103. package/dist/clis/douban/utils.d.ts +4 -1
  104. package/dist/clis/douban/utils.js +156 -1
  105. package/dist/clis/doubao/ask.js +1 -1
  106. package/dist/clis/doubao/new.js +1 -1
  107. package/dist/clis/doubao/read.js +1 -1
  108. package/dist/clis/doubao/send.js +1 -1
  109. package/dist/clis/doubao/status.js +1 -1
  110. package/dist/clis/doubao-app/ask.js +1 -1
  111. package/dist/clis/doubao-app/new.js +1 -1
  112. package/dist/clis/doubao-app/read.js +1 -1
  113. package/dist/clis/doubao-app/send.js +1 -1
  114. package/dist/clis/douyin/_shared/browser-fetch.d.ts +10 -0
  115. package/dist/clis/douyin/_shared/browser-fetch.js +30 -0
  116. package/dist/clis/douyin/_shared/browser-fetch.test.d.ts +1 -0
  117. package/dist/clis/douyin/_shared/browser-fetch.test.js +31 -0
  118. package/dist/clis/douyin/_shared/creation-id.d.ts +1 -0
  119. package/dist/clis/douyin/_shared/creation-id.js +5 -0
  120. package/dist/clis/douyin/_shared/creation-id.test.d.ts +1 -0
  121. package/dist/clis/douyin/_shared/creation-id.test.js +22 -0
  122. package/dist/clis/douyin/_shared/imagex-upload.d.ts +20 -0
  123. package/dist/clis/douyin/_shared/imagex-upload.js +53 -0
  124. package/dist/clis/douyin/_shared/imagex-upload.test.d.ts +1 -0
  125. package/dist/clis/douyin/_shared/imagex-upload.test.js +87 -0
  126. package/dist/clis/douyin/_shared/sts2.d.ts +8 -0
  127. package/dist/clis/douyin/_shared/sts2.js +15 -0
  128. package/dist/clis/douyin/_shared/text-extra.d.ts +18 -0
  129. package/dist/clis/douyin/_shared/text-extra.js +15 -0
  130. package/dist/clis/douyin/_shared/text-extra.test.d.ts +1 -0
  131. package/dist/clis/douyin/_shared/text-extra.test.js +37 -0
  132. package/dist/clis/douyin/_shared/timing.d.ts +2 -0
  133. package/dist/clis/douyin/_shared/timing.js +22 -0
  134. package/dist/clis/douyin/_shared/timing.test.d.ts +1 -0
  135. package/dist/clis/douyin/_shared/timing.test.js +28 -0
  136. package/dist/clis/douyin/_shared/tos-upload-short-read.test.d.ts +11 -0
  137. package/dist/clis/douyin/_shared/tos-upload-short-read.test.js +83 -0
  138. package/dist/clis/douyin/_shared/tos-upload.d.ts +53 -0
  139. package/dist/clis/douyin/_shared/tos-upload.js +295 -0
  140. package/dist/clis/douyin/_shared/tos-upload.test.d.ts +1 -0
  141. package/dist/clis/douyin/_shared/tos-upload.test.js +229 -0
  142. package/dist/clis/douyin/_shared/transcode.d.ts +27 -0
  143. package/dist/clis/douyin/_shared/transcode.js +45 -0
  144. package/dist/clis/douyin/_shared/transcode.test.d.ts +1 -0
  145. package/dist/clis/douyin/_shared/transcode.test.js +93 -0
  146. package/dist/clis/douyin/_shared/types.d.ts +26 -0
  147. package/dist/clis/douyin/_shared/types.js +1 -0
  148. package/dist/clis/douyin/activities.d.ts +1 -0
  149. package/dist/clis/douyin/activities.js +20 -0
  150. package/dist/clis/douyin/activities.test.d.ts +1 -0
  151. package/dist/clis/douyin/activities.test.js +22 -0
  152. package/dist/clis/douyin/collections.d.ts +1 -0
  153. package/dist/clis/douyin/collections.js +22 -0
  154. package/dist/clis/douyin/collections.test.d.ts +1 -0
  155. package/dist/clis/douyin/collections.test.js +23 -0
  156. package/dist/clis/douyin/delete.d.ts +1 -0
  157. package/dist/clis/douyin/delete.js +18 -0
  158. package/dist/clis/douyin/delete.test.d.ts +1 -0
  159. package/dist/clis/douyin/delete.test.js +11 -0
  160. package/dist/clis/douyin/draft.d.ts +14 -0
  161. package/dist/clis/douyin/draft.js +237 -0
  162. package/dist/clis/douyin/draft.test.d.ts +1 -0
  163. package/dist/clis/douyin/draft.test.js +11 -0
  164. package/dist/clis/douyin/drafts.d.ts +1 -0
  165. package/dist/clis/douyin/drafts.js +23 -0
  166. package/dist/clis/douyin/drafts.test.d.ts +1 -0
  167. package/dist/clis/douyin/drafts.test.js +11 -0
  168. package/dist/clis/douyin/hashtag.d.ts +1 -0
  169. package/dist/clis/douyin/hashtag.js +45 -0
  170. package/dist/clis/douyin/hashtag.test.d.ts +1 -0
  171. package/dist/clis/douyin/hashtag.test.js +25 -0
  172. package/dist/clis/douyin/location.d.ts +1 -0
  173. package/dist/clis/douyin/location.js +24 -0
  174. package/dist/clis/douyin/location.test.d.ts +1 -0
  175. package/dist/clis/douyin/location.test.js +23 -0
  176. package/dist/clis/douyin/profile.d.ts +1 -0
  177. package/dist/clis/douyin/profile.js +28 -0
  178. package/dist/clis/douyin/profile.test.d.ts +1 -0
  179. package/dist/clis/douyin/profile.test.js +11 -0
  180. package/dist/clis/douyin/publish.d.ts +14 -0
  181. package/dist/clis/douyin/publish.js +288 -0
  182. package/dist/clis/douyin/publish.test.d.ts +1 -0
  183. package/dist/clis/douyin/publish.test.js +38 -0
  184. package/dist/clis/douyin/stats.d.ts +1 -0
  185. package/dist/clis/douyin/stats.js +27 -0
  186. package/dist/clis/douyin/stats.test.d.ts +1 -0
  187. package/dist/clis/douyin/stats.test.js +22 -0
  188. package/dist/clis/douyin/update.d.ts +1 -0
  189. package/dist/clis/douyin/update.js +31 -0
  190. package/dist/clis/douyin/update.test.d.ts +1 -0
  191. package/dist/clis/douyin/update.test.js +11 -0
  192. package/dist/clis/douyin/videos.d.ts +1 -0
  193. package/dist/clis/douyin/videos.js +34 -0
  194. package/dist/clis/douyin/videos.test.d.ts +1 -0
  195. package/dist/clis/douyin/videos.test.js +11 -0
  196. package/dist/clis/grok/ask.d.ts +4 -0
  197. package/dist/clis/grok/ask.js +28 -10
  198. package/dist/clis/grok/ask.test.js +18 -0
  199. package/dist/clis/hackernews/search.yaml +1 -1
  200. package/dist/clis/instagram/search.yaml +2 -1
  201. package/dist/clis/jd/item.d.ts +1 -0
  202. package/dist/clis/jd/item.js +96 -0
  203. package/dist/clis/jd/item.test.d.ts +1 -0
  204. package/dist/clis/jd/item.test.js +28 -0
  205. package/dist/clis/jike/feed.js +1 -1
  206. package/dist/clis/jike/search.js +1 -1
  207. package/dist/clis/linkedin/search.js +5 -4
  208. package/dist/clis/linkedin/timeline.d.ts +21 -0
  209. package/dist/clis/linkedin/timeline.js +503 -0
  210. package/dist/clis/linkedin/timeline.test.d.ts +1 -0
  211. package/dist/clis/linkedin/timeline.test.js +81 -0
  212. package/dist/clis/linux-do/search.yaml +3 -1
  213. package/dist/clis/medium/feed.js +1 -1
  214. package/dist/clis/medium/search.js +2 -2
  215. package/dist/clis/medium/user.js +1 -1
  216. package/dist/clis/medium/{shared.js → utils.js} +2 -1
  217. package/dist/clis/pixiv/detail.yaml +49 -0
  218. package/dist/clis/pixiv/download.d.ts +7 -0
  219. package/dist/clis/pixiv/download.js +78 -0
  220. package/dist/clis/pixiv/download.test.d.ts +1 -0
  221. package/dist/clis/pixiv/download.test.js +87 -0
  222. package/dist/clis/pixiv/illusts.d.ts +8 -0
  223. package/dist/clis/pixiv/illusts.js +65 -0
  224. package/dist/clis/pixiv/illusts.test.d.ts +1 -0
  225. package/dist/clis/pixiv/illusts.test.js +99 -0
  226. package/dist/clis/pixiv/ranking.yaml +53 -0
  227. package/dist/clis/pixiv/search.d.ts +6 -0
  228. package/dist/clis/pixiv/search.js +43 -0
  229. package/dist/clis/pixiv/search.test.d.ts +1 -0
  230. package/dist/clis/pixiv/search.test.js +83 -0
  231. package/dist/clis/pixiv/test-utils.d.ts +12 -0
  232. package/dist/clis/pixiv/test-utils.js +23 -0
  233. package/dist/clis/pixiv/user.yaml +46 -0
  234. package/dist/clis/pixiv/utils.d.ts +27 -0
  235. package/dist/clis/pixiv/utils.js +49 -0
  236. package/dist/clis/reddit/comment.js +2 -1
  237. package/dist/clis/reddit/read.js +4 -3
  238. package/dist/clis/reddit/read.test.d.ts +1 -0
  239. package/dist/clis/reddit/read.test.js +28 -0
  240. package/dist/clis/reddit/save.js +2 -1
  241. package/dist/clis/reddit/saved.js +7 -3
  242. package/dist/clis/reddit/subscribe.js +2 -1
  243. package/dist/clis/reddit/upvote.js +2 -1
  244. package/dist/clis/reddit/upvoted.js +7 -3
  245. package/dist/clis/reuters/search.js +0 -1
  246. package/dist/clis/sinablog/article.js +1 -1
  247. package/dist/clis/sinablog/hot.js +1 -1
  248. package/dist/clis/sinablog/user.js +1 -1
  249. package/dist/clis/substack/feed.js +1 -1
  250. package/dist/clis/substack/publication.js +1 -1
  251. package/dist/clis/substack/search.js +3 -2
  252. package/dist/clis/substack/{shared.js → utils.js} +3 -2
  253. package/dist/clis/tiktok/search.yaml +2 -1
  254. package/dist/clis/twitter/accept.js +2 -1
  255. package/dist/clis/twitter/article.js +4 -1
  256. package/dist/clis/twitter/block.js +2 -1
  257. package/dist/clis/twitter/bookmark.js +2 -1
  258. package/dist/clis/twitter/bookmarks.js +3 -2
  259. package/dist/clis/twitter/delete.js +2 -1
  260. package/dist/clis/twitter/follow.js +2 -1
  261. package/dist/clis/twitter/followers.js +3 -2
  262. package/dist/clis/twitter/following.js +3 -2
  263. package/dist/clis/twitter/hide-reply.js +2 -1
  264. package/dist/clis/twitter/like.js +2 -1
  265. package/dist/clis/twitter/notifications.js +2 -1
  266. package/dist/clis/twitter/post.js +2 -1
  267. package/dist/clis/twitter/profile.js +5 -2
  268. package/dist/clis/twitter/reply-dm.js +2 -1
  269. package/dist/clis/twitter/reply.js +2 -1
  270. package/dist/clis/twitter/search.js +32 -13
  271. package/dist/clis/twitter/search.test.d.ts +1 -0
  272. package/dist/clis/twitter/search.test.js +156 -0
  273. package/dist/clis/twitter/thread.js +2 -2
  274. package/dist/clis/twitter/timeline.js +3 -2
  275. package/dist/clis/twitter/trending.js +3 -2
  276. package/dist/clis/twitter/unblock.js +2 -1
  277. package/dist/clis/twitter/unbookmark.js +2 -1
  278. package/dist/clis/twitter/unfollow.js +2 -1
  279. package/dist/clis/v2ex/daily.js +3 -2
  280. package/dist/clis/v2ex/me.js +3 -2
  281. package/dist/clis/v2ex/notifications.js +4 -4
  282. package/dist/clis/web/read.d.ts +16 -0
  283. package/dist/clis/web/read.js +202 -0
  284. package/dist/clis/weibo/comments.d.ts +1 -0
  285. package/dist/clis/weibo/comments.js +53 -0
  286. package/dist/clis/weibo/feed.d.ts +1 -0
  287. package/dist/clis/weibo/feed.js +56 -0
  288. package/dist/clis/weibo/hot.js +0 -1
  289. package/dist/clis/weibo/me.d.ts +1 -0
  290. package/dist/clis/weibo/me.js +76 -0
  291. package/dist/clis/weibo/post.d.ts +1 -0
  292. package/dist/clis/weibo/post.js +75 -0
  293. package/dist/clis/weibo/user.d.ts +1 -0
  294. package/dist/clis/weibo/user.js +63 -0
  295. package/dist/clis/weibo/utils.d.ts +6 -0
  296. package/dist/clis/weibo/utils.js +30 -0
  297. package/dist/clis/weread/search.js +3 -2
  298. package/dist/clis/xueqiu/danjuan-utils.d.ts +55 -0
  299. package/dist/clis/xueqiu/danjuan-utils.js +126 -0
  300. package/dist/clis/xueqiu/danjuan-utils.test.d.ts +1 -0
  301. package/dist/clis/xueqiu/danjuan-utils.test.js +41 -0
  302. package/dist/clis/xueqiu/fund-holdings.d.ts +1 -0
  303. package/dist/clis/xueqiu/fund-holdings.js +28 -0
  304. package/dist/clis/xueqiu/fund-snapshot.d.ts +1 -0
  305. package/dist/clis/xueqiu/fund-snapshot.js +25 -0
  306. package/dist/clis/xueqiu/search.yaml +2 -1
  307. package/dist/clis/yahoo-finance/quote.js +0 -1
  308. package/dist/clis/youtube/channel.d.ts +1 -0
  309. package/dist/clis/youtube/channel.js +150 -0
  310. package/dist/clis/youtube/comments.d.ts +1 -0
  311. package/dist/clis/youtube/comments.js +95 -0
  312. package/dist/clis/youtube/search.js +0 -1
  313. package/dist/clis/youtube/transcript.js +5 -4
  314. package/dist/clis/youtube/video.js +3 -2
  315. package/dist/clis/zhihu/search.yaml +2 -1
  316. package/dist/daemon.js +7 -3
  317. package/dist/discovery.js +11 -10
  318. package/dist/doctor.js +2 -1
  319. package/dist/download/index.d.ts +4 -12
  320. package/dist/download/index.js +33 -12
  321. package/dist/download/index.test.js +79 -2
  322. package/dist/download/media-download.js +4 -2
  323. package/dist/engine.test.js +76 -4
  324. package/dist/execution.d.ts +1 -9
  325. package/dist/execution.js +56 -46
  326. package/dist/explore.js +12 -111
  327. package/dist/external-clis.yaml +0 -25
  328. package/dist/external.js +7 -5
  329. package/dist/external.test.js +4 -0
  330. package/dist/generate.d.ts +0 -9
  331. package/dist/generate.js +4 -20
  332. package/dist/hooks.d.ts +46 -0
  333. package/dist/hooks.js +56 -0
  334. package/dist/hooks.test.d.ts +4 -0
  335. package/dist/hooks.test.js +92 -0
  336. package/dist/interceptor.js +70 -23
  337. package/dist/main.js +2 -0
  338. package/dist/output.js +12 -6
  339. package/dist/pipeline/executor.js +1 -1
  340. package/dist/pipeline/steps/browser.js +1 -3
  341. package/dist/pipeline/steps/download.js +42 -26
  342. package/dist/pipeline/steps/download.test.d.ts +1 -0
  343. package/dist/pipeline/steps/download.test.js +101 -0
  344. package/dist/pipeline/steps/fetch.js +40 -22
  345. package/dist/pipeline/steps/fetch.test.d.ts +1 -0
  346. package/dist/pipeline/steps/fetch.test.js +123 -0
  347. package/dist/pipeline/steps/transform.js +2 -6
  348. package/dist/pipeline/template.js +66 -52
  349. package/dist/pipeline/template.test.js +28 -0
  350. package/dist/pipeline/transform.test.js +18 -0
  351. package/dist/plugin.d.ts +40 -1
  352. package/dist/plugin.js +214 -17
  353. package/dist/plugin.test.d.ts +1 -1
  354. package/dist/plugin.test.js +219 -3
  355. package/dist/record.js +6 -98
  356. package/dist/registry-api.d.ts +2 -0
  357. package/dist/registry-api.js +1 -0
  358. package/dist/registry.d.ts +5 -2
  359. package/dist/registry.js +1 -2
  360. package/dist/runtime.d.ts +0 -1
  361. package/dist/runtime.js +14 -4
  362. package/dist/snapshotFormatter.d.ts +7 -14
  363. package/dist/snapshotFormatter.js +38 -78
  364. package/dist/utils.d.ts +9 -0
  365. package/dist/utils.js +29 -0
  366. package/dist/validate.js +3 -5
  367. package/dist/weread-search-regression.test.d.ts +1 -0
  368. package/dist/weread-search-regression.test.js +39 -0
  369. package/dist/yaml-schema.d.ts +26 -0
  370. package/dist/yaml-schema.js +5 -0
  371. package/docs/.vitepress/config.mts +16 -0
  372. package/docs/adapters/browser/dictionary.md +27 -0
  373. package/docs/adapters/browser/douyin.md +75 -0
  374. package/docs/adapters/browser/jd.md +27 -0
  375. package/docs/adapters/browser/linkedin.md +6 -0
  376. package/docs/adapters/browser/pixiv.md +92 -0
  377. package/docs/adapters/browser/twitter.md +6 -0
  378. package/docs/adapters/browser/web.md +30 -0
  379. package/docs/adapters/browser/xueqiu.md +27 -9
  380. package/docs/adapters/index.md +9 -2
  381. package/docs/comparison.md +125 -0
  382. package/docs/developer/contributing.md +21 -2
  383. package/docs/developer/testing.md +14 -8
  384. package/docs/developer/ts-adapter.md +18 -0
  385. package/docs/developer/yaml-adapter.md +16 -0
  386. package/docs/guide/plugins.md +10 -0
  387. package/docs/zh/guide/plugins.md +10 -0
  388. package/extension/dist/background.js +100 -35
  389. package/extension/manifest.json +6 -2
  390. package/extension/package.json +1 -1
  391. package/extension/popup.html +84 -0
  392. package/extension/popup.js +25 -0
  393. package/extension/src/background.test.ts +46 -1
  394. package/extension/src/background.ts +128 -34
  395. package/extension/src/cdp.ts +9 -9
  396. package/package.json +3 -2
  397. package/scripts/check-doc-coverage.sh +2 -0
  398. package/src/analysis.ts +170 -0
  399. package/src/browser/cdp.test.ts +66 -0
  400. package/src/browser/cdp.ts +59 -44
  401. package/src/browser/dom-snapshot.test.ts +42 -0
  402. package/src/browser/dom-snapshot.ts +56 -3
  403. package/src/browser/index.ts +2 -2
  404. package/src/browser/mcp.ts +2 -4
  405. package/src/browser/page.ts +34 -37
  406. package/src/browser/stealth.ts +24 -10
  407. package/src/browser.test.ts +2 -2
  408. package/src/build-manifest.test.ts +14 -0
  409. package/src/build-manifest.ts +13 -31
  410. package/src/cascade.ts +5 -3
  411. package/src/cli.ts +66 -34
  412. package/src/clis/_shared/desktop-commands.ts +121 -0
  413. package/src/clis/antigravity/serve.ts +6 -3
  414. package/src/clis/apple-podcasts/search.ts +2 -1
  415. package/src/clis/arxiv/search.ts +3 -3
  416. package/src/clis/bbc/news.ts +0 -1
  417. package/src/clis/bilibili/dynamic.test.ts +79 -0
  418. package/src/clis/bilibili/favorite.ts +5 -2
  419. package/src/clis/bilibili/following.ts +3 -2
  420. package/src/clis/bilibili/subtitle.ts +8 -7
  421. package/src/clis/bilibili/utils.ts +2 -2
  422. package/src/clis/boss/batchgreet.ts +1 -1
  423. package/src/clis/boss/chatlist.ts +1 -1
  424. package/src/clis/boss/chatmsg.ts +1 -1
  425. package/src/clis/boss/detail.ts +1 -1
  426. package/src/clis/boss/exchange.ts +1 -1
  427. package/src/clis/boss/greet.ts +1 -1
  428. package/src/clis/boss/invite.ts +1 -1
  429. package/src/clis/boss/joblist.ts +1 -1
  430. package/src/clis/boss/mark.ts +4 -3
  431. package/src/clis/boss/recommend.ts +1 -1
  432. package/src/clis/boss/resume.ts +1 -1
  433. package/src/clis/boss/search.ts +1 -1
  434. package/src/clis/boss/send.ts +5 -4
  435. package/src/clis/boss/stats.ts +1 -1
  436. package/src/clis/chatgpt/ask.ts +5 -0
  437. package/src/clis/chatgpt/new.ts +7 -2
  438. package/src/clis/chatgpt/read.ts +7 -2
  439. package/src/clis/chatgpt/send.ts +3 -2
  440. package/src/clis/chatgpt/status.ts +6 -1
  441. package/src/clis/chatwise/ask.ts +7 -2
  442. package/src/clis/chatwise/export.ts +2 -0
  443. package/src/clis/chatwise/history.ts +2 -0
  444. package/src/clis/chatwise/model.ts +7 -3
  445. package/src/clis/chatwise/new.ts +3 -20
  446. package/src/clis/chatwise/read.ts +2 -0
  447. package/src/clis/chatwise/screenshot.ts +3 -32
  448. package/src/clis/chatwise/send.ts +7 -2
  449. package/src/clis/chatwise/shared.ts +8 -0
  450. package/src/clis/chatwise/status.ts +3 -24
  451. package/src/clis/codex/ask.ts +5 -2
  452. package/src/clis/codex/dump.ts +2 -27
  453. package/src/clis/codex/new.ts +2 -28
  454. package/src/clis/codex/screenshot.ts +2 -32
  455. package/src/clis/codex/send.ts +5 -4
  456. package/src/clis/codex/status.ts +2 -24
  457. package/src/clis/ctrip/search.ts +0 -1
  458. package/src/clis/cursor/ask.ts +2 -1
  459. package/src/clis/cursor/composer.ts +2 -1
  460. package/src/clis/cursor/dump.ts +2 -27
  461. package/src/clis/cursor/new.ts +2 -20
  462. package/src/clis/cursor/read.ts +2 -1
  463. package/src/clis/cursor/screenshot.ts +1 -36
  464. package/src/clis/cursor/send.ts +2 -1
  465. package/src/clis/cursor/status.ts +2 -22
  466. package/src/clis/dictionary/examples.yaml +25 -0
  467. package/src/clis/dictionary/search.yaml +27 -0
  468. package/src/clis/dictionary/synonyms.yaml +25 -0
  469. package/src/clis/douban/book-hot.ts +1 -1
  470. package/src/clis/douban/movie-hot.ts +1 -1
  471. package/src/clis/douban/search.ts +1 -1
  472. package/src/clis/douban/utils.ts +165 -1
  473. package/src/clis/doubao/ask.ts +1 -1
  474. package/src/clis/doubao/new.ts +1 -1
  475. package/src/clis/doubao/read.ts +1 -1
  476. package/src/clis/doubao/send.ts +1 -1
  477. package/src/clis/doubao/status.ts +1 -1
  478. package/src/clis/doubao-app/ask.ts +1 -1
  479. package/src/clis/doubao-app/new.ts +1 -1
  480. package/src/clis/doubao-app/read.ts +1 -1
  481. package/src/clis/doubao-app/send.ts +1 -1
  482. package/src/clis/douyin/_shared/browser-fetch.test.ts +38 -0
  483. package/src/clis/douyin/_shared/browser-fetch.ts +45 -0
  484. package/src/clis/douyin/_shared/creation-id.test.ts +26 -0
  485. package/src/clis/douyin/_shared/creation-id.ts +8 -0
  486. package/src/clis/douyin/_shared/imagex-upload.test.ts +113 -0
  487. package/src/clis/douyin/_shared/imagex-upload.ts +76 -0
  488. package/src/clis/douyin/_shared/sts2.ts +20 -0
  489. package/src/clis/douyin/_shared/text-extra.test.ts +42 -0
  490. package/src/clis/douyin/_shared/text-extra.ts +33 -0
  491. package/src/clis/douyin/_shared/timing.test.ts +38 -0
  492. package/src/clis/douyin/_shared/timing.ts +22 -0
  493. package/src/clis/douyin/_shared/tos-upload-short-read.test.ts +102 -0
  494. package/src/clis/douyin/_shared/tos-upload.test.ts +281 -0
  495. package/src/clis/douyin/_shared/tos-upload.ts +444 -0
  496. package/src/clis/douyin/_shared/transcode.test.ts +117 -0
  497. package/src/clis/douyin/_shared/transcode.ts +78 -0
  498. package/src/clis/douyin/_shared/types.ts +29 -0
  499. package/src/clis/douyin/activities.test.ts +25 -0
  500. package/src/clis/douyin/activities.ts +23 -0
  501. package/src/clis/douyin/collections.test.ts +26 -0
  502. package/src/clis/douyin/collections.ts +25 -0
  503. package/src/clis/douyin/delete.test.ts +12 -0
  504. package/src/clis/douyin/delete.ts +20 -0
  505. package/src/clis/douyin/draft.test.ts +12 -0
  506. package/src/clis/douyin/draft.ts +282 -0
  507. package/src/clis/douyin/drafts.test.ts +12 -0
  508. package/src/clis/douyin/drafts.ts +27 -0
  509. package/src/clis/douyin/hashtag.test.ts +28 -0
  510. package/src/clis/douyin/hashtag.ts +56 -0
  511. package/src/clis/douyin/location.test.ts +26 -0
  512. package/src/clis/douyin/location.ts +27 -0
  513. package/src/clis/douyin/profile.test.ts +12 -0
  514. package/src/clis/douyin/profile.ts +37 -0
  515. package/src/clis/douyin/publish.test.ts +45 -0
  516. package/src/clis/douyin/publish.ts +340 -0
  517. package/src/clis/douyin/stats.test.ts +25 -0
  518. package/src/clis/douyin/stats.ts +30 -0
  519. package/src/clis/douyin/update.test.ts +12 -0
  520. package/src/clis/douyin/update.ts +43 -0
  521. package/src/clis/douyin/videos.test.ts +12 -0
  522. package/src/clis/douyin/videos.ts +49 -0
  523. package/src/clis/grok/ask.test.ts +25 -0
  524. package/src/clis/grok/ask.ts +25 -12
  525. package/src/clis/hackernews/search.yaml +1 -1
  526. package/src/clis/instagram/search.yaml +2 -1
  527. package/src/clis/jd/item.test.ts +35 -0
  528. package/src/clis/jd/item.ts +101 -0
  529. package/src/clis/jike/feed.ts +1 -1
  530. package/src/clis/jike/search.ts +1 -1
  531. package/src/clis/linkedin/search.ts +5 -4
  532. package/src/clis/linkedin/timeline.test.ts +99 -0
  533. package/src/clis/linkedin/timeline.ts +532 -0
  534. package/src/clis/linux-do/search.yaml +3 -1
  535. package/src/clis/medium/feed.ts +1 -1
  536. package/src/clis/medium/search.ts +2 -2
  537. package/src/clis/medium/user.ts +1 -1
  538. package/src/clis/medium/{shared.ts → utils.ts} +2 -1
  539. package/src/clis/pixiv/detail.yaml +49 -0
  540. package/src/clis/pixiv/download.test.ts +114 -0
  541. package/src/clis/pixiv/download.ts +91 -0
  542. package/src/clis/pixiv/illusts.test.ts +115 -0
  543. package/src/clis/pixiv/illusts.ts +78 -0
  544. package/src/clis/pixiv/ranking.yaml +53 -0
  545. package/src/clis/pixiv/search.test.ts +97 -0
  546. package/src/clis/pixiv/search.ts +53 -0
  547. package/src/clis/pixiv/test-utils.ts +29 -0
  548. package/src/clis/pixiv/user.yaml +46 -0
  549. package/src/clis/pixiv/utils.ts +62 -0
  550. package/src/clis/reddit/comment.ts +2 -1
  551. package/src/clis/reddit/read.test.ts +34 -0
  552. package/src/clis/reddit/read.ts +4 -3
  553. package/src/clis/reddit/save.ts +2 -1
  554. package/src/clis/reddit/saved.ts +6 -2
  555. package/src/clis/reddit/subscribe.ts +2 -1
  556. package/src/clis/reddit/upvote.ts +2 -1
  557. package/src/clis/reddit/upvoted.ts +6 -2
  558. package/src/clis/reuters/search.ts +0 -1
  559. package/src/clis/sinablog/article.ts +1 -1
  560. package/src/clis/sinablog/hot.ts +1 -1
  561. package/src/clis/sinablog/user.ts +1 -1
  562. package/src/clis/substack/feed.ts +1 -1
  563. package/src/clis/substack/publication.ts +1 -1
  564. package/src/clis/substack/search.ts +3 -2
  565. package/src/clis/substack/{shared.ts → utils.ts} +3 -2
  566. package/src/clis/tiktok/search.yaml +2 -1
  567. package/src/clis/twitter/accept.ts +2 -1
  568. package/src/clis/twitter/article.ts +3 -1
  569. package/src/clis/twitter/block.ts +2 -1
  570. package/src/clis/twitter/bookmark.ts +2 -1
  571. package/src/clis/twitter/bookmarks.ts +3 -2
  572. package/src/clis/twitter/delete.ts +2 -1
  573. package/src/clis/twitter/follow.ts +2 -1
  574. package/src/clis/twitter/followers.ts +3 -2
  575. package/src/clis/twitter/following.ts +3 -2
  576. package/src/clis/twitter/hide-reply.ts +2 -1
  577. package/src/clis/twitter/like.ts +2 -1
  578. package/src/clis/twitter/notifications.ts +2 -1
  579. package/src/clis/twitter/post.ts +2 -1
  580. package/src/clis/twitter/profile.ts +4 -2
  581. package/src/clis/twitter/reply-dm.ts +2 -1
  582. package/src/clis/twitter/reply.ts +2 -1
  583. package/src/clis/twitter/search.test.ts +180 -0
  584. package/src/clis/twitter/search.ts +40 -14
  585. package/src/clis/twitter/thread.ts +2 -2
  586. package/src/clis/twitter/timeline.ts +3 -2
  587. package/src/clis/twitter/trending.ts +3 -2
  588. package/src/clis/twitter/unblock.ts +2 -1
  589. package/src/clis/twitter/unbookmark.ts +2 -1
  590. package/src/clis/twitter/unfollow.ts +2 -1
  591. package/src/clis/v2ex/daily.ts +3 -2
  592. package/src/clis/v2ex/me.ts +3 -2
  593. package/src/clis/v2ex/notifications.ts +3 -4
  594. package/src/clis/web/read.ts +210 -0
  595. package/src/clis/weibo/comments.ts +54 -0
  596. package/src/clis/weibo/feed.ts +57 -0
  597. package/src/clis/weibo/hot.ts +0 -1
  598. package/src/clis/weibo/me.ts +77 -0
  599. package/src/clis/weibo/post.ts +77 -0
  600. package/src/clis/weibo/user.ts +64 -0
  601. package/src/clis/weibo/utils.ts +32 -0
  602. package/src/clis/weread/search.ts +3 -2
  603. package/src/clis/xueqiu/danjuan-utils.test.ts +49 -0
  604. package/src/clis/xueqiu/danjuan-utils.ts +176 -0
  605. package/src/clis/xueqiu/fund-holdings.ts +32 -0
  606. package/src/clis/xueqiu/fund-snapshot.ts +27 -0
  607. package/src/clis/xueqiu/search.yaml +2 -1
  608. package/src/clis/yahoo-finance/quote.ts +0 -1
  609. package/src/clis/youtube/channel.ts +155 -0
  610. package/src/clis/youtube/comments.ts +97 -0
  611. package/src/clis/youtube/search.ts +0 -1
  612. package/src/clis/youtube/transcript.ts +5 -4
  613. package/src/clis/youtube/video.ts +3 -2
  614. package/src/clis/zhihu/search.yaml +2 -1
  615. package/src/daemon.ts +5 -4
  616. package/src/discovery.ts +12 -34
  617. package/src/doctor.ts +3 -2
  618. package/src/download/index.test.ts +93 -2
  619. package/src/download/index.ts +44 -23
  620. package/src/download/media-download.ts +5 -3
  621. package/src/engine.test.ts +84 -3
  622. package/src/execution.ts +62 -46
  623. package/src/explore.ts +21 -90
  624. package/src/external-clis.yaml +0 -25
  625. package/src/external.test.ts +9 -0
  626. package/src/external.ts +12 -10
  627. package/src/generate.ts +4 -41
  628. package/src/hooks.test.ts +126 -0
  629. package/src/hooks.ts +90 -0
  630. package/src/interceptor.ts +73 -23
  631. package/src/main.ts +2 -0
  632. package/src/output.ts +14 -6
  633. package/src/pipeline/executor.ts +1 -1
  634. package/src/pipeline/steps/browser.ts +1 -3
  635. package/src/pipeline/steps/download.test.ts +136 -0
  636. package/src/pipeline/steps/download.ts +47 -34
  637. package/src/pipeline/steps/fetch.test.ts +179 -0
  638. package/src/pipeline/steps/fetch.ts +39 -23
  639. package/src/pipeline/steps/transform.ts +2 -6
  640. package/src/pipeline/template.test.ts +28 -0
  641. package/src/pipeline/template.ts +67 -79
  642. package/src/pipeline/transform.test.ts +20 -0
  643. package/src/plugin.test.ts +251 -3
  644. package/src/plugin.ts +265 -21
  645. package/src/record.ts +12 -84
  646. package/src/registry-api.ts +2 -0
  647. package/src/registry.ts +7 -4
  648. package/src/runtime.ts +14 -4
  649. package/src/snapshotFormatter.ts +43 -121
  650. package/src/utils.ts +39 -0
  651. package/src/validate.ts +3 -5
  652. package/src/weread-search-regression.test.ts +44 -0
  653. package/src/yaml-schema.ts +28 -0
  654. package/tests/e2e/browser-auth.test.ts +25 -0
  655. package/tests/e2e/browser-public-extended.test.ts +162 -0
  656. package/tests/e2e/browser-public.test.ts +7 -146
  657. package/tests/e2e/plugin-management.test.ts +137 -0
  658. package/tests/e2e/public-commands.test.ts +34 -1
  659. package/vitest.config.ts +33 -8
  660. package/.github/workflows/pkg-pr-new.yml +0 -30
  661. package/dist/clis/douban/shared.d.ts +0 -4
  662. package/dist/clis/douban/shared.js +0 -155
  663. package/src/clis/douban/shared.ts +0 -165
  664. /package/dist/clis/boss/{common.d.ts → utils.d.ts} +0 -0
  665. /package/dist/clis/boss/{common.js → utils.js} +0 -0
  666. /package/dist/clis/doubao/{common.d.ts → utils.d.ts} +0 -0
  667. /package/dist/clis/doubao/{common.js → utils.js} +0 -0
  668. /package/dist/clis/doubao-app/{common.d.ts → utils.d.ts} +0 -0
  669. /package/dist/clis/doubao-app/{common.js → utils.js} +0 -0
  670. /package/dist/clis/jike/{shared.d.ts → utils.d.ts} +0 -0
  671. /package/dist/clis/jike/{shared.js → utils.js} +0 -0
  672. /package/dist/clis/medium/{shared.d.ts → utils.d.ts} +0 -0
  673. /package/dist/clis/sinablog/{shared.d.ts → utils.d.ts} +0 -0
  674. /package/dist/clis/sinablog/{shared.js → utils.js} +0 -0
  675. /package/dist/clis/substack/{shared.d.ts → utils.d.ts} +0 -0
  676. /package/src/clis/boss/{common.ts → utils.ts} +0 -0
  677. /package/src/clis/doubao/{common.ts → utils.ts} +0 -0
  678. /package/src/clis/doubao-app/{common.ts → utils.ts} +0 -0
  679. /package/src/clis/jike/{shared.ts → utils.ts} +0 -0
  680. /package/src/clis/sinablog/{shared.ts → utils.ts} +0 -0
@@ -1,4 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
+ import { SelectorError } from '../../errors.js';
2
3
  export const composerCommand = cli({
3
4
  site: 'cursor',
4
5
  name: 'composer',
@@ -26,7 +27,7 @@ export const composerCommand = cli({
26
27
  return true;
27
28
  })(${JSON.stringify(textToInsert)})`);
28
29
  if (!typed) {
29
- throw new Error('Could not find Cursor Composer input element after pressing Cmd+I.');
30
+ throw new SelectorError('Cursor Composer input element', 'Could not find Cursor Composer input element after pressing Cmd+I.');
30
31
  }
31
32
  await page.wait(0.5);
32
33
  await page.pressKey('Enter');
@@ -1,25 +1,2 @@
1
- import { cli, Strategy } from '../../registry.js';
2
- import * as fs from 'fs';
3
- export const dumpCommand = cli({
4
- site: 'cursor',
5
- name: 'dump',
6
- description: 'Dump the DOM and Accessibility tree of Cursor for reverse-engineering',
7
- domain: 'localhost',
8
- strategy: Strategy.UI,
9
- browser: true,
10
- columns: ['action', 'files'],
11
- func: async (page) => {
12
- // Extract full HTML
13
- const dom = await page.evaluate('document.body.innerHTML');
14
- fs.writeFileSync('/tmp/cursor-dom.html', dom);
15
- // Get accessibility snapshot
16
- const snap = await page.snapshot({ interactive: false });
17
- fs.writeFileSync('/tmp/cursor-snapshot.json', JSON.stringify(snap, null, 2));
18
- return [
19
- {
20
- action: 'Dom extraction finished',
21
- files: '/tmp/cursor-dom.html, /tmp/cursor-snapshot.json',
22
- },
23
- ];
24
- },
25
- });
1
+ import { makeDumpCommand } from '../_shared/desktop-commands.js';
2
+ export const dumpCommand = makeDumpCommand('cursor');
@@ -1,18 +1,2 @@
1
- import { cli, Strategy } from '../../registry.js';
2
- export const newCommand = cli({
3
- site: 'cursor',
4
- name: 'new',
5
- description: 'Start a new Cursor chat or Composer session',
6
- domain: 'localhost',
7
- strategy: Strategy.UI,
8
- browser: true,
9
- args: [],
10
- columns: ['Status'],
11
- func: async (page) => {
12
- // Use keyboard shortcut — most robust approach, avoids brittle DOM selectors
13
- const isMac = process.platform === 'darwin';
14
- await page.pressKey(isMac ? 'Meta+N' : 'Control+N');
15
- await page.wait(1);
16
- return [{ Status: 'Success' }];
17
- },
18
- });
1
+ import { makeNewCommand } from '../_shared/desktop-commands.js';
2
+ export const newCommand = makeNewCommand('cursor', 'Cursor chat or Composer');
@@ -1,4 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
+ import { EmptyResultError } from '../../errors.js';
2
3
  export const readCommand = cli({
3
4
  site: 'cursor',
4
5
  name: 'read',
@@ -36,7 +37,7 @@ export const readCommand = cli({
36
37
  })()
37
38
  `);
38
39
  if (!history || history.length === 0) {
39
- throw new Error('No conversation history found in Cursor.');
40
+ throw new EmptyResultError('cursor read', 'No conversation history found in Cursor.');
40
41
  }
41
42
  return history;
42
43
  },
@@ -1,31 +1,2 @@
1
- import * as fs from 'node:fs';
2
- import { cli, Strategy } from '../../registry.js';
3
- function makeScreenshotCommand(site) {
4
- return cli({
5
- site,
6
- name: 'screenshot',
7
- description: `Capture a snapshot of the current ${site} window (DOM + Accessibility tree)`,
8
- domain: 'localhost',
9
- strategy: Strategy.UI,
10
- browser: true,
11
- args: [
12
- { name: 'output', required: false, help: `Output file path (default: /tmp/${site}-snapshot.txt)` },
13
- ],
14
- columns: ['Status', 'File'],
15
- func: async (page, kwargs) => {
16
- const outputPath = kwargs.output || `/tmp/${site}-snapshot.txt`;
17
- // Get both the accessibility snapshot and the raw DOM HTML
18
- const snap = await page.snapshot({ compact: true });
19
- const html = await page.evaluate('document.documentElement.outerHTML');
20
- const htmlPath = outputPath.replace(/\.\w+$/, '') + '-dom.html';
21
- const snapPath = outputPath.replace(/\.\w+$/, '') + '-a11y.txt';
22
- fs.writeFileSync(htmlPath, html);
23
- fs.writeFileSync(snapPath, typeof snap === 'string' ? snap : JSON.stringify(snap, null, 2));
24
- return [
25
- { Status: 'Success', File: htmlPath },
26
- { Status: 'Success', File: snapPath },
27
- ];
28
- },
29
- });
30
- }
1
+ import { makeScreenshotCommand } from '../_shared/desktop-commands.js';
31
2
  export const screenshotCursor = makeScreenshotCommand('cursor');
@@ -1,4 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
+ import { SelectorError } from '../../errors.js';
2
3
  export const sendCommand = cli({
3
4
  site: 'cursor',
4
5
  name: 'send',
@@ -23,7 +24,7 @@ export const sendCommand = cli({
23
24
  return true;
24
25
  })(${JSON.stringify(textToInsert)})`);
25
26
  if (!injected) {
26
- throw new Error('Could not find Cursor Composer input element.');
27
+ throw new SelectorError('Cursor Composer input element');
27
28
  }
28
29
  // Submit the command. In Cursor, Enter usually submits the chat.
29
30
  await page.wait(0.5);
@@ -1,21 +1,2 @@
1
- import { cli, Strategy } from '../../registry.js';
2
- export const statusCommand = cli({
3
- site: 'cursor',
4
- name: 'status',
5
- description: 'Check active CDP connection to Cursor AI Editor',
6
- domain: 'localhost',
7
- strategy: Strategy.UI, // Interactive UI manipulation
8
- browser: true,
9
- columns: ['Status', 'Url', 'Title'],
10
- func: async (page) => {
11
- const url = await page.evaluate('window.location.href');
12
- const title = await page.evaluate('document.title');
13
- return [
14
- {
15
- Status: 'Connected',
16
- Url: url,
17
- Title: title,
18
- },
19
- ];
20
- },
21
- });
1
+ import { makeStatusCommand } from '../_shared/desktop-commands.js';
2
+ export const statusCommand = makeStatusCommand('cursor', 'Cursor AI Editor');
@@ -0,0 +1,25 @@
1
+ site: dictionary
2
+ name: examples
3
+ description: Read real-world example sentences utilizing the word
4
+ domain: api.dictionaryapi.dev
5
+ strategy: public
6
+ browser: false
7
+
8
+ args:
9
+ word:
10
+ type: string
11
+ required: true
12
+ positional: true
13
+ description: Word to get example sentences for
14
+
15
+ pipeline:
16
+ - fetch:
17
+ url: "https://api.dictionaryapi.dev/api/v2/entries/en/${{ args.word | urlencode }}"
18
+
19
+ - map:
20
+ word: "${{ item.word }}"
21
+ example: "${{ (() => { if (item.meanings) { for (const m of item.meanings) { if (m.definitions) { for (const d of m.definitions) { if (d.example) return d.example; } } } } return 'No example found in API.'; })() }}"
22
+
23
+ - limit: 1
24
+
25
+ columns: [word, example]
@@ -0,0 +1,27 @@
1
+ site: dictionary
2
+ name: search
3
+ description: Search the Free Dictionary API for definitions, parts of speech, and pronunciations.
4
+ domain: api.dictionaryapi.dev
5
+ strategy: public
6
+ browser: false
7
+
8
+ args:
9
+ word:
10
+ type: string
11
+ required: true
12
+ positional: true
13
+ description: Word to define (e.g., serendipity)
14
+
15
+ pipeline:
16
+ - fetch:
17
+ url: "https://api.dictionaryapi.dev/api/v2/entries/en/${{ args.word | urlencode }}"
18
+
19
+ - map:
20
+ word: "${{ item.word }}"
21
+ phonetic: "${{ (() => { if (item.phonetic) return item.phonetic; if (item.phonetics) { for (const p of item.phonetics) { if (p.text) return p.text; } } return ''; })() }}"
22
+ type: "${{ (() => { if (item.meanings && item.meanings[0] && item.meanings[0].partOfSpeech) return item.meanings[0].partOfSpeech; return 'N/A'; })() }}"
23
+ definition: "${{ (() => { if (item.meanings && item.meanings[0] && item.meanings[0].definitions && item.meanings[0].definitions[0] && item.meanings[0].definitions[0].definition) return item.meanings[0].definitions[0].definition; return 'No definition found in API.'; })() }}"
24
+
25
+ - limit: 1
26
+
27
+ columns: [word, phonetic, type, definition]
@@ -0,0 +1,25 @@
1
+ site: dictionary
2
+ name: synonyms
3
+ description: Find synonyms for a specific word
4
+ domain: api.dictionaryapi.dev
5
+ strategy: public
6
+ browser: false
7
+
8
+ args:
9
+ word:
10
+ type: string
11
+ required: true
12
+ positional: true
13
+ description: Word to find synonyms for (e.g., serendipity)
14
+
15
+ pipeline:
16
+ - fetch:
17
+ url: "https://api.dictionaryapi.dev/api/v2/entries/en/${{ args.word | urlencode }}"
18
+
19
+ - map:
20
+ word: "${{ item.word }}"
21
+ synonyms: "${{ (() => { const s = new Set(); if (item.meanings) { for (const m of item.meanings) { if (m.synonyms) { for (const syn of m.synonyms) s.add(syn); } if (m.definitions) { for (const d of m.definitions) { if (d.synonyms) { for (const syn of d.synonyms) s.add(syn); } } } } } const arr = Array.from(s); return arr.length > 0 ? arr.slice(0, 5).join(', ') : 'No synonyms found in API.'; })() }}"
22
+
23
+ - limit: 1
24
+
25
+ columns: [word, synonyms]
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { loadDoubanBookHot } from './shared.js';
2
+ import { loadDoubanBookHot } from './utils.js';
3
3
  cli({
4
4
  site: 'douban',
5
5
  name: 'book-hot',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { loadDoubanMovieHot } from './shared.js';
2
+ import { loadDoubanMovieHot } from './utils.js';
3
3
  cli({
4
4
  site: 'douban',
5
5
  name: 'movie-hot',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { searchDouban } from './shared.js';
2
+ import { searchDouban } from './utils.js';
3
3
  cli({
4
4
  site: 'douban',
5
5
  name: 'search',
@@ -1,7 +1,10 @@
1
1
  /**
2
- * Douban movie adapter utilities.
2
+ * Douban adapter utilities.
3
3
  */
4
4
  import type { IPage } from '../../types.js';
5
+ export declare function loadDoubanBookHot(page: IPage, limit: number): Promise<any[]>;
6
+ export declare function loadDoubanMovieHot(page: IPage, limit: number): Promise<any[]>;
7
+ export declare function searchDouban(page: IPage, type: string, keyword: string, limit: number): Promise<any[]>;
5
8
  /**
6
9
  * Get current user's Douban ID from movie.douban.com/mine page
7
10
  */
@@ -1,6 +1,161 @@
1
1
  /**
2
- * Douban movie adapter utilities.
2
+ * Douban adapter utilities.
3
3
  */
4
+ import { CliError } from '../../errors.js';
5
+ function clampLimit(limit) {
6
+ return Math.max(1, Math.min(limit || 20, 50));
7
+ }
8
+ async function ensureDoubanReady(page) {
9
+ const state = await page.evaluate(`
10
+ (() => {
11
+ const title = (document.title || '').trim();
12
+ const href = (location.href || '').trim();
13
+ const blocked = href.includes('sec.douban.com') || /登录跳转/.test(title) || /异常请求/.test(document.body?.innerText || '');
14
+ return { blocked, title, href };
15
+ })()
16
+ `);
17
+ if (state?.blocked) {
18
+ throw new CliError('AUTH_REQUIRED', 'Douban requires a logged-in browser session before these commands can load data.', 'Please sign in to douban.com in the browser that opencli reuses, then rerun the command.');
19
+ }
20
+ }
21
+ export async function loadDoubanBookHot(page, limit) {
22
+ const safeLimit = clampLimit(limit);
23
+ await page.goto('https://book.douban.com/chart');
24
+ await page.wait(4);
25
+ await ensureDoubanReady(page);
26
+ const data = await page.evaluate(`
27
+ (() => {
28
+ const normalize = (value) => (value || '').replace(/\\s+/g, ' ').trim();
29
+ const books = [];
30
+ for (const el of Array.from(document.querySelectorAll('.media.clearfix'))) {
31
+ try {
32
+ const titleEl = el.querySelector('h2 a[href*="/subject/"]');
33
+ const title = normalize(titleEl?.textContent);
34
+ let url = titleEl?.getAttribute('href') || '';
35
+ if (!title || !url) continue;
36
+ if (!url.startsWith('http')) url = 'https://book.douban.com' + url;
37
+
38
+ const info = normalize(el.querySelector('.subject-abstract, .pl, .pub')?.textContent);
39
+ const infoParts = info.split('/').map((part) => part.trim()).filter(Boolean);
40
+ const ratingText = normalize(el.querySelector('.subject-rating .font-small, .rating_nums, .rating')?.textContent);
41
+ const quote = Array.from(el.querySelectorAll('.subject-tags .tag'))
42
+ .map((node) => normalize(node.textContent))
43
+ .filter(Boolean)
44
+ .join(' / ');
45
+
46
+ books.push({
47
+ rank: parseInt(normalize(el.querySelector('.green-num-box')?.textContent), 10) || books.length + 1,
48
+ title,
49
+ rating: parseFloat(ratingText) || 0,
50
+ quote,
51
+ author: infoParts[0] || '',
52
+ publisher: infoParts.find((part) => /出版社|出版公司|Press/i.test(part)) || infoParts[2] || '',
53
+ year: infoParts.find((part) => /\\d{4}(?:-\\d{1,2})?/.test(part))?.match(/\\d{4}/)?.[0] || '',
54
+ price: infoParts.find((part) => /元|USD|\\$|¥/.test(part)) || '',
55
+ url,
56
+ cover: el.querySelector('img')?.getAttribute('src') || '',
57
+ });
58
+ } catch {}
59
+ }
60
+ return books.slice(0, ${safeLimit});
61
+ })()
62
+ `);
63
+ return Array.isArray(data) ? data : [];
64
+ }
65
+ export async function loadDoubanMovieHot(page, limit) {
66
+ const safeLimit = clampLimit(limit);
67
+ await page.goto('https://movie.douban.com/chart');
68
+ await page.wait(4);
69
+ await ensureDoubanReady(page);
70
+ const data = await page.evaluate(`
71
+ (() => {
72
+ const normalize = (value) => (value || '').replace(/\\s+/g, ' ').trim();
73
+ const results = [];
74
+ for (const el of Array.from(document.querySelectorAll('.item'))) {
75
+ const titleEl = el.querySelector('.pl2 a');
76
+ const title = normalize(titleEl?.textContent);
77
+ let url = titleEl?.getAttribute('href') || '';
78
+ if (!title || !url) continue;
79
+ if (!url.startsWith('http')) url = 'https://movie.douban.com' + url;
80
+
81
+ const info = normalize(el.querySelector('.pl2 p')?.textContent);
82
+ const infoParts = info.split('/').map((part) => part.trim()).filter(Boolean);
83
+ const releaseIndex = (() => {
84
+ for (let i = infoParts.length - 1; i >= 0; i -= 1) {
85
+ if (/\\d{4}-\\d{2}-\\d{2}|\\d{4}\\/\\d{2}\\/\\d{2}/.test(infoParts[i])) return i;
86
+ }
87
+ return -1;
88
+ })();
89
+ const directorPart = releaseIndex >= 1 ? infoParts[releaseIndex - 1] : '';
90
+ const regionPart = releaseIndex >= 2 ? infoParts[releaseIndex - 2] : '';
91
+ const yearMatch = info.match(/\\b(19|20)\\d{2}\\b/);
92
+ results.push({
93
+ rank: results.length + 1,
94
+ title,
95
+ rating: parseFloat(normalize(el.querySelector('.rating_nums')?.textContent)) || 0,
96
+ quote: normalize(el.querySelector('.inq')?.textContent),
97
+ director: directorPart.replace(/^导演:\\s*/, ''),
98
+ year: yearMatch?.[0] || '',
99
+ region: regionPart,
100
+ url,
101
+ cover: el.querySelector('img')?.getAttribute('src') || '',
102
+ });
103
+ if (results.length >= ${safeLimit}) break;
104
+ }
105
+ return results;
106
+ })()
107
+ `);
108
+ return Array.isArray(data) ? data : [];
109
+ }
110
+ export async function searchDouban(page, type, keyword, limit) {
111
+ const safeLimit = clampLimit(limit);
112
+ await page.goto(`https://search.douban.com/${encodeURIComponent(type)}/subject_search?search_text=${encodeURIComponent(keyword)}`);
113
+ await page.wait(2);
114
+ await ensureDoubanReady(page);
115
+ const data = await page.evaluate(`
116
+ (async () => {
117
+ const type = ${JSON.stringify(type)};
118
+ const normalize = (value) => (value || '').replace(/\\s+/g, ' ').trim();
119
+ const seen = new Set();
120
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
121
+
122
+ for (let i = 0; i < 20; i += 1) {
123
+ if (document.querySelector('.item-root .title-text, .item-root .title a')) break;
124
+ await sleep(300);
125
+ }
126
+
127
+ const items = Array.from(document.querySelectorAll('.item-root'));
128
+
129
+ const results = [];
130
+ for (const el of items) {
131
+ const titleEl = el.querySelector('.title-text, .title a, a[title]');
132
+ const title = normalize(titleEl?.textContent) || normalize(titleEl?.getAttribute('title'));
133
+ let url = titleEl?.getAttribute('href') || '';
134
+ if (!title || !url) continue;
135
+ if (!url.startsWith('http')) url = 'https://search.douban.com' + url;
136
+ if (!url.includes('/subject/') || seen.has(url)) continue;
137
+ seen.add(url);
138
+ const ratingText = normalize(el.querySelector('.rating_nums')?.textContent);
139
+ const abstract = normalize(
140
+ el.querySelector('.meta.abstract, .meta, .abstract, p')?.textContent,
141
+ );
142
+ results.push({
143
+ rank: results.length + 1,
144
+ id: url.match(/subject\\/(\\d+)/)?.[1] || '',
145
+ type,
146
+ title,
147
+ rating: ratingText.includes('.') ? parseFloat(ratingText) : 0,
148
+ abstract: abstract.slice(0, 100) + (abstract.length > 100 ? '...' : ''),
149
+ url,
150
+ cover: el.querySelector('img')?.getAttribute('src') || '',
151
+ });
152
+ if (results.length >= ${safeLimit}) break;
153
+ }
154
+ return results;
155
+ })()
156
+ `);
157
+ return Array.isArray(data) ? data : [];
158
+ }
4
159
  /**
5
160
  * Get current user's Douban ID from movie.douban.com/mine page
6
161
  */
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { DOUBAO_DOMAIN, getDoubaoTranscriptLines, getDoubaoVisibleTurns, sendDoubaoMessage, waitForDoubaoResponse } from './common.js';
2
+ import { DOUBAO_DOMAIN, getDoubaoTranscriptLines, getDoubaoVisibleTurns, sendDoubaoMessage, waitForDoubaoResponse } from './utils.js';
3
3
  export const askCommand = cli({
4
4
  site: 'doubao',
5
5
  name: 'ask',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { DOUBAO_DOMAIN, startNewDoubaoChat } from './common.js';
2
+ import { DOUBAO_DOMAIN, startNewDoubaoChat } from './utils.js';
3
3
  export const newCommand = cli({
4
4
  site: 'doubao',
5
5
  name: 'new',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { DOUBAO_DOMAIN, getDoubaoVisibleTurns } from './common.js';
2
+ import { DOUBAO_DOMAIN, getDoubaoVisibleTurns } from './utils.js';
3
3
  export const readCommand = cli({
4
4
  site: 'doubao',
5
5
  name: 'read',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { DOUBAO_DOMAIN, sendDoubaoMessage } from './common.js';
2
+ import { DOUBAO_DOMAIN, sendDoubaoMessage } from './utils.js';
3
3
  export const sendCommand = cli({
4
4
  site: 'doubao',
5
5
  name: 'send',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { DOUBAO_DOMAIN, getDoubaoPageState } from './common.js';
2
+ import { DOUBAO_DOMAIN, getDoubaoPageState } from './utils.js';
3
3
  export const statusCommand = cli({
4
4
  site: 'doubao',
5
5
  name: 'status',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { SEL, injectTextScript, clickSendScript, pollResponseScript } from './common.js';
2
+ import { SEL, injectTextScript, clickSendScript, pollResponseScript } from './utils.js';
3
3
  export const askCommand = cli({
4
4
  site: 'doubao-app',
5
5
  name: 'ask',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { clickNewChatScript } from './common.js';
2
+ import { clickNewChatScript } from './utils.js';
3
3
  export const newCommand = cli({
4
4
  site: 'doubao-app',
5
5
  name: 'new',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { readMessagesScript } from './common.js';
2
+ import { readMessagesScript } from './utils.js';
3
3
  export const readCommand = cli({
4
4
  site: 'doubao-app',
5
5
  name: 'read',
@@ -1,5 +1,5 @@
1
1
  import { cli, Strategy } from '../../registry.js';
2
- import { injectTextScript, clickSendScript } from './common.js';
2
+ import { injectTextScript, clickSendScript } from './utils.js';
3
3
  export const sendCommand = cli({
4
4
  site: 'doubao-app',
5
5
  name: 'send',
@@ -0,0 +1,10 @@
1
+ import type { IPage } from '../../../types.js';
2
+ export interface FetchOptions {
3
+ body?: unknown;
4
+ headers?: Record<string, string>;
5
+ }
6
+ /**
7
+ * Execute a fetch() call inside the Chrome browser context via page.evaluate.
8
+ * This ensures a_bogus signing and cookies are handled automatically by the browser.
9
+ */
10
+ export declare function browserFetch(page: IPage, method: 'GET' | 'POST', url: string, options?: FetchOptions): Promise<unknown>;
@@ -0,0 +1,30 @@
1
+ import { CommandExecutionError } from '../../../errors.js';
2
+ /**
3
+ * Execute a fetch() call inside the Chrome browser context via page.evaluate.
4
+ * This ensures a_bogus signing and cookies are handled automatically by the browser.
5
+ */
6
+ export async function browserFetch(page, method, url, options = {}) {
7
+ const js = `
8
+ (async () => {
9
+ const res = await fetch(${JSON.stringify(url)}, {
10
+ method: ${JSON.stringify(method)},
11
+ credentials: 'include',
12
+ headers: {
13
+ 'Content-Type': 'application/json',
14
+ ...${JSON.stringify(options.headers ?? {})}
15
+ },
16
+ ${options.body ? `body: JSON.stringify(${JSON.stringify(options.body)}),` : ''}
17
+ });
18
+ return res.json();
19
+ })()
20
+ `;
21
+ const result = await page.evaluate(js);
22
+ if (result && typeof result === 'object' && 'status_code' in result) {
23
+ const code = result.status_code;
24
+ if (code !== 0) {
25
+ const msg = result.status_msg ?? 'unknown error';
26
+ throw new CommandExecutionError(`Douyin API error ${code}: ${msg}`);
27
+ }
28
+ }
29
+ return result;
30
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { browserFetch } from './browser-fetch.js';
3
+ function makePage(result) {
4
+ return {
5
+ goto: vi.fn(), evaluate: vi.fn().mockResolvedValue(result),
6
+ getCookies: vi.fn(), snapshot: vi.fn(), click: vi.fn(),
7
+ typeText: vi.fn(), pressKey: vi.fn(), scrollTo: vi.fn(),
8
+ getFormState: vi.fn(), wait: vi.fn(), tabs: vi.fn(),
9
+ closeTab: vi.fn(), newTab: vi.fn(), selectTab: vi.fn(),
10
+ networkRequests: vi.fn(), consoleMessages: vi.fn(),
11
+ scroll: vi.fn(), autoScroll: vi.fn(),
12
+ installInterceptor: vi.fn(), getInterceptedRequests: vi.fn(),
13
+ screenshot: vi.fn(),
14
+ };
15
+ }
16
+ describe('browserFetch', () => {
17
+ it('returns parsed JSON on success', async () => {
18
+ const page = makePage({ status_code: 0, data: { ak: 'KEY' } });
19
+ const result = await browserFetch(page, 'GET', 'https://creator.douyin.com/api/test');
20
+ expect(result).toEqual({ status_code: 0, data: { ak: 'KEY' } });
21
+ });
22
+ it('throws when status_code is non-zero', async () => {
23
+ const page = makePage({ status_code: 8, message: 'fail' });
24
+ await expect(browserFetch(page, 'GET', 'https://creator.douyin.com/api/test')).rejects.toThrow('Douyin API error 8');
25
+ });
26
+ it('returns result even when no status_code field', async () => {
27
+ const page = makePage({ some_field: 'value' });
28
+ const result = await browserFetch(page, 'GET', 'https://creator.douyin.com/api/test');
29
+ expect(result).toEqual({ some_field: 'value' });
30
+ });
31
+ });
@@ -0,0 +1 @@
1
+ export declare function generateCreationId(): string;
@@ -0,0 +1,5 @@
1
+ const CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789';
2
+ export function generateCreationId() {
3
+ const random = Array.from({ length: 4 }, () => CHARS[Math.floor(Math.random() * CHARS.length)]).join('');
4
+ return 'pin' + random + Date.now();
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { generateCreationId } from './creation-id.js';
3
+ describe('generateCreationId', () => {
4
+ it('starts with "pin"', () => {
5
+ expect(generateCreationId()).toMatch(/^pin/);
6
+ });
7
+ it('has 4 random lowercase-alphanumeric chars after "pin"', () => {
8
+ expect(generateCreationId()).toMatch(/^pin[a-z0-9]{4}/);
9
+ });
10
+ it('ends with a numeric timestamp (ms)', () => {
11
+ const before = Date.now();
12
+ const id = generateCreationId();
13
+ const after = Date.now();
14
+ const ts = parseInt(id.replace(/^pin[a-z0-9]{4}/, ''), 10);
15
+ expect(ts).toBeGreaterThanOrEqual(before);
16
+ expect(ts).toBeLessThanOrEqual(after);
17
+ });
18
+ it('generates unique IDs', () => {
19
+ const ids = new Set(Array.from({ length: 100 }, generateCreationId));
20
+ expect(ids.size).toBe(100);
21
+ });
22
+ });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * ImageX cover image uploader.
3
+ *
4
+ * Uploads a JPEG/PNG image to ByteDance ImageX via a pre-signed PUT URL
5
+ * obtained from the Douyin "apply cover upload" API.
6
+ */
7
+ export interface ImageXUploadInfo {
8
+ /** Pre-signed PUT target URL (provided by the apply cover upload API) */
9
+ upload_url: string;
10
+ /** Image URI to use in create_v2 (returned from the apply step) */
11
+ store_uri: string;
12
+ }
13
+ /**
14
+ * Upload a cover image to ByteDance ImageX via a pre-signed PUT URL.
15
+ *
16
+ * @param imagePath - Local file path to the image (JPEG/PNG/etc.)
17
+ * @param uploadInfo - Upload URL and store_uri from the apply cover upload API
18
+ * @returns The store_uri (= image_uri for use in create_v2)
19
+ */
20
+ export declare function imagexUpload(imagePath: string, uploadInfo: ImageXUploadInfo): Promise<string>;