@jackwener/opencli 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (769) hide show
  1. package/.agents/skills/cross-project-adapter-migration/SKILL.md +2 -2
  2. package/.github/pull_request_template.md +7 -0
  3. package/.github/workflows/doc-check.yml +36 -0
  4. package/.github/workflows/docs.yml +7 -42
  5. package/CHANGELOG.md +23 -0
  6. package/CLI-EXPLORER.md +9 -8
  7. package/CONTRIBUTING.md +39 -1
  8. package/README.md +33 -19
  9. package/README.zh-CN.md +64 -27
  10. package/SKILL.md +102 -33
  11. package/dist/browser/cdp.d.ts +4 -4
  12. package/dist/browser/cdp.js +45 -17
  13. package/dist/browser/daemon-client.d.ts +2 -1
  14. package/dist/browser/dom-helpers.js +38 -7
  15. package/dist/browser/dom-snapshot.d.ts +86 -0
  16. package/dist/browser/dom-snapshot.js +729 -0
  17. package/dist/browser/dom-snapshot.test.d.ts +11 -0
  18. package/dist/browser/dom-snapshot.test.js +212 -0
  19. package/dist/browser/index.d.ts +2 -0
  20. package/dist/browser/index.js +1 -0
  21. package/dist/browser/page.d.ts +18 -25
  22. package/dist/browser/page.js +44 -5
  23. package/dist/build-manifest.d.ts +11 -4
  24. package/dist/build-manifest.js +79 -34
  25. package/dist/build-manifest.test.js +58 -2
  26. package/dist/cli-manifest.json +4273 -1771
  27. package/dist/cli.d.ts +6 -0
  28. package/dist/cli.js +255 -162
  29. package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
  30. package/dist/clis/apple-podcasts/commands.test.js +76 -0
  31. package/dist/clis/apple-podcasts/search.js +2 -2
  32. package/dist/clis/apple-podcasts/top.js +9 -2
  33. package/dist/clis/arxiv/search.js +1 -1
  34. package/dist/clis/barchart/greeks.js +1 -1
  35. package/dist/clis/barchart/options.js +1 -1
  36. package/dist/clis/barchart/quote.js +1 -1
  37. package/dist/clis/bilibili/download.js +1 -1
  38. package/dist/clis/bilibili/dynamic.js +1 -1
  39. package/dist/clis/bilibili/favorite.js +1 -1
  40. package/dist/clis/bilibili/feed.js +1 -1
  41. package/dist/clis/bilibili/following.js +2 -2
  42. package/dist/clis/bilibili/history.js +1 -1
  43. package/dist/clis/bilibili/me.js +1 -1
  44. package/dist/clis/bilibili/ranking.js +1 -1
  45. package/dist/clis/bilibili/search.js +3 -3
  46. package/dist/clis/bilibili/subtitle.js +2 -2
  47. package/dist/clis/bilibili/user-videos.js +2 -2
  48. package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
  49. package/dist/clis/bloomberg/businessweek.js +17 -0
  50. package/dist/clis/bloomberg/economics.js +17 -0
  51. package/dist/clis/bloomberg/feeds.d.ts +1 -0
  52. package/dist/clis/bloomberg/feeds.js +15 -0
  53. package/dist/clis/bloomberg/industries.d.ts +1 -0
  54. package/dist/clis/bloomberg/industries.js +17 -0
  55. package/dist/clis/bloomberg/main.d.ts +1 -0
  56. package/dist/clis/bloomberg/main.js +17 -0
  57. package/dist/clis/bloomberg/markets.d.ts +1 -0
  58. package/dist/clis/bloomberg/markets.js +17 -0
  59. package/dist/clis/bloomberg/news.d.ts +1 -0
  60. package/dist/clis/bloomberg/news.js +105 -0
  61. package/dist/clis/bloomberg/opinions.d.ts +1 -0
  62. package/dist/clis/bloomberg/opinions.js +17 -0
  63. package/dist/clis/bloomberg/politics.d.ts +1 -0
  64. package/dist/clis/bloomberg/politics.js +17 -0
  65. package/dist/clis/bloomberg/tech.d.ts +1 -0
  66. package/dist/clis/bloomberg/tech.js +17 -0
  67. package/dist/clis/bloomberg/utils.d.ts +34 -0
  68. package/dist/clis/bloomberg/utils.js +364 -0
  69. package/dist/clis/bloomberg/utils.test.d.ts +1 -0
  70. package/dist/clis/bloomberg/utils.test.js +129 -0
  71. package/dist/clis/boss/batchgreet.js +12 -99
  72. package/dist/clis/boss/chatlist.js +9 -26
  73. package/dist/clis/boss/chatmsg.js +11 -42
  74. package/dist/clis/boss/common.d.ts +92 -0
  75. package/dist/clis/boss/common.js +223 -0
  76. package/dist/clis/boss/detail.js +8 -50
  77. package/dist/clis/boss/exchange.js +13 -79
  78. package/dist/clis/boss/greet.js +20 -147
  79. package/dist/clis/boss/invite.js +26 -121
  80. package/dist/clis/boss/joblist.js +6 -31
  81. package/dist/clis/boss/mark.js +12 -85
  82. package/dist/clis/boss/recommend.js +10 -49
  83. package/dist/clis/boss/resume.js +18 -118
  84. package/dist/clis/boss/search.js +13 -61
  85. package/dist/clis/boss/send.js +18 -152
  86. package/dist/clis/boss/stats.js +20 -71
  87. package/dist/clis/chaoxing/assignments.js +1 -1
  88. package/dist/clis/chaoxing/exams.js +1 -1
  89. package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
  90. package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
  91. package/dist/clis/chaoxing/utils.test.d.ts +1 -0
  92. package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
  93. package/dist/clis/chatgpt/read.js +1 -1
  94. package/dist/clis/chatwise/export.js +1 -1
  95. package/dist/clis/chatwise/model.js +2 -2
  96. package/dist/clis/chatwise/screenshot.js +1 -1
  97. package/dist/clis/codex/export.js +1 -1
  98. package/dist/clis/codex/model.js +2 -2
  99. package/dist/clis/codex/screenshot.js +1 -1
  100. package/dist/clis/coupang/add-to-cart.js +3 -4
  101. package/dist/clis/coupang/search.js +2 -4
  102. package/dist/clis/coupang/utils.test.d.ts +1 -0
  103. package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
  104. package/dist/clis/ctrip/search.js +1 -1
  105. package/dist/clis/cursor/export.js +1 -1
  106. package/dist/clis/cursor/model.js +2 -2
  107. package/dist/clis/cursor/screenshot.js +1 -1
  108. package/dist/clis/devto/tag.yaml +34 -0
  109. package/dist/clis/devto/top.yaml +29 -0
  110. package/dist/clis/devto/user.yaml +33 -0
  111. package/dist/clis/douban/book-hot.d.ts +1 -0
  112. package/dist/clis/douban/book-hot.js +14 -0
  113. package/dist/clis/douban/marks.d.ts +1 -0
  114. package/dist/clis/douban/marks.js +115 -0
  115. package/dist/clis/douban/movie-hot.d.ts +1 -0
  116. package/dist/clis/douban/movie-hot.js +14 -0
  117. package/dist/clis/douban/reviews.d.ts +1 -0
  118. package/dist/clis/douban/reviews.js +106 -0
  119. package/dist/clis/douban/search.d.ts +1 -0
  120. package/dist/clis/douban/search.js +16 -0
  121. package/dist/clis/douban/shared.d.ts +4 -0
  122. package/dist/clis/douban/shared.js +155 -0
  123. package/dist/clis/douban/subject.yaml +76 -0
  124. package/dist/clis/douban/top250.yaml +70 -0
  125. package/dist/clis/douban/utils.d.ts +35 -0
  126. package/dist/clis/douban/utils.js +48 -0
  127. package/dist/clis/facebook/add-friend.yaml +43 -0
  128. package/dist/clis/facebook/events.yaml +44 -0
  129. package/dist/clis/facebook/feed.yaml +63 -0
  130. package/dist/clis/facebook/friends.yaml +42 -0
  131. package/dist/clis/facebook/groups.yaml +50 -0
  132. package/dist/clis/facebook/join-group.yaml +44 -0
  133. package/dist/clis/facebook/memories.yaml +39 -0
  134. package/dist/clis/facebook/notifications.yaml +40 -0
  135. package/dist/clis/facebook/profile.yaml +37 -0
  136. package/dist/clis/facebook/search.yaml +46 -0
  137. package/dist/clis/google/news.d.ts +5 -0
  138. package/dist/clis/google/news.js +58 -0
  139. package/dist/clis/google/search.d.ts +10 -0
  140. package/dist/clis/google/search.js +127 -0
  141. package/dist/clis/google/suggest.d.ts +5 -0
  142. package/dist/clis/google/suggest.js +34 -0
  143. package/dist/clis/google/trends.d.ts +5 -0
  144. package/dist/clis/google/trends.js +38 -0
  145. package/dist/clis/google/utils.d.ts +9 -0
  146. package/dist/clis/google/utils.js +23 -0
  147. package/dist/clis/google/utils.test.d.ts +1 -0
  148. package/dist/clis/google/utils.test.js +75 -0
  149. package/dist/clis/grok/ask.d.ts +14 -0
  150. package/dist/clis/grok/ask.js +257 -65
  151. package/dist/clis/grok/ask.test.d.ts +1 -0
  152. package/dist/clis/grok/ask.test.js +36 -0
  153. package/dist/clis/instagram/comment.yaml +52 -0
  154. package/dist/clis/instagram/explore.yaml +43 -0
  155. package/dist/clis/instagram/follow.yaml +41 -0
  156. package/dist/clis/instagram/followers.yaml +51 -0
  157. package/dist/clis/instagram/following.yaml +51 -0
  158. package/dist/clis/instagram/like.yaml +46 -0
  159. package/dist/clis/instagram/profile.yaml +42 -0
  160. package/dist/clis/instagram/save.yaml +46 -0
  161. package/dist/clis/instagram/saved.yaml +40 -0
  162. package/dist/clis/instagram/search.yaml +43 -0
  163. package/dist/clis/instagram/unfollow.yaml +38 -0
  164. package/dist/clis/instagram/unlike.yaml +46 -0
  165. package/dist/clis/instagram/unsave.yaml +46 -0
  166. package/dist/clis/instagram/user.yaml +54 -0
  167. package/dist/clis/jike/comment.js +2 -3
  168. package/dist/clis/jike/create.js +1 -2
  169. package/dist/clis/jike/feed.js +0 -1
  170. package/dist/clis/jike/like.js +1 -2
  171. package/dist/clis/jike/notifications.js +0 -1
  172. package/dist/clis/jike/post.yaml +1 -0
  173. package/dist/clis/jike/repost.js +2 -3
  174. package/dist/clis/jike/search.js +2 -3
  175. package/dist/clis/jike/topic.yaml +1 -0
  176. package/dist/clis/jike/user.yaml +1 -0
  177. package/dist/clis/jimeng/generate.yaml +1 -0
  178. package/dist/clis/jimeng/history.yaml +0 -1
  179. package/dist/clis/linkedin/search.js +7 -7
  180. package/dist/clis/linux-do/category.yaml +2 -0
  181. package/dist/clis/linux-do/search.yaml +4 -3
  182. package/dist/clis/linux-do/topic.yaml +1 -0
  183. package/dist/clis/lobsters/active.yaml +29 -0
  184. package/dist/clis/lobsters/hot.yaml +29 -0
  185. package/dist/clis/lobsters/newest.yaml +29 -0
  186. package/dist/clis/lobsters/tag.yaml +34 -0
  187. package/dist/clis/medium/feed.d.ts +1 -0
  188. package/dist/clis/medium/feed.js +15 -0
  189. package/dist/clis/medium/search.d.ts +1 -0
  190. package/dist/clis/medium/search.js +15 -0
  191. package/dist/clis/medium/shared.d.ts +5 -0
  192. package/dist/clis/medium/shared.js +78 -0
  193. package/dist/clis/medium/user.d.ts +1 -0
  194. package/dist/clis/medium/user.js +15 -0
  195. package/dist/clis/notion/export.js +1 -1
  196. package/dist/clis/reddit/comment.js +3 -4
  197. package/dist/clis/reddit/read.js +4 -5
  198. package/dist/clis/reddit/save.js +2 -3
  199. package/dist/clis/reddit/saved.js +0 -1
  200. package/dist/clis/reddit/search.yaml +1 -0
  201. package/dist/clis/reddit/subreddit.yaml +1 -0
  202. package/dist/clis/reddit/subscribe.js +1 -2
  203. package/dist/clis/reddit/upvote.js +2 -3
  204. package/dist/clis/reddit/upvoted.js +0 -1
  205. package/dist/clis/reddit/user-comments.yaml +1 -0
  206. package/dist/clis/reddit/user-posts.yaml +1 -0
  207. package/dist/clis/reddit/user.yaml +1 -0
  208. package/dist/clis/reuters/search.js +1 -1
  209. package/dist/clis/sinablog/article.d.ts +1 -0
  210. package/dist/clis/sinablog/article.js +14 -0
  211. package/dist/clis/sinablog/hot.d.ts +1 -0
  212. package/dist/clis/sinablog/hot.js +14 -0
  213. package/dist/clis/sinablog/search.d.ts +1 -0
  214. package/dist/clis/sinablog/search.js +51 -0
  215. package/dist/clis/sinablog/shared.d.ts +7 -0
  216. package/dist/clis/sinablog/shared.js +187 -0
  217. package/dist/clis/sinablog/user.d.ts +1 -0
  218. package/dist/clis/sinablog/user.js +15 -0
  219. package/dist/clis/smzdm/search.js +2 -3
  220. package/dist/clis/stackoverflow/search.yaml +1 -0
  221. package/dist/clis/steam/top-sellers.yaml +29 -0
  222. package/dist/clis/substack/feed.d.ts +1 -0
  223. package/dist/clis/substack/feed.js +15 -0
  224. package/dist/clis/substack/publication.d.ts +1 -0
  225. package/dist/clis/substack/publication.js +15 -0
  226. package/dist/clis/substack/search.d.ts +1 -0
  227. package/dist/clis/substack/search.js +77 -0
  228. package/dist/clis/substack/shared.d.ts +4 -0
  229. package/dist/clis/substack/shared.js +129 -0
  230. package/dist/clis/tiktok/comment.yaml +66 -0
  231. package/dist/clis/tiktok/explore.yaml +39 -0
  232. package/dist/clis/tiktok/follow.yaml +39 -0
  233. package/dist/clis/tiktok/following.yaml +46 -0
  234. package/dist/clis/tiktok/friends.yaml +47 -0
  235. package/dist/clis/tiktok/like.yaml +38 -0
  236. package/dist/clis/tiktok/live.yaml +51 -0
  237. package/dist/clis/tiktok/notifications.yaml +52 -0
  238. package/dist/clis/tiktok/profile.yaml +45 -0
  239. package/dist/clis/tiktok/save.yaml +34 -0
  240. package/dist/clis/tiktok/search.yaml +46 -0
  241. package/dist/clis/tiktok/unfollow.yaml +44 -0
  242. package/dist/clis/tiktok/unlike.yaml +38 -0
  243. package/dist/clis/tiktok/unsave.yaml +36 -0
  244. package/dist/clis/tiktok/user.yaml +44 -0
  245. package/dist/clis/twitter/accept.js +2 -2
  246. package/dist/clis/twitter/article.js +2 -2
  247. package/dist/clis/twitter/block.d.ts +1 -0
  248. package/dist/clis/twitter/block.js +88 -0
  249. package/dist/clis/twitter/delete.js +1 -1
  250. package/dist/clis/twitter/download.d.ts +1 -1
  251. package/dist/clis/twitter/download.js +3 -3
  252. package/dist/clis/twitter/followers.js +1 -1
  253. package/dist/clis/twitter/following.js +1 -1
  254. package/dist/clis/twitter/hide-reply.d.ts +1 -0
  255. package/dist/clis/twitter/hide-reply.js +66 -0
  256. package/dist/clis/twitter/like.js +1 -1
  257. package/dist/clis/twitter/post.js +1 -1
  258. package/dist/clis/twitter/reply-dm.js +1 -1
  259. package/dist/clis/twitter/reply.js +2 -2
  260. package/dist/clis/twitter/search.js +1 -1
  261. package/dist/clis/twitter/thread.js +2 -2
  262. package/dist/clis/twitter/timeline.d.ts +23 -0
  263. package/dist/clis/twitter/timeline.js +42 -14
  264. package/dist/clis/twitter/timeline.test.d.ts +1 -0
  265. package/dist/clis/twitter/timeline.test.js +102 -0
  266. package/dist/clis/twitter/trending.d.ts +1 -0
  267. package/dist/clis/twitter/trending.js +91 -0
  268. package/dist/clis/twitter/unblock.d.ts +1 -0
  269. package/dist/clis/twitter/unblock.js +71 -0
  270. package/dist/clis/v2ex/topic.yaml +1 -0
  271. package/dist/clis/weibo/hot.js +0 -1
  272. package/dist/clis/weread/book.js +1 -1
  273. package/dist/clis/weread/highlights.js +1 -1
  274. package/dist/clis/weread/notes.js +1 -1
  275. package/dist/clis/weread/search.js +1 -1
  276. package/dist/clis/wikipedia/random.d.ts +1 -0
  277. package/dist/clis/wikipedia/random.js +19 -0
  278. package/dist/clis/wikipedia/search.js +4 -4
  279. package/dist/clis/wikipedia/summary.js +4 -9
  280. package/dist/clis/wikipedia/trending.d.ts +1 -0
  281. package/dist/clis/wikipedia/trending.js +35 -0
  282. package/dist/clis/wikipedia/utils.d.ts +28 -0
  283. package/dist/clis/wikipedia/utils.js +13 -0
  284. package/dist/clis/xiaohongshu/creator-note-detail.d.ts +15 -0
  285. package/dist/clis/xiaohongshu/creator-note-detail.js +69 -5
  286. package/dist/clis/xiaohongshu/creator-note-detail.test.js +82 -33
  287. package/dist/clis/xiaohongshu/creator-notes.js +35 -5
  288. package/dist/clis/xiaohongshu/creator-notes.test.js +37 -6
  289. package/dist/clis/xiaohongshu/creator-profile.js +0 -1
  290. package/dist/clis/xiaohongshu/creator-stats.js +0 -1
  291. package/dist/clis/xiaohongshu/download.js +2 -3
  292. package/dist/clis/xiaohongshu/feed.yaml +0 -1
  293. package/dist/clis/xiaohongshu/notifications.yaml +0 -1
  294. package/dist/clis/xiaohongshu/search.js +2 -2
  295. package/dist/clis/xiaohongshu/user.js +1 -2
  296. package/dist/clis/xueqiu/earnings-date.yaml +69 -0
  297. package/dist/clis/xueqiu/search.yaml +2 -1
  298. package/dist/clis/xueqiu/stock.yaml +2 -0
  299. package/dist/clis/yahoo-finance/quote.js +1 -2
  300. package/dist/clis/youtube/search.js +1 -1
  301. package/dist/clis/youtube/transcript.js +1 -1
  302. package/dist/clis/youtube/video.js +1 -1
  303. package/dist/clis/zhihu/download.js +1 -2
  304. package/dist/clis/zhihu/question.js +1 -1
  305. package/dist/clis/zhihu/search.yaml +4 -3
  306. package/dist/commanderAdapter.d.ts +21 -0
  307. package/dist/commanderAdapter.js +117 -0
  308. package/dist/{engine.d.ts → discovery.d.ts} +6 -4
  309. package/dist/{engine.js → discovery.js} +93 -104
  310. package/dist/doctor.js +3 -1
  311. package/dist/doctor.test.js +46 -2
  312. package/dist/download/index.d.ts +2 -6
  313. package/dist/download/index.js +19 -46
  314. package/dist/engine.test.d.ts +0 -3
  315. package/dist/engine.test.js +80 -11
  316. package/dist/execution.d.ts +24 -0
  317. package/dist/execution.js +153 -0
  318. package/dist/explore.d.ts +76 -3
  319. package/dist/explore.js +132 -111
  320. package/dist/external-clis.yaml +48 -0
  321. package/dist/external.d.ts +7 -2
  322. package/dist/external.js +11 -14
  323. package/dist/generate.d.ts +41 -2
  324. package/dist/generate.js +5 -4
  325. package/dist/main.js +2 -1
  326. package/dist/pipeline/executor.d.ts +2 -2
  327. package/dist/pipeline/executor.js +2 -2
  328. package/dist/pipeline/executor.test.js +33 -6
  329. package/dist/pipeline/registry.d.ts +1 -1
  330. package/dist/pipeline/steps/browser.d.ts +7 -7
  331. package/dist/pipeline/steps/browser.js +21 -7
  332. package/dist/pipeline/steps/fetch.d.ts +1 -1
  333. package/dist/pipeline/steps/fetch.js +11 -7
  334. package/dist/pipeline/steps/transform.d.ts +6 -5
  335. package/dist/pipeline/steps/transform.js +30 -9
  336. package/dist/pipeline/template.d.ts +6 -6
  337. package/dist/pipeline/template.js +43 -5
  338. package/dist/pipeline/template.test.js +18 -0
  339. package/dist/pipeline/transform.test.js +11 -0
  340. package/dist/plugin.d.ts +31 -0
  341. package/dist/plugin.js +216 -0
  342. package/dist/plugin.test.d.ts +4 -0
  343. package/dist/plugin.test.js +76 -0
  344. package/dist/registry-api.d.ts +11 -0
  345. package/dist/registry-api.js +9 -0
  346. package/dist/registry.d.ts +13 -0
  347. package/dist/registry.js +8 -1
  348. package/dist/runtime.d.ts +5 -0
  349. package/dist/runtime.js +8 -0
  350. package/dist/serialization.d.ts +34 -0
  351. package/dist/serialization.js +63 -0
  352. package/dist/synthesize.d.ts +94 -4
  353. package/dist/synthesize.js +5 -4
  354. package/dist/types.d.ts +43 -27
  355. package/dist/validate.js +8 -2
  356. package/docs/.vitepress/config.mts +20 -7
  357. package/docs/adapters/browser/arxiv.md +27 -0
  358. package/docs/adapters/browser/barchart.md +33 -0
  359. package/docs/adapters/browser/bilibili.md +9 -0
  360. package/docs/adapters/browser/bloomberg.md +70 -0
  361. package/docs/adapters/browser/chaoxing.md +39 -0
  362. package/docs/adapters/browser/devto.md +35 -0
  363. package/docs/adapters/browser/douban.md +38 -0
  364. package/docs/adapters/browser/facebook.md +36 -0
  365. package/docs/adapters/browser/google.md +62 -0
  366. package/docs/adapters/browser/grok.md +53 -0
  367. package/docs/adapters/browser/hf.md +42 -0
  368. package/docs/adapters/browser/instagram.md +46 -0
  369. package/docs/adapters/browser/jike.md +45 -0
  370. package/docs/adapters/browser/jimeng.md +39 -0
  371. package/docs/adapters/browser/linux-do.md +45 -0
  372. package/docs/adapters/browser/lobsters.md +32 -0
  373. package/docs/adapters/browser/medium.md +32 -0
  374. package/docs/adapters/browser/reddit.md +9 -0
  375. package/docs/adapters/browser/sinablog.md +36 -0
  376. package/docs/adapters/browser/sinafinance.md +35 -0
  377. package/docs/adapters/browser/stackoverflow.md +35 -0
  378. package/docs/adapters/browser/steam.md +26 -0
  379. package/docs/adapters/browser/substack.md +38 -0
  380. package/docs/adapters/browser/tiktok.md +68 -0
  381. package/docs/adapters/browser/twitter.md +3 -0
  382. package/docs/adapters/browser/weread.md +48 -0
  383. package/docs/adapters/browser/wikipedia.md +39 -0
  384. package/docs/adapters/browser/xiaohongshu.md +5 -1
  385. package/docs/adapters/browser/xueqiu.md +10 -0
  386. package/docs/adapters/browser/yahoo-finance.md +6 -5
  387. package/docs/adapters/desktop/antigravity.md +6 -0
  388. package/docs/adapters/desktop/chatgpt.md +5 -4
  389. package/docs/adapters/desktop/codex.md +5 -1
  390. package/docs/adapters/desktop/cursor.md +4 -0
  391. package/docs/adapters/desktop/discord.md +7 -7
  392. package/docs/adapters/index.md +14 -4
  393. package/docs/advanced/download.md +4 -4
  394. package/docs/developer/architecture.md +17 -4
  395. package/docs/guide/getting-started.md +1 -0
  396. package/docs/guide/plugins.md +153 -0
  397. package/docs/zh/guide/plugins.md +107 -0
  398. package/extension/src/background.ts +18 -11
  399. package/package.json +10 -5
  400. package/scripts/check-doc-coverage.sh +69 -0
  401. package/scripts/clean-dist.cjs +13 -0
  402. package/scripts/copy-yaml.cjs +7 -0
  403. package/src/browser/cdp.ts +77 -32
  404. package/src/browser/daemon-client.ts +2 -1
  405. package/src/browser/dom-helpers.ts +38 -7
  406. package/src/browser/dom-snapshot.test.ts +249 -0
  407. package/src/browser/dom-snapshot.ts +770 -0
  408. package/src/browser/index.ts +2 -0
  409. package/src/browser/page.ts +57 -20
  410. package/src/build-manifest.test.ts +70 -2
  411. package/src/build-manifest.ts +114 -40
  412. package/src/cli.ts +287 -139
  413. package/src/clis/apple-podcasts/commands.test.ts +95 -0
  414. package/src/clis/apple-podcasts/search.ts +2 -2
  415. package/src/clis/apple-podcasts/top.ts +12 -2
  416. package/src/clis/arxiv/search.ts +1 -1
  417. package/src/clis/barchart/greeks.ts +1 -1
  418. package/src/clis/barchart/options.ts +1 -1
  419. package/src/clis/barchart/quote.ts +1 -1
  420. package/src/clis/bilibili/download.ts +1 -1
  421. package/src/clis/bilibili/dynamic.ts +1 -1
  422. package/src/clis/bilibili/favorite.ts +1 -1
  423. package/src/clis/bilibili/feed.ts +1 -1
  424. package/src/clis/bilibili/following.ts +2 -2
  425. package/src/clis/bilibili/history.ts +1 -1
  426. package/src/clis/bilibili/me.ts +1 -1
  427. package/src/clis/bilibili/ranking.ts +1 -1
  428. package/src/clis/bilibili/search.ts +3 -3
  429. package/src/clis/bilibili/subtitle.ts +2 -2
  430. package/src/clis/bilibili/user-videos.ts +2 -2
  431. package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
  432. package/src/clis/bloomberg/businessweek.ts +18 -0
  433. package/src/clis/bloomberg/economics.ts +18 -0
  434. package/src/clis/bloomberg/feeds.ts +16 -0
  435. package/src/clis/bloomberg/industries.ts +18 -0
  436. package/src/clis/bloomberg/main.ts +18 -0
  437. package/src/clis/bloomberg/markets.ts +18 -0
  438. package/src/clis/bloomberg/news.ts +136 -0
  439. package/src/clis/bloomberg/opinions.ts +18 -0
  440. package/src/clis/bloomberg/politics.ts +18 -0
  441. package/src/clis/bloomberg/tech.ts +18 -0
  442. package/src/clis/bloomberg/utils.test.ts +135 -0
  443. package/src/clis/bloomberg/utils.ts +429 -0
  444. package/src/clis/boss/batchgreet.ts +16 -108
  445. package/src/clis/boss/chatlist.ts +13 -27
  446. package/src/clis/boss/chatmsg.ts +16 -40
  447. package/src/clis/boss/common.ts +287 -0
  448. package/src/clis/boss/detail.ts +9 -55
  449. package/src/clis/boss/exchange.ts +15 -89
  450. package/src/clis/boss/greet.ts +25 -162
  451. package/src/clis/boss/invite.ts +36 -133
  452. package/src/clis/boss/joblist.ts +7 -36
  453. package/src/clis/boss/mark.ts +13 -94
  454. package/src/clis/boss/recommend.ts +12 -57
  455. package/src/clis/boss/resume.ts +19 -124
  456. package/src/clis/boss/search.ts +14 -67
  457. package/src/clis/boss/send.ts +22 -162
  458. package/src/clis/boss/stats.ts +21 -76
  459. package/src/clis/chaoxing/assignments.ts +1 -1
  460. package/src/clis/chaoxing/exams.ts +1 -1
  461. package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
  462. package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
  463. package/src/clis/chatgpt/read.ts +1 -1
  464. package/src/clis/chatwise/export.ts +1 -1
  465. package/src/clis/chatwise/model.ts +2 -2
  466. package/src/clis/chatwise/screenshot.ts +1 -1
  467. package/src/clis/codex/export.ts +1 -1
  468. package/src/clis/codex/model.ts +2 -2
  469. package/src/clis/codex/screenshot.ts +1 -1
  470. package/src/clis/coupang/add-to-cart.ts +3 -4
  471. package/src/clis/coupang/search.ts +2 -4
  472. package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
  473. package/src/clis/ctrip/search.ts +1 -1
  474. package/src/clis/cursor/export.ts +1 -1
  475. package/src/clis/cursor/model.ts +2 -2
  476. package/src/clis/cursor/screenshot.ts +1 -1
  477. package/src/clis/devto/tag.yaml +34 -0
  478. package/src/clis/devto/top.yaml +29 -0
  479. package/src/clis/devto/user.yaml +33 -0
  480. package/src/clis/douban/book-hot.ts +15 -0
  481. package/src/clis/douban/marks.ts +135 -0
  482. package/src/clis/douban/movie-hot.ts +15 -0
  483. package/src/clis/douban/reviews.ts +127 -0
  484. package/src/clis/douban/search.ts +17 -0
  485. package/src/clis/douban/shared.ts +165 -0
  486. package/src/clis/douban/subject.yaml +76 -0
  487. package/src/clis/douban/top250.yaml +70 -0
  488. package/src/clis/douban/utils.ts +81 -0
  489. package/src/clis/facebook/add-friend.yaml +43 -0
  490. package/src/clis/facebook/events.yaml +44 -0
  491. package/src/clis/facebook/feed.yaml +63 -0
  492. package/src/clis/facebook/friends.yaml +42 -0
  493. package/src/clis/facebook/groups.yaml +50 -0
  494. package/src/clis/facebook/join-group.yaml +44 -0
  495. package/src/clis/facebook/memories.yaml +39 -0
  496. package/src/clis/facebook/notifications.yaml +40 -0
  497. package/src/clis/facebook/profile.yaml +37 -0
  498. package/src/clis/facebook/search.yaml +46 -0
  499. package/src/clis/google/news.ts +66 -0
  500. package/src/clis/google/search.ts +133 -0
  501. package/src/clis/google/suggest.ts +40 -0
  502. package/src/clis/google/trends.ts +44 -0
  503. package/src/clis/google/utils.test.ts +82 -0
  504. package/src/clis/google/utils.ts +24 -0
  505. package/src/clis/grok/ask.test.ts +53 -0
  506. package/src/clis/grok/ask.ts +300 -69
  507. package/src/clis/instagram/comment.yaml +52 -0
  508. package/src/clis/instagram/explore.yaml +43 -0
  509. package/src/clis/instagram/follow.yaml +41 -0
  510. package/src/clis/instagram/followers.yaml +51 -0
  511. package/src/clis/instagram/following.yaml +51 -0
  512. package/src/clis/instagram/like.yaml +46 -0
  513. package/src/clis/instagram/profile.yaml +42 -0
  514. package/src/clis/instagram/save.yaml +46 -0
  515. package/src/clis/instagram/saved.yaml +40 -0
  516. package/src/clis/instagram/search.yaml +43 -0
  517. package/src/clis/instagram/unfollow.yaml +38 -0
  518. package/src/clis/instagram/unlike.yaml +46 -0
  519. package/src/clis/instagram/unsave.yaml +46 -0
  520. package/src/clis/instagram/user.yaml +54 -0
  521. package/src/clis/jike/comment.ts +2 -3
  522. package/src/clis/jike/create.ts +1 -2
  523. package/src/clis/jike/feed.ts +0 -1
  524. package/src/clis/jike/like.ts +1 -2
  525. package/src/clis/jike/notifications.ts +0 -1
  526. package/src/clis/jike/post.yaml +1 -0
  527. package/src/clis/jike/repost.ts +2 -3
  528. package/src/clis/jike/search.ts +2 -3
  529. package/src/clis/jike/topic.yaml +1 -0
  530. package/src/clis/jike/user.yaml +1 -0
  531. package/src/clis/jimeng/generate.yaml +1 -0
  532. package/src/clis/jimeng/history.yaml +0 -1
  533. package/src/clis/linkedin/search.ts +7 -7
  534. package/src/clis/linux-do/category.yaml +2 -0
  535. package/src/clis/linux-do/search.yaml +4 -3
  536. package/src/clis/linux-do/topic.yaml +1 -0
  537. package/src/clis/lobsters/active.yaml +29 -0
  538. package/src/clis/lobsters/hot.yaml +29 -0
  539. package/src/clis/lobsters/newest.yaml +29 -0
  540. package/src/clis/lobsters/tag.yaml +34 -0
  541. package/src/clis/medium/feed.ts +16 -0
  542. package/src/clis/medium/search.ts +16 -0
  543. package/src/clis/medium/shared.ts +83 -0
  544. package/src/clis/medium/user.ts +16 -0
  545. package/src/clis/notion/export.ts +1 -1
  546. package/src/clis/reddit/comment.ts +3 -4
  547. package/src/clis/reddit/read.ts +4 -5
  548. package/src/clis/reddit/save.ts +2 -3
  549. package/src/clis/reddit/saved.ts +0 -1
  550. package/src/clis/reddit/search.yaml +1 -0
  551. package/src/clis/reddit/subreddit.yaml +1 -0
  552. package/src/clis/reddit/subscribe.ts +1 -2
  553. package/src/clis/reddit/upvote.ts +2 -3
  554. package/src/clis/reddit/upvoted.ts +0 -1
  555. package/src/clis/reddit/user-comments.yaml +1 -0
  556. package/src/clis/reddit/user-posts.yaml +1 -0
  557. package/src/clis/reddit/user.yaml +1 -0
  558. package/src/clis/reuters/search.ts +1 -1
  559. package/src/clis/sinablog/article.ts +15 -0
  560. package/src/clis/sinablog/hot.ts +15 -0
  561. package/src/clis/sinablog/search.ts +56 -0
  562. package/src/clis/sinablog/shared.ts +198 -0
  563. package/src/clis/sinablog/user.ts +16 -0
  564. package/src/clis/smzdm/search.ts +2 -3
  565. package/src/clis/stackoverflow/search.yaml +1 -0
  566. package/src/clis/steam/top-sellers.yaml +29 -0
  567. package/src/clis/substack/feed.ts +16 -0
  568. package/src/clis/substack/publication.ts +16 -0
  569. package/src/clis/substack/search.ts +91 -0
  570. package/src/clis/substack/shared.ts +132 -0
  571. package/src/clis/tiktok/comment.yaml +66 -0
  572. package/src/clis/tiktok/explore.yaml +39 -0
  573. package/src/clis/tiktok/follow.yaml +39 -0
  574. package/src/clis/tiktok/following.yaml +46 -0
  575. package/src/clis/tiktok/friends.yaml +47 -0
  576. package/src/clis/tiktok/like.yaml +38 -0
  577. package/src/clis/tiktok/live.yaml +51 -0
  578. package/src/clis/tiktok/notifications.yaml +52 -0
  579. package/src/clis/tiktok/profile.yaml +45 -0
  580. package/src/clis/tiktok/save.yaml +34 -0
  581. package/src/clis/tiktok/search.yaml +46 -0
  582. package/src/clis/tiktok/unfollow.yaml +44 -0
  583. package/src/clis/tiktok/unlike.yaml +38 -0
  584. package/src/clis/tiktok/unsave.yaml +36 -0
  585. package/src/clis/tiktok/user.yaml +44 -0
  586. package/src/clis/twitter/accept.ts +2 -2
  587. package/src/clis/twitter/article.ts +2 -2
  588. package/src/clis/twitter/block.ts +92 -0
  589. package/src/clis/twitter/delete.ts +1 -1
  590. package/src/clis/twitter/download.ts +3 -3
  591. package/src/clis/twitter/followers.ts +1 -1
  592. package/src/clis/twitter/following.ts +1 -1
  593. package/src/clis/twitter/hide-reply.ts +70 -0
  594. package/src/clis/twitter/like.ts +1 -1
  595. package/src/clis/twitter/post.ts +1 -1
  596. package/src/clis/twitter/reply-dm.ts +1 -1
  597. package/src/clis/twitter/reply.ts +2 -2
  598. package/src/clis/twitter/search.ts +1 -1
  599. package/src/clis/twitter/thread.ts +2 -2
  600. package/src/clis/twitter/timeline.test.ts +109 -0
  601. package/src/clis/twitter/timeline.ts +59 -19
  602. package/src/clis/twitter/trending.ts +113 -0
  603. package/src/clis/twitter/unblock.ts +75 -0
  604. package/src/clis/v2ex/topic.yaml +1 -0
  605. package/src/clis/weibo/hot.ts +0 -1
  606. package/src/clis/weread/book.ts +1 -1
  607. package/src/clis/weread/highlights.ts +1 -1
  608. package/src/clis/weread/notes.ts +1 -1
  609. package/src/clis/weread/search.ts +1 -1
  610. package/src/clis/wikipedia/random.ts +19 -0
  611. package/src/clis/wikipedia/search.ts +11 -5
  612. package/src/clis/wikipedia/summary.ts +4 -9
  613. package/src/clis/wikipedia/trending.ts +41 -0
  614. package/src/clis/wikipedia/utils.ts +31 -0
  615. package/src/clis/xiaohongshu/creator-note-detail.test.ts +84 -33
  616. package/src/clis/xiaohongshu/creator-note-detail.ts +89 -5
  617. package/src/clis/xiaohongshu/creator-notes.test.ts +41 -6
  618. package/src/clis/xiaohongshu/creator-notes.ts +44 -5
  619. package/src/clis/xiaohongshu/creator-profile.ts +0 -1
  620. package/src/clis/xiaohongshu/creator-stats.ts +0 -1
  621. package/src/clis/xiaohongshu/download.ts +2 -3
  622. package/src/clis/xiaohongshu/feed.yaml +0 -1
  623. package/src/clis/xiaohongshu/notifications.yaml +0 -1
  624. package/src/clis/xiaohongshu/search.ts +2 -2
  625. package/src/clis/xiaohongshu/user.ts +1 -2
  626. package/src/clis/xueqiu/earnings-date.yaml +69 -0
  627. package/src/clis/xueqiu/search.yaml +2 -1
  628. package/src/clis/xueqiu/stock.yaml +2 -0
  629. package/src/clis/yahoo-finance/quote.ts +1 -2
  630. package/src/clis/youtube/search.ts +1 -1
  631. package/src/clis/youtube/transcript.ts +1 -1
  632. package/src/clis/youtube/video.ts +1 -1
  633. package/src/clis/zhihu/download.ts +1 -2
  634. package/src/clis/zhihu/question.ts +1 -1
  635. package/src/clis/zhihu/search.yaml +4 -3
  636. package/src/commanderAdapter.ts +120 -0
  637. package/src/discovery.ts +277 -0
  638. package/src/doctor.test.ts +59 -2
  639. package/src/doctor.ts +4 -2
  640. package/src/download/index.ts +21 -54
  641. package/src/engine.test.ts +85 -11
  642. package/src/execution.ts +164 -0
  643. package/src/explore.ts +211 -117
  644. package/src/external-clis.yaml +9 -0
  645. package/src/external.ts +15 -12
  646. package/src/generate.ts +58 -9
  647. package/src/main.ts +2 -1
  648. package/src/pipeline/executor.test.ts +35 -6
  649. package/src/pipeline/executor.ts +11 -7
  650. package/src/pipeline/registry.ts +3 -3
  651. package/src/pipeline/steps/browser.ts +29 -15
  652. package/src/pipeline/steps/fetch.ts +18 -13
  653. package/src/pipeline/steps/transform.ts +40 -15
  654. package/src/pipeline/template.test.ts +18 -0
  655. package/src/pipeline/template.ts +86 -13
  656. package/src/pipeline/transform.test.ts +15 -2
  657. package/src/plugin.test.ts +86 -0
  658. package/src/plugin.ts +254 -0
  659. package/src/registry-api.ts +12 -0
  660. package/src/registry.ts +24 -1
  661. package/src/runtime.ts +9 -0
  662. package/src/serialization.ts +79 -0
  663. package/src/synthesize.ts +102 -21
  664. package/src/types.ts +45 -13
  665. package/src/validate.ts +19 -4
  666. package/tests/e2e/browser-public.test.ts +36 -0
  667. package/tests/e2e/public-commands.test.ts +119 -1
  668. package/dist/clis/feishu/new.d.ts +0 -1
  669. package/dist/clis/feishu/new.js +0 -27
  670. package/dist/clis/feishu/read.d.ts +0 -1
  671. package/dist/clis/feishu/read.js +0 -40
  672. package/dist/clis/feishu/search.d.ts +0 -1
  673. package/dist/clis/feishu/search.js +0 -30
  674. package/dist/clis/feishu/send.d.ts +0 -1
  675. package/dist/clis/feishu/send.js +0 -39
  676. package/dist/clis/feishu/status.d.ts +0 -1
  677. package/dist/clis/feishu/status.js +0 -28
  678. package/dist/clis/neteasemusic/like.d.ts +0 -1
  679. package/dist/clis/neteasemusic/like.js +0 -25
  680. package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
  681. package/dist/clis/neteasemusic/lyrics.js +0 -47
  682. package/dist/clis/neteasemusic/next.d.ts +0 -1
  683. package/dist/clis/neteasemusic/next.js +0 -26
  684. package/dist/clis/neteasemusic/play.d.ts +0 -1
  685. package/dist/clis/neteasemusic/play.js +0 -26
  686. package/dist/clis/neteasemusic/playing.d.ts +0 -1
  687. package/dist/clis/neteasemusic/playing.js +0 -59
  688. package/dist/clis/neteasemusic/playlist.d.ts +0 -1
  689. package/dist/clis/neteasemusic/playlist.js +0 -46
  690. package/dist/clis/neteasemusic/prev.d.ts +0 -1
  691. package/dist/clis/neteasemusic/prev.js +0 -25
  692. package/dist/clis/neteasemusic/search.d.ts +0 -1
  693. package/dist/clis/neteasemusic/search.js +0 -52
  694. package/dist/clis/neteasemusic/status.d.ts +0 -1
  695. package/dist/clis/neteasemusic/status.js +0 -16
  696. package/dist/clis/neteasemusic/volume.d.ts +0 -1
  697. package/dist/clis/neteasemusic/volume.js +0 -54
  698. package/dist/clis/twitter/trending.yaml +0 -46
  699. package/dist/clis/wechat/chats.d.ts +0 -1
  700. package/dist/clis/wechat/chats.js +0 -28
  701. package/dist/clis/wechat/contacts.d.ts +0 -1
  702. package/dist/clis/wechat/contacts.js +0 -28
  703. package/dist/clis/wechat/read.d.ts +0 -1
  704. package/dist/clis/wechat/read.js +0 -58
  705. package/dist/clis/wechat/search.d.ts +0 -1
  706. package/dist/clis/wechat/search.js +0 -31
  707. package/dist/clis/wechat/send.d.ts +0 -1
  708. package/dist/clis/wechat/send.js +0 -42
  709. package/dist/clis/wechat/status.d.ts +0 -1
  710. package/dist/clis/wechat/status.js +0 -29
  711. package/dist/pipeline.d.ts +0 -7
  712. package/dist/pipeline.js +0 -7
  713. package/docs/adapters/browser/github.md +0 -26
  714. package/docs/adapters/desktop/feishu.md +0 -20
  715. package/docs/adapters/desktop/neteasemusic.md +0 -31
  716. package/docs/adapters/desktop/wechat.md +0 -28
  717. package/docs/public/CNAME +0 -1
  718. package/src/clis/antigravity/README.md +0 -5
  719. package/src/clis/antigravity/README.zh-CN.md +0 -51
  720. package/src/clis/chaoxing/README.md +0 -14
  721. package/src/clis/chaoxing/README.zh-CN.md +0 -35
  722. package/src/clis/chatgpt/README.md +0 -5
  723. package/src/clis/chatgpt/README.zh-CN.md +0 -44
  724. package/src/clis/chatwise/README.md +0 -5
  725. package/src/clis/chatwise/README.zh-CN.md +0 -38
  726. package/src/clis/codex/README.md +0 -5
  727. package/src/clis/codex/README.zh-CN.md +0 -33
  728. package/src/clis/cursor/README.md +0 -5
  729. package/src/clis/cursor/README.zh-CN.md +0 -33
  730. package/src/clis/discord-app/README.md +0 -5
  731. package/src/clis/discord-app/README.zh-CN.md +0 -28
  732. package/src/clis/feishu/README.md +0 -5
  733. package/src/clis/feishu/README.zh-CN.md +0 -20
  734. package/src/clis/feishu/new.ts +0 -32
  735. package/src/clis/feishu/read.ts +0 -48
  736. package/src/clis/feishu/search.ts +0 -35
  737. package/src/clis/feishu/send.ts +0 -46
  738. package/src/clis/feishu/status.ts +0 -34
  739. package/src/clis/neteasemusic/README.md +0 -5
  740. package/src/clis/neteasemusic/README.zh-CN.md +0 -31
  741. package/src/clis/neteasemusic/like.ts +0 -28
  742. package/src/clis/neteasemusic/lyrics.ts +0 -53
  743. package/src/clis/neteasemusic/next.ts +0 -30
  744. package/src/clis/neteasemusic/play.ts +0 -30
  745. package/src/clis/neteasemusic/playing.ts +0 -62
  746. package/src/clis/neteasemusic/playlist.ts +0 -51
  747. package/src/clis/neteasemusic/prev.ts +0 -29
  748. package/src/clis/neteasemusic/search.ts +0 -58
  749. package/src/clis/neteasemusic/status.ts +0 -18
  750. package/src/clis/neteasemusic/volume.ts +0 -61
  751. package/src/clis/notion/README.md +0 -5
  752. package/src/clis/notion/README.zh-CN.md +0 -29
  753. package/src/clis/twitter/trending.yaml +0 -46
  754. package/src/clis/wechat/README.md +0 -5
  755. package/src/clis/wechat/README.zh-CN.md +0 -28
  756. package/src/clis/wechat/chats.ts +0 -33
  757. package/src/clis/wechat/contacts.ts +0 -33
  758. package/src/clis/wechat/read.ts +0 -72
  759. package/src/clis/wechat/search.ts +0 -36
  760. package/src/clis/wechat/send.ts +0 -49
  761. package/src/clis/wechat/status.ts +0 -35
  762. package/src/engine.ts +0 -274
  763. package/src/pipeline.ts +0 -8
  764. /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
  765. /package/dist/{chaoxing.test.d.ts → clis/bloomberg/businessweek.d.ts} +0 -0
  766. /package/dist/{coupang.test.d.ts → clis/bloomberg/economics.d.ts} +0 -0
  767. /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
  768. /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
  769. /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
@@ -7,5 +7,44 @@
7
7
  * Includes Strategy Cascade: if the initial strategy fails,
8
8
  * automatically downgrades and retries.
9
9
  */
10
- export declare function generateCliFromUrl(opts: any): Promise<any>;
11
- export declare function renderGenerateSummary(r: any): string;
10
+ import type { IBrowserFactory } from './runtime.js';
11
+ import { type SynthesizeCandidateSummary } from './synthesize.js';
12
+ interface RegisterCandidatesResult {
13
+ ok: boolean;
14
+ count: number;
15
+ }
16
+ export interface GenerateCliOptions {
17
+ url: string;
18
+ BrowserFactory: new () => IBrowserFactory;
19
+ builtinClis?: string;
20
+ userClis?: string;
21
+ goal?: string | null;
22
+ site?: string;
23
+ waitSeconds?: number;
24
+ top?: number;
25
+ register?: boolean;
26
+ workspace?: string;
27
+ }
28
+ export interface GenerateCliResult {
29
+ ok: boolean;
30
+ goal?: string | null;
31
+ normalized_goal?: string | null;
32
+ site: string;
33
+ selected_candidate: SynthesizeCandidateSummary | null;
34
+ selected_command: string;
35
+ explore: {
36
+ endpoint_count: number;
37
+ api_endpoint_count: number;
38
+ capability_count: number;
39
+ top_strategy: string;
40
+ framework: Record<string, boolean>;
41
+ };
42
+ synthesize: {
43
+ candidate_count: number;
44
+ candidates: Array<Pick<SynthesizeCandidateSummary, 'name' | 'strategy' | 'confidence'>>;
45
+ };
46
+ register: RegisterCandidatesResult | null;
47
+ }
48
+ export declare function generateCliFromUrl(opts: GenerateCliOptions): Promise<GenerateCliResult>;
49
+ export declare function renderGenerateSummary(r: GenerateCliResult): string;
50
+ export {};
package/dist/generate.js CHANGED
@@ -9,8 +9,9 @@
9
9
  */
10
10
  import { exploreUrl } from './explore.js';
11
11
  import { synthesizeFromExplore } from './synthesize.js';
12
- // TODO: implement real CLI registration (copy candidate YAML to user clis dir)
13
- function registerCandidates(_opts) { return { ok: true, count: 0 }; }
12
+ function registerCandidates(_opts) {
13
+ return { ok: true, count: 0 };
14
+ }
14
15
  const CAPABILITY_ALIASES = {
15
16
  search: ['search', '搜索', '查找', 'query', 'keyword'],
16
17
  hot: ['hot', '热门', '热榜', '热搜', 'popular', 'top', 'ranking'],
@@ -58,7 +59,7 @@ export async function generateCliFromUrl(opts) {
58
59
  const exploreResult = await exploreUrl(opts.url, {
59
60
  BrowserFactory: opts.BrowserFactory,
60
61
  site: opts.site,
61
- goal: normalizeGoal(opts.goal) ?? opts.goal,
62
+ goal: normalizeGoal(opts.goal) ?? opts.goal ?? undefined,
62
63
  waitSeconds: opts.waitSeconds ?? 3,
63
64
  workspace: opts.workspace,
64
65
  });
@@ -68,7 +69,7 @@ export async function generateCliFromUrl(opts) {
68
69
  });
69
70
  // Step 3: Select best candidate for goal
70
71
  const selected = selectCandidate(synthesizeResult.candidates ?? [], opts.goal);
71
- const selectedSite = selected?.site ?? synthesizeResult.site ?? exploreResult.site;
72
+ const selectedSite = synthesizeResult.site ?? exploreResult.site;
72
73
  // Step 4: Register (if requested)
73
74
  let registerResult = null;
74
75
  if (opts.register !== false && synthesizeResult.candidate_count > 0) {
package/dist/main.js CHANGED
@@ -5,7 +5,7 @@
5
5
  import * as os from 'node:os';
6
6
  import * as path from 'node:path';
7
7
  import { fileURLToPath } from 'node:url';
8
- import { discoverClis } from './engine.js';
8
+ import { discoverClis, discoverPlugins } from './discovery.js';
9
9
  import { getCompletions } from './completion.js';
10
10
  import { runCli } from './cli.js';
11
11
  const __filename = fileURLToPath(import.meta.url);
@@ -13,6 +13,7 @@ const __dirname = path.dirname(__filename);
13
13
  const BUILTIN_CLIS = path.resolve(__dirname, 'clis');
14
14
  const USER_CLIS = path.join(os.homedir(), '.opencli', 'clis');
15
15
  await discoverClis(BUILTIN_CLIS, USER_CLIS);
16
+ await discoverPlugins();
16
17
  // ── Fast-path: handle --get-completions before commander parses ─────────
17
18
  // Usage: opencli --get-completions --cursor <N> [word1 word2 ...]
18
19
  const getCompIdx = process.argv.indexOf('--get-completions');
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import type { IPage } from '../types.js';
5
5
  export interface PipelineContext {
6
- args?: Record<string, any>;
6
+ args?: Record<string, unknown>;
7
7
  debug?: boolean;
8
8
  }
9
- export declare function executePipeline(page: IPage | null, pipeline: any[], ctx?: PipelineContext): Promise<any>;
9
+ export declare function executePipeline(page: IPage | null, pipeline: unknown[], ctx?: PipelineContext): Promise<unknown>;
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { getStep } from './registry.js';
5
5
  import { log } from '../logger.js';
6
+ import { ConfigError } from '../errors.js';
6
7
  export async function executePipeline(page, pipeline, ctx = {}) {
7
8
  const args = ctx.args ?? {};
8
9
  const debug = ctx.debug ?? false;
@@ -20,8 +21,7 @@ export async function executePipeline(page, pipeline, ctx = {}) {
20
21
  data = await handler(page, params, data, args);
21
22
  }
22
23
  else {
23
- if (debug)
24
- log.warn(`Unknown step: ${op}`);
24
+ throw new ConfigError(`Unknown pipeline step "${op}" at index ${i}.`, 'Check the YAML pipeline step name or register the custom step before execution.');
25
25
  }
26
26
  if (debug)
27
27
  debugStepResult(op, data);
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { describe, it, expect, vi } from 'vitest';
5
5
  import { executePipeline } from './index.js';
6
+ import { ConfigError } from '../errors.js';
6
7
  /** Create a minimal mock page for testing */
7
8
  function createMockPage(overrides = {}) {
8
9
  return {
@@ -13,6 +14,7 @@ function createMockPage(overrides = {}) {
13
14
  click: vi.fn(),
14
15
  typeText: vi.fn(),
15
16
  pressKey: vi.fn(),
17
+ getFormState: vi.fn().mockResolvedValue({}),
16
18
  wait: vi.fn(),
17
19
  tabs: vi.fn().mockResolvedValue([]),
18
20
  closeTab: vi.fn(),
@@ -21,6 +23,7 @@ function createMockPage(overrides = {}) {
21
23
  networkRequests: vi.fn().mockResolvedValue([]),
22
24
  consoleMessages: vi.fn().mockResolvedValue(''),
23
25
  scroll: vi.fn(),
26
+ scrollTo: vi.fn(),
24
27
  autoScroll: vi.fn(),
25
28
  installInterceptor: vi.fn(),
26
29
  getInterceptedRequests: vi.fn().mockResolvedValue([]),
@@ -70,6 +73,30 @@ describe('executePipeline', () => {
70
73
  { name: 'World', score: 20 },
71
74
  ]);
72
75
  });
76
+ it('runs inline select inside map step', async () => {
77
+ const page = createMockPage({
78
+ evaluate: vi.fn().mockResolvedValue({
79
+ posts: [
80
+ { title: 'First', rank: 1 },
81
+ { title: 'Second', rank: 2 },
82
+ ],
83
+ }),
84
+ });
85
+ const result = await executePipeline(page, [
86
+ { evaluate: 'test' },
87
+ {
88
+ map: {
89
+ select: 'posts',
90
+ title: '${{ item.title }}',
91
+ rank: '${{ item.rank }}',
92
+ },
93
+ },
94
+ ]);
95
+ expect(result).toEqual([
96
+ { title: 'First', rank: 1 },
97
+ { title: 'Second', rank: 2 },
98
+ ]);
99
+ });
73
100
  it('executes limit step', async () => {
74
101
  const page = createMockPage({
75
102
  evaluate: vi.fn().mockResolvedValue([1, 2, 3, 4, 5]),
@@ -107,13 +134,13 @@ describe('executePipeline', () => {
107
134
  ]);
108
135
  expect(page.wait).toHaveBeenCalledWith(2);
109
136
  });
110
- it('handles unknown steps gracefully in debug mode', async () => {
111
- const stderr = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
112
- await executePipeline(null, [
137
+ it('fails fast on unknown steps', async () => {
138
+ await expect(executePipeline(null, [
139
+ { unknownStep: 'test' },
140
+ ], { debug: true })).rejects.toBeInstanceOf(ConfigError);
141
+ await expect(executePipeline(null, [
113
142
  { unknownStep: 'test' },
114
- ], { debug: true });
115
- expect(stderr).toHaveBeenCalledWith(expect.stringContaining('Unknown step'));
116
- stderr.mockRestore();
143
+ ], { debug: true })).rejects.toThrow('Unknown pipeline step "unknownStep"');
117
144
  });
118
145
  it('passes args through template rendering', async () => {
119
146
  const page = createMockPage({
@@ -8,7 +8,7 @@ import type { IPage } from '../types.js';
8
8
  * TData is the type of the `data` state flowing into the step.
9
9
  * TResult is the expected return type.
10
10
  */
11
- export type StepHandler<TData = any, TResult = any> = (page: IPage | null, params: any, data: TData, args: Record<string, any>) => Promise<TResult>;
11
+ export type StepHandler<TData = unknown, TResult = unknown, TParams = unknown> = (page: IPage | null, params: TParams, data: TData, args: Record<string, unknown>) => Promise<TResult>;
12
12
  /**
13
13
  * Get a registered step handler by name.
14
14
  */
@@ -3,10 +3,10 @@
3
3
  * Browser interaction primitives.
4
4
  */
5
5
  import type { IPage } from '../../types.js';
6
- export declare function stepNavigate(page: IPage | null, params: any, data: any, args: Record<string, any>): Promise<any>;
7
- export declare function stepClick(page: IPage | null, params: any, data: any, args: Record<string, any>): Promise<any>;
8
- export declare function stepType(page: IPage | null, params: any, data: any, args: Record<string, any>): Promise<any>;
9
- export declare function stepWait(page: IPage | null, params: any, data: any, args: Record<string, any>): Promise<any>;
10
- export declare function stepPress(page: IPage | null, params: any, data: any, args: Record<string, any>): Promise<any>;
11
- export declare function stepSnapshot(page: IPage | null, params: any, _data: any, _args: Record<string, any>): Promise<any>;
12
- export declare function stepEvaluate(page: IPage | null, params: any, data: any, args: Record<string, any>): Promise<any>;
6
+ export declare function stepNavigate(page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
7
+ export declare function stepClick(page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
8
+ export declare function stepType(page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
9
+ export declare function stepWait(page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
10
+ export declare function stepPress(page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
11
+ export declare function stepSnapshot(page: IPage | null, params: unknown, _data: unknown, _args: Record<string, unknown>): Promise<unknown>;
12
+ export declare function stepEvaluate(page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
@@ -3,9 +3,18 @@
3
3
  * Browser interaction primitives.
4
4
  */
5
5
  import { render } from '../template.js';
6
+ function isRecord(value) {
7
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
8
+ }
6
9
  export async function stepNavigate(page, params, data, args) {
7
- const url = render(params, { args, data });
8
- await page.goto(String(url));
10
+ if (isRecord(params) && 'url' in params) {
11
+ const url = String(render(params.url, { args, data }));
12
+ await page.goto(url, { waitUntil: params.waitUntil, settleMs: typeof params.settleMs === 'number' ? params.settleMs : undefined });
13
+ }
14
+ else {
15
+ const url = render(params, { args, data });
16
+ await page.goto(String(url));
17
+ }
9
18
  return data;
10
19
  }
11
20
  export async function stepClick(page, params, data, args) {
@@ -13,7 +22,7 @@ export async function stepClick(page, params, data, args) {
13
22
  return data;
14
23
  }
15
24
  export async function stepType(page, params, data, args) {
16
- if (typeof params === 'object' && params) {
25
+ if (isRecord(params)) {
17
26
  const ref = String(render(params.ref ?? '', { args, data })).replace(/^@/, '');
18
27
  const text = String(render(params.text ?? '', { args, data }));
19
28
  await page.typeText(ref, text);
@@ -25,11 +34,11 @@ export async function stepType(page, params, data, args) {
25
34
  export async function stepWait(page, params, data, args) {
26
35
  if (typeof params === 'number')
27
36
  await page.wait(params);
28
- else if (typeof params === 'object' && params) {
37
+ else if (isRecord(params)) {
29
38
  if ('text' in params) {
30
39
  await page.wait({
31
40
  text: String(render(params.text, { args, data })),
32
- timeout: params.timeout
41
+ timeout: typeof params.timeout === 'number' ? params.timeout : undefined,
33
42
  });
34
43
  }
35
44
  else if ('time' in params)
@@ -44,8 +53,13 @@ export async function stepPress(page, params, data, args) {
44
53
  return data;
45
54
  }
46
55
  export async function stepSnapshot(page, params, _data, _args) {
47
- const opts = (typeof params === 'object' && params) ? params : {};
48
- return page.snapshot({ interactive: opts.interactive ?? false, compact: opts.compact ?? false, maxDepth: opts.max_depth, raw: opts.raw ?? false });
56
+ const opts = isRecord(params) ? params : {};
57
+ return page.snapshot({
58
+ interactive: typeof opts.interactive === 'boolean' ? opts.interactive : false,
59
+ compact: typeof opts.compact === 'boolean' ? opts.compact : false,
60
+ maxDepth: typeof opts.max_depth === 'number' ? opts.max_depth : undefined,
61
+ raw: typeof opts.raw === 'boolean' ? opts.raw : false,
62
+ });
49
63
  }
50
64
  export async function stepEvaluate(page, params, data, args) {
51
65
  const js = String(render(params, { args, data }));
@@ -2,4 +2,4 @@
2
2
  * Pipeline step: fetch — HTTP API requests.
3
3
  */
4
4
  import type { IPage } from '../../types.js';
5
- export declare function stepFetch(page: IPage | null, params: any, data: any, args: Record<string, any>): Promise<any>;
5
+ export declare function stepFetch(page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
@@ -2,6 +2,9 @@
2
2
  * Pipeline step: fetch — HTTP API requests.
3
3
  */
4
4
  import { render } from '../template.js';
5
+ function isRecord(value) {
6
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
7
+ }
5
8
  /** Simple async concurrency limiter */
6
9
  async function mapConcurrent(items, limit, fn) {
7
10
  const results = new Array(items.length);
@@ -53,7 +56,7 @@ async function fetchSingle(page, url, method, queryParams, headers, args, data)
53
56
  async function fetchBatchInBrowser(page, urls, method, headers, concurrency) {
54
57
  const headersJs = JSON.stringify(headers);
55
58
  const urlsJs = JSON.stringify(urls);
56
- return page.evaluate(`
59
+ return (await page.evaluate(`
57
60
  async () => {
58
61
  const urls = ${urlsJs};
59
62
  const method = "${method}";
@@ -79,17 +82,18 @@ async function fetchBatchInBrowser(page, urls, method, headers, concurrency) {
79
82
  await Promise.all(workers);
80
83
  return results;
81
84
  }
82
- `);
85
+ `));
83
86
  }
84
87
  export async function stepFetch(page, params, data, args) {
85
- const urlOrObj = typeof params === 'string' ? params : (params?.url ?? '');
86
- const method = params?.method ?? 'GET';
87
- const queryParams = params?.params ?? {};
88
- const headers = params?.headers ?? {};
88
+ const paramObject = isRecord(params) ? params : {};
89
+ const urlOrObj = typeof params === 'string' ? params : (paramObject.url ?? '');
90
+ const method = typeof paramObject.method === 'string' ? paramObject.method : 'GET';
91
+ const queryParams = isRecord(paramObject.params) ? paramObject.params : {};
92
+ const headers = isRecord(paramObject.headers) ? paramObject.headers : {};
89
93
  const urlTemplate = String(urlOrObj);
90
94
  // Per-item fetch when data is array and URL references item
91
95
  if (Array.isArray(data) && urlTemplate.includes('item')) {
92
- const concurrency = typeof params?.concurrency === 'number' ? params.concurrency : 5;
96
+ const concurrency = typeof paramObject.concurrency === 'number' ? paramObject.concurrency : 5;
93
97
  // Render all URLs upfront
94
98
  const renderedHeaders = {};
95
99
  for (const [k, v] of Object.entries(headers))
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Pipeline steps: data transforms — select, map, filter, sort, limit.
3
3
  */
4
- export declare function stepSelect(_page: any, params: any, data: any, args: Record<string, any>): Promise<any>;
5
- export declare function stepMap(_page: any, params: any, data: any, args: Record<string, any>): Promise<any>;
6
- export declare function stepFilter(_page: any, params: any, data: any, args: Record<string, any>): Promise<any>;
7
- export declare function stepSort(_page: any, params: any, data: any, _args: Record<string, any>): Promise<any>;
8
- export declare function stepLimit(_page: any, params: any, data: any, args: Record<string, any>): Promise<any>;
4
+ import type { IPage } from '../../types.js';
5
+ export declare function stepSelect(_page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
6
+ export declare function stepMap(_page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
7
+ export declare function stepFilter(_page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
8
+ export declare function stepSort(_page: IPage | null, params: unknown, data: unknown, _args: Record<string, unknown>): Promise<unknown>;
9
+ export declare function stepLimit(_page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
@@ -2,12 +2,15 @@
2
2
  * Pipeline steps: data transforms — select, map, filter, sort, limit.
3
3
  */
4
4
  import { render, evalExpr } from '../template.js';
5
+ function isRecord(value) {
6
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
7
+ }
5
8
  export async function stepSelect(_page, params, data, args) {
6
9
  const pathStr = String(render(params, { args, data }));
7
10
  if (data && typeof data === 'object') {
8
11
  let current = data;
9
12
  for (const part of pathStr.split('.')) {
10
- if (current && typeof current === 'object' && !Array.isArray(current))
13
+ if (isRecord(current))
11
14
  current = current[part];
12
15
  else if (Array.isArray(current) && /^\d+$/.test(part))
13
16
  current = current[parseInt(part, 10)];
@@ -21,15 +24,26 @@ export async function stepSelect(_page, params, data, args) {
21
24
  export async function stepMap(_page, params, data, args) {
22
25
  if (!data || typeof data !== 'object')
23
26
  return data;
24
- let items = Array.isArray(data) ? data : [data];
25
- if (!Array.isArray(data) && typeof data === 'object' && 'data' in data)
26
- items = data.data;
27
+ let source = data;
28
+ // Support inline select: { map: { select: 'path', key: '${{ item.x }}' } }
29
+ if (isRecord(params) && 'select' in params) {
30
+ source = await stepSelect(null, params.select, data, args);
31
+ }
32
+ if (!source || typeof source !== 'object')
33
+ return source;
34
+ let items = Array.isArray(source) ? source : [source];
35
+ if (isRecord(source) && Array.isArray(source.data))
36
+ items = source.data;
27
37
  const result = [];
38
+ const templateParams = isRecord(params) ? params : {};
28
39
  for (let i = 0; i < items.length; i++) {
29
40
  const item = items[i];
30
41
  const row = {};
31
- for (const [key, template] of Object.entries(params))
32
- row[key] = render(template, { args, data, item, index: i });
42
+ for (const [key, template] of Object.entries(templateParams)) {
43
+ if (key === 'select')
44
+ continue;
45
+ row[key] = render(template, { args, data: source, item, index: i });
46
+ }
33
47
  result.push(row);
34
48
  }
35
49
  return result;
@@ -42,9 +56,16 @@ export async function stepFilter(_page, params, data, args) {
42
56
  export async function stepSort(_page, params, data, _args) {
43
57
  if (!Array.isArray(data))
44
58
  return data;
45
- const key = typeof params === 'object' ? (params.by ?? '') : String(params);
46
- const reverse = typeof params === 'object' ? params.order === 'desc' : false;
47
- return [...data].sort((a, b) => { const va = a[key] ?? ''; const vb = b[key] ?? ''; const cmp = va < vb ? -1 : va > vb ? 1 : 0; return reverse ? -cmp : cmp; });
59
+ const key = isRecord(params) ? String(params.by ?? '') : String(params);
60
+ const reverse = isRecord(params) ? params.order === 'desc' : false;
61
+ return [...data].sort((a, b) => {
62
+ const left = isRecord(a) ? a[key] : undefined;
63
+ const right = isRecord(b) ? b[key] : undefined;
64
+ const va = left ?? '';
65
+ const vb = right ?? '';
66
+ const cmp = va < vb ? -1 : va > vb ? 1 : 0;
67
+ return reverse ? -cmp : cmp;
68
+ });
48
69
  }
49
70
  export async function stepLimit(_page, params, data, args) {
50
71
  if (!Array.isArray(data))
@@ -2,14 +2,14 @@
2
2
  * Pipeline template engine: ${{ ... }} expression rendering.
3
3
  */
4
4
  export interface RenderContext {
5
- args?: Record<string, any>;
6
- data?: any;
7
- item?: any;
5
+ args?: Record<string, unknown>;
6
+ data?: unknown;
7
+ item?: unknown;
8
8
  index?: number;
9
9
  }
10
- export declare function render(template: any, ctx: RenderContext): any;
11
- export declare function evalExpr(expr: string, ctx: RenderContext): any;
12
- export declare function resolvePath(pathStr: string, ctx: RenderContext): any;
10
+ export declare function render(template: unknown, ctx: RenderContext): unknown;
11
+ export declare function evalExpr(expr: string, ctx: RenderContext): unknown;
12
+ export declare function resolvePath(pathStr: string, ctx: RenderContext): unknown;
13
13
  /**
14
14
  * Normalize JavaScript source for browser evaluate() calls.
15
15
  */
@@ -1,16 +1,20 @@
1
1
  /**
2
2
  * Pipeline template engine: ${{ ... }} expression rendering.
3
3
  */
4
+ function isRecord(value) {
5
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
6
+ }
4
7
  export function render(template, ctx) {
5
8
  if (typeof template !== 'string')
6
9
  return template;
10
+ const trimmed = template.trim();
7
11
  // Full expression: entire string is a single ${{ ... }}
8
12
  // Use [^}] to prevent matching across }} boundaries (e.g. "${{ a }}-${{ b }}")
9
- const fullMatch = template.match(/^\$\{\{\s*([^}]*(?:\}[^}][^}]*)*)\s*\}\}$/);
10
- if (fullMatch && !template.includes('}}-') && !template.includes('}}${{'))
13
+ const fullMatch = trimmed.match(/^\$\{\{\s*([^}]*(?:\}[^}][^}]*)*)\s*\}\}$/);
14
+ if (fullMatch && !trimmed.includes('}}-') && !trimmed.includes('}}${{'))
11
15
  return evalExpr(fullMatch[1].trim(), ctx);
12
16
  // Check if the entire string is a single expression (no other text around it)
13
- const singleExpr = template.match(/^\$\{\{\s*([\s\S]*?)\s*\}\}$/);
17
+ const singleExpr = trimmed.match(/^\$\{\{\s*([\s\S]*?)\s*\}\}$/);
14
18
  if (singleExpr) {
15
19
  // Verify it's truly a single expression (no other ${{ inside)
16
20
  const inner = singleExpr[1];
@@ -62,7 +66,10 @@ export function evalExpr(expr, ctx) {
62
66
  const right = orMatch[2].trim();
63
67
  return right.replace(/^['"]|['"]$/g, '');
64
68
  }
65
- return resolvePath(expr, { args, item, data, index });
69
+ const resolved = resolvePath(expr, { args, item, data, index });
70
+ if (resolved !== null && resolved !== undefined)
71
+ return resolved;
72
+ return evalJsExpr(expr, { args, item, data, index });
66
73
  }
67
74
  /**
68
75
  * Apply a named filter to a value.
@@ -142,6 +149,10 @@ function applyFilter(filterExpr, value) {
142
149
  const parts = value.split(/[/\\]/);
143
150
  return parts[parts.length - 1] || value;
144
151
  }
152
+ case 'urlencode':
153
+ return typeof value === 'string' ? encodeURIComponent(value) : value;
154
+ case 'urldecode':
155
+ return typeof value === 'string' ? decodeURIComponent(value) : value;
145
156
  default:
146
157
  return value;
147
158
  }
@@ -174,7 +185,7 @@ export function resolvePath(pathStr, ctx) {
174
185
  rest = parts;
175
186
  }
176
187
  for (const part of rest) {
177
- if (obj && typeof obj === 'object' && !Array.isArray(obj))
188
+ if (isRecord(obj))
178
189
  obj = obj[part];
179
190
  else if (Array.isArray(obj) && /^\d+$/.test(part))
180
191
  obj = obj[parseInt(part, 10)];
@@ -183,6 +194,33 @@ export function resolvePath(pathStr, ctx) {
183
194
  }
184
195
  return obj;
185
196
  }
197
+ /**
198
+ * Evaluate arbitrary JS expressions as a last-resort fallback.
199
+ *
200
+ * ⚠️ SECURITY NOTE: Uses `new Function()` to execute the expression.
201
+ * This is acceptable here because:
202
+ * 1. YAML adapters are authored by trusted repo contributors only.
203
+ * 2. The expression runs in the same Node.js process (no sandbox).
204
+ * 3. Only a curated set of globals is exposed (no require/import/process/fs).
205
+ * If opencli ever loads untrusted third-party adapters, this MUST be replaced
206
+ * with a proper sandboxed evaluator.
207
+ */
208
+ function evalJsExpr(expr, ctx) {
209
+ // Guard against absurdly long expressions that could indicate injection.
210
+ if (expr.length > 2000)
211
+ return undefined;
212
+ const args = ctx.args ?? {};
213
+ const item = ctx.item ?? {};
214
+ const data = ctx.data;
215
+ const index = ctx.index ?? 0;
216
+ try {
217
+ const fn = new Function('args', 'item', 'data', 'index', 'encodeURIComponent', 'decodeURIComponent', 'JSON', 'Math', 'Number', 'String', 'Boolean', 'Array', 'Object', 'Date', `"use strict"; return (${expr});`);
218
+ return fn(args, item, data, index, encodeURIComponent, decodeURIComponent, JSON, Math, Number, String, Boolean, Array, Object, Date);
219
+ }
220
+ catch {
221
+ return undefined;
222
+ }
223
+ }
186
224
  /**
187
225
  * Normalize JavaScript source for browser evaluate() calls.
188
226
  */
@@ -54,6 +54,15 @@ describe('evalExpr', () => {
54
54
  it('resolves simple path', () => {
55
55
  expect(evalExpr('item.title', { item: { title: 'Test' } })).toBe('Test');
56
56
  });
57
+ it('evaluates JS helper expressions', () => {
58
+ expect(evalExpr('encodeURIComponent(args.keyword)', { args: { keyword: 'hello world' } })).toBe('hello%20world');
59
+ });
60
+ it('evaluates ternary expressions', () => {
61
+ expect(evalExpr("args.kind === 'tech' ? 'technology' : args.kind", { args: { kind: 'tech' } })).toBe('technology');
62
+ });
63
+ it('evaluates method calls on values', () => {
64
+ expect(evalExpr("args.username.startsWith('@') ? args.username : '@' + args.username", { args: { username: 'alice' } })).toBe('@alice');
65
+ });
57
66
  it('applies join filter', () => {
58
67
  expect(evalExpr('item.tags | join(,)', { item: { tags: ['a', 'b', 'c'] } })).toBe('a,b,c');
59
68
  });
@@ -100,6 +109,15 @@ describe('render', () => {
100
109
  it('renders URL template', () => {
101
110
  expect(render('https://api.example.com/search?q=${{ args.keyword }}', { args: { keyword: 'test' } })).toBe('https://api.example.com/search?q=test');
102
111
  });
112
+ it('renders inline helper expressions', () => {
113
+ expect(render('https://example.com/search?q=${{ encodeURIComponent(args.keyword) }}', { args: { keyword: 'hello world' } })).toBe('https://example.com/search?q=hello%20world');
114
+ });
115
+ it('renders full multiline expressions', () => {
116
+ expect(render("${{\n args.topic ? `https://medium.com/tag/${args.topic}` : 'https://medium.com/tag/technology'\n}}", { args: { topic: 'ai' } })).toBe('https://medium.com/tag/ai');
117
+ });
118
+ it('renders block expressions with surrounding whitespace', () => {
119
+ expect(render("\n ${{ args.kind === 'tech' ? 'technology' : args.kind }}\n", { args: { kind: 'tech' } })).toBe('technology');
120
+ });
103
121
  });
104
122
  describe('normalizeEvaluateSource', () => {
105
123
  it('wraps bare expression', () => {
@@ -49,6 +49,17 @@ describe('stepMap', () => {
49
49
  it('returns null/undefined as-is', async () => {
50
50
  expect(await stepMap(null, { x: '${{ item.x }}' }, null, {})).toBeNull();
51
51
  });
52
+ it('supports inline select before mapping', async () => {
53
+ const result = await stepMap(null, {
54
+ select: 'posts',
55
+ title: '${{ item.title }}',
56
+ rank: '${{ index + 1 }}',
57
+ }, { posts: [{ title: 'One' }, { title: 'Two' }] }, {});
58
+ expect(result).toEqual([
59
+ { title: 'One', rank: 1 },
60
+ { title: 'Two', rank: 2 },
61
+ ]);
62
+ });
52
63
  });
53
64
  describe('stepFilter', () => {
54
65
  it('filters by expression', async () => {
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Plugin management: install, uninstall, and list plugins.
3
+ *
4
+ * Plugins live in ~/.opencli/plugins/<name>/.
5
+ * Install source format: "github:user/repo"
6
+ */
7
+ export interface PluginInfo {
8
+ name: string;
9
+ path: string;
10
+ commands: string[];
11
+ source?: string;
12
+ }
13
+ /**
14
+ * Install a plugin from a source.
15
+ * Currently supports "github:user/repo" format (git clone wrapper).
16
+ */
17
+ export declare function installPlugin(source: string): string;
18
+ /**
19
+ * Uninstall a plugin by name.
20
+ */
21
+ export declare function uninstallPlugin(name: string): void;
22
+ /**
23
+ * List all installed plugins.
24
+ */
25
+ export declare function listPlugins(): PluginInfo[];
26
+ /** Parse a plugin source string into clone URL and name */
27
+ declare function parseSource(source: string): {
28
+ cloneUrl: string;
29
+ name: string;
30
+ } | null;
31
+ export { parseSource as _parseSource };