@jackwener/opencli 1.1.1 → 1.2.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 (523) hide show
  1. package/.github/workflows/build-extension.yml +3 -3
  2. package/.github/workflows/ci.yml +6 -6
  3. package/.github/workflows/doc-check.yml +3 -3
  4. package/.github/workflows/e2e-headed.yml +2 -2
  5. package/.github/workflows/pkg-pr-new.yml +2 -2
  6. package/.github/workflows/release.yml +3 -3
  7. package/.github/workflows/security.yml +2 -2
  8. package/CONTRIBUTING.md +39 -1
  9. package/README.md +13 -10
  10. package/README.zh-CN.md +43 -17
  11. package/SKILL.md +10 -5
  12. package/dist/browser/cdp.d.ts +4 -4
  13. package/dist/browser/cdp.js +39 -16
  14. package/dist/browser/daemon-client.d.ts +4 -2
  15. package/dist/browser/daemon-client.js +17 -4
  16. package/dist/browser/dom-helpers.js +38 -7
  17. package/dist/browser/dom-snapshot.d.ts +86 -0
  18. package/dist/browser/dom-snapshot.js +729 -0
  19. package/dist/browser/dom-snapshot.test.d.ts +11 -0
  20. package/dist/browser/dom-snapshot.test.js +212 -0
  21. package/dist/browser/index.d.ts +2 -0
  22. package/dist/browser/index.js +1 -0
  23. package/dist/browser/mcp.js +3 -1
  24. package/dist/browser/page.d.ts +14 -24
  25. package/dist/browser/page.js +46 -6
  26. package/dist/build-manifest.d.ts +11 -4
  27. package/dist/build-manifest.js +59 -21
  28. package/dist/build-manifest.test.js +58 -2
  29. package/dist/cli-manifest.json +3856 -1509
  30. package/dist/cli.js +66 -0
  31. package/dist/clis/barchart/greeks.js +1 -1
  32. package/dist/clis/barchart/options.js +1 -1
  33. package/dist/clis/barchart/quote.js +1 -1
  34. package/dist/clis/bilibili/download.js +1 -1
  35. package/dist/clis/bilibili/following.js +1 -1
  36. package/dist/clis/bilibili/subtitle.js +1 -1
  37. package/dist/clis/bilibili/user-videos.js +1 -1
  38. package/dist/clis/boss/batchgreet.js +10 -97
  39. package/dist/clis/boss/chatlist.js +8 -25
  40. package/dist/clis/boss/chatmsg.js +11 -42
  41. package/dist/clis/boss/common.d.ts +92 -0
  42. package/dist/clis/boss/common.js +223 -0
  43. package/dist/clis/boss/detail.js +7 -49
  44. package/dist/clis/boss/exchange.js +13 -79
  45. package/dist/clis/boss/greet.js +18 -145
  46. package/dist/clis/boss/invite.js +26 -121
  47. package/dist/clis/boss/joblist.js +6 -31
  48. package/dist/clis/boss/mark.js +12 -85
  49. package/dist/clis/boss/recommend.js +10 -49
  50. package/dist/clis/boss/resume.js +18 -118
  51. package/dist/clis/boss/search.js +12 -60
  52. package/dist/clis/boss/send.js +17 -151
  53. package/dist/clis/boss/stats.js +18 -69
  54. package/dist/clis/coupang/add-to-cart.js +1 -1
  55. package/dist/clis/devto/tag.yaml +34 -0
  56. package/dist/clis/devto/top.yaml +29 -0
  57. package/dist/clis/devto/user.yaml +33 -0
  58. package/dist/clis/douban/book-hot.d.ts +1 -0
  59. package/dist/clis/douban/book-hot.js +14 -0
  60. package/dist/clis/douban/marks.d.ts +1 -0
  61. package/dist/clis/douban/marks.js +115 -0
  62. package/dist/clis/douban/movie-hot.d.ts +1 -0
  63. package/dist/clis/douban/movie-hot.js +14 -0
  64. package/dist/clis/douban/reviews.d.ts +1 -0
  65. package/dist/clis/douban/reviews.js +106 -0
  66. package/dist/clis/douban/search.d.ts +1 -0
  67. package/dist/clis/douban/search.js +16 -0
  68. package/dist/clis/douban/shared.d.ts +4 -0
  69. package/dist/clis/douban/shared.js +155 -0
  70. package/dist/clis/douban/subject.yaml +76 -0
  71. package/dist/clis/douban/top250.yaml +70 -0
  72. package/dist/clis/douban/utils.d.ts +35 -0
  73. package/dist/clis/douban/utils.js +48 -0
  74. package/dist/clis/facebook/add-friend.yaml +43 -0
  75. package/dist/clis/facebook/events.yaml +44 -0
  76. package/dist/clis/facebook/feed.yaml +63 -0
  77. package/dist/clis/facebook/friends.yaml +42 -0
  78. package/dist/clis/facebook/groups.yaml +50 -0
  79. package/dist/clis/facebook/join-group.yaml +44 -0
  80. package/dist/clis/facebook/memories.yaml +39 -0
  81. package/dist/clis/facebook/notifications.yaml +40 -0
  82. package/dist/clis/facebook/profile.yaml +37 -0
  83. package/dist/clis/facebook/search.yaml +46 -0
  84. package/dist/clis/google/news.d.ts +5 -0
  85. package/dist/clis/google/news.js +58 -0
  86. package/dist/clis/google/search.d.ts +10 -0
  87. package/dist/clis/google/search.js +127 -0
  88. package/dist/clis/google/suggest.d.ts +5 -0
  89. package/dist/clis/google/suggest.js +34 -0
  90. package/dist/clis/google/trends.d.ts +5 -0
  91. package/dist/clis/google/trends.js +38 -0
  92. package/dist/clis/google/utils.d.ts +9 -0
  93. package/dist/clis/google/utils.js +23 -0
  94. package/dist/clis/google/utils.test.d.ts +1 -0
  95. package/dist/clis/google/utils.test.js +75 -0
  96. package/dist/clis/grok/ask.d.ts +14 -0
  97. package/dist/clis/grok/ask.js +257 -65
  98. package/dist/clis/grok/ask.test.d.ts +1 -0
  99. package/dist/clis/grok/ask.test.js +36 -0
  100. package/dist/clis/instagram/comment.yaml +52 -0
  101. package/dist/clis/instagram/explore.yaml +43 -0
  102. package/dist/clis/instagram/follow.yaml +41 -0
  103. package/dist/clis/instagram/followers.yaml +51 -0
  104. package/dist/clis/instagram/following.yaml +51 -0
  105. package/dist/clis/instagram/like.yaml +46 -0
  106. package/dist/clis/instagram/profile.yaml +42 -0
  107. package/dist/clis/instagram/save.yaml +46 -0
  108. package/dist/clis/instagram/saved.yaml +40 -0
  109. package/dist/clis/instagram/search.yaml +43 -0
  110. package/dist/clis/instagram/unfollow.yaml +38 -0
  111. package/dist/clis/instagram/unlike.yaml +46 -0
  112. package/dist/clis/instagram/unsave.yaml +46 -0
  113. package/dist/clis/instagram/user.yaml +54 -0
  114. package/dist/clis/jike/repost.js +1 -1
  115. package/dist/clis/jimeng/generate.yaml +1 -0
  116. package/dist/clis/linux-do/category.yaml +1 -0
  117. package/dist/clis/lobsters/active.yaml +29 -0
  118. package/dist/clis/lobsters/hot.yaml +29 -0
  119. package/dist/clis/lobsters/newest.yaml +29 -0
  120. package/dist/clis/lobsters/tag.yaml +34 -0
  121. package/dist/clis/medium/feed.d.ts +1 -0
  122. package/dist/clis/medium/feed.js +15 -0
  123. package/dist/clis/medium/search.d.ts +1 -0
  124. package/dist/clis/medium/search.js +15 -0
  125. package/dist/clis/medium/shared.d.ts +5 -0
  126. package/dist/clis/medium/shared.js +78 -0
  127. package/dist/clis/medium/user.d.ts +1 -0
  128. package/dist/clis/medium/user.js +15 -0
  129. package/dist/clis/reddit/comment.js +1 -1
  130. package/dist/clis/reddit/read.js +1 -1
  131. package/dist/clis/reddit/save.js +1 -1
  132. package/dist/clis/reddit/subreddit.yaml +1 -0
  133. package/dist/clis/reddit/subscribe.js +1 -1
  134. package/dist/clis/reddit/upvote.js +1 -1
  135. package/dist/clis/sinablog/article.d.ts +1 -0
  136. package/dist/clis/sinablog/article.js +14 -0
  137. package/dist/clis/sinablog/hot.d.ts +1 -0
  138. package/dist/clis/sinablog/hot.js +14 -0
  139. package/dist/clis/sinablog/search.d.ts +1 -0
  140. package/dist/clis/sinablog/search.js +51 -0
  141. package/dist/clis/sinablog/shared.d.ts +7 -0
  142. package/dist/clis/sinablog/shared.js +187 -0
  143. package/dist/clis/sinablog/user.d.ts +1 -0
  144. package/dist/clis/sinablog/user.js +15 -0
  145. package/dist/clis/substack/feed.d.ts +1 -0
  146. package/dist/clis/substack/feed.js +15 -0
  147. package/dist/clis/substack/publication.d.ts +1 -0
  148. package/dist/clis/substack/publication.js +15 -0
  149. package/dist/clis/substack/search.d.ts +1 -0
  150. package/dist/clis/substack/search.js +77 -0
  151. package/dist/clis/substack/shared.d.ts +4 -0
  152. package/dist/clis/substack/shared.js +129 -0
  153. package/dist/clis/tiktok/comment.yaml +66 -0
  154. package/dist/clis/tiktok/explore.yaml +39 -0
  155. package/dist/clis/tiktok/follow.yaml +39 -0
  156. package/dist/clis/tiktok/following.yaml +46 -0
  157. package/dist/clis/tiktok/friends.yaml +47 -0
  158. package/dist/clis/tiktok/like.yaml +38 -0
  159. package/dist/clis/tiktok/live.yaml +51 -0
  160. package/dist/clis/tiktok/notifications.yaml +52 -0
  161. package/dist/clis/tiktok/profile.yaml +45 -0
  162. package/dist/clis/tiktok/save.yaml +34 -0
  163. package/dist/clis/tiktok/search.yaml +46 -0
  164. package/dist/clis/tiktok/unfollow.yaml +44 -0
  165. package/dist/clis/tiktok/unlike.yaml +38 -0
  166. package/dist/clis/tiktok/unsave.yaml +36 -0
  167. package/dist/clis/tiktok/user.yaml +44 -0
  168. package/dist/clis/twitter/download.d.ts +1 -1
  169. package/dist/clis/twitter/download.js +3 -3
  170. package/dist/clis/twitter/followers.js +1 -1
  171. package/dist/clis/twitter/following.js +1 -1
  172. package/dist/clis/twitter/thread.js +1 -1
  173. package/dist/clis/twitter/timeline.d.ts +23 -0
  174. package/dist/clis/twitter/timeline.js +42 -14
  175. package/dist/clis/twitter/timeline.test.d.ts +1 -0
  176. package/dist/clis/twitter/timeline.test.js +102 -0
  177. package/dist/clis/wikipedia/random.d.ts +1 -0
  178. package/dist/clis/wikipedia/random.js +19 -0
  179. package/dist/clis/wikipedia/search.js +3 -3
  180. package/dist/clis/wikipedia/summary.js +4 -9
  181. package/dist/clis/wikipedia/trending.d.ts +1 -0
  182. package/dist/clis/wikipedia/trending.js +35 -0
  183. package/dist/clis/wikipedia/utils.d.ts +28 -0
  184. package/dist/clis/wikipedia/utils.js +13 -0
  185. package/dist/clis/xiaohongshu/creator-note-detail.js +1 -1
  186. package/dist/clis/xiaohongshu/creator-note-detail.test.js +2 -0
  187. package/dist/clis/xiaohongshu/creator-notes.test.js +2 -0
  188. package/dist/clis/xiaohongshu/download.js +1 -1
  189. package/dist/clis/xueqiu/earnings-date.yaml +69 -0
  190. package/dist/clis/xueqiu/search.yaml +2 -1
  191. package/dist/clis/xueqiu/stock.yaml +2 -0
  192. package/dist/clis/yahoo-finance/quote.js +1 -1
  193. package/dist/commanderAdapter.js +13 -7
  194. package/dist/daemon.js +21 -0
  195. package/dist/discovery.d.ts +8 -0
  196. package/dist/discovery.js +105 -19
  197. package/dist/doctor.js +3 -1
  198. package/dist/doctor.test.js +46 -2
  199. package/dist/engine.test.d.ts +0 -3
  200. package/dist/engine.test.js +74 -6
  201. package/dist/execution.d.ts +4 -2
  202. package/dist/execution.js +31 -7
  203. package/dist/explore.d.ts +76 -3
  204. package/dist/explore.js +11 -4
  205. package/dist/generate.d.ts +41 -2
  206. package/dist/generate.js +5 -4
  207. package/dist/main.js +2 -1
  208. package/dist/pipeline/executor.d.ts +4 -2
  209. package/dist/pipeline/executor.js +54 -15
  210. package/dist/pipeline/executor.test.js +33 -6
  211. package/dist/pipeline/registry.d.ts +1 -1
  212. package/dist/pipeline/steps/browser.d.ts +7 -7
  213. package/dist/pipeline/steps/browser.js +15 -7
  214. package/dist/pipeline/steps/fetch.d.ts +1 -1
  215. package/dist/pipeline/steps/fetch.js +11 -7
  216. package/dist/pipeline/steps/transform.d.ts +6 -5
  217. package/dist/pipeline/steps/transform.js +30 -9
  218. package/dist/pipeline/template.d.ts +6 -6
  219. package/dist/pipeline/template.js +43 -5
  220. package/dist/pipeline/template.test.js +18 -0
  221. package/dist/pipeline/transform.test.js +11 -0
  222. package/dist/plugin.d.ts +31 -0
  223. package/dist/plugin.js +216 -0
  224. package/dist/plugin.test.d.ts +4 -0
  225. package/dist/plugin.test.js +76 -0
  226. package/dist/registry-api.d.ts +11 -0
  227. package/dist/registry-api.js +9 -0
  228. package/dist/registry.d.ts +11 -0
  229. package/dist/registry.js +6 -1
  230. package/dist/synthesize.d.ts +94 -4
  231. package/dist/synthesize.js +5 -4
  232. package/dist/types.d.ts +39 -26
  233. package/dist/validate.js +8 -2
  234. package/docs/.vitepress/config.mts +6 -4
  235. package/docs/adapters/browser/barchart.md +6 -5
  236. package/docs/adapters/browser/bilibili.md +9 -0
  237. package/docs/adapters/browser/devto.md +35 -0
  238. package/docs/adapters/browser/douban.md +38 -0
  239. package/docs/adapters/browser/facebook.md +36 -0
  240. package/docs/adapters/browser/google.md +62 -0
  241. package/docs/adapters/browser/grok.md +26 -8
  242. package/docs/adapters/browser/instagram.md +46 -0
  243. package/docs/adapters/browser/lobsters.md +32 -0
  244. package/docs/adapters/browser/medium.md +32 -0
  245. package/docs/adapters/browser/reddit.md +9 -0
  246. package/docs/adapters/browser/sinablog.md +36 -0
  247. package/docs/adapters/browser/substack.md +38 -0
  248. package/docs/adapters/browser/tiktok.md +68 -0
  249. package/docs/adapters/browser/wikipedia.md +11 -2
  250. package/docs/adapters/browser/xueqiu.md +10 -0
  251. package/docs/adapters/browser/yahoo-finance.md +6 -5
  252. package/docs/adapters/desktop/antigravity.md +6 -0
  253. package/docs/adapters/desktop/chatgpt.md +2 -1
  254. package/docs/adapters/desktop/codex.md +5 -1
  255. package/docs/adapters/desktop/cursor.md +4 -0
  256. package/docs/adapters/desktop/discord.md +7 -7
  257. package/docs/adapters/index.md +1 -4
  258. package/docs/guide/getting-started.md +1 -0
  259. package/docs/guide/plugins.md +153 -0
  260. package/docs/zh/guide/plugins.md +107 -0
  261. package/extension/dist/background.js +91 -23
  262. package/extension/src/background.ts +82 -29
  263. package/extension/src/cdp.ts +42 -1
  264. package/package.json +10 -5
  265. package/scripts/clean-dist.cjs +13 -0
  266. package/src/browser/cdp.ts +71 -31
  267. package/src/browser/daemon-client.ts +21 -5
  268. package/src/browser/dom-helpers.ts +38 -7
  269. package/src/browser/dom-snapshot.test.ts +249 -0
  270. package/src/browser/dom-snapshot.ts +770 -0
  271. package/src/browser/index.ts +2 -0
  272. package/src/browser/mcp.ts +3 -1
  273. package/src/browser/page.ts +57 -21
  274. package/src/build-manifest.test.ts +70 -2
  275. package/src/build-manifest.ts +94 -26
  276. package/src/cli.ts +71 -2
  277. package/src/clis/barchart/greeks.ts +1 -1
  278. package/src/clis/barchart/options.ts +1 -1
  279. package/src/clis/barchart/quote.ts +1 -1
  280. package/src/clis/bilibili/download.ts +1 -1
  281. package/src/clis/bilibili/following.ts +1 -1
  282. package/src/clis/bilibili/subtitle.ts +1 -1
  283. package/src/clis/bilibili/user-videos.ts +1 -1
  284. package/src/clis/boss/batchgreet.ts +14 -106
  285. package/src/clis/boss/chatlist.ts +12 -26
  286. package/src/clis/boss/chatmsg.ts +16 -40
  287. package/src/clis/boss/common.ts +287 -0
  288. package/src/clis/boss/detail.ts +8 -54
  289. package/src/clis/boss/exchange.ts +15 -89
  290. package/src/clis/boss/greet.ts +23 -160
  291. package/src/clis/boss/invite.ts +36 -133
  292. package/src/clis/boss/joblist.ts +7 -36
  293. package/src/clis/boss/mark.ts +13 -94
  294. package/src/clis/boss/recommend.ts +12 -57
  295. package/src/clis/boss/resume.ts +19 -124
  296. package/src/clis/boss/search.ts +13 -66
  297. package/src/clis/boss/send.ts +21 -161
  298. package/src/clis/boss/stats.ts +19 -74
  299. package/src/clis/coupang/add-to-cart.ts +1 -1
  300. package/src/clis/devto/tag.yaml +34 -0
  301. package/src/clis/devto/top.yaml +29 -0
  302. package/src/clis/devto/user.yaml +33 -0
  303. package/src/clis/douban/book-hot.ts +15 -0
  304. package/src/clis/douban/marks.ts +135 -0
  305. package/src/clis/douban/movie-hot.ts +15 -0
  306. package/src/clis/douban/reviews.ts +127 -0
  307. package/src/clis/douban/search.ts +17 -0
  308. package/src/clis/douban/shared.ts +165 -0
  309. package/src/clis/douban/subject.yaml +76 -0
  310. package/src/clis/douban/top250.yaml +70 -0
  311. package/src/clis/douban/utils.ts +81 -0
  312. package/src/clis/facebook/add-friend.yaml +43 -0
  313. package/src/clis/facebook/events.yaml +44 -0
  314. package/src/clis/facebook/feed.yaml +63 -0
  315. package/src/clis/facebook/friends.yaml +42 -0
  316. package/src/clis/facebook/groups.yaml +50 -0
  317. package/src/clis/facebook/join-group.yaml +44 -0
  318. package/src/clis/facebook/memories.yaml +39 -0
  319. package/src/clis/facebook/notifications.yaml +40 -0
  320. package/src/clis/facebook/profile.yaml +37 -0
  321. package/src/clis/facebook/search.yaml +46 -0
  322. package/src/clis/google/news.ts +66 -0
  323. package/src/clis/google/search.ts +133 -0
  324. package/src/clis/google/suggest.ts +40 -0
  325. package/src/clis/google/trends.ts +44 -0
  326. package/src/clis/google/utils.test.ts +82 -0
  327. package/src/clis/google/utils.ts +24 -0
  328. package/src/clis/grok/ask.test.ts +53 -0
  329. package/src/clis/grok/ask.ts +300 -69
  330. package/src/clis/instagram/comment.yaml +52 -0
  331. package/src/clis/instagram/explore.yaml +43 -0
  332. package/src/clis/instagram/follow.yaml +41 -0
  333. package/src/clis/instagram/followers.yaml +51 -0
  334. package/src/clis/instagram/following.yaml +51 -0
  335. package/src/clis/instagram/like.yaml +46 -0
  336. package/src/clis/instagram/profile.yaml +42 -0
  337. package/src/clis/instagram/save.yaml +46 -0
  338. package/src/clis/instagram/saved.yaml +40 -0
  339. package/src/clis/instagram/search.yaml +43 -0
  340. package/src/clis/instagram/unfollow.yaml +38 -0
  341. package/src/clis/instagram/unlike.yaml +46 -0
  342. package/src/clis/instagram/unsave.yaml +46 -0
  343. package/src/clis/instagram/user.yaml +54 -0
  344. package/src/clis/jike/repost.ts +1 -1
  345. package/src/clis/jimeng/generate.yaml +1 -0
  346. package/src/clis/linux-do/category.yaml +1 -0
  347. package/src/clis/lobsters/active.yaml +29 -0
  348. package/src/clis/lobsters/hot.yaml +29 -0
  349. package/src/clis/lobsters/newest.yaml +29 -0
  350. package/src/clis/lobsters/tag.yaml +34 -0
  351. package/src/clis/medium/feed.ts +16 -0
  352. package/src/clis/medium/search.ts +16 -0
  353. package/src/clis/medium/shared.ts +83 -0
  354. package/src/clis/medium/user.ts +16 -0
  355. package/src/clis/reddit/comment.ts +1 -1
  356. package/src/clis/reddit/read.ts +1 -1
  357. package/src/clis/reddit/save.ts +1 -1
  358. package/src/clis/reddit/subreddit.yaml +1 -0
  359. package/src/clis/reddit/subscribe.ts +1 -1
  360. package/src/clis/reddit/upvote.ts +1 -1
  361. package/src/clis/sinablog/article.ts +15 -0
  362. package/src/clis/sinablog/hot.ts +15 -0
  363. package/src/clis/sinablog/search.ts +56 -0
  364. package/src/clis/sinablog/shared.ts +198 -0
  365. package/src/clis/sinablog/user.ts +16 -0
  366. package/src/clis/substack/feed.ts +16 -0
  367. package/src/clis/substack/publication.ts +16 -0
  368. package/src/clis/substack/search.ts +91 -0
  369. package/src/clis/substack/shared.ts +132 -0
  370. package/src/clis/tiktok/comment.yaml +66 -0
  371. package/src/clis/tiktok/explore.yaml +39 -0
  372. package/src/clis/tiktok/follow.yaml +39 -0
  373. package/src/clis/tiktok/following.yaml +46 -0
  374. package/src/clis/tiktok/friends.yaml +47 -0
  375. package/src/clis/tiktok/like.yaml +38 -0
  376. package/src/clis/tiktok/live.yaml +51 -0
  377. package/src/clis/tiktok/notifications.yaml +52 -0
  378. package/src/clis/tiktok/profile.yaml +45 -0
  379. package/src/clis/tiktok/save.yaml +34 -0
  380. package/src/clis/tiktok/search.yaml +46 -0
  381. package/src/clis/tiktok/unfollow.yaml +44 -0
  382. package/src/clis/tiktok/unlike.yaml +38 -0
  383. package/src/clis/tiktok/unsave.yaml +36 -0
  384. package/src/clis/tiktok/user.yaml +44 -0
  385. package/src/clis/twitter/download.ts +3 -3
  386. package/src/clis/twitter/followers.ts +1 -1
  387. package/src/clis/twitter/following.ts +1 -1
  388. package/src/clis/twitter/thread.ts +1 -1
  389. package/src/clis/twitter/timeline.test.ts +109 -0
  390. package/src/clis/twitter/timeline.ts +59 -19
  391. package/src/clis/wikipedia/random.ts +19 -0
  392. package/src/clis/wikipedia/search.ts +10 -4
  393. package/src/clis/wikipedia/summary.ts +4 -9
  394. package/src/clis/wikipedia/trending.ts +41 -0
  395. package/src/clis/wikipedia/utils.ts +31 -0
  396. package/src/clis/xiaohongshu/creator-note-detail.test.ts +2 -0
  397. package/src/clis/xiaohongshu/creator-note-detail.ts +1 -1
  398. package/src/clis/xiaohongshu/creator-notes.test.ts +2 -0
  399. package/src/clis/xiaohongshu/download.ts +1 -1
  400. package/src/clis/xueqiu/earnings-date.yaml +69 -0
  401. package/src/clis/xueqiu/search.yaml +2 -1
  402. package/src/clis/xueqiu/stock.yaml +2 -0
  403. package/src/clis/yahoo-finance/quote.ts +1 -1
  404. package/src/commanderAdapter.ts +17 -10
  405. package/src/daemon.ts +23 -0
  406. package/src/discovery.ts +134 -24
  407. package/src/doctor.test.ts +59 -2
  408. package/src/doctor.ts +4 -2
  409. package/src/engine.test.ts +79 -6
  410. package/src/execution.ts +42 -16
  411. package/src/explore.ts +77 -9
  412. package/src/generate.ts +58 -9
  413. package/src/main.ts +2 -1
  414. package/src/pipeline/executor.test.ts +35 -6
  415. package/src/pipeline/executor.ts +68 -19
  416. package/src/pipeline/registry.ts +3 -3
  417. package/src/pipeline/steps/browser.ts +24 -15
  418. package/src/pipeline/steps/fetch.ts +18 -13
  419. package/src/pipeline/steps/transform.ts +40 -15
  420. package/src/pipeline/template.test.ts +18 -0
  421. package/src/pipeline/template.ts +86 -13
  422. package/src/pipeline/transform.test.ts +15 -2
  423. package/src/plugin.test.ts +86 -0
  424. package/src/plugin.ts +254 -0
  425. package/src/registry-api.ts +12 -0
  426. package/src/registry.ts +19 -1
  427. package/src/synthesize.ts +102 -21
  428. package/src/types.ts +44 -12
  429. package/src/validate.ts +19 -4
  430. package/tests/e2e/browser-public.test.ts +11 -0
  431. package/tests/e2e/public-commands.test.ts +64 -0
  432. package/dist/clis/feishu/new.d.ts +0 -1
  433. package/dist/clis/feishu/new.js +0 -27
  434. package/dist/clis/feishu/read.d.ts +0 -1
  435. package/dist/clis/feishu/read.js +0 -40
  436. package/dist/clis/feishu/search.d.ts +0 -1
  437. package/dist/clis/feishu/search.js +0 -30
  438. package/dist/clis/feishu/send.d.ts +0 -1
  439. package/dist/clis/feishu/send.js +0 -39
  440. package/dist/clis/feishu/status.d.ts +0 -1
  441. package/dist/clis/feishu/status.js +0 -28
  442. package/dist/clis/neteasemusic/like.d.ts +0 -1
  443. package/dist/clis/neteasemusic/like.js +0 -25
  444. package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
  445. package/dist/clis/neteasemusic/lyrics.js +0 -47
  446. package/dist/clis/neteasemusic/next.d.ts +0 -1
  447. package/dist/clis/neteasemusic/next.js +0 -26
  448. package/dist/clis/neteasemusic/play.d.ts +0 -1
  449. package/dist/clis/neteasemusic/play.js +0 -26
  450. package/dist/clis/neteasemusic/playing.d.ts +0 -1
  451. package/dist/clis/neteasemusic/playing.js +0 -59
  452. package/dist/clis/neteasemusic/playlist.d.ts +0 -1
  453. package/dist/clis/neteasemusic/playlist.js +0 -46
  454. package/dist/clis/neteasemusic/prev.d.ts +0 -1
  455. package/dist/clis/neteasemusic/prev.js +0 -25
  456. package/dist/clis/neteasemusic/search.d.ts +0 -1
  457. package/dist/clis/neteasemusic/search.js +0 -52
  458. package/dist/clis/neteasemusic/status.d.ts +0 -1
  459. package/dist/clis/neteasemusic/status.js +0 -16
  460. package/dist/clis/neteasemusic/volume.d.ts +0 -1
  461. package/dist/clis/neteasemusic/volume.js +0 -54
  462. package/dist/clis/wechat/chats.d.ts +0 -1
  463. package/dist/clis/wechat/chats.js +0 -28
  464. package/dist/clis/wechat/contacts.d.ts +0 -1
  465. package/dist/clis/wechat/contacts.js +0 -28
  466. package/dist/clis/wechat/read.d.ts +0 -1
  467. package/dist/clis/wechat/read.js +0 -58
  468. package/dist/clis/wechat/search.d.ts +0 -1
  469. package/dist/clis/wechat/search.js +0 -31
  470. package/dist/clis/wechat/send.d.ts +0 -1
  471. package/dist/clis/wechat/send.js +0 -42
  472. package/dist/clis/wechat/status.d.ts +0 -1
  473. package/dist/clis/wechat/status.js +0 -29
  474. package/dist/pipeline.d.ts +0 -7
  475. package/dist/pipeline.js +0 -7
  476. package/docs/adapters/browser/github.md +0 -26
  477. package/docs/adapters/desktop/feishu.md +0 -20
  478. package/docs/adapters/desktop/neteasemusic.md +0 -31
  479. package/docs/adapters/desktop/wechat.md +0 -28
  480. package/src/clis/antigravity/README.md +0 -5
  481. package/src/clis/antigravity/README.zh-CN.md +0 -51
  482. package/src/clis/chaoxing/README.md +0 -14
  483. package/src/clis/chaoxing/README.zh-CN.md +0 -35
  484. package/src/clis/chatgpt/README.md +0 -5
  485. package/src/clis/chatgpt/README.zh-CN.md +0 -44
  486. package/src/clis/chatwise/README.md +0 -5
  487. package/src/clis/chatwise/README.zh-CN.md +0 -38
  488. package/src/clis/codex/README.md +0 -5
  489. package/src/clis/codex/README.zh-CN.md +0 -33
  490. package/src/clis/cursor/README.md +0 -5
  491. package/src/clis/cursor/README.zh-CN.md +0 -33
  492. package/src/clis/discord-app/README.md +0 -5
  493. package/src/clis/discord-app/README.zh-CN.md +0 -28
  494. package/src/clis/feishu/README.md +0 -5
  495. package/src/clis/feishu/README.zh-CN.md +0 -20
  496. package/src/clis/feishu/new.ts +0 -32
  497. package/src/clis/feishu/read.ts +0 -48
  498. package/src/clis/feishu/search.ts +0 -35
  499. package/src/clis/feishu/send.ts +0 -46
  500. package/src/clis/feishu/status.ts +0 -34
  501. package/src/clis/neteasemusic/README.md +0 -5
  502. package/src/clis/neteasemusic/README.zh-CN.md +0 -31
  503. package/src/clis/neteasemusic/like.ts +0 -28
  504. package/src/clis/neteasemusic/lyrics.ts +0 -53
  505. package/src/clis/neteasemusic/next.ts +0 -30
  506. package/src/clis/neteasemusic/play.ts +0 -30
  507. package/src/clis/neteasemusic/playing.ts +0 -62
  508. package/src/clis/neteasemusic/playlist.ts +0 -51
  509. package/src/clis/neteasemusic/prev.ts +0 -29
  510. package/src/clis/neteasemusic/search.ts +0 -58
  511. package/src/clis/neteasemusic/status.ts +0 -18
  512. package/src/clis/neteasemusic/volume.ts +0 -61
  513. package/src/clis/notion/README.md +0 -5
  514. package/src/clis/notion/README.zh-CN.md +0 -29
  515. package/src/clis/wechat/README.md +0 -5
  516. package/src/clis/wechat/README.zh-CN.md +0 -28
  517. package/src/clis/wechat/chats.ts +0 -33
  518. package/src/clis/wechat/contacts.ts +0 -33
  519. package/src/clis/wechat/read.ts +0 -72
  520. package/src/clis/wechat/search.ts +0 -36
  521. package/src/clis/wechat/send.ts +0 -49
  522. package/src/clis/wechat/status.ts +0 -35
  523. package/src/pipeline.ts +0 -8
package/src/execution.ts CHANGED
@@ -11,19 +11,25 @@
11
11
 
12
12
  import { type CliCommand, type InternalCliCommand, type Arg, Strategy, getRegistry, fullName } from './registry.js';
13
13
  import type { IPage } from './types.js';
14
- import { executePipeline } from './pipeline.js';
14
+ import { pathToFileURL } from 'node:url';
15
+ import { executePipeline } from './pipeline/index.js';
15
16
  import { AdapterLoadError } from './errors.js';
16
17
  import { shouldUseBrowserSession } from './capabilityRouting.js';
17
18
  import { getBrowserFactory, browserSession, runWithTimeout, DEFAULT_BROWSER_COMMAND_TIMEOUT } from './runtime.js';
18
19
 
19
20
  /** Set of TS module paths that have been loaded */
20
21
  const _loadedModules = new Set<string>();
22
+ type CommandArgs = Record<string, unknown>;
23
+
24
+ function getErrorMessage(error: unknown): string {
25
+ return error instanceof Error ? error.message : String(error);
26
+ }
21
27
 
22
28
  /**
23
29
  * Validates and coerces arguments based on the command's Arg definitions.
24
30
  */
25
- export function coerceAndValidateArgs(cmdArgs: Arg[], kwargs: Record<string, any>): Record<string, any> {
26
- const result: Record<string, any> = { ...kwargs };
31
+ export function coerceAndValidateArgs(cmdArgs: Arg[], kwargs: CommandArgs): CommandArgs {
32
+ const result: CommandArgs = { ...kwargs };
27
33
 
28
34
  for (const argDef of cmdArgs) {
29
35
  const val = result[argDef.name];
@@ -72,20 +78,20 @@ export function coerceAndValidateArgs(cmdArgs: Arg[], kwargs: Record<string, any
72
78
  async function runCommand(
73
79
  cmd: CliCommand,
74
80
  page: IPage | null,
75
- kwargs: Record<string, any>,
81
+ kwargs: CommandArgs,
76
82
  debug: boolean,
77
- ): Promise<any> {
83
+ ): Promise<unknown> {
78
84
  // Lazy-load TS module on first execution (manifest fast-path)
79
85
  const internal = cmd as InternalCliCommand;
80
86
  if (internal._lazy && internal._modulePath) {
81
87
  const modulePath = internal._modulePath;
82
88
  if (!_loadedModules.has(modulePath)) {
83
89
  try {
84
- await import(`file://${modulePath}`);
90
+ await import(pathToFileURL(modulePath).href);
85
91
  _loadedModules.add(modulePath);
86
- } catch (err: any) {
92
+ } catch (err) {
87
93
  throw new AdapterLoadError(
88
- `Failed to load adapter module ${modulePath}: ${err.message}`,
94
+ `Failed to load adapter module ${modulePath}: ${getErrorMessage(err)}`,
89
95
  'Check that the adapter file exists and has no syntax errors.',
90
96
  );
91
97
  }
@@ -101,6 +107,24 @@ async function runCommand(
101
107
  throw new Error(`Command ${fullName(cmd)} has no func or pipeline`);
102
108
  }
103
109
 
110
+ /**
111
+ * Resolve the pre-navigation URL for a command, or null to skip.
112
+ *
113
+ * COOKIE/HEADER strategies need the browser on the target domain so
114
+ * `fetch(url, { credentials: 'include' })` carries cookies.
115
+ * Adapters that handle their own navigation set `navigateBefore: false`.
116
+ */
117
+ function resolvePreNav(cmd: CliCommand): string | null {
118
+ if (cmd.navigateBefore === false) return null;
119
+ if (typeof cmd.navigateBefore === 'string') return cmd.navigateBefore;
120
+
121
+ // Default: pre-navigate for COOKIE/HEADER strategies with a domain
122
+ if ((cmd.strategy === Strategy.COOKIE || cmd.strategy === Strategy.HEADER) && cmd.domain) {
123
+ return `https://${cmd.domain}`;
124
+ }
125
+ return null;
126
+ }
127
+
104
128
  /**
105
129
  * Execute a CLI command. Automatically manages browser sessions when needed.
106
130
  *
@@ -109,22 +133,24 @@ async function runCommand(
109
133
  */
110
134
  export async function executeCommand(
111
135
  cmd: CliCommand,
112
- rawKwargs: Record<string, any>,
136
+ rawKwargs: CommandArgs,
113
137
  debug: boolean = false,
114
- ): Promise<any> {
115
- let kwargs: Record<string, any>;
138
+ ): Promise<unknown> {
139
+ let kwargs: CommandArgs;
116
140
  try {
117
141
  kwargs = coerceAndValidateArgs(cmd.args, rawKwargs);
118
- } catch (err: any) {
119
- throw new Error(`[Argument Validation Error]\n${err.message}`);
142
+ } catch (err) {
143
+ throw new Error(`[Argument Validation Error]\n${getErrorMessage(err)}`);
120
144
  }
121
145
 
122
146
  if (shouldUseBrowserSession(cmd)) {
123
147
  const BrowserFactory = getBrowserFactory();
124
148
  return browserSession(BrowserFactory, async (page) => {
125
- // Cookie/header strategies require same-origin context for credentialed fetch.
126
- if ((cmd.strategy === Strategy.COOKIE || cmd.strategy === Strategy.HEADER) && cmd.domain) {
127
- try { await page.goto(`https://${cmd.domain}`); await page.wait(2); } catch {}
149
+ // Pre-navigate to target domain for cookie/header context if needed.
150
+ // Each adapter controls this via `navigateBefore` (see CliCommand docs).
151
+ const preNavUrl = resolvePreNav(cmd);
152
+ if (preNavUrl) {
153
+ try { await page.goto(preNavUrl); await page.wait(2); } catch {}
128
154
  }
129
155
  return runWithTimeout(runCommand(cmd, page, kwargs, debug), {
130
156
  timeout: cmd.timeoutSeconds ?? DEFAULT_BROWSER_COMMAND_TIMEOUT,
package/src/explore.ts CHANGED
@@ -9,10 +9,12 @@
9
9
  import * as fs from 'node:fs';
10
10
  import * as path from 'node:path';
11
11
  import { DEFAULT_BROWSER_EXPLORE_TIMEOUT, browserSession, runWithTimeout } from './runtime.js';
12
+ import type { IBrowserFactory } from './runtime.js';
12
13
  import { VOLATILE_PARAMS, SEARCH_PARAMS, PAGINATION_PARAMS, LIMIT_PARAMS, FIELD_ROLES } from './constants.js';
13
14
  import { detectFramework } from './scripts/framework.js';
14
15
  import { discoverStores } from './scripts/store.js';
15
16
  import { interactFuzz } from './scripts/interact.js';
17
+ import type { IPage } from './types.js';
16
18
 
17
19
  // ── Site name detection ────────────────────────────────────────────────────
18
20
 
@@ -68,7 +70,61 @@ interface InferredCapability {
68
70
  name: string; description: string; strategy: string; confidence: number;
69
71
  endpoint: string; itemPath: string | null;
70
72
  recommendedColumns: string[];
71
- recommendedArgs: Array<{ name: string; type: string; required: boolean; default?: any }>;
73
+ recommendedArgs: Array<{ name: string; type: string; required: boolean; default?: unknown }>;
74
+ storeHint?: { store: string; action: string };
75
+ }
76
+
77
+ export interface ExploreManifest {
78
+ site: string;
79
+ target_url: string;
80
+ final_url: string;
81
+ title: string;
82
+ framework: Record<string, boolean>;
83
+ stores: Array<{ type: DiscoveredStore['type']; id: string; actions: string[] }>;
84
+ top_strategy: string;
85
+ explored_at?: string;
86
+ }
87
+
88
+ export interface ExploreAuthSummary {
89
+ top_strategy: string;
90
+ indicators: string[];
91
+ framework: Record<string, boolean>;
92
+ }
93
+
94
+ export interface ExploreEndpointArtifact {
95
+ pattern: string;
96
+ method: string;
97
+ url: string;
98
+ status: number | null;
99
+ contentType: string;
100
+ score: number;
101
+ queryParams: string[];
102
+ itemPath: string | null;
103
+ itemCount: number;
104
+ detectedFields: Record<string, string>;
105
+ authIndicators: string[];
106
+ }
107
+
108
+ export interface ExploreResult {
109
+ site: string;
110
+ target_url: string;
111
+ final_url: string;
112
+ title: string;
113
+ framework: Record<string, boolean>;
114
+ stores: DiscoveredStore[];
115
+ top_strategy: string;
116
+ endpoint_count: number;
117
+ api_endpoint_count: number;
118
+ capabilities: InferredCapability[];
119
+ auth_indicators: string[];
120
+ out_dir: string;
121
+ }
122
+
123
+ export interface ExploreBundle {
124
+ manifest: ExploreManifest;
125
+ endpoints: ExploreEndpointArtifact[];
126
+ capabilities: InferredCapability[];
127
+ auth: ExploreAuthSummary;
72
128
  }
73
129
 
74
130
  /**
@@ -167,7 +223,11 @@ function flattenFields(obj: unknown, prefix: string, maxDepth: number): string[]
167
223
  return names;
168
224
  }
169
225
 
170
- function scoreEndpoint(ep: { contentType: string; responseAnalysis: any; pattern: string; status: number | null; hasSearchParam: boolean; hasPaginationParam: boolean; hasLimitParam: boolean }): number {
226
+ function isBooleanRecord(value: unknown): value is Record<string, boolean> {
227
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
228
+ }
229
+
230
+ function scoreEndpoint(ep: { contentType: string; responseAnalysis: AnalyzedEndpoint['responseAnalysis']; pattern: string; status: number | null; hasSearchParam: boolean; hasPaginationParam: boolean; hasLimitParam: boolean }): number {
171
231
  let s = 0;
172
232
  if (ep.contentType.includes('json')) s += 10;
173
233
  if (ep.responseAnalysis) { s += 5; s += Math.min(ep.responseAnalysis.itemCount, 10); s += Object.keys(ep.responseAnalysis.detectedFields).length * 2; }
@@ -321,7 +381,7 @@ function inferCapabilitiesFromEndpoints(
321
381
  /** Write explore artifacts (manifest, endpoints, capabilities, auth, stores) to disk. */
322
382
  async function writeExploreArtifacts(
323
383
  targetDir: string,
324
- result: Record<string, any>,
384
+ result: Omit<ExploreResult, 'out_dir'>,
325
385
  analyzedEndpoints: AnalyzedEndpoint[],
326
386
  stores: DiscoveredStore[],
327
387
  ): Promise<void> {
@@ -354,12 +414,12 @@ async function writeExploreArtifacts(
354
414
  export async function exploreUrl(
355
415
  url: string,
356
416
  opts: {
357
- BrowserFactory: new () => any;
417
+ BrowserFactory: new () => IBrowserFactory;
358
418
  site?: string; goal?: string; authenticated?: boolean;
359
419
  outDir?: string; waitSeconds?: number; query?: string;
360
420
  clickLabels?: string[]; auto?: boolean; workspace?: string;
361
421
  },
362
- ): Promise<Record<string, any>> {
422
+ ): Promise<ExploreResult> {
363
423
  const waitSeconds = opts.waitSeconds ?? 3.0;
364
424
  const exploreTimeout = Math.max(DEFAULT_BROWSER_EXPLORE_TIMEOUT, 45.0 + waitSeconds * 8.0);
365
425
 
@@ -432,7 +492,10 @@ export async function exploreUrl(
432
492
 
433
493
  // Step 6: Detect framework
434
494
  let framework: Record<string, boolean> = {};
435
- try { const fw = await page.evaluate(FRAMEWORK_DETECT_JS); if (fw && typeof fw === 'object') framework = fw; } catch {}
495
+ try {
496
+ const fw = await page.evaluate(FRAMEWORK_DETECT_JS);
497
+ if (isBooleanRecord(fw)) framework = fw;
498
+ } catch {}
436
499
 
437
500
  // Step 6.5: Discover stores (Pinia / Vuex)
438
501
  let stores: DiscoveredStore[] = [];
@@ -467,7 +530,7 @@ export async function exploreUrl(
467
530
  }, { workspace: opts.workspace });
468
531
  }
469
532
 
470
- export function renderExploreSummary(result: Record<string, any>): string {
533
+ export function renderExploreSummary(result: ExploreResult): string {
471
534
  const lines = [
472
535
  'opencli probe: OK', `Site: ${result.site}`, `URL: ${result.target_url}`,
473
536
  `Title: ${result.title || '(none)'}`, `Strategy: ${result.top_strategy}`,
@@ -492,10 +555,15 @@ export function renderExploreSummary(result: Record<string, any>): string {
492
555
  return lines.join('\n');
493
556
  }
494
557
 
495
- async function readPageMetadata(page: any /* IPage */): Promise<{ url: string; title: string }> {
558
+ async function readPageMetadata(page: IPage): Promise<{ url: string; title: string }> {
496
559
  try {
497
560
  const result = await page.evaluate(`() => ({ url: window.location.href, title: document.title || '' })`);
498
- if (result && typeof result === 'object') return { url: String(result.url ?? ''), title: String(result.title ?? '') };
561
+ if (result && typeof result === 'object' && !Array.isArray(result)) {
562
+ return {
563
+ url: String((result as Record<string, unknown>).url ?? ''),
564
+ title: String((result as Record<string, unknown>).title ?? ''),
565
+ };
566
+ }
499
567
  } catch {}
500
568
  return { url: '', title: '' };
501
569
  }
package/src/generate.ts CHANGED
@@ -9,10 +9,59 @@
9
9
  */
10
10
 
11
11
  import { exploreUrl } from './explore.js';
12
- import { synthesizeFromExplore } from './synthesize.js';
12
+ import type { IBrowserFactory } from './runtime.js';
13
+ import { synthesizeFromExplore, type SynthesizeCandidateSummary, type SynthesizeResult } from './synthesize.js';
13
14
 
14
15
  // TODO: implement real CLI registration (copy candidate YAML to user clis dir)
15
- function registerCandidates(_opts: any): any { return { ok: true, count: 0 }; }
16
+ interface RegisterCandidatesOptions {
17
+ target: string;
18
+ builtinClis?: string;
19
+ userClis?: string;
20
+ name?: string;
21
+ }
22
+
23
+ interface RegisterCandidatesResult {
24
+ ok: boolean;
25
+ count: number;
26
+ }
27
+
28
+ export interface GenerateCliOptions {
29
+ url: string;
30
+ BrowserFactory: new () => IBrowserFactory;
31
+ builtinClis?: string;
32
+ userClis?: string;
33
+ goal?: string | null;
34
+ site?: string;
35
+ waitSeconds?: number;
36
+ top?: number;
37
+ register?: boolean;
38
+ workspace?: string;
39
+ }
40
+
41
+ export interface GenerateCliResult {
42
+ ok: boolean;
43
+ goal?: string | null;
44
+ normalized_goal?: string | null;
45
+ site: string;
46
+ selected_candidate: SynthesizeCandidateSummary | null;
47
+ selected_command: string;
48
+ explore: {
49
+ endpoint_count: number;
50
+ api_endpoint_count: number;
51
+ capability_count: number;
52
+ top_strategy: string;
53
+ framework: Record<string, boolean>;
54
+ };
55
+ synthesize: {
56
+ candidate_count: number;
57
+ candidates: Array<Pick<SynthesizeCandidateSummary, 'name' | 'strategy' | 'confidence'>>;
58
+ };
59
+ register: RegisterCandidatesResult | null;
60
+ }
61
+
62
+ function registerCandidates(_opts: RegisterCandidatesOptions): RegisterCandidatesResult {
63
+ return { ok: true, count: 0 };
64
+ }
16
65
 
17
66
  const CAPABILITY_ALIASES: Record<string, string[]> = {
18
67
  search: ['search', '搜索', '查找', 'query', 'keyword'],
@@ -41,7 +90,7 @@ function normalizeGoal(goal?: string | null): string | null {
41
90
  /**
42
91
  * Select the best candidate matching the user's goal.
43
92
  */
44
- function selectCandidate(candidates: any[], goal?: string | null): any {
93
+ function selectCandidate(candidates: SynthesizeResult['candidates'], goal?: string | null): SynthesizeCandidateSummary | null {
45
94
  if (!candidates.length) return null;
46
95
  if (!goal) return candidates[0]; // highest confidence first
47
96
 
@@ -58,12 +107,12 @@ function selectCandidate(candidates: any[], goal?: string | null): any {
58
107
  return partial ?? candidates[0];
59
108
  }
60
109
 
61
- export async function generateCliFromUrl(opts: any): Promise<any> {
110
+ export async function generateCliFromUrl(opts: GenerateCliOptions): Promise<GenerateCliResult> {
62
111
  // Step 1: Deep Explore
63
112
  const exploreResult = await exploreUrl(opts.url, {
64
113
  BrowserFactory: opts.BrowserFactory,
65
114
  site: opts.site,
66
- goal: normalizeGoal(opts.goal) ?? opts.goal,
115
+ goal: normalizeGoal(opts.goal) ?? opts.goal ?? undefined,
67
116
  waitSeconds: opts.waitSeconds ?? 3,
68
117
  workspace: opts.workspace,
69
118
  });
@@ -75,10 +124,10 @@ export async function generateCliFromUrl(opts: any): Promise<any> {
75
124
 
76
125
  // Step 3: Select best candidate for goal
77
126
  const selected = selectCandidate(synthesizeResult.candidates ?? [], opts.goal);
78
- const selectedSite = selected?.site ?? synthesizeResult.site ?? exploreResult.site;
127
+ const selectedSite = synthesizeResult.site ?? exploreResult.site;
79
128
 
80
129
  // Step 4: Register (if requested)
81
- let registerResult: any = null;
130
+ let registerResult: RegisterCandidatesResult | null = null;
82
131
  if (opts.register !== false && synthesizeResult.candidate_count > 0) {
83
132
  try {
84
133
  registerResult = registerCandidates({
@@ -108,7 +157,7 @@ export async function generateCliFromUrl(opts: any): Promise<any> {
108
157
  },
109
158
  synthesize: {
110
159
  candidate_count: synthesizeResult.candidate_count,
111
- candidates: (synthesizeResult.candidates ?? []).map((c: any) => ({
160
+ candidates: (synthesizeResult.candidates ?? []).map((c) => ({
112
161
  name: c.name,
113
162
  strategy: c.strategy,
114
163
  confidence: c.confidence,
@@ -118,7 +167,7 @@ export async function generateCliFromUrl(opts: any): Promise<any> {
118
167
  };
119
168
  }
120
169
 
121
- export function renderGenerateSummary(r: any): string {
170
+ export function renderGenerateSummary(r: GenerateCliResult): string {
122
171
  const lines = [
123
172
  `opencli generate: ${r.ok ? 'OK' : 'FAIL'}`,
124
173
  `Site: ${r.site}`,
package/src/main.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  import * as os from 'node:os';
7
7
  import * as path from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
- import { discoverClis } from './discovery.js';
9
+ import { discoverClis, discoverPlugins } from './discovery.js';
10
10
  import { getCompletions } from './completion.js';
11
11
  import { runCli } from './cli.js';
12
12
 
@@ -16,6 +16,7 @@ const BUILTIN_CLIS = path.resolve(__dirname, 'clis');
16
16
  const USER_CLIS = path.join(os.homedir(), '.opencli', 'clis');
17
17
 
18
18
  await discoverClis(BUILTIN_CLIS, USER_CLIS);
19
+ await discoverPlugins();
19
20
 
20
21
  // ── Fast-path: handle --get-completions before commander parses ─────────
21
22
  // Usage: opencli --get-completions --cursor <N> [word1 word2 ...]
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { describe, it, expect, vi } from 'vitest';
6
6
  import { executePipeline } from './index.js';
7
+ import { ConfigError } from '../errors.js';
7
8
  import type { IPage } from '../types.js';
8
9
 
9
10
  /** Create a minimal mock page for testing */
@@ -16,6 +17,7 @@ function createMockPage(overrides: Partial<IPage> = {}): IPage {
16
17
  click: vi.fn(),
17
18
  typeText: vi.fn(),
18
19
  pressKey: vi.fn(),
20
+ getFormState: vi.fn().mockResolvedValue({}),
19
21
  wait: vi.fn(),
20
22
  tabs: vi.fn().mockResolvedValue([]),
21
23
  closeTab: vi.fn(),
@@ -24,6 +26,7 @@ function createMockPage(overrides: Partial<IPage> = {}): IPage {
24
26
  networkRequests: vi.fn().mockResolvedValue([]),
25
27
  consoleMessages: vi.fn().mockResolvedValue(''),
26
28
  scroll: vi.fn(),
29
+ scrollTo: vi.fn(),
27
30
  autoScroll: vi.fn(),
28
31
  installInterceptor: vi.fn(),
29
32
  getInterceptedRequests: vi.fn().mockResolvedValue([]),
@@ -79,6 +82,32 @@ describe('executePipeline', () => {
79
82
  ]);
80
83
  });
81
84
 
85
+ it('runs inline select inside map step', async () => {
86
+ const page = createMockPage({
87
+ evaluate: vi.fn().mockResolvedValue({
88
+ posts: [
89
+ { title: 'First', rank: 1 },
90
+ { title: 'Second', rank: 2 },
91
+ ],
92
+ }),
93
+ });
94
+ const result = await executePipeline(page, [
95
+ { evaluate: 'test' },
96
+ {
97
+ map: {
98
+ select: 'posts',
99
+ title: '${{ item.title }}',
100
+ rank: '${{ item.rank }}',
101
+ },
102
+ },
103
+ ]);
104
+
105
+ expect(result).toEqual([
106
+ { title: 'First', rank: 1 },
107
+ { title: 'Second', rank: 2 },
108
+ ]);
109
+ });
110
+
82
111
  it('executes limit step', async () => {
83
112
  const page = createMockPage({
84
113
  evaluate: vi.fn().mockResolvedValue([1, 2, 3, 4, 5]),
@@ -120,13 +149,13 @@ describe('executePipeline', () => {
120
149
  expect(page.wait).toHaveBeenCalledWith(2);
121
150
  });
122
151
 
123
- it('handles unknown steps gracefully in debug mode', async () => {
124
- const stderr = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
125
- await executePipeline(null, [
152
+ it('fails fast on unknown steps', async () => {
153
+ await expect(executePipeline(null, [
154
+ { unknownStep: 'test' },
155
+ ], { debug: true })).rejects.toBeInstanceOf(ConfigError);
156
+ await expect(executePipeline(null, [
126
157
  { unknownStep: 'test' },
127
- ], { debug: true });
128
- expect(stderr).toHaveBeenCalledWith(expect.stringContaining('Unknown step'));
129
- stderr.mockRestore();
158
+ ], { debug: true })).rejects.toThrow('Unknown pipeline step "unknownStep"');
130
159
  });
131
160
 
132
161
  it('passes args through template rendering', async () => {
@@ -2,46 +2,95 @@
2
2
  * Pipeline executor: runs YAML pipeline steps sequentially.
3
3
  */
4
4
 
5
- import chalk from 'chalk';
5
+
6
6
  import type { IPage } from '../types.js';
7
7
  import { getStep, type StepHandler } from './registry.js';
8
8
  import { log } from '../logger.js';
9
+ import { ConfigError } from '../errors.js';
9
10
 
10
11
  export interface PipelineContext {
11
- args?: Record<string, any>;
12
+ args?: Record<string, unknown>;
12
13
  debug?: boolean;
14
+ /** Max retry attempts per step (default: 2 for browser steps, 0 for others) */
15
+ stepRetries?: number;
13
16
  }
14
17
 
18
+ /** Steps that interact with the browser and may fail transiently */
19
+ const BROWSER_STEPS = new Set(['navigate', 'evaluate', 'click', 'type', 'press', 'wait', 'snapshot', 'scroll']);
20
+
15
21
  export async function executePipeline(
16
22
  page: IPage | null,
17
- pipeline: any[],
23
+ pipeline: unknown[],
18
24
  ctx: PipelineContext = {},
19
- ): Promise<any> {
25
+ ): Promise<unknown> {
20
26
  const args = ctx.args ?? {};
21
27
  const debug = ctx.debug ?? false;
22
- let data: any = null;
28
+ let data: unknown = null;
23
29
  const total = pipeline.length;
24
30
 
25
- for (let i = 0; i < pipeline.length; i++) {
26
- const step = pipeline[i];
27
- if (!step || typeof step !== 'object') continue;
28
- for (const [op, params] of Object.entries(step)) {
29
- if (debug) debugStepStart(i + 1, total, op, params);
31
+ try {
32
+ for (let i = 0; i < pipeline.length; i++) {
33
+ const step = pipeline[i];
34
+ if (!step || typeof step !== 'object') continue;
35
+ for (const [op, params] of Object.entries(step)) {
36
+ if (debug) debugStepStart(i + 1, total, op, params);
30
37
 
31
- const handler = getStep(op);
32
- if (handler) {
33
- data = await handler(page, params, data, args);
34
- } else {
35
- if (debug) log.warn(`Unknown step: ${op}`);
36
- }
38
+ const handler = getStep(op);
39
+ if (handler) {
40
+ data = await executeStepWithRetry(handler, page, params, data, args, op, ctx.stepRetries);
41
+ } else {
42
+ throw new ConfigError(
43
+ `Unknown pipeline step "${op}" at index ${i}.`,
44
+ 'Check the YAML pipeline step name or register the custom step before execution.',
45
+ );
46
+ }
37
47
 
38
- if (debug) debugStepResult(op, data);
48
+ if (debug) debugStepResult(op, data);
49
+ }
50
+ }
51
+ } catch (err) {
52
+ // Attempt cleanup: close automation window on pipeline failure
53
+ if (page && typeof (page as unknown as Record<string, unknown>).closeWindow === 'function') {
54
+ try { await (page as unknown as { closeWindow: () => Promise<void> }).closeWindow(); } catch { /* ignore */ }
39
55
  }
56
+ throw err;
40
57
  }
41
58
  return data;
42
59
  }
43
60
 
44
- function debugStepStart(stepNum: number, total: number, op: string, params: any): void {
61
+ async function executeStepWithRetry(
62
+ handler: StepHandler,
63
+ page: IPage | null,
64
+ params: unknown,
65
+ data: unknown,
66
+ args: Record<string, unknown>,
67
+ op: string,
68
+ configRetries?: number,
69
+ ): Promise<unknown> {
70
+ const maxRetries = configRetries ?? (BROWSER_STEPS.has(op) ? 2 : 0);
71
+
72
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
73
+ try {
74
+ return await handler(page, params, data, args);
75
+ } catch (err) {
76
+ if (attempt >= maxRetries) throw err;
77
+ // Only retry on transient browser errors
78
+ const msg = err instanceof Error ? err.message : '';
79
+ const isTransient = msg.includes('Extension disconnected')
80
+ || msg.includes('attach failed')
81
+ || msg.includes('no longer exists')
82
+ || msg.includes('CDP connection')
83
+ || msg.includes('Daemon command failed');
84
+ if (!isTransient) throw err;
85
+ // Brief delay before retry
86
+ await new Promise(resolve => setTimeout(resolve, 1000));
87
+ }
88
+ }
89
+ // Unreachable
90
+ throw new Error(`Step "${op}" failed after ${maxRetries} retries`);
91
+ }
92
+
93
+ function debugStepStart(stepNum: number, total: number, op: string, params: unknown): void {
45
94
  let preview = '';
46
95
  if (typeof params === 'string') {
47
96
  preview = params.length <= 80 ? ` → ${params}` : ` → ${params.slice(0, 77)}...`;
@@ -51,7 +100,7 @@ function debugStepStart(stepNum: number, total: number, op: string, params: any)
51
100
  log.step(stepNum, total, op, preview);
52
101
  }
53
102
 
54
- function debugStepResult(op: string, data: any): void {
103
+ function debugStepResult(op: string, data: unknown): void {
55
104
  if (data === null || data === undefined) {
56
105
  log.stepResult('(no data)');
57
106
  } else if (Array.isArray(data)) {
@@ -18,11 +18,11 @@ import { stepDownload } from './steps/download.js';
18
18
  * TData is the type of the `data` state flowing into the step.
19
19
  * TResult is the expected return type.
20
20
  */
21
- export type StepHandler<TData = any, TResult = any> = (
21
+ export type StepHandler<TData = unknown, TResult = unknown, TParams = unknown> = (
22
22
  page: IPage | null,
23
- params: any,
23
+ params: TParams,
24
24
  data: TData,
25
- args: Record<string, any>
25
+ args: Record<string, unknown>
26
26
  ) => Promise<TResult>;
27
27
 
28
28
  const _stepRegistry = new Map<string, StepHandler>();