@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
package/dist/explore.js CHANGED
@@ -8,11 +8,12 @@
8
8
  import * as fs from 'node:fs';
9
9
  import * as path from 'node:path';
10
10
  import { DEFAULT_BROWSER_EXPLORE_TIMEOUT, browserSession, runWithTimeout } from './runtime.js';
11
- import { VOLATILE_PARAMS, SEARCH_PARAMS, PAGINATION_PARAMS, LIMIT_PARAMS, FIELD_ROLES } from './constants.js';
11
+ import { LIMIT_PARAMS } from './constants.js';
12
12
  import { detectFramework } from './scripts/framework.js';
13
13
  import { discoverStores } from './scripts/store.js';
14
14
  import { interactFuzz } from './scripts/interact.js';
15
15
  import { log } from './logger.js';
16
+ import { urlToPattern, findArrayPath, flattenFields, detectFieldRoles, inferCapabilityName, inferStrategy, detectAuthFromHeaders, classifyQueryParams, } from './analysis.js';
16
17
  // ── Site name detection ────────────────────────────────────────────────────
17
18
  const KNOWN_SITE_ALIASES = {
18
19
  'x.com': 'twitter', 'twitter.com': 'twitter',
@@ -75,78 +76,16 @@ function parseNetworkRequests(raw) {
75
76
  }
76
77
  return [];
77
78
  }
78
- function urlToPattern(url) {
79
- try {
80
- const p = new URL(url);
81
- const pathNorm = p.pathname.replace(/\/\d+/g, '/{id}').replace(/\/[0-9a-fA-F]{8,}/g, '/{hex}').replace(/\/BV[a-zA-Z0-9]{10}/g, '/{bvid}');
82
- const params = [];
83
- p.searchParams.forEach((_v, k) => { if (!VOLATILE_PARAMS.has(k))
84
- params.push(k); });
85
- return `${p.host}${pathNorm}${params.length ? '?' + params.sort().map(k => `${k}={}`).join('&') : ''}`;
86
- }
87
- catch {
88
- return url;
89
- }
90
- }
91
- function detectAuthIndicators(headers) {
92
- if (!headers)
93
- return [];
94
- const indicators = [];
95
- const keys = Object.keys(headers).map(k => k.toLowerCase());
96
- if (keys.some(k => k === 'authorization'))
97
- indicators.push('bearer');
98
- if (keys.some(k => k.startsWith('x-csrf') || k.startsWith('x-xsrf')))
99
- indicators.push('csrf');
100
- if (keys.some(k => k.startsWith('x-s') || k === 'x-t' || k === 'x-s-common'))
101
- indicators.push('signature');
102
- return indicators;
103
- }
104
79
  function analyzeResponseBody(body) {
105
80
  if (!body || typeof body !== 'object')
106
81
  return null;
107
- const candidates = [];
108
- function findArrays(obj, path, depth) {
109
- if (depth > 4)
110
- return;
111
- if (Array.isArray(obj) && obj.length >= 2 && obj.some(item => item && typeof item === 'object' && !Array.isArray(item))) {
112
- candidates.push({ path, items: obj });
113
- }
114
- if (obj && typeof obj === 'object' && !Array.isArray(obj)) {
115
- for (const [key, val] of Object.entries(obj))
116
- findArrays(val, path ? `${path}.${key}` : key, depth + 1);
117
- }
118
- }
119
- findArrays(body, '', 0);
120
- if (!candidates.length)
82
+ const result = findArrayPath(body);
83
+ if (!result)
121
84
  return null;
122
- candidates.sort((a, b) => b.items.length - a.items.length);
123
- const best = candidates[0];
124
- const sample = best.items[0];
85
+ const sample = result.items[0];
125
86
  const sampleFields = sample && typeof sample === 'object' ? flattenFields(sample, '', 2) : [];
126
- const detectedFields = {};
127
- for (const [role, aliases] of Object.entries(FIELD_ROLES)) {
128
- for (const f of sampleFields) {
129
- if (aliases.includes(f.split('.').pop()?.toLowerCase() ?? '')) {
130
- detectedFields[role] = f;
131
- break;
132
- }
133
- }
134
- }
135
- return { itemPath: best.path || null, itemCount: best.items.length, detectedFields, sampleFields };
136
- }
137
- function flattenFields(obj, prefix, maxDepth) {
138
- if (maxDepth <= 0 || !obj || typeof obj !== 'object')
139
- return [];
140
- const names = [];
141
- const record = obj;
142
- for (const key of Object.keys(record)) {
143
- const full = prefix ? `${prefix}.${key}` : key;
144
- names.push(full);
145
- const val = record[key];
146
- if (val && typeof val === 'object' && !Array.isArray(val))
147
- names.push(...flattenFields(val, full, maxDepth - 1));
148
- }
149
- return names;
87
+ const detectedFields = detectFieldRoles(sampleFields);
88
+ return { itemPath: result.path || null, itemCount: result.items.length, detectedFields, sampleFields };
150
89
  }
151
90
  function isBooleanRecord(value) {
152
91
  return typeof value === 'object' && value !== null && !Array.isArray(value)
@@ -176,39 +115,6 @@ function scoreEndpoint(ep) {
176
115
  s -= 3;
177
116
  return s;
178
117
  }
179
- function inferCapabilityName(url, goal) {
180
- if (goal)
181
- return goal;
182
- const u = url.toLowerCase();
183
- if (u.includes('hot') || u.includes('popular') || u.includes('ranking') || u.includes('trending'))
184
- return 'hot';
185
- if (u.includes('search'))
186
- return 'search';
187
- if (u.includes('feed') || u.includes('timeline') || u.includes('dynamic'))
188
- return 'feed';
189
- if (u.includes('comment') || u.includes('reply'))
190
- return 'comments';
191
- if (u.includes('history'))
192
- return 'history';
193
- if (u.includes('profile') || u.includes('userinfo') || u.includes('/me'))
194
- return 'me';
195
- if (u.includes('favorite') || u.includes('collect') || u.includes('bookmark'))
196
- return 'favorite';
197
- try {
198
- const segs = new URL(url).pathname.split('/').filter(s => s && !s.match(/^\d+$/) && !s.match(/^[0-9a-f]{8,}$/i));
199
- if (segs.length)
200
- return segs[segs.length - 1].replace(/[^a-z0-9]/gi, '_').toLowerCase();
201
- }
202
- catch { }
203
- return 'data';
204
- }
205
- function inferStrategy(authIndicators) {
206
- if (authIndicators.includes('signature'))
207
- return 'intercept';
208
- if (authIndicators.includes('bearer') || authIndicators.includes('csrf'))
209
- return 'header';
210
- return 'cookie';
211
- }
212
118
  // ── Framework detection ────────────────────────────────────────────────────
213
119
  const FRAMEWORK_DETECT_JS = detectFramework.toString();
214
120
  // ── Store discovery ────────────────────────────────────────────────────────
@@ -231,18 +137,13 @@ function analyzeEndpoints(networkEntries) {
231
137
  const key = `${entry.method}:${pattern}`;
232
138
  if (seen.has(key))
233
139
  continue;
234
- const qp = [];
235
- try {
236
- new URL(entry.url).searchParams.forEach((_v, k) => { if (!VOLATILE_PARAMS.has(k))
237
- qp.push(k); });
238
- }
239
- catch { }
140
+ const { params: qp, hasSearch, hasPagination, hasLimit } = classifyQueryParams(entry.url);
240
141
  const ep = {
241
142
  pattern, method: entry.method, url: entry.url, status: entry.status, contentType: ct,
242
- queryParams: qp, hasSearchParam: qp.some(p => SEARCH_PARAMS.has(p)),
243
- hasPaginationParam: qp.some(p => PAGINATION_PARAMS.has(p)),
244
- hasLimitParam: qp.some(p => LIMIT_PARAMS.has(p)),
245
- authIndicators: detectAuthIndicators(entry.requestHeaders),
143
+ queryParams: qp, hasSearchParam: hasSearch,
144
+ hasPaginationParam: hasPagination,
145
+ hasLimitParam: hasLimit || qp.some(p => LIMIT_PARAMS.has(p)),
146
+ authIndicators: detectAuthFromHeaders(entry.requestHeaders),
246
147
  responseAnalysis: entry.responseBody ? analyzeResponseBody(entry.responseBody) : null,
247
148
  score: 0,
248
149
  };
@@ -14,22 +14,6 @@
14
14
  install:
15
15
  mac: "brew install --cask obsidian"
16
16
 
17
- - name: readwise
18
- binary: readwise
19
- description: "Readwise & Reader CLI — highlights, annotations, reading list"
20
- homepage: "https://github.com/readwiseio/readwise-cli"
21
- tags: [reading, highlights]
22
- install:
23
- default: "npm install -g @readwiseio/readwise-cli"
24
-
25
- - name: kubectl
26
- binary: kubectl
27
- description: "Kubernetes command-line tool"
28
- homepage: "https://kubernetes.io/docs/reference/kubectl/"
29
- tags: [kubernetes, k8s, devops]
30
- install:
31
- mac: "brew install kubectl"
32
-
33
17
  - name: docker
34
18
  binary: docker
35
19
  description: "Docker command-line interface"
@@ -37,12 +21,3 @@
37
21
  tags: [docker, containers, devops]
38
22
  install:
39
23
  mac: "brew install --cask docker"
40
-
41
- - name: gws
42
- binary: gws
43
- description: "Google Workspace CLI — Docs, Sheets, Drive, Gmail, Calendar"
44
- homepage: "https://github.com/nicholasgasior/gws"
45
- tags: [google, docs, sheets, drive, workspace]
46
- install:
47
- mac: "brew install gws"
48
- default: "npm install -g @nicholasgasior/gws"
package/dist/external.js CHANGED
@@ -6,6 +6,7 @@ import { spawnSync, execFileSync } from 'node:child_process';
6
6
  import yaml from 'js-yaml';
7
7
  import chalk from 'chalk';
8
8
  import { log } from './logger.js';
9
+ import { getErrorMessage } from './errors.js';
9
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
11
  function getUserRegistryPath() {
11
12
  const home = os.homedir();
@@ -24,7 +25,7 @@ export function loadExternalClis() {
24
25
  }
25
26
  }
26
27
  catch (err) {
27
- log.warn(`Failed to parse built-in external-clis.yaml: ${err.message}`);
28
+ log.warn(`Failed to parse built-in external-clis.yaml: ${getErrorMessage(err)}`);
28
29
  }
29
30
  // 2. Load user custom
30
31
  const userPath = getUserRegistryPath();
@@ -38,7 +39,7 @@ export function loadExternalClis() {
38
39
  }
39
40
  }
40
41
  catch (err) {
41
- log.warn(`Failed to parse user external-clis.yaml: ${err.message}`);
42
+ log.warn(`Failed to parse user external-clis.yaml: ${getErrorMessage(err)}`);
42
43
  }
43
44
  return Array.from(configs.values()).sort((a, b) => a.name.localeCompare(b.name));
44
45
  }
@@ -78,7 +79,7 @@ export function getInstallCmd(installConfig) {
78
79
  * Object with `binary` and `args` fields, or throws on unsafe input.
79
80
  */
80
81
  export function parseCommand(cmd) {
81
- const shellOperators = /&&|\|\|?|;|[><`]/;
82
+ const shellOperators = /&&|\|\|?|;|[><`$#\n\r]|\$\(/;
82
83
  if (shellOperators.test(cmd)) {
83
84
  throw new Error(`Install command contains unsafe shell operators and cannot be executed securely: "${cmd}". ` +
84
85
  `Please install the tool manually.`);
@@ -97,7 +98,8 @@ export function parseCommand(cmd) {
97
98
  return { binary, args };
98
99
  }
99
100
  function shouldRetryWithCmdShim(binary, err) {
100
- return os.platform() === 'win32' && !path.extname(binary) && err.code === 'ENOENT';
101
+ const code = err instanceof Error ? err.code : undefined;
102
+ return os.platform() === 'win32' && !path.extname(binary) && code === 'ENOENT';
101
103
  }
102
104
  function runInstallCommand(cmd) {
103
105
  const { binary, args } = parseCommand(cmd);
@@ -133,7 +135,7 @@ export function installExternalCli(cli) {
133
135
  return true;
134
136
  }
135
137
  catch (err) {
136
- console.error(chalk.red(`❌ Failed to install '${cli.name}': ${err.message}`));
138
+ console.error(chalk.red(`❌ Failed to install '${cli.name}': ${getErrorMessage(err)}`));
137
139
  return false;
138
140
  }
139
141
  }
@@ -25,6 +25,10 @@ describe('parseCommand', () => {
25
25
  it('rejects shell operators', () => {
26
26
  expect(() => parseCommand('brew install gh && rm -rf /')).toThrow('Install command contains unsafe shell operators');
27
27
  });
28
+ it('rejects command substitution and multiline input', () => {
29
+ expect(() => parseCommand('brew install $(whoami)')).toThrow('Install command contains unsafe shell operators');
30
+ expect(() => parseCommand('brew install gh\nrm -rf /')).toThrow('Install command contains unsafe shell operators');
31
+ });
28
32
  });
29
33
  describe('installExternalCli', () => {
30
34
  const cli = {
@@ -9,20 +9,13 @@
9
9
  */
10
10
  import type { IBrowserFactory } from './runtime.js';
11
11
  import { type SynthesizeCandidateSummary } from './synthesize.js';
12
- interface RegisterCandidatesResult {
13
- ok: boolean;
14
- count: number;
15
- }
16
12
  export interface GenerateCliOptions {
17
13
  url: string;
18
14
  BrowserFactory: new () => IBrowserFactory;
19
- builtinClis?: string;
20
- userClis?: string;
21
15
  goal?: string | null;
22
16
  site?: string;
23
17
  waitSeconds?: number;
24
18
  top?: number;
25
- register?: boolean;
26
19
  workspace?: string;
27
20
  }
28
21
  export interface GenerateCliResult {
@@ -43,8 +36,6 @@ export interface GenerateCliResult {
43
36
  candidate_count: number;
44
37
  candidates: Array<Pick<SynthesizeCandidateSummary, 'name' | 'strategy' | 'confidence'>>;
45
38
  };
46
- register: RegisterCandidatesResult | null;
47
39
  }
48
40
  export declare function generateCliFromUrl(opts: GenerateCliOptions): Promise<GenerateCliResult>;
49
41
  export declare function renderGenerateSummary(r: GenerateCliResult): string;
50
- export {};
package/dist/generate.js CHANGED
@@ -9,9 +9,6 @@
9
9
  */
10
10
  import { exploreUrl } from './explore.js';
11
11
  import { synthesizeFromExplore } from './synthesize.js';
12
- function registerCandidates(_opts) {
13
- return { ok: true, count: 0 };
14
- }
15
12
  const CAPABILITY_ALIASES = {
16
13
  search: ['search', '搜索', '查找', 'query', 'keyword'],
17
14
  hot: ['hot', '热门', '热榜', '热搜', 'popular', 'top', 'ranking'],
@@ -51,7 +48,10 @@ function selectCandidate(candidates, goal) {
51
48
  return exact;
52
49
  }
53
50
  const lower = (goal ?? '').trim().toLowerCase();
54
- const partial = candidates.find(c => c.name?.toLowerCase().includes(lower) || lower.includes(c.name?.toLowerCase()));
51
+ const partial = candidates.find(c => {
52
+ const cName = c.name?.toLowerCase() ?? '';
53
+ return cName.includes(lower) || lower.includes(cName);
54
+ });
55
55
  return partial ?? candidates[0];
56
56
  }
57
57
  export async function generateCliFromUrl(opts) {
@@ -70,19 +70,6 @@ export async function generateCliFromUrl(opts) {
70
70
  // Step 3: Select best candidate for goal
71
71
  const selected = selectCandidate(synthesizeResult.candidates ?? [], opts.goal);
72
72
  const selectedSite = synthesizeResult.site ?? exploreResult.site;
73
- // Step 4: Register (if requested)
74
- let registerResult = null;
75
- if (opts.register !== false && synthesizeResult.candidate_count > 0) {
76
- try {
77
- registerResult = registerCandidates({
78
- target: synthesizeResult.out_dir,
79
- builtinClis: opts.builtinClis,
80
- userClis: opts.userClis,
81
- name: selected?.name,
82
- });
83
- }
84
- catch { }
85
- }
86
73
  const ok = exploreResult.endpoint_count > 0 && synthesizeResult.candidate_count > 0;
87
74
  return {
88
75
  ok,
@@ -106,7 +93,6 @@ export async function generateCliFromUrl(opts) {
106
93
  confidence: c.confidence,
107
94
  })),
108
95
  },
109
- register: registerResult,
110
96
  };
111
97
  }
112
98
  export function renderGenerateSummary(r) {
@@ -127,8 +113,6 @@ export function renderGenerateSummary(r) {
127
113
  for (const c of r.synthesize?.candidates ?? []) {
128
114
  lines.push(` • ${c.name} (${c.strategy}, ${((c.confidence ?? 0) * 100).toFixed(0)}%)`);
129
115
  }
130
- if (r.register)
131
- lines.push(`\nRegistered: ${r.register.count ?? 0}`);
132
116
  const fw = r.explore?.framework ?? {};
133
117
  const fwNames = Object.entries(fw).filter(([, v]) => v).map(([k]) => k);
134
118
  if (fwNames.length)
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Plugin lifecycle hooks: allows plugins to tap into opencli's execution lifecycle.
3
+ *
4
+ * Hooks use globalThis (like the command registry) to guarantee a single shared
5
+ * instance across all module copies — critical when TS plugins are loaded via
6
+ * npm link / peerDependency symlinks.
7
+ *
8
+ * Available hooks:
9
+ * onStartup — fired once after all commands & plugins are discovered
10
+ * onBeforeExecute — fired before every command execution
11
+ * onAfterExecute — fired after every command execution (receives result)
12
+ */
13
+ export type HookName = 'onStartup' | 'onBeforeExecute' | 'onAfterExecute';
14
+ export interface HookContext {
15
+ /** Command full name in "site/name" format, or "__startup__" for onStartup */
16
+ command: string;
17
+ /** Coerced and validated arguments */
18
+ args: Record<string, unknown>;
19
+ /** Epoch ms when execution started (set by executeCommand) */
20
+ startedAt?: number;
21
+ /** Epoch ms when execution finished (set by executeCommand) */
22
+ finishedAt?: number;
23
+ /** Error thrown by the command, if execution failed */
24
+ error?: unknown;
25
+ /** Plugins can attach arbitrary data here for cross-hook communication */
26
+ [key: string]: unknown;
27
+ }
28
+ export type HookFn = (ctx: HookContext, result?: unknown) => void | Promise<void>;
29
+ declare global {
30
+ var __opencli_hooks__: Map<HookName, HookFn[]> | undefined;
31
+ }
32
+ /** Register a hook that fires once after all plugins are discovered. */
33
+ export declare function onStartup(fn: HookFn): void;
34
+ /** Register a hook that fires before every command execution. */
35
+ export declare function onBeforeExecute(fn: HookFn): void;
36
+ /** Register a hook that fires after every command execution with the result. */
37
+ export declare function onAfterExecute(fn: HookFn): void;
38
+ /**
39
+ * Trigger all registered handlers for a hook.
40
+ * Each handler is wrapped in try/catch — a failing hook never blocks command execution.
41
+ */
42
+ export declare function emitHook(name: HookName, ctx: HookContext, result?: unknown): Promise<void>;
43
+ /**
44
+ * Remove all registered hooks. Intended for testing only.
45
+ */
46
+ export declare function clearAllHooks(): void;
package/dist/hooks.js ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Plugin lifecycle hooks: allows plugins to tap into opencli's execution lifecycle.
3
+ *
4
+ * Hooks use globalThis (like the command registry) to guarantee a single shared
5
+ * instance across all module copies — critical when TS plugins are loaded via
6
+ * npm link / peerDependency symlinks.
7
+ *
8
+ * Available hooks:
9
+ * onStartup — fired once after all commands & plugins are discovered
10
+ * onBeforeExecute — fired before every command execution
11
+ * onAfterExecute — fired after every command execution (receives result)
12
+ */
13
+ import { log } from './logger.js';
14
+ const _hooks = globalThis.__opencli_hooks__ ??= new Map();
15
+ // ── Registration API (used by plugins) ─────────────────────────────────────
16
+ function addHook(name, fn) {
17
+ const list = _hooks.get(name) ?? [];
18
+ list.push(fn);
19
+ _hooks.set(name, list);
20
+ }
21
+ /** Register a hook that fires once after all plugins are discovered. */
22
+ export function onStartup(fn) {
23
+ addHook('onStartup', fn);
24
+ }
25
+ /** Register a hook that fires before every command execution. */
26
+ export function onBeforeExecute(fn) {
27
+ addHook('onBeforeExecute', fn);
28
+ }
29
+ /** Register a hook that fires after every command execution with the result. */
30
+ export function onAfterExecute(fn) {
31
+ addHook('onAfterExecute', fn);
32
+ }
33
+ // ── Emit API (used internally by opencli core) ─────────────────────────────
34
+ /**
35
+ * Trigger all registered handlers for a hook.
36
+ * Each handler is wrapped in try/catch — a failing hook never blocks command execution.
37
+ */
38
+ export async function emitHook(name, ctx, result) {
39
+ const handlers = _hooks.get(name);
40
+ if (!handlers || handlers.length === 0)
41
+ return;
42
+ for (const fn of handlers) {
43
+ try {
44
+ await fn(ctx, result);
45
+ }
46
+ catch (err) {
47
+ log.warn(`Hook ${name} handler failed: ${err instanceof Error ? err.message : String(err)}`);
48
+ }
49
+ }
50
+ }
51
+ /**
52
+ * Remove all registered hooks. Intended for testing only.
53
+ */
54
+ export function clearAllHooks() {
55
+ _hooks.clear();
56
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Tests for the plugin lifecycle hooks system.
3
+ */
4
+ export {};
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Tests for the plugin lifecycle hooks system.
3
+ */
4
+ import { describe, it, expect, beforeEach } from 'vitest';
5
+ import { onStartup, onBeforeExecute, onAfterExecute, emitHook, clearAllHooks, } from './hooks.js';
6
+ beforeEach(() => {
7
+ clearAllHooks();
8
+ });
9
+ describe('hook registration and emission', () => {
10
+ it('onBeforeExecute hook is called with context', async () => {
11
+ const calls = [];
12
+ onBeforeExecute((ctx) => { calls.push({ ...ctx }); });
13
+ await emitHook('onBeforeExecute', { command: 'test/cmd', args: { limit: 5 }, startedAt: 100 });
14
+ expect(calls).toHaveLength(1);
15
+ expect(calls[0].command).toBe('test/cmd');
16
+ expect(calls[0].args).toEqual({ limit: 5 });
17
+ expect(calls[0].startedAt).toBe(100);
18
+ });
19
+ it('onAfterExecute hook receives result', async () => {
20
+ const results = [];
21
+ onAfterExecute((_ctx, result) => { results.push(result); });
22
+ const mockResult = [{ title: 'item1' }, { title: 'item2' }];
23
+ await emitHook('onAfterExecute', { command: 'test/cmd', args: {} }, mockResult);
24
+ expect(results).toHaveLength(1);
25
+ expect(results[0]).toEqual(mockResult);
26
+ });
27
+ it('onStartup hook fires', async () => {
28
+ let fired = false;
29
+ onStartup(() => { fired = true; });
30
+ await emitHook('onStartup', { command: '__startup__', args: {} });
31
+ expect(fired).toBe(true);
32
+ });
33
+ it('multiple hooks on the same event fire in order', async () => {
34
+ const order = [];
35
+ onBeforeExecute(() => { order.push(1); });
36
+ onBeforeExecute(() => { order.push(2); });
37
+ onBeforeExecute(() => { order.push(3); });
38
+ await emitHook('onBeforeExecute', { command: 'test/cmd', args: {} });
39
+ expect(order).toEqual([1, 2, 3]);
40
+ });
41
+ it('async hooks are awaited', async () => {
42
+ const order = [];
43
+ onBeforeExecute(async () => {
44
+ await new Promise((r) => setTimeout(r, 10));
45
+ order.push('async-done');
46
+ });
47
+ onBeforeExecute(() => { order.push('sync'); });
48
+ await emitHook('onBeforeExecute', { command: 'test/cmd', args: {} });
49
+ expect(order).toEqual(['async-done', 'sync']);
50
+ });
51
+ });
52
+ describe('hook error isolation', () => {
53
+ it('failing hook does not prevent other hooks from running', async () => {
54
+ const calls = [];
55
+ onBeforeExecute(() => { calls.push('first'); });
56
+ onBeforeExecute(() => { throw new Error('boom'); });
57
+ onBeforeExecute(() => { calls.push('third'); });
58
+ await emitHook('onBeforeExecute', { command: 'test/cmd', args: {} });
59
+ // First and third should still run despite the second throwing
60
+ expect(calls).toEqual(['first', 'third']);
61
+ });
62
+ it('async hook rejection does not prevent other hooks', async () => {
63
+ const calls = [];
64
+ onAfterExecute(() => { calls.push('before-reject'); });
65
+ onAfterExecute(async () => { throw new Error('async boom'); });
66
+ onAfterExecute(() => { calls.push('after-reject'); });
67
+ await emitHook('onAfterExecute', { command: 'test/cmd', args: {} }, null);
68
+ expect(calls).toEqual(['before-reject', 'after-reject']);
69
+ });
70
+ });
71
+ describe('no-op when no hooks registered', () => {
72
+ it('emitHook with no registered hooks does nothing', async () => {
73
+ // Should not throw
74
+ await emitHook('onBeforeExecute', { command: 'test/cmd', args: {} });
75
+ await emitHook('onAfterExecute', { command: 'test/cmd', args: {} }, []);
76
+ await emitHook('onStartup', { command: '__startup__', args: {} });
77
+ });
78
+ });
79
+ describe('clearAllHooks', () => {
80
+ it('removes all hooks', async () => {
81
+ let called = false;
82
+ onStartup(() => { called = true; });
83
+ clearAllHooks();
84
+ await emitHook('onStartup', { command: '__startup__', args: {} });
85
+ expect(called).toBe(false);
86
+ });
87
+ });
88
+ describe('globalThis singleton', () => {
89
+ it('uses globalThis.__opencli_hooks__ for shared state', () => {
90
+ expect(globalThis.__opencli_hooks__).toBeInstanceOf(Map);
91
+ });
92
+ });