@jackwener/opencli 1.6.8 → 1.6.10

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 (572) hide show
  1. package/README.md +117 -59
  2. package/README.zh-CN.md +123 -79
  3. package/dist/clis/_shared/common.d.ts +3 -0
  4. package/dist/clis/_shared/common.js +22 -0
  5. package/dist/clis/bilibili/hot.d.ts +1 -0
  6. package/dist/clis/bilibili/hot.js +35 -0
  7. package/dist/clis/bluesky/feeds.d.ts +1 -0
  8. package/dist/clis/bluesky/feeds.js +27 -0
  9. package/dist/clis/bluesky/followers.d.ts +1 -0
  10. package/dist/clis/bluesky/followers.js +27 -0
  11. package/dist/clis/bluesky/following.d.ts +1 -0
  12. package/dist/clis/bluesky/following.js +27 -0
  13. package/dist/clis/bluesky/profile.d.ts +1 -0
  14. package/dist/clis/bluesky/profile.js +29 -0
  15. package/dist/clis/bluesky/search.d.ts +1 -0
  16. package/dist/clis/bluesky/search.js +28 -0
  17. package/dist/clis/bluesky/starter-packs.d.ts +1 -0
  18. package/dist/clis/bluesky/starter-packs.js +28 -0
  19. package/dist/clis/bluesky/thread.d.ts +1 -0
  20. package/dist/clis/bluesky/thread.js +30 -0
  21. package/dist/clis/bluesky/trending.d.ts +1 -0
  22. package/dist/clis/bluesky/trending.js +19 -0
  23. package/dist/clis/bluesky/user.d.ts +1 -0
  24. package/dist/clis/bluesky/user.js +33 -0
  25. package/dist/clis/cnki/search.d.ts +1 -0
  26. package/dist/clis/cnki/search.js +60 -0
  27. package/dist/clis/cnki/search.test.d.ts +1 -0
  28. package/dist/clis/cnki/search.test.js +18 -0
  29. package/dist/clis/devto/tag.d.ts +1 -0
  30. package/dist/clis/devto/tag.js +32 -0
  31. package/dist/clis/devto/top.d.ts +1 -0
  32. package/dist/clis/devto/top.js +26 -0
  33. package/dist/clis/devto/user.d.ts +1 -0
  34. package/dist/clis/devto/user.js +31 -0
  35. package/dist/clis/dictionary/examples.d.ts +1 -0
  36. package/dist/clis/dictionary/examples.js +27 -0
  37. package/dist/clis/dictionary/search.d.ts +1 -0
  38. package/dist/clis/dictionary/search.js +29 -0
  39. package/dist/clis/dictionary/synonyms.d.ts +1 -0
  40. package/dist/clis/dictionary/synonyms.js +27 -0
  41. package/dist/clis/douban/subject.d.ts +1 -0
  42. package/dist/clis/douban/subject.js +118 -0
  43. package/dist/clis/douban/top250.d.ts +1 -0
  44. package/dist/clis/douban/top250.js +67 -0
  45. package/dist/clis/facebook/add-friend.d.ts +1 -0
  46. package/dist/clis/facebook/add-friend.js +43 -0
  47. package/dist/clis/facebook/events.d.ts +1 -0
  48. package/dist/clis/facebook/events.js +40 -0
  49. package/dist/clis/facebook/feed.d.ts +1 -0
  50. package/dist/clis/facebook/feed.js +59 -0
  51. package/dist/clis/facebook/friends.d.ts +1 -0
  52. package/dist/clis/facebook/friends.js +38 -0
  53. package/dist/clis/facebook/groups.d.ts +1 -0
  54. package/dist/clis/facebook/groups.js +46 -0
  55. package/dist/clis/facebook/join-group.d.ts +1 -0
  56. package/dist/clis/facebook/join-group.js +44 -0
  57. package/dist/clis/facebook/memories.d.ts +1 -0
  58. package/dist/clis/facebook/memories.js +35 -0
  59. package/dist/clis/facebook/notifications.d.ts +1 -0
  60. package/dist/clis/facebook/notifications.js +36 -0
  61. package/dist/clis/facebook/profile.d.ts +1 -0
  62. package/dist/clis/facebook/profile.js +37 -0
  63. package/dist/clis/facebook/search.d.ts +1 -0
  64. package/dist/clis/facebook/search.js +38 -0
  65. package/dist/clis/facebook/search.test.d.ts +1 -1
  66. package/dist/clis/facebook/search.test.js +6 -9
  67. package/dist/clis/gitee/index.d.ts +3 -0
  68. package/dist/clis/gitee/index.js +3 -0
  69. package/dist/clis/gitee/search.d.ts +1 -0
  70. package/dist/clis/gitee/search.js +136 -0
  71. package/dist/clis/gitee/trending.d.ts +1 -0
  72. package/dist/clis/gitee/trending.js +567 -0
  73. package/dist/clis/gitee/user.d.ts +1 -0
  74. package/dist/clis/gitee/user.js +199 -0
  75. package/dist/clis/gitee/user.test.d.ts +1 -0
  76. package/dist/clis/gitee/user.test.js +63 -0
  77. package/dist/clis/hackernews/ask.d.ts +1 -0
  78. package/dist/clis/hackernews/ask.js +29 -0
  79. package/dist/clis/hackernews/best.d.ts +1 -0
  80. package/dist/clis/hackernews/best.js +29 -0
  81. package/dist/clis/hackernews/jobs.d.ts +1 -0
  82. package/dist/clis/hackernews/jobs.js +27 -0
  83. package/dist/clis/hackernews/new.d.ts +1 -0
  84. package/dist/clis/hackernews/new.js +29 -0
  85. package/dist/clis/hackernews/search.d.ts +1 -0
  86. package/dist/clis/hackernews/search.js +36 -0
  87. package/dist/clis/hackernews/show.d.ts +1 -0
  88. package/dist/clis/hackernews/show.js +29 -0
  89. package/dist/clis/hackernews/top.d.ts +1 -0
  90. package/dist/clis/hackernews/top.js +29 -0
  91. package/dist/clis/hackernews/user.d.ts +1 -0
  92. package/dist/clis/hackernews/user.js +22 -0
  93. package/dist/clis/hupu/hot.d.ts +1 -0
  94. package/dist/clis/hupu/hot.js +40 -0
  95. package/dist/clis/instagram/comment.d.ts +1 -0
  96. package/dist/clis/instagram/comment.js +47 -0
  97. package/dist/clis/instagram/explore.d.ts +1 -0
  98. package/dist/clis/instagram/explore.js +41 -0
  99. package/dist/clis/instagram/follow.d.ts +1 -0
  100. package/dist/clis/instagram/follow.js +43 -0
  101. package/dist/clis/instagram/followers.d.ts +1 -0
  102. package/dist/clis/instagram/followers.js +45 -0
  103. package/dist/clis/instagram/following.d.ts +1 -0
  104. package/dist/clis/instagram/following.js +45 -0
  105. package/dist/clis/instagram/like.d.ts +1 -0
  106. package/dist/clis/instagram/like.js +45 -0
  107. package/dist/clis/instagram/profile.d.ts +1 -0
  108. package/dist/clis/instagram/profile.js +39 -0
  109. package/dist/clis/instagram/save.d.ts +1 -0
  110. package/dist/clis/instagram/save.js +45 -0
  111. package/dist/clis/instagram/saved.d.ts +1 -0
  112. package/dist/clis/instagram/saved.js +38 -0
  113. package/dist/clis/instagram/search.d.ts +1 -0
  114. package/dist/clis/instagram/search.js +38 -0
  115. package/dist/clis/instagram/unfollow.d.ts +1 -0
  116. package/dist/clis/instagram/unfollow.js +40 -0
  117. package/dist/clis/instagram/unlike.d.ts +1 -0
  118. package/dist/clis/instagram/unlike.js +45 -0
  119. package/dist/clis/instagram/unsave.d.ts +1 -0
  120. package/dist/clis/instagram/unsave.js +45 -0
  121. package/dist/clis/instagram/user.d.ts +1 -0
  122. package/dist/clis/instagram/user.js +48 -0
  123. package/dist/clis/jd/add-cart.d.ts +1 -0
  124. package/dist/clis/jd/add-cart.js +71 -0
  125. package/dist/clis/jd/cart.d.ts +1 -0
  126. package/dist/clis/jd/cart.js +79 -0
  127. package/dist/clis/jd/commands.test.d.ts +5 -0
  128. package/dist/clis/jd/commands.test.js +64 -0
  129. package/dist/clis/jd/detail.d.ts +1 -0
  130. package/dist/clis/jd/detail.js +62 -0
  131. package/dist/clis/jd/reviews.d.ts +1 -0
  132. package/dist/clis/jd/reviews.js +54 -0
  133. package/dist/clis/jd/search.d.ts +1 -0
  134. package/dist/clis/jd/search.js +65 -0
  135. package/dist/clis/jianyu/search.d.ts +14 -0
  136. package/dist/clis/jianyu/search.js +135 -0
  137. package/dist/clis/jianyu/search.test.d.ts +1 -0
  138. package/dist/clis/jianyu/search.test.js +23 -0
  139. package/dist/clis/jike/post.d.ts +1 -0
  140. package/dist/clis/jike/post.js +61 -0
  141. package/dist/clis/jike/topic.d.ts +1 -0
  142. package/dist/clis/jike/topic.js +51 -0
  143. package/dist/clis/jike/user.d.ts +1 -0
  144. package/dist/clis/jike/user.js +50 -0
  145. package/dist/clis/jimeng/generate.d.ts +1 -0
  146. package/dist/clis/jimeng/generate.js +83 -0
  147. package/dist/clis/jimeng/history.d.ts +1 -0
  148. package/dist/clis/jimeng/history.js +47 -0
  149. package/dist/clis/jimeng/new.d.ts +1 -0
  150. package/dist/clis/jimeng/new.js +43 -0
  151. package/dist/clis/jimeng/workspaces.d.ts +1 -0
  152. package/dist/clis/jimeng/workspaces.js +41 -0
  153. package/dist/clis/linux-do/categories.d.ts +1 -0
  154. package/dist/clis/linux-do/categories.js +65 -0
  155. package/dist/clis/linux-do/search.d.ts +1 -0
  156. package/dist/clis/linux-do/search.js +41 -0
  157. package/dist/clis/linux-do/tags.d.ts +1 -0
  158. package/dist/clis/linux-do/tags.js +39 -0
  159. package/dist/clis/linux-do/topic-content.test.js +5 -5
  160. package/dist/clis/linux-do/topic.d.ts +1 -0
  161. package/dist/clis/linux-do/topic.js +56 -0
  162. package/dist/clis/linux-do/user-posts.d.ts +1 -0
  163. package/dist/clis/linux-do/user-posts.js +61 -0
  164. package/dist/clis/linux-do/user-topics.d.ts +1 -0
  165. package/dist/clis/linux-do/user-topics.js +48 -0
  166. package/dist/clis/lobsters/active.d.ts +1 -0
  167. package/dist/clis/lobsters/active.js +26 -0
  168. package/dist/clis/lobsters/hot.d.ts +1 -0
  169. package/dist/clis/lobsters/hot.js +26 -0
  170. package/dist/clis/lobsters/newest.d.ts +1 -0
  171. package/dist/clis/lobsters/newest.js +26 -0
  172. package/dist/clis/lobsters/tag.d.ts +1 -0
  173. package/dist/clis/lobsters/tag.js +32 -0
  174. package/dist/clis/pixiv/detail.d.ts +1 -0
  175. package/dist/clis/pixiv/detail.js +58 -0
  176. package/dist/clis/pixiv/ranking.d.ts +1 -0
  177. package/dist/clis/pixiv/ranking.js +59 -0
  178. package/dist/clis/pixiv/user.d.ts +1 -0
  179. package/dist/clis/pixiv/user.js +52 -0
  180. package/dist/clis/quark/ls.d.ts +1 -0
  181. package/dist/clis/quark/ls.js +63 -0
  182. package/dist/clis/quark/mkdir.d.ts +1 -0
  183. package/dist/clis/quark/mkdir.js +36 -0
  184. package/dist/clis/quark/mv.d.ts +1 -0
  185. package/dist/clis/quark/mv.js +53 -0
  186. package/dist/clis/quark/rename.d.ts +1 -0
  187. package/dist/clis/quark/rename.js +26 -0
  188. package/dist/clis/quark/rm.d.ts +1 -0
  189. package/dist/clis/quark/rm.js +24 -0
  190. package/dist/clis/quark/save.d.ts +1 -0
  191. package/dist/clis/quark/save.js +80 -0
  192. package/dist/clis/quark/share-tree.d.ts +1 -0
  193. package/dist/clis/quark/share-tree.js +45 -0
  194. package/dist/clis/quark/utils.d.ts +50 -0
  195. package/dist/clis/quark/utils.js +146 -0
  196. package/dist/clis/quark/utils.test.d.ts +1 -0
  197. package/dist/clis/quark/utils.test.js +58 -0
  198. package/dist/clis/reddit/frontpage.d.ts +1 -0
  199. package/dist/clis/reddit/frontpage.js +31 -0
  200. package/dist/clis/reddit/hot.d.ts +1 -0
  201. package/dist/clis/reddit/hot.js +45 -0
  202. package/dist/clis/reddit/popular.d.ts +1 -0
  203. package/dist/clis/reddit/popular.js +41 -0
  204. package/dist/clis/reddit/search.d.ts +1 -0
  205. package/dist/clis/reddit/search.js +65 -0
  206. package/dist/clis/reddit/subreddit.d.ts +1 -0
  207. package/dist/clis/reddit/subreddit.js +52 -0
  208. package/dist/clis/reddit/user-comments.d.ts +1 -0
  209. package/dist/clis/reddit/user-comments.js +44 -0
  210. package/dist/clis/reddit/user-posts.d.ts +1 -0
  211. package/dist/clis/reddit/user-posts.js +42 -0
  212. package/dist/clis/reddit/user.d.ts +1 -0
  213. package/dist/clis/reddit/user.js +37 -0
  214. package/dist/clis/stackoverflow/bounties.d.ts +1 -0
  215. package/dist/clis/stackoverflow/bounties.js +27 -0
  216. package/dist/clis/stackoverflow/hot.d.ts +1 -0
  217. package/dist/clis/stackoverflow/hot.js +24 -0
  218. package/dist/clis/stackoverflow/search.d.ts +1 -0
  219. package/dist/clis/stackoverflow/search.js +27 -0
  220. package/dist/clis/stackoverflow/unanswered.d.ts +1 -0
  221. package/dist/clis/stackoverflow/unanswered.js +26 -0
  222. package/dist/clis/steam/top-sellers.d.ts +1 -0
  223. package/dist/clis/steam/top-sellers.js +25 -0
  224. package/dist/clis/taobao/add-cart.d.ts +1 -0
  225. package/dist/clis/taobao/add-cart.js +149 -0
  226. package/dist/clis/taobao/cart.d.ts +1 -0
  227. package/dist/clis/taobao/cart.js +95 -0
  228. package/dist/clis/taobao/commands.test.d.ts +5 -0
  229. package/dist/clis/taobao/commands.test.js +64 -0
  230. package/dist/clis/taobao/detail.d.ts +1 -0
  231. package/dist/clis/taobao/detail.js +70 -0
  232. package/dist/clis/taobao/reviews.d.ts +1 -0
  233. package/dist/clis/taobao/reviews.js +76 -0
  234. package/dist/clis/taobao/search.d.ts +1 -0
  235. package/dist/clis/taobao/search.js +96 -0
  236. package/dist/clis/tiktok/comment.d.ts +1 -0
  237. package/dist/clis/tiktok/comment.js +57 -0
  238. package/dist/clis/tiktok/explore.d.ts +1 -0
  239. package/dist/clis/tiktok/explore.js +35 -0
  240. package/dist/clis/tiktok/follow.d.ts +1 -0
  241. package/dist/clis/tiktok/follow.js +39 -0
  242. package/dist/clis/tiktok/following.d.ts +1 -0
  243. package/dist/clis/tiktok/following.js +42 -0
  244. package/dist/clis/tiktok/friends.d.ts +1 -0
  245. package/dist/clis/tiktok/friends.js +43 -0
  246. package/dist/clis/tiktok/like.d.ts +1 -0
  247. package/dist/clis/tiktok/like.js +33 -0
  248. package/dist/clis/tiktok/live.d.ts +1 -0
  249. package/dist/clis/tiktok/live.js +47 -0
  250. package/dist/clis/tiktok/notifications.d.ts +1 -0
  251. package/dist/clis/tiktok/notifications.js +49 -0
  252. package/dist/clis/tiktok/profile.d.ts +1 -0
  253. package/dist/clis/tiktok/profile.js +54 -0
  254. package/dist/clis/tiktok/save.d.ts +1 -0
  255. package/dist/clis/tiktok/save.js +29 -0
  256. package/dist/clis/tiktok/search.d.ts +1 -0
  257. package/dist/clis/tiktok/search.js +39 -0
  258. package/dist/clis/tiktok/unfollow.d.ts +1 -0
  259. package/dist/clis/tiktok/unfollow.js +44 -0
  260. package/dist/clis/tiktok/unlike.d.ts +1 -0
  261. package/dist/clis/tiktok/unlike.js +33 -0
  262. package/dist/clis/tiktok/unsave.d.ts +1 -0
  263. package/dist/clis/tiktok/unsave.js +31 -0
  264. package/dist/clis/tiktok/user.d.ts +1 -0
  265. package/dist/clis/tiktok/user.js +41 -0
  266. package/dist/clis/twitter/reply.js +3 -8
  267. package/dist/clis/twitter/reply.test.js +5 -5
  268. package/dist/clis/v2ex/hot.d.ts +1 -0
  269. package/dist/clis/v2ex/hot.js +25 -0
  270. package/dist/clis/v2ex/latest.d.ts +1 -0
  271. package/dist/clis/v2ex/latest.js +25 -0
  272. package/dist/clis/v2ex/member.d.ts +1 -0
  273. package/dist/clis/v2ex/member.js +27 -0
  274. package/dist/clis/v2ex/node.d.ts +1 -0
  275. package/dist/clis/v2ex/node.js +38 -0
  276. package/dist/clis/v2ex/nodes.d.ts +1 -0
  277. package/dist/clis/v2ex/nodes.js +25 -0
  278. package/dist/clis/v2ex/replies.d.ts +1 -0
  279. package/dist/clis/v2ex/replies.js +26 -0
  280. package/dist/clis/v2ex/topic.d.ts +1 -0
  281. package/dist/clis/v2ex/topic.js +30 -0
  282. package/dist/clis/v2ex/user.d.ts +1 -0
  283. package/dist/clis/v2ex/user.js +33 -0
  284. package/dist/clis/xiaoe/catalog.d.ts +1 -0
  285. package/dist/clis/xiaoe/catalog.js +125 -0
  286. package/dist/clis/xiaoe/content.d.ts +1 -0
  287. package/dist/clis/xiaoe/content.js +39 -0
  288. package/dist/clis/xiaoe/courses.d.ts +1 -0
  289. package/dist/clis/xiaoe/courses.js +69 -0
  290. package/dist/clis/xiaoe/detail.d.ts +1 -0
  291. package/dist/clis/xiaoe/detail.js +35 -0
  292. package/dist/clis/xiaoe/play-url.d.ts +1 -0
  293. package/dist/clis/xiaoe/play-url.js +120 -0
  294. package/dist/clis/xiaohongshu/feed.d.ts +1 -0
  295. package/dist/clis/xiaohongshu/feed.js +32 -0
  296. package/dist/clis/xiaohongshu/note.js +8 -3
  297. package/dist/clis/xiaohongshu/note.test.js +11 -0
  298. package/dist/clis/xiaohongshu/notifications.d.ts +1 -0
  299. package/dist/clis/xiaohongshu/notifications.js +38 -0
  300. package/dist/clis/xueqiu/earnings-date.d.ts +1 -0
  301. package/dist/clis/xueqiu/earnings-date.js +61 -0
  302. package/dist/clis/xueqiu/feed.d.ts +1 -0
  303. package/dist/clis/xueqiu/feed.js +48 -0
  304. package/dist/clis/xueqiu/groups.d.ts +1 -0
  305. package/dist/clis/xueqiu/groups.js +25 -0
  306. package/dist/clis/xueqiu/hot-stock.d.ts +1 -0
  307. package/dist/clis/xueqiu/hot-stock.js +44 -0
  308. package/dist/clis/xueqiu/hot.d.ts +1 -0
  309. package/dist/clis/xueqiu/hot.js +44 -0
  310. package/dist/clis/xueqiu/kline.d.ts +1 -0
  311. package/dist/clis/xueqiu/kline.js +64 -0
  312. package/dist/clis/xueqiu/search.d.ts +1 -0
  313. package/dist/clis/xueqiu/search.js +49 -0
  314. package/dist/clis/xueqiu/stock.d.ts +1 -0
  315. package/dist/clis/xueqiu/stock.js +72 -0
  316. package/dist/clis/xueqiu/watchlist.d.ts +1 -0
  317. package/dist/clis/xueqiu/watchlist.js +45 -0
  318. package/dist/clis/zhihu/answer.d.ts +1 -0
  319. package/dist/clis/zhihu/answer.js +194 -0
  320. package/dist/clis/zhihu/answer.test.d.ts +1 -0
  321. package/dist/clis/zhihu/answer.test.js +81 -0
  322. package/dist/clis/zhihu/comment.d.ts +1 -0
  323. package/dist/clis/zhihu/comment.js +335 -0
  324. package/dist/clis/zhihu/comment.test.d.ts +1 -0
  325. package/dist/clis/zhihu/comment.test.js +54 -0
  326. package/dist/clis/zhihu/favorite.d.ts +1 -0
  327. package/dist/clis/zhihu/favorite.js +224 -0
  328. package/dist/clis/zhihu/favorite.test.d.ts +1 -0
  329. package/dist/clis/zhihu/favorite.test.js +196 -0
  330. package/dist/clis/zhihu/follow.d.ts +1 -0
  331. package/dist/clis/zhihu/follow.js +80 -0
  332. package/dist/clis/zhihu/follow.test.d.ts +1 -0
  333. package/dist/clis/zhihu/follow.test.js +45 -0
  334. package/dist/clis/zhihu/hot.d.ts +1 -0
  335. package/dist/clis/zhihu/hot.js +43 -0
  336. package/dist/clis/zhihu/like.d.ts +1 -0
  337. package/dist/clis/zhihu/like.js +91 -0
  338. package/dist/clis/zhihu/like.test.d.ts +1 -0
  339. package/dist/clis/zhihu/like.test.js +64 -0
  340. package/dist/clis/zhihu/search.d.ts +1 -0
  341. package/dist/clis/zhihu/search.js +52 -0
  342. package/dist/clis/zhihu/target.d.ts +24 -0
  343. package/dist/clis/zhihu/target.js +91 -0
  344. package/dist/clis/zhihu/target.test.d.ts +1 -0
  345. package/dist/clis/zhihu/target.test.js +77 -0
  346. package/dist/clis/zhihu/write-shared.d.ts +32 -0
  347. package/dist/clis/zhihu/write-shared.js +221 -0
  348. package/dist/clis/zhihu/write-shared.test.d.ts +1 -0
  349. package/dist/clis/zhihu/write-shared.test.js +175 -0
  350. package/dist/src/browser/bridge.d.ts +2 -0
  351. package/dist/src/browser/bridge.js +30 -24
  352. package/dist/src/browser/daemon-client.d.ts +30 -10
  353. package/dist/src/browser/daemon-client.js +42 -27
  354. package/dist/src/browser/daemon-client.test.js +32 -25
  355. package/dist/src/browser/dom-helpers.test.js +3 -2
  356. package/dist/src/browser/errors.d.ts +26 -1
  357. package/dist/src/browser/errors.js +40 -7
  358. package/dist/src/browser/errors.test.d.ts +1 -0
  359. package/dist/src/browser/errors.test.js +51 -0
  360. package/dist/src/browser/index.d.ts +2 -1
  361. package/dist/src/browser/index.js +1 -1
  362. package/dist/src/browser/page.d.ts +9 -8
  363. package/dist/src/browser/page.js +33 -31
  364. package/dist/src/browser.test.js +27 -8
  365. package/dist/src/build-manifest.d.ts +5 -11
  366. package/dist/src/build-manifest.js +6 -75
  367. package/dist/src/build-manifest.test.js +1 -39
  368. package/dist/src/cascade.js +3 -2
  369. package/dist/src/cli.d.ts +3 -3
  370. package/dist/src/cli.js +73 -65
  371. package/dist/src/cli.test.js +20 -15
  372. package/dist/src/clis/binance/asks.d.ts +1 -0
  373. package/dist/src/clis/binance/asks.js +20 -0
  374. package/dist/src/clis/binance/commands.test.d.ts +3 -0
  375. package/dist/src/clis/binance/commands.test.js +58 -0
  376. package/dist/src/clis/binance/depth.d.ts +1 -0
  377. package/dist/src/clis/binance/depth.js +20 -0
  378. package/dist/src/clis/binance/gainers.d.ts +1 -0
  379. package/dist/src/clis/binance/gainers.js +21 -0
  380. package/dist/src/clis/binance/klines.d.ts +1 -0
  381. package/dist/src/clis/binance/klines.js +20 -0
  382. package/dist/src/clis/binance/losers.d.ts +1 -0
  383. package/dist/src/clis/binance/losers.js +21 -0
  384. package/dist/src/clis/binance/pairs.d.ts +1 -0
  385. package/dist/src/clis/binance/pairs.js +20 -0
  386. package/dist/src/clis/binance/price.d.ts +1 -0
  387. package/dist/src/clis/binance/price.js +17 -0
  388. package/dist/src/clis/binance/prices.d.ts +1 -0
  389. package/dist/src/clis/binance/prices.js +18 -0
  390. package/dist/src/clis/binance/ticker.d.ts +1 -0
  391. package/dist/src/clis/binance/ticker.js +20 -0
  392. package/dist/src/clis/binance/top.d.ts +1 -0
  393. package/dist/src/clis/binance/top.js +20 -0
  394. package/dist/src/clis/binance/trades.d.ts +1 -0
  395. package/dist/src/clis/binance/trades.js +19 -0
  396. package/dist/src/commanderAdapter.js +19 -6
  397. package/dist/src/completion-fast.d.ts +25 -0
  398. package/dist/src/completion-fast.js +140 -0
  399. package/dist/src/completion.d.ts +1 -0
  400. package/dist/src/completion.js +1 -0
  401. package/dist/src/diagnostic.d.ts +1 -0
  402. package/dist/src/diagnostic.js +64 -2
  403. package/dist/src/diagnostic.test.js +93 -3
  404. package/dist/src/discovery.d.ts +3 -3
  405. package/dist/src/discovery.js +34 -97
  406. package/dist/src/doctor.d.ts +2 -0
  407. package/dist/src/doctor.js +59 -31
  408. package/dist/src/doctor.test.js +89 -16
  409. package/dist/src/download/index.d.ts +1 -1
  410. package/dist/src/engine.test.js +4 -19
  411. package/dist/src/execution.js +1 -13
  412. package/dist/src/explore.js +1 -1
  413. package/dist/src/generate-verified.d.ts +105 -0
  414. package/dist/src/generate-verified.js +696 -0
  415. package/dist/src/generate-verified.test.d.ts +1 -0
  416. package/dist/src/generate-verified.test.js +925 -0
  417. package/dist/src/generate.d.ts +11 -6
  418. package/dist/src/generate.js +4 -7
  419. package/dist/src/main.js +65 -12
  420. package/dist/src/pipeline/steps/download.d.ts +1 -17
  421. package/dist/src/pipeline/steps/download.js +20 -31
  422. package/dist/src/pipeline/steps/intercept.d.ts +1 -1
  423. package/dist/src/pipeline/steps/intercept.js +1 -1
  424. package/dist/src/pipeline/steps/tap.d.ts +1 -1
  425. package/dist/src/pipeline/steps/tap.js +1 -1
  426. package/dist/src/plugin-scaffold.d.ts +2 -2
  427. package/dist/src/plugin-scaffold.js +24 -21
  428. package/dist/src/plugin-scaffold.test.js +1 -1
  429. package/dist/src/plugin.d.ts +3 -2
  430. package/dist/src/plugin.js +29 -14
  431. package/dist/src/plugin.test.js +47 -32
  432. package/dist/src/record.js +26 -25
  433. package/dist/src/runtime-detect.js +3 -7
  434. package/dist/src/scripts/framework.d.ts +3 -0
  435. package/dist/src/scripts/framework.js +8 -4
  436. package/dist/src/scripts/store.d.ts +5 -1
  437. package/dist/src/scripts/store.js +5 -1
  438. package/dist/src/skill-generate.d.ts +30 -0
  439. package/dist/src/skill-generate.js +75 -0
  440. package/dist/src/skill-generate.test.d.ts +1 -0
  441. package/dist/src/skill-generate.test.js +173 -0
  442. package/dist/src/synthesize.d.ts +1 -1
  443. package/dist/src/synthesize.js +7 -8
  444. package/dist/src/types.d.ts +3 -1
  445. package/package.json +5 -5
  446. package/dist/clis/bilibili/hot.yaml +0 -38
  447. package/dist/clis/bluesky/feeds.yaml +0 -29
  448. package/dist/clis/bluesky/followers.yaml +0 -33
  449. package/dist/clis/bluesky/following.yaml +0 -33
  450. package/dist/clis/bluesky/profile.yaml +0 -27
  451. package/dist/clis/bluesky/search.yaml +0 -34
  452. package/dist/clis/bluesky/starter-packs.yaml +0 -34
  453. package/dist/clis/bluesky/thread.yaml +0 -32
  454. package/dist/clis/bluesky/trending.yaml +0 -27
  455. package/dist/clis/bluesky/user.yaml +0 -34
  456. package/dist/clis/devto/tag.yaml +0 -34
  457. package/dist/clis/devto/top.yaml +0 -29
  458. package/dist/clis/devto/user.yaml +0 -33
  459. package/dist/clis/dictionary/examples.yaml +0 -25
  460. package/dist/clis/dictionary/search.yaml +0 -27
  461. package/dist/clis/dictionary/synonyms.yaml +0 -25
  462. package/dist/clis/douban/subject.yaml +0 -107
  463. package/dist/clis/douban/top250.yaml +0 -70
  464. package/dist/clis/facebook/add-friend.yaml +0 -43
  465. package/dist/clis/facebook/events.yaml +0 -44
  466. package/dist/clis/facebook/feed.yaml +0 -63
  467. package/dist/clis/facebook/friends.yaml +0 -42
  468. package/dist/clis/facebook/groups.yaml +0 -50
  469. package/dist/clis/facebook/join-group.yaml +0 -44
  470. package/dist/clis/facebook/memories.yaml +0 -39
  471. package/dist/clis/facebook/notifications.yaml +0 -40
  472. package/dist/clis/facebook/profile.yaml +0 -37
  473. package/dist/clis/facebook/search.yaml +0 -47
  474. package/dist/clis/hackernews/ask.yaml +0 -38
  475. package/dist/clis/hackernews/best.yaml +0 -38
  476. package/dist/clis/hackernews/jobs.yaml +0 -36
  477. package/dist/clis/hackernews/new.yaml +0 -38
  478. package/dist/clis/hackernews/search.yaml +0 -44
  479. package/dist/clis/hackernews/show.yaml +0 -38
  480. package/dist/clis/hackernews/top.yaml +0 -38
  481. package/dist/clis/hackernews/user.yaml +0 -25
  482. package/dist/clis/hupu/hot.yaml +0 -43
  483. package/dist/clis/instagram/comment.yaml +0 -52
  484. package/dist/clis/instagram/explore.yaml +0 -43
  485. package/dist/clis/instagram/follow.yaml +0 -41
  486. package/dist/clis/instagram/followers.yaml +0 -51
  487. package/dist/clis/instagram/following.yaml +0 -51
  488. package/dist/clis/instagram/like.yaml +0 -46
  489. package/dist/clis/instagram/profile.yaml +0 -42
  490. package/dist/clis/instagram/save.yaml +0 -46
  491. package/dist/clis/instagram/saved.yaml +0 -40
  492. package/dist/clis/instagram/search.yaml +0 -44
  493. package/dist/clis/instagram/unfollow.yaml +0 -38
  494. package/dist/clis/instagram/unlike.yaml +0 -46
  495. package/dist/clis/instagram/unsave.yaml +0 -46
  496. package/dist/clis/instagram/user.yaml +0 -54
  497. package/dist/clis/jike/post.yaml +0 -59
  498. package/dist/clis/jike/topic.yaml +0 -53
  499. package/dist/clis/jike/user.yaml +0 -52
  500. package/dist/clis/jimeng/generate.yaml +0 -85
  501. package/dist/clis/jimeng/history.yaml +0 -46
  502. package/dist/clis/linux-do/categories.yaml +0 -70
  503. package/dist/clis/linux-do/search.yaml +0 -48
  504. package/dist/clis/linux-do/tags.yaml +0 -41
  505. package/dist/clis/linux-do/topic.yaml +0 -62
  506. package/dist/clis/linux-do/user-posts.yaml +0 -67
  507. package/dist/clis/linux-do/user-topics.yaml +0 -54
  508. package/dist/clis/lobsters/active.yaml +0 -29
  509. package/dist/clis/lobsters/hot.yaml +0 -29
  510. package/dist/clis/lobsters/newest.yaml +0 -29
  511. package/dist/clis/lobsters/tag.yaml +0 -34
  512. package/dist/clis/pixiv/detail.yaml +0 -49
  513. package/dist/clis/pixiv/ranking.yaml +0 -53
  514. package/dist/clis/pixiv/user.yaml +0 -46
  515. package/dist/clis/reddit/frontpage.yaml +0 -30
  516. package/dist/clis/reddit/hot.yaml +0 -47
  517. package/dist/clis/reddit/popular.yaml +0 -40
  518. package/dist/clis/reddit/search.yaml +0 -61
  519. package/dist/clis/reddit/subreddit.yaml +0 -50
  520. package/dist/clis/reddit/user-comments.yaml +0 -46
  521. package/dist/clis/reddit/user-posts.yaml +0 -44
  522. package/dist/clis/reddit/user.yaml +0 -40
  523. package/dist/clis/stackoverflow/bounties.yaml +0 -29
  524. package/dist/clis/stackoverflow/hot.yaml +0 -28
  525. package/dist/clis/stackoverflow/search.yaml +0 -33
  526. package/dist/clis/stackoverflow/unanswered.yaml +0 -28
  527. package/dist/clis/steam/top-sellers.yaml +0 -29
  528. package/dist/clis/tiktok/comment.yaml +0 -66
  529. package/dist/clis/tiktok/explore.yaml +0 -39
  530. package/dist/clis/tiktok/follow.yaml +0 -39
  531. package/dist/clis/tiktok/following.yaml +0 -46
  532. package/dist/clis/tiktok/friends.yaml +0 -47
  533. package/dist/clis/tiktok/like.yaml +0 -38
  534. package/dist/clis/tiktok/live.yaml +0 -51
  535. package/dist/clis/tiktok/notifications.yaml +0 -52
  536. package/dist/clis/tiktok/profile.yaml +0 -45
  537. package/dist/clis/tiktok/save.yaml +0 -34
  538. package/dist/clis/tiktok/search.yaml +0 -47
  539. package/dist/clis/tiktok/unfollow.yaml +0 -44
  540. package/dist/clis/tiktok/unlike.yaml +0 -38
  541. package/dist/clis/tiktok/unsave.yaml +0 -36
  542. package/dist/clis/tiktok/user.yaml +0 -44
  543. package/dist/clis/v2ex/hot.yaml +0 -28
  544. package/dist/clis/v2ex/latest.yaml +0 -28
  545. package/dist/clis/v2ex/member.yaml +0 -29
  546. package/dist/clis/v2ex/node.yaml +0 -34
  547. package/dist/clis/v2ex/nodes.yaml +0 -31
  548. package/dist/clis/v2ex/replies.yaml +0 -32
  549. package/dist/clis/v2ex/topic.yaml +0 -33
  550. package/dist/clis/v2ex/user.yaml +0 -34
  551. package/dist/clis/xiaoe/catalog.yaml +0 -129
  552. package/dist/clis/xiaoe/content.yaml +0 -43
  553. package/dist/clis/xiaoe/courses.yaml +0 -73
  554. package/dist/clis/xiaoe/detail.yaml +0 -39
  555. package/dist/clis/xiaoe/play-url.yaml +0 -124
  556. package/dist/clis/xiaohongshu/feed.yaml +0 -31
  557. package/dist/clis/xiaohongshu/notifications.yaml +0 -37
  558. package/dist/clis/xueqiu/earnings-date.yaml +0 -69
  559. package/dist/clis/xueqiu/feed.yaml +0 -53
  560. package/dist/clis/xueqiu/groups.yaml +0 -23
  561. package/dist/clis/xueqiu/hot-stock.yaml +0 -49
  562. package/dist/clis/xueqiu/hot.yaml +0 -46
  563. package/dist/clis/xueqiu/kline.yaml +0 -65
  564. package/dist/clis/xueqiu/search.yaml +0 -55
  565. package/dist/clis/xueqiu/stock.yaml +0 -69
  566. package/dist/clis/xueqiu/watchlist.yaml +0 -46
  567. package/dist/clis/zhihu/hot.yaml +0 -46
  568. package/dist/clis/zhihu/search.yaml +0 -59
  569. package/dist/src/browser/discover.d.ts +0 -15
  570. package/dist/src/browser/discover.js +0 -19
  571. package/dist/src/yaml-schema.d.ts +0 -29
  572. package/dist/src/yaml-schema.js +0 -22
@@ -0,0 +1,696 @@
1
+ /**
2
+ * Verified adapter generation:
3
+ * discover → synthesize → candidate-bound probe → single-session verify.
4
+ *
5
+ * v1 contract keeps scope narrow:
6
+ * - PUBLIC + COOKIE only
7
+ * - read-only JSON API surfaces
8
+ * - single best candidate only
9
+ * - bounded repair: select/itemPath replacement once
10
+ *
11
+ * Contract design principles:
12
+ * 1. machine-readable
13
+ * 2. explicit + explainable
14
+ * 3. testable + versioned
15
+ * 4. taxonomy by skill decision needs (not internal error sources)
16
+ * 5. early hint / terminal outcome share consistent decision language
17
+ */
18
+ import * as fs from 'node:fs';
19
+ import * as path from 'node:path';
20
+ import { exploreUrl } from './explore.js';
21
+ import { loadExploreBundle, synthesizeFromExplore } from './synthesize.js';
22
+ import { normalizeGoal, selectCandidate } from './generate.js';
23
+ import { browserSession } from './runtime.js';
24
+ import { executePipeline } from './pipeline/index.js';
25
+ import { registerCommand, Strategy } from './registry.js';
26
+ import { AuthRequiredError, BrowserConnectError, CommandExecutionError, SelectorError, TimeoutError, getErrorMessage, } from './errors.js';
27
+ import { USER_CLIS_DIR } from './discovery.js';
28
+ // ── Helpers ───────────────────────────────────────────────────────────────────
29
+ function parseSupportedStrategy(value) {
30
+ return value === Strategy.PUBLIC || value === Strategy.COOKIE ? value : null;
31
+ }
32
+ function commandName(site, name) {
33
+ return `${site}/${name}`;
34
+ }
35
+ function buildStats(args) {
36
+ return {
37
+ endpoint_count: args.endpointCount,
38
+ api_endpoint_count: args.apiEndpointCount,
39
+ candidate_count: args.candidateCount,
40
+ verified: args.verified ?? false,
41
+ repair_attempted: args.repairAttempted ?? false,
42
+ explore_dir: args.exploreDir,
43
+ };
44
+ }
45
+ function readCandidateJson(filePath) {
46
+ const loaded = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
47
+ if (!loaded || typeof loaded !== 'object') {
48
+ throw new CommandExecutionError(`Generated candidate is invalid: ${filePath}`);
49
+ }
50
+ return loaded;
51
+ }
52
+ function chooseEndpoint(capability, endpoints) {
53
+ if (!endpoints.length)
54
+ return null;
55
+ if (capability?.endpoint) {
56
+ const endpointPattern = capability.endpoint;
57
+ const exact = endpoints.find((endpoint) => endpoint.pattern === endpointPattern || endpoint.url.includes(endpointPattern));
58
+ if (exact)
59
+ return exact;
60
+ }
61
+ return [...endpoints].sort((a, b) => {
62
+ const aScore = (a.itemCount ?? 0) * 10 + Object.keys(a.detectedFields ?? {}).length;
63
+ const bScore = (b.itemCount ?? 0) * 10 + Object.keys(b.detectedFields ?? {}).length;
64
+ return bScore - aScore;
65
+ })[0] ?? null;
66
+ }
67
+ function cloneCandidate(candidate) {
68
+ return JSON.parse(JSON.stringify(candidate));
69
+ }
70
+ function hasBrowserOnlyStep(pipeline) {
71
+ return pipeline.some((step) => {
72
+ const op = Object.keys(step)[0];
73
+ return op === 'navigate' || op === 'wait' || op === 'evaluate' || op === 'click' || op === 'tap' || op === 'type' || op === 'press';
74
+ });
75
+ }
76
+ function detectBrowserFlag(candidate) {
77
+ return candidate.browser ?? hasBrowserOnlyStep(candidate.pipeline);
78
+ }
79
+ function candidateToCommand(candidate, source) {
80
+ return {
81
+ site: candidate.site,
82
+ name: candidate.name,
83
+ description: candidate.description,
84
+ domain: candidate.domain,
85
+ strategy: parseSupportedStrategy(candidate.strategy) ?? Strategy.COOKIE,
86
+ browser: detectBrowserFlag(candidate),
87
+ args: Object.entries(candidate.args ?? {}).map(([name, def]) => ({
88
+ name,
89
+ type: def.type,
90
+ required: def.required,
91
+ default: def.default,
92
+ help: def.description,
93
+ })),
94
+ columns: candidate.columns,
95
+ pipeline: candidate.pipeline,
96
+ source,
97
+ };
98
+ }
99
+ function buildDefaultArgs(candidate) {
100
+ const args = {};
101
+ for (const [name, def] of Object.entries(candidate.args ?? {})) {
102
+ if (def.default !== undefined) {
103
+ args[name] = def.default;
104
+ continue;
105
+ }
106
+ if (def.type === 'int' || def.type === 'number') {
107
+ args[name] = name === 'page' ? 1 : 20;
108
+ continue;
109
+ }
110
+ if (def.type === 'boolean' || def.type === 'bool') {
111
+ args[name] = false;
112
+ continue;
113
+ }
114
+ if (name === 'keyword' || name === 'query') {
115
+ args[name] = 'test';
116
+ continue;
117
+ }
118
+ if (def.required)
119
+ args[name] = 'test';
120
+ }
121
+ return args;
122
+ }
123
+ function getUnsupportedVerificationArgs(candidate) {
124
+ return Object.entries(candidate.args ?? {})
125
+ .filter(([name, def]) => {
126
+ if (!def.required || def.default !== undefined)
127
+ return false;
128
+ if (def.type === 'int' || def.type === 'number')
129
+ return false;
130
+ if (def.type === 'boolean' || def.type === 'bool')
131
+ return false;
132
+ if (name === 'keyword' || name === 'query')
133
+ return false;
134
+ return true;
135
+ })
136
+ .map(([name]) => name);
137
+ }
138
+ function assessResult(result, expectedFields = []) {
139
+ if (!Array.isArray(result))
140
+ return { ok: false, reason: 'non-array-result' };
141
+ if (result.length === 0)
142
+ return { ok: false, reason: 'empty-result' };
143
+ const sample = result[0];
144
+ if (!sample || typeof sample !== 'object' || Array.isArray(sample)) {
145
+ return { ok: false, reason: 'sparse-fields' };
146
+ }
147
+ const record = sample;
148
+ const keys = Object.keys(record);
149
+ const populated = keys.filter((key) => record[key] !== null && record[key] !== undefined && record[key] !== '');
150
+ if (populated.length < 2)
151
+ return { ok: false, reason: 'sparse-fields' };
152
+ if (expectedFields.length > 0) {
153
+ const matched = expectedFields.filter((field) => keys.includes(field));
154
+ if (matched.length === 0)
155
+ return { ok: false, reason: 'sparse-fields' };
156
+ }
157
+ return { ok: true };
158
+ }
159
+ function withItemPath(candidate, itemPath) {
160
+ if (!itemPath)
161
+ return null;
162
+ const next = cloneCandidate(candidate);
163
+ const selectIndex = next.pipeline.findIndex((step) => 'select' in step);
164
+ if (selectIndex === -1)
165
+ return null;
166
+ const current = next.pipeline[selectIndex];
167
+ if (current.select === itemPath)
168
+ return null;
169
+ next.pipeline[selectIndex] = { select: itemPath };
170
+ return next;
171
+ }
172
+ function applyStrategy(candidate, strategy) {
173
+ const next = cloneCandidate(candidate);
174
+ next.strategy = strategy;
175
+ if (strategy === Strategy.COOKIE)
176
+ next.browser = true;
177
+ return next;
178
+ }
179
+ // ── Escalation builders ───────────────────────────────────────────────────────
180
+ function mapVerifyFailureToEscalation(reason) {
181
+ return reason; // VerifyFailureReason is a subset of EscalationReason
182
+ }
183
+ function suggestAction(reason) {
184
+ switch (reason) {
185
+ case 'unsupported-required-args': return 'ask-for-sample-arg';
186
+ case 'timeout': return 'inspect-with-browser';
187
+ case 'selector-mismatch': return 'inspect-with-browser';
188
+ case 'empty-result': return 'inspect-with-browser';
189
+ case 'sparse-fields': return 'inspect-with-browser';
190
+ case 'non-array-result': return 'inspect-with-browser';
191
+ case 'verify-inconclusive': return 'manual-review';
192
+ }
193
+ }
194
+ function buildEscalation(stage, reason, summary, site, opts) {
195
+ return {
196
+ stage,
197
+ reason,
198
+ confidence: opts?.confidence ?? 'medium',
199
+ suggested_action: suggestAction(reason),
200
+ candidate: {
201
+ name: summary.name,
202
+ command: commandName(site, summary.name),
203
+ path: summary.path ?? null,
204
+ reusability: opts?.reusability ?? 'unverified-candidate',
205
+ },
206
+ };
207
+ }
208
+ // ── Verification ──────────────────────────────────────────────────────────────
209
+ async function verifyCandidate(page, candidate, expectedFields) {
210
+ try {
211
+ const result = await executePipeline(page, candidate.pipeline, {
212
+ args: buildDefaultArgs(candidate),
213
+ });
214
+ return assessResult(result, expectedFields);
215
+ }
216
+ catch (error) {
217
+ if (error instanceof BrowserConnectError) {
218
+ return { ok: false, terminal: 'blocked', reason: 'execution-environment-unavailable', issue: getErrorMessage(error) };
219
+ }
220
+ if (error instanceof AuthRequiredError) {
221
+ return { ok: false, terminal: 'blocked', reason: 'auth-too-complex', issue: getErrorMessage(error) };
222
+ }
223
+ if (error instanceof SelectorError) {
224
+ return { ok: false, terminal: 'needs-human-check', escalationReason: 'selector-mismatch', issue: getErrorMessage(error) };
225
+ }
226
+ if (error instanceof TimeoutError) {
227
+ return { ok: false, terminal: 'needs-human-check', escalationReason: 'timeout', issue: getErrorMessage(error) };
228
+ }
229
+ if (error instanceof CommandExecutionError) {
230
+ return { ok: false, terminal: 'needs-human-check', escalationReason: 'verify-inconclusive', issue: getErrorMessage(error) };
231
+ }
232
+ return { ok: false, terminal: 'needs-human-check', escalationReason: 'verify-inconclusive', issue: getErrorMessage(error) };
233
+ }
234
+ }
235
+ async function probeCandidateStrategy(page, endpointUrl) {
236
+ const { cascadeProbe } = await import('./cascade.js');
237
+ const result = await cascadeProbe(page, endpointUrl, { maxStrategy: Strategy.COOKIE });
238
+ const success = result.probes.find((probe) => probe.success);
239
+ return parseSupportedStrategy(success?.strategy);
240
+ }
241
+ // ── Artifact persistence ──────────────────────────────────────────────────────
242
+ function candidateToTs(candidate) {
243
+ const strategyMap = {
244
+ public: 'Strategy.PUBLIC',
245
+ cookie: 'Strategy.COOKIE',
246
+ header: 'Strategy.HEADER',
247
+ intercept: 'Strategy.INTERCEPT',
248
+ ui: 'Strategy.UI',
249
+ };
250
+ const stratEnum = strategyMap[candidate.strategy?.toLowerCase()] ?? 'Strategy.COOKIE';
251
+ const browser = detectBrowserFlag(candidate);
252
+ const argsArray = Object.entries(candidate.args ?? {}).map(([name, def]) => {
253
+ const parts = [`name: '${name}'`];
254
+ if (def.type && def.type !== 'str')
255
+ parts.push(`type: '${def.type}'`);
256
+ if (def.required)
257
+ parts.push('required: true');
258
+ if (def.default !== undefined)
259
+ parts.push(`default: ${JSON.stringify(def.default)}`);
260
+ if (def.description)
261
+ parts.push(`help: '${def.description.replace(/'/g, "\\'")}'`);
262
+ return ` { ${parts.join(', ')} }`;
263
+ });
264
+ const formatStepValue = (v) => {
265
+ if (typeof v === 'string') {
266
+ if (v.includes('\n') || v.includes("'")) {
267
+ return '`' + v.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${') + '`';
268
+ }
269
+ return `'${v.replace(/\\/g, '\\\\')}'`;
270
+ }
271
+ if (typeof v === 'number' || typeof v === 'boolean')
272
+ return String(v);
273
+ if (v === null || v === undefined)
274
+ return 'undefined';
275
+ if (Array.isArray(v))
276
+ return `[${v.map(formatStepValue).join(', ')}]`;
277
+ if (typeof v === 'object') {
278
+ const entries = Object.entries(v);
279
+ const items = entries.map(([k, val]) => `${k}: ${formatStepValue(val)}`);
280
+ return `{ ${items.join(', ')} }`;
281
+ }
282
+ return String(v);
283
+ };
284
+ const pipelineSteps = (candidate.pipeline ?? []).map((step) => {
285
+ const entries = Object.entries(step);
286
+ if (entries.length === 1) {
287
+ const [op, value] = entries[0];
288
+ return ` { ${op}: ${formatStepValue(value)} }`;
289
+ }
290
+ return ` ${formatStepValue(step)}`;
291
+ });
292
+ const lines = [];
293
+ lines.push("import { cli, Strategy } from '@jackwener/opencli/registry';");
294
+ lines.push('');
295
+ lines.push('cli({');
296
+ lines.push(` site: '${candidate.site}',`);
297
+ lines.push(` name: '${candidate.name}',`);
298
+ if (candidate.description)
299
+ lines.push(` description: '${candidate.description.replace(/'/g, "\\'")}',`);
300
+ if (candidate.domain)
301
+ lines.push(` domain: '${candidate.domain}',`);
302
+ lines.push(` strategy: ${stratEnum},`);
303
+ lines.push(` browser: ${browser},`);
304
+ if (argsArray.length > 0) {
305
+ lines.push(` args: [`);
306
+ lines.push(argsArray.join(',\n') + ',');
307
+ lines.push(' ],');
308
+ }
309
+ if (candidate.columns?.length) {
310
+ lines.push(` columns: [${candidate.columns.map(c => `'${c}'`).join(', ')}],`);
311
+ }
312
+ if (pipelineSteps.length > 0) {
313
+ lines.push(' pipeline: [');
314
+ lines.push(pipelineSteps.join(',\n') + ',');
315
+ lines.push(' ],');
316
+ }
317
+ lines.push('});');
318
+ lines.push('');
319
+ return lines.join('\n');
320
+ }
321
+ async function registerVerifiedAdapter(candidate, metadata) {
322
+ const siteDir = path.join(USER_CLIS_DIR, candidate.site);
323
+ const adapterPath = path.join(siteDir, `${candidate.name}.ts`);
324
+ const metadataPath = path.join(siteDir, `${candidate.name}.meta.json`);
325
+ await fs.promises.mkdir(siteDir, { recursive: true });
326
+ await fs.promises.writeFile(adapterPath, candidateToTs(candidate));
327
+ await fs.promises.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
328
+ registerCommand(candidateToCommand(candidate, adapterPath));
329
+ return { adapterPath, metadataPath };
330
+ }
331
+ async function writeVerifiedArtifact(candidate, exploreDir, metadata) {
332
+ const outDir = path.join(exploreDir, 'verified');
333
+ const adapterPath = path.join(outDir, `${candidate.name}.verified.ts`);
334
+ const metadataPath = path.join(outDir, `${candidate.name}.verified.meta.json`);
335
+ await fs.promises.mkdir(outDir, { recursive: true });
336
+ await fs.promises.writeFile(adapterPath, candidateToTs(candidate));
337
+ await fs.promises.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
338
+ return { adapterPath, metadataPath };
339
+ }
340
+ // ── Session error classification ──────────────────────────────────────────────
341
+ function classifySessionError(error, summary, stats, site) {
342
+ if (error instanceof BrowserConnectError) {
343
+ return {
344
+ status: 'blocked',
345
+ reason: 'execution-environment-unavailable',
346
+ stage: 'verify',
347
+ confidence: 'high',
348
+ message: getErrorMessage(error),
349
+ stats,
350
+ };
351
+ }
352
+ if (error instanceof AuthRequiredError) {
353
+ return {
354
+ status: 'blocked',
355
+ reason: 'auth-too-complex',
356
+ stage: 'verify',
357
+ confidence: 'high',
358
+ message: getErrorMessage(error),
359
+ stats,
360
+ };
361
+ }
362
+ return {
363
+ status: 'needs-human-check',
364
+ escalation: buildEscalation('verify', 'verify-inconclusive', summary, site, {
365
+ reusability: 'unverified-candidate',
366
+ confidence: 'low',
367
+ }),
368
+ reusability: 'unverified-candidate',
369
+ message: getErrorMessage(error),
370
+ stats,
371
+ };
372
+ }
373
+ // ── Main orchestrator ─────────────────────────────────────────────────────────
374
+ export async function generateVerifiedFromUrl(opts) {
375
+ const normalizedGoal = normalizeGoal(opts.goal) ?? opts.goal ?? undefined;
376
+ const exploreResult = await exploreUrl(opts.url, {
377
+ BrowserFactory: opts.BrowserFactory,
378
+ site: opts.site,
379
+ goal: normalizedGoal,
380
+ waitSeconds: opts.waitSeconds ?? 3,
381
+ workspace: opts.workspace,
382
+ });
383
+ const bundle = loadExploreBundle(exploreResult.out_dir);
384
+ const synthesizeResult = synthesizeFromExplore(exploreResult.out_dir, { top: opts.top ?? 3 });
385
+ const selected = selectCandidate(synthesizeResult.candidates ?? [], opts.goal);
386
+ const baseStats = buildStats({
387
+ endpointCount: exploreResult.endpoint_count,
388
+ apiEndpointCount: exploreResult.api_endpoint_count,
389
+ candidateCount: synthesizeResult.candidate_count,
390
+ exploreDir: exploreResult.out_dir,
391
+ });
392
+ // ── Early hint: explore result ──────────────────────────────────────────
393
+ if (exploreResult.api_endpoint_count === 0) {
394
+ opts.onEarlyHint?.({
395
+ stage: 'explore',
396
+ continue: false,
397
+ reason: 'no-viable-api-surface',
398
+ confidence: 'high',
399
+ });
400
+ return {
401
+ status: 'blocked',
402
+ reason: 'no-viable-api-surface',
403
+ stage: 'explore',
404
+ confidence: 'high',
405
+ message: 'No JSON API endpoints discovered on this site.',
406
+ stats: baseStats,
407
+ };
408
+ }
409
+ opts.onEarlyHint?.({
410
+ stage: 'explore',
411
+ continue: true,
412
+ reason: 'api-surface-looks-viable',
413
+ confidence: 'medium',
414
+ });
415
+ // ── Early hint: synthesize result ───────────────────────────────────────
416
+ if (!selected || synthesizeResult.candidate_count === 0) {
417
+ opts.onEarlyHint?.({
418
+ stage: 'synthesize',
419
+ continue: false,
420
+ reason: 'no-viable-candidate',
421
+ confidence: 'high',
422
+ });
423
+ return {
424
+ status: 'blocked',
425
+ reason: 'no-viable-candidate',
426
+ stage: 'synthesize',
427
+ confidence: 'high',
428
+ message: 'No candidate met the quality threshold for verification.',
429
+ stats: baseStats,
430
+ };
431
+ }
432
+ const context = {
433
+ capability: bundle.capabilities.find((capability) => capability.name === selected.name),
434
+ endpoint: chooseEndpoint(bundle.capabilities.find((capability) => capability.name === selected.name), bundle.endpoints),
435
+ };
436
+ if (!context.endpoint) {
437
+ opts.onEarlyHint?.({
438
+ stage: 'synthesize',
439
+ continue: false,
440
+ reason: 'no-viable-candidate',
441
+ confidence: 'medium',
442
+ });
443
+ return {
444
+ status: 'blocked',
445
+ reason: 'no-viable-candidate',
446
+ stage: 'synthesize',
447
+ confidence: 'medium',
448
+ message: 'No endpoint could be matched to the selected candidate.',
449
+ stats: baseStats,
450
+ };
451
+ }
452
+ const expectedFields = Object.keys(context.endpoint.detectedFields ?? {});
453
+ const originalCandidate = readCandidateJson(selected.path);
454
+ const unsupportedArgs = getUnsupportedVerificationArgs(originalCandidate);
455
+ // ── Escalation: unsupported required args ───────────────────────────────
456
+ // Note: unsupported-required-args goes directly to P1 terminal.
457
+ // No P2 hint is emitted — this is a P1-only decision per design guardrail.
458
+ if (unsupportedArgs.length > 0) {
459
+ return {
460
+ status: 'needs-human-check',
461
+ escalation: buildEscalation('synthesize', 'unsupported-required-args', selected, bundle.manifest.site, {
462
+ reusability: 'unverified-candidate',
463
+ confidence: 'high',
464
+ }),
465
+ reusability: 'unverified-candidate',
466
+ message: `Auto-verification does not support required args: ${unsupportedArgs.join(', ')}`,
467
+ stats: baseStats,
468
+ };
469
+ }
470
+ opts.onEarlyHint?.({
471
+ stage: 'synthesize',
472
+ continue: true,
473
+ reason: 'candidate-ready-for-verify',
474
+ confidence: 'high',
475
+ candidate: {
476
+ name: selected.name,
477
+ command: commandName(bundle.manifest.site, selected.name),
478
+ path: selected.path ?? null,
479
+ reusability: 'unverified-candidate',
480
+ },
481
+ });
482
+ // ── Phase 3: single browser session (probe + verify + repair) ───────────
483
+ try {
484
+ return await browserSession(opts.BrowserFactory, async (page) => {
485
+ await page.goto(bundle.manifest.final_url ?? bundle.manifest.target_url);
486
+ // ── Probe: candidate-bound strategy ─────────────────────────────────
487
+ const bestStrategy = await probeCandidateStrategy(page, context.endpoint.url);
488
+ if (!bestStrategy) {
489
+ opts.onEarlyHint?.({
490
+ stage: 'cascade',
491
+ continue: false,
492
+ reason: 'auth-too-complex',
493
+ confidence: 'high',
494
+ });
495
+ return {
496
+ status: 'blocked',
497
+ reason: 'auth-too-complex',
498
+ stage: 'cascade',
499
+ confidence: 'high',
500
+ message: 'No PUBLIC or COOKIE strategy succeeded for this endpoint.',
501
+ stats: baseStats,
502
+ };
503
+ }
504
+ opts.onEarlyHint?.({
505
+ stage: 'cascade',
506
+ continue: true,
507
+ reason: 'candidate-ready-for-verify',
508
+ confidence: 'high',
509
+ candidate: {
510
+ name: selected.name,
511
+ command: commandName(bundle.manifest.site, selected.name),
512
+ path: selected.path ?? null,
513
+ reusability: 'unverified-candidate',
514
+ },
515
+ });
516
+ const candidate = applyStrategy(originalCandidate, bestStrategy);
517
+ const goalStr = normalizedGoal ?? opts.goal ?? null;
518
+ const buildMetadata = () => ({
519
+ artifact_kind: 'verified',
520
+ schema_version: 1,
521
+ source_url: opts.url,
522
+ goal: goalStr,
523
+ strategy: bestStrategy,
524
+ verified: true,
525
+ reusable: true,
526
+ reusability_reason: 'verified-artifact',
527
+ });
528
+ // ── First verify attempt ────────────────────────────────────────────
529
+ const firstAttempt = await verifyCandidate(page, candidate, expectedFields);
530
+ if (firstAttempt.ok) {
531
+ const artifact = opts.noRegister
532
+ ? await writeVerifiedArtifact(candidate, exploreResult.out_dir, buildMetadata())
533
+ : await registerVerifiedAdapter(candidate, buildMetadata());
534
+ return {
535
+ status: 'success',
536
+ adapter: {
537
+ site: candidate.site,
538
+ name: candidate.name,
539
+ command: commandName(candidate.site, candidate.name),
540
+ strategy: bestStrategy,
541
+ path: artifact.adapterPath,
542
+ metadata_path: artifact.metadataPath,
543
+ reusability: 'verified-artifact',
544
+ },
545
+ reusability: 'verified-artifact',
546
+ stats: buildStats({
547
+ endpointCount: exploreResult.endpoint_count,
548
+ apiEndpointCount: exploreResult.api_endpoint_count,
549
+ candidateCount: synthesizeResult.candidate_count,
550
+ verified: true,
551
+ repairAttempted: false,
552
+ exploreDir: exploreResult.out_dir,
553
+ }),
554
+ };
555
+ }
556
+ // ── Terminal from first attempt ─────────────────────────────────────
557
+ if ('terminal' in firstAttempt) {
558
+ if (firstAttempt.terminal === 'blocked') {
559
+ return {
560
+ status: 'blocked',
561
+ reason: firstAttempt.reason ?? 'execution-environment-unavailable',
562
+ stage: 'verify',
563
+ confidence: 'high',
564
+ message: firstAttempt.issue,
565
+ stats: baseStats,
566
+ };
567
+ }
568
+ return {
569
+ status: 'needs-human-check',
570
+ escalation: buildEscalation('verify', firstAttempt.escalationReason ?? 'verify-inconclusive', selected, bundle.manifest.site, { reusability: 'unverified-candidate', confidence: 'medium' }),
571
+ reusability: 'unverified-candidate',
572
+ message: firstAttempt.issue,
573
+ stats: baseStats,
574
+ };
575
+ }
576
+ // ── Bounded repair: itemPath relocation ─────────────────────────────
577
+ const repaired = firstAttempt.reason === 'empty-result'
578
+ ? withItemPath(candidate, context.endpoint?.itemPath ?? null)
579
+ : null;
580
+ if (!repaired) {
581
+ const escalationReason = mapVerifyFailureToEscalation(firstAttempt.reason);
582
+ return {
583
+ status: 'needs-human-check',
584
+ escalation: buildEscalation('verify', escalationReason, selected, bundle.manifest.site, {
585
+ reusability: 'unverified-candidate',
586
+ confidence: 'medium',
587
+ }),
588
+ reusability: 'unverified-candidate',
589
+ message: `Verification failed: ${firstAttempt.reason}`,
590
+ stats: buildStats({
591
+ endpointCount: exploreResult.endpoint_count,
592
+ apiEndpointCount: exploreResult.api_endpoint_count,
593
+ candidateCount: synthesizeResult.candidate_count,
594
+ repairAttempted: firstAttempt.reason === 'empty-result',
595
+ exploreDir: exploreResult.out_dir,
596
+ }),
597
+ };
598
+ }
599
+ // ── Second verify attempt (after repair) ───────────────────────────
600
+ const secondAttempt = await verifyCandidate(page, repaired, expectedFields);
601
+ const repairedStats = buildStats({
602
+ endpointCount: exploreResult.endpoint_count,
603
+ apiEndpointCount: exploreResult.api_endpoint_count,
604
+ candidateCount: synthesizeResult.candidate_count,
605
+ repairAttempted: true,
606
+ exploreDir: exploreResult.out_dir,
607
+ });
608
+ if (secondAttempt.ok) {
609
+ const artifact = opts.noRegister
610
+ ? await writeVerifiedArtifact(repaired, exploreResult.out_dir, buildMetadata())
611
+ : await registerVerifiedAdapter(repaired, buildMetadata());
612
+ return {
613
+ status: 'success',
614
+ adapter: {
615
+ site: repaired.site,
616
+ name: repaired.name,
617
+ command: commandName(repaired.site, repaired.name),
618
+ strategy: bestStrategy,
619
+ path: artifact.adapterPath,
620
+ metadata_path: artifact.metadataPath,
621
+ reusability: 'verified-artifact',
622
+ },
623
+ reusability: 'verified-artifact',
624
+ stats: { ...repairedStats, verified: true },
625
+ };
626
+ }
627
+ if ('terminal' in secondAttempt) {
628
+ if (secondAttempt.terminal === 'blocked') {
629
+ return {
630
+ status: 'blocked',
631
+ reason: secondAttempt.reason ?? 'execution-environment-unavailable',
632
+ stage: 'fallback',
633
+ confidence: 'high',
634
+ message: secondAttempt.issue,
635
+ stats: repairedStats,
636
+ };
637
+ }
638
+ return {
639
+ status: 'needs-human-check',
640
+ escalation: buildEscalation('fallback', secondAttempt.escalationReason ?? 'verify-inconclusive', selected, bundle.manifest.site, { reusability: 'unverified-candidate', confidence: 'low' }),
641
+ reusability: 'unverified-candidate',
642
+ message: secondAttempt.issue,
643
+ stats: repairedStats,
644
+ };
645
+ }
646
+ // ── Repair exhausted ────────────────────────────────────────────────
647
+ const escalationReason = mapVerifyFailureToEscalation(secondAttempt.reason);
648
+ return {
649
+ status: 'needs-human-check',
650
+ escalation: buildEscalation('fallback', escalationReason, selected, bundle.manifest.site, {
651
+ reusability: 'unverified-candidate',
652
+ confidence: 'low',
653
+ }),
654
+ reusability: 'unverified-candidate',
655
+ message: `Repair exhausted: ${secondAttempt.reason}`,
656
+ stats: repairedStats,
657
+ };
658
+ }, { workspace: opts.workspace });
659
+ }
660
+ catch (error) {
661
+ return classifySessionError(error, selected, baseStats, bundle.manifest.site);
662
+ }
663
+ }
664
+ // ── Render ────────────────────────────────────────────────────────────────────
665
+ export function renderGenerateVerifiedSummary(result) {
666
+ const lines = [
667
+ `opencli generate: ${result.status.toUpperCase()}`,
668
+ ];
669
+ if (result.status === 'success' && result.adapter) {
670
+ lines.push(`Command: ${result.adapter.command}`);
671
+ lines.push(`Strategy: ${result.adapter.strategy}`);
672
+ lines.push(`Path: ${result.adapter.path}`);
673
+ }
674
+ else if (result.status === 'blocked') {
675
+ lines.push(`Reason: ${result.reason}`);
676
+ lines.push(`Stage: ${result.stage}`);
677
+ lines.push(`Confidence: ${result.confidence}`);
678
+ if (result.message)
679
+ lines.push(`Message: ${result.message}`);
680
+ }
681
+ else if (result.status === 'needs-human-check' && result.escalation) {
682
+ lines.push(`Stage: ${result.escalation.stage}`);
683
+ lines.push(`Reason: ${result.escalation.reason}`);
684
+ lines.push(`Suggested action: ${result.escalation.suggested_action}`);
685
+ lines.push(`Candidate: ${result.escalation.candidate.command}`);
686
+ lines.push(`Reusability: ${result.escalation.candidate.reusability}`);
687
+ if (result.message)
688
+ lines.push(`Message: ${result.message}`);
689
+ }
690
+ lines.push('');
691
+ lines.push(`Explore: ${result.stats.endpoint_count} endpoints, ${result.stats.api_endpoint_count} API`);
692
+ lines.push(`Candidates: ${result.stats.candidate_count}`);
693
+ lines.push(`Verified: ${result.stats.verified ? 'yes' : 'no'}`);
694
+ lines.push(`Repair attempted: ${result.stats.repair_attempted ? 'yes' : 'no'}`);
695
+ return lines.join('\n');
696
+ }