@zenalexa/unicli 0.212.1 → 0.213.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1229) hide show
  1. package/AGENTS.md +129 -10
  2. package/README.md +12 -4
  3. package/README.zh-CN.md +340 -0
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +13 -257
  6. package/dist/cli.js.map +1 -1
  7. package/dist/commands/dev.d.ts.map +1 -1
  8. package/dist/commands/dev.js +8 -3
  9. package/dist/commands/dev.js.map +1 -1
  10. package/dist/commands/dispatch.d.ts +14 -0
  11. package/dist/commands/dispatch.d.ts.map +1 -0
  12. package/dist/commands/dispatch.js +258 -0
  13. package/dist/commands/dispatch.js.map +1 -0
  14. package/dist/commands/ext.d.ts.map +1 -1
  15. package/dist/commands/ext.js +8 -6
  16. package/dist/commands/ext.js.map +1 -1
  17. package/dist/commands/health.d.ts.map +1 -1
  18. package/dist/commands/health.js +20 -22
  19. package/dist/commands/health.js.map +1 -1
  20. package/dist/commands/lint.d.ts +1 -0
  21. package/dist/commands/lint.d.ts.map +1 -1
  22. package/dist/commands/lint.js +14 -59
  23. package/dist/commands/lint.js.map +1 -1
  24. package/dist/commands/migrate-schema.d.ts.map +1 -1
  25. package/dist/commands/migrate-schema.js +19 -1
  26. package/dist/commands/migrate-schema.js.map +1 -1
  27. package/dist/commands/search.d.ts.map +1 -1
  28. package/dist/commands/search.js +7 -1
  29. package/dist/commands/search.js.map +1 -1
  30. package/dist/commands/skills.js +1 -1
  31. package/dist/commands/skills.js.map +1 -1
  32. package/dist/commands/usage.d.ts.map +1 -1
  33. package/dist/commands/usage.js +20 -19
  34. package/dist/commands/usage.js.map +1 -1
  35. package/dist/core/schema-v2.d.ts +8 -0
  36. package/dist/core/schema-v2.d.ts.map +1 -1
  37. package/dist/core/schema-v2.js +12 -0
  38. package/dist/core/schema-v2.js.map +1 -1
  39. package/dist/discovery/loader.d.ts.map +1 -1
  40. package/dist/discovery/loader.js +1 -0
  41. package/dist/discovery/loader.js.map +1 -1
  42. package/dist/engine/executor.d.ts +77 -0
  43. package/dist/engine/executor.d.ts.map +1 -0
  44. package/dist/engine/executor.js +189 -0
  45. package/dist/engine/executor.js.map +1 -0
  46. package/dist/engine/runtime.d.ts +23 -0
  47. package/dist/engine/runtime.d.ts.map +1 -0
  48. package/dist/engine/runtime.js +167 -0
  49. package/dist/engine/runtime.js.map +1 -0
  50. package/dist/engine/ssrf.d.ts +18 -0
  51. package/dist/engine/ssrf.d.ts.map +1 -0
  52. package/dist/engine/ssrf.js +55 -0
  53. package/dist/engine/ssrf.js.map +1 -0
  54. package/dist/engine/step-registry.d.ts +16 -0
  55. package/dist/engine/step-registry.d.ts.map +1 -0
  56. package/dist/engine/step-registry.js +25 -0
  57. package/dist/engine/step-registry.js.map +1 -0
  58. package/dist/engine/steps/append.d.ts +3 -0
  59. package/dist/engine/steps/append.d.ts.map +1 -0
  60. package/dist/engine/steps/append.js +20 -0
  61. package/dist/engine/steps/append.js.map +1 -0
  62. package/dist/engine/steps/assert.d.ts +10 -0
  63. package/dist/engine/steps/assert.d.ts.map +1 -0
  64. package/dist/engine/steps/assert.js +58 -0
  65. package/dist/engine/steps/assert.js.map +1 -0
  66. package/dist/engine/steps/browser-helpers.d.ts +13 -0
  67. package/dist/engine/steps/browser-helpers.d.ts.map +1 -0
  68. package/dist/engine/steps/browser-helpers.js +85 -0
  69. package/dist/engine/steps/browser-helpers.js.map +1 -0
  70. package/dist/engine/steps/click.d.ts +9 -0
  71. package/dist/engine/steps/click.d.ts.map +1 -0
  72. package/dist/engine/steps/click.js +32 -0
  73. package/dist/engine/steps/click.js.map +1 -0
  74. package/dist/engine/steps/cua.d.ts +5 -17
  75. package/dist/engine/steps/cua.d.ts.map +1 -1
  76. package/dist/engine/steps/cua.js +5 -17
  77. package/dist/engine/steps/cua.js.map +1 -1
  78. package/dist/engine/steps/desktop-ax.d.ts +3 -10
  79. package/dist/engine/steps/desktop-ax.d.ts.map +1 -1
  80. package/dist/engine/steps/desktop-ax.js +3 -10
  81. package/dist/engine/steps/desktop-ax.js.map +1 -1
  82. package/dist/engine/steps/download.d.ts +12 -9
  83. package/dist/engine/steps/download.d.ts.map +1 -1
  84. package/dist/engine/steps/download.js +58 -9
  85. package/dist/engine/steps/download.js.map +1 -1
  86. package/dist/engine/steps/each.d.ts +9 -0
  87. package/dist/engine/steps/each.d.ts.map +1 -0
  88. package/dist/engine/steps/each.js +44 -0
  89. package/dist/engine/steps/each.js.map +1 -0
  90. package/dist/engine/steps/evaluate.d.ts +6 -0
  91. package/dist/engine/steps/evaluate.d.ts.map +1 -0
  92. package/dist/engine/steps/evaluate.js +13 -0
  93. package/dist/engine/steps/evaluate.js.map +1 -0
  94. package/dist/engine/steps/exec.d.ts +12 -0
  95. package/dist/engine/steps/exec.d.ts.map +1 -0
  96. package/dist/engine/steps/exec.js +161 -0
  97. package/dist/engine/steps/exec.js.map +1 -0
  98. package/dist/engine/steps/extract.d.ts +14 -0
  99. package/dist/engine/steps/extract.d.ts.map +1 -0
  100. package/dist/engine/steps/extract.js +51 -0
  101. package/dist/engine/steps/extract.js.map +1 -0
  102. package/dist/engine/steps/fetch-text.d.ts +4 -0
  103. package/dist/engine/steps/fetch-text.d.ts.map +1 -0
  104. package/dist/engine/steps/fetch-text.js +53 -0
  105. package/dist/engine/steps/fetch-text.js.map +1 -0
  106. package/dist/engine/steps/fetch.d.ts +12 -9
  107. package/dist/engine/steps/fetch.d.ts.map +1 -1
  108. package/dist/engine/steps/fetch.js +185 -9
  109. package/dist/engine/steps/fetch.js.map +1 -1
  110. package/dist/engine/steps/filter.d.ts +3 -0
  111. package/dist/engine/steps/filter.d.ts.map +1 -0
  112. package/dist/engine/steps/filter.js +14 -0
  113. package/dist/engine/steps/filter.js.map +1 -0
  114. package/dist/engine/steps/html-to-md.d.ts +3 -0
  115. package/dist/engine/steps/html-to-md.d.ts.map +1 -0
  116. package/dist/engine/steps/html-to-md.js +13 -0
  117. package/dist/engine/steps/html-to-md.js.map +1 -0
  118. package/dist/engine/steps/if.d.ts +8 -0
  119. package/dist/engine/steps/if.d.ts.map +1 -0
  120. package/dist/engine/steps/if.js +36 -0
  121. package/dist/engine/steps/if.js.map +1 -0
  122. package/dist/engine/steps/index.d.ts +33 -14
  123. package/dist/engine/steps/index.d.ts.map +1 -1
  124. package/dist/engine/steps/index.js +33 -14
  125. package/dist/engine/steps/index.js.map +1 -1
  126. package/dist/engine/steps/intercept.d.ts +12 -0
  127. package/dist/engine/steps/intercept.d.ts.map +1 -0
  128. package/dist/engine/steps/intercept.js +67 -0
  129. package/dist/engine/steps/intercept.js.map +1 -0
  130. package/dist/engine/steps/limit.d.ts +3 -0
  131. package/dist/engine/steps/limit.d.ts.map +1 -0
  132. package/dist/engine/steps/limit.js +17 -0
  133. package/dist/engine/steps/limit.js.map +1 -0
  134. package/dist/engine/steps/map.d.ts +3 -0
  135. package/dist/engine/steps/map.d.ts.map +1 -0
  136. package/dist/engine/steps/map.js +20 -0
  137. package/dist/engine/steps/map.js.map +1 -0
  138. package/dist/engine/steps/navigate.d.ts +8 -0
  139. package/dist/engine/steps/navigate.d.ts.map +1 -0
  140. package/dist/engine/steps/navigate.js +15 -0
  141. package/dist/engine/steps/navigate.js.map +1 -0
  142. package/dist/engine/steps/parallel.d.ts +4 -0
  143. package/dist/engine/steps/parallel.d.ts.map +1 -0
  144. package/dist/engine/steps/parallel.js +56 -0
  145. package/dist/engine/steps/parallel.js.map +1 -0
  146. package/dist/engine/steps/parse-rss.d.ts +6 -0
  147. package/dist/engine/steps/parse-rss.d.ts.map +1 -0
  148. package/dist/engine/steps/parse-rss.js +57 -0
  149. package/dist/engine/steps/parse-rss.js.map +1 -0
  150. package/dist/engine/steps/press.d.ts +3 -0
  151. package/dist/engine/steps/press.d.ts.map +1 -0
  152. package/dist/engine/steps/press.js +22 -0
  153. package/dist/engine/steps/press.js.map +1 -0
  154. package/dist/engine/steps/scroll.d.ts +3 -0
  155. package/dist/engine/steps/scroll.d.ts.map +1 -0
  156. package/dist/engine/steps/scroll.js +26 -0
  157. package/dist/engine/steps/scroll.js.map +1 -0
  158. package/dist/engine/steps/select.d.ts +3 -0
  159. package/dist/engine/steps/select.d.ts.map +1 -0
  160. package/dist/engine/steps/select.js +21 -0
  161. package/dist/engine/steps/select.js.map +1 -0
  162. package/dist/engine/steps/set.d.ts +3 -0
  163. package/dist/engine/steps/set.d.ts.map +1 -0
  164. package/dist/engine/steps/set.js +13 -0
  165. package/dist/engine/steps/set.js.map +1 -0
  166. package/dist/engine/steps/snapshot.d.ts +3 -0
  167. package/dist/engine/steps/snapshot.d.ts.map +1 -0
  168. package/dist/engine/steps/snapshot.js +17 -0
  169. package/dist/engine/steps/snapshot.js.map +1 -0
  170. package/dist/engine/steps/sort.d.ts +7 -0
  171. package/dist/engine/steps/sort.d.ts.map +1 -0
  172. package/dist/engine/steps/sort.js +21 -0
  173. package/dist/engine/steps/sort.js.map +1 -0
  174. package/dist/engine/steps/tap.d.ts +12 -0
  175. package/dist/engine/steps/tap.d.ts.map +1 -0
  176. package/dist/engine/steps/tap.js +110 -0
  177. package/dist/engine/steps/tap.js.map +1 -0
  178. package/dist/engine/steps/type.d.ts +8 -0
  179. package/dist/engine/steps/type.d.ts.map +1 -0
  180. package/dist/engine/steps/type.js +20 -0
  181. package/dist/engine/steps/type.js.map +1 -0
  182. package/dist/engine/steps/wait.d.ts +8 -0
  183. package/dist/engine/steps/wait.d.ts.map +1 -0
  184. package/dist/engine/steps/wait.js +17 -0
  185. package/dist/engine/steps/wait.js.map +1 -0
  186. package/dist/engine/steps/websocket.d.ts +3 -9
  187. package/dist/engine/steps/websocket.d.ts.map +1 -1
  188. package/dist/engine/steps/websocket.js +13 -9
  189. package/dist/engine/steps/websocket.js.map +1 -1
  190. package/dist/engine/steps/write-temp.d.ts +7 -0
  191. package/dist/engine/steps/write-temp.d.ts.map +1 -0
  192. package/dist/engine/steps/write-temp.js +19 -0
  193. package/dist/engine/steps/write-temp.js.map +1 -0
  194. package/dist/engine/template.d.ts +42 -0
  195. package/dist/engine/template.d.ts.map +1 -0
  196. package/dist/engine/template.js +383 -0
  197. package/dist/engine/template.js.map +1 -0
  198. package/dist/engine/yaml-runner.d.ts +9 -258
  199. package/dist/engine/yaml-runner.d.ts.map +1 -1
  200. package/dist/engine/yaml-runner.js +15 -2173
  201. package/dist/engine/yaml-runner.js.map +1 -1
  202. package/dist/errors.d.ts +17 -0
  203. package/dist/errors.d.ts.map +1 -0
  204. package/dist/errors.js +16 -0
  205. package/dist/errors.js.map +1 -0
  206. package/dist/manifest.json +1 -1
  207. package/dist/mcp/server.js +1 -1
  208. package/dist/mcp/server.js.map +1 -1
  209. package/dist/mcp/streamable-http.d.ts +1 -1
  210. package/dist/mcp/streamable-http.d.ts.map +1 -1
  211. package/dist/mcp/streamable-http.js +9 -3
  212. package/dist/mcp/streamable-http.js.map +1 -1
  213. package/dist/output/envelope.d.ts +83 -0
  214. package/dist/output/envelope.d.ts.map +1 -0
  215. package/dist/output/envelope.js +96 -0
  216. package/dist/output/envelope.js.map +1 -0
  217. package/dist/output/formatter.d.ts +30 -12
  218. package/dist/output/formatter.d.ts.map +1 -1
  219. package/dist/output/formatter.js +143 -75
  220. package/dist/output/formatter.js.map +1 -1
  221. package/dist/output/md.d.ts +20 -0
  222. package/dist/output/md.d.ts.map +1 -0
  223. package/dist/output/md.js +300 -0
  224. package/dist/output/md.js.map +1 -0
  225. package/dist/protocol/acp.js +1 -1
  226. package/dist/protocol/acp.js.map +1 -1
  227. package/dist/transport/adapters/http.d.ts +5 -5
  228. package/dist/transport/adapters/http.js +6 -6
  229. package/dist/transport/adapters/http.js.map +1 -1
  230. package/dist/transport/bus.d.ts +47 -1
  231. package/dist/transport/bus.d.ts.map +1 -1
  232. package/dist/transport/bus.js +68 -0
  233. package/dist/transport/bus.js.map +1 -1
  234. package/dist/types.d.ts +8 -0
  235. package/dist/types.d.ts.map +1 -1
  236. package/dist/types.js.map +1 -1
  237. package/package.json +102 -4
  238. package/src/adapters/1688/item.yaml +3 -0
  239. package/src/adapters/1688/search.yaml +3 -0
  240. package/src/adapters/1688/store.yaml +3 -0
  241. package/src/adapters/36kr/article.yaml +3 -0
  242. package/src/adapters/36kr/hot.yaml +3 -0
  243. package/src/adapters/36kr/latest.yaml +5 -1
  244. package/src/adapters/36kr/news.yaml +3 -0
  245. package/src/adapters/36kr/search.yaml +3 -0
  246. package/src/adapters/adguardhome/add-rule.yaml +3 -0
  247. package/src/adapters/adguardhome/rules.yaml +5 -1
  248. package/src/adapters/adguardhome/stats.yaml +5 -1
  249. package/src/adapters/adguardhome/status.yaml +5 -1
  250. package/src/adapters/adguardhome/toggle.yaml +3 -0
  251. package/src/adapters/amazon/bestsellers.yaml +3 -0
  252. package/src/adapters/amazon/discussion.yaml +3 -0
  253. package/src/adapters/amazon/movers-shakers.yaml +3 -0
  254. package/src/adapters/amazon/new-releases.yaml +3 -0
  255. package/src/adapters/amazon/offer.yaml +3 -0
  256. package/src/adapters/amazon/product.yaml +3 -0
  257. package/src/adapters/amazon/rankings.yaml +3 -0
  258. package/src/adapters/amazon/search.yaml +3 -0
  259. package/src/adapters/apple-music/rate-album.yaml +5 -0
  260. package/src/adapters/apple-notes/list.yaml +3 -0
  261. package/src/adapters/apple-notes/read.yaml +3 -0
  262. package/src/adapters/apple-notes/search.yaml +3 -0
  263. package/src/adapters/apple-podcasts/episodes.yaml +3 -0
  264. package/src/adapters/apple-podcasts/search.yaml +3 -0
  265. package/src/adapters/apple-podcasts/top.yaml +5 -1
  266. package/src/adapters/arxiv/paper.test.ts +15 -0
  267. package/src/adapters/arxiv/paper.yaml +3 -0
  268. package/src/adapters/arxiv/search.test.ts +15 -0
  269. package/src/adapters/arxiv/search.yaml +3 -0
  270. package/src/adapters/arxiv/trending.yaml +5 -1
  271. package/src/adapters/audacity/convert.yaml +3 -0
  272. package/src/adapters/audacity/effects.yaml +3 -0
  273. package/src/adapters/audacity/info.yaml +3 -0
  274. package/src/adapters/audacity/mix.yaml +3 -0
  275. package/src/adapters/audacity/normalize.yaml +3 -0
  276. package/src/adapters/audacity/spectrogram.yaml +3 -0
  277. package/src/adapters/audacity/split-channels.yaml +3 -0
  278. package/src/adapters/audacity/trim.yaml +3 -0
  279. package/src/adapters/autoagent/eval-run.yaml +3 -0
  280. package/src/adapters/aws/s3-ls.yaml +3 -0
  281. package/src/adapters/az/account.yaml +5 -1
  282. package/src/adapters/baidu/hot.yaml +3 -0
  283. package/src/adapters/baidu/search.yaml +3 -0
  284. package/src/adapters/band/bands.yaml +3 -0
  285. package/src/adapters/band/mentions.yaml +3 -0
  286. package/src/adapters/band/post.yaml +3 -0
  287. package/src/adapters/band/posts.yaml +3 -0
  288. package/src/adapters/barchart/flow.yaml +3 -0
  289. package/src/adapters/barchart/greeks.yaml +3 -0
  290. package/src/adapters/barchart/options.yaml +3 -0
  291. package/src/adapters/barchart/quote.yaml +3 -0
  292. package/src/adapters/bbc/news.yaml +3 -0
  293. package/src/adapters/bbc/technology.yaml +3 -0
  294. package/src/adapters/bbc/top.yaml +3 -0
  295. package/src/adapters/bbc/world.yaml +3 -0
  296. package/src/adapters/bilibili/coin.yaml +5 -1
  297. package/src/adapters/bilibili/dynamic.test.ts +15 -0
  298. package/src/adapters/bilibili/dynamic.yaml +3 -0
  299. package/src/adapters/bilibili/favorites.test.ts +15 -0
  300. package/src/adapters/bilibili/favorites.yaml +3 -0
  301. package/src/adapters/bilibili/feed.yaml +5 -1
  302. package/src/adapters/bilibili/following.test.ts +15 -0
  303. package/src/adapters/bilibili/following.yaml +3 -0
  304. package/src/adapters/bilibili/history.yaml +5 -1
  305. package/src/adapters/bilibili/hot.test.ts +15 -0
  306. package/src/adapters/bilibili/hot.yaml +3 -0
  307. package/src/adapters/bilibili/later.yaml +5 -1
  308. package/src/adapters/bilibili/live.yaml +5 -1
  309. package/src/adapters/bilibili/me.test.ts +15 -0
  310. package/src/adapters/bilibili/me.yaml +3 -0
  311. package/src/adapters/bilibili/ranking.yaml +5 -1
  312. package/src/adapters/bilibili/trending.test.ts +15 -0
  313. package/src/adapters/bilibili/trending.yaml +3 -0
  314. package/src/adapters/binance/hot.yaml +3 -0
  315. package/src/adapters/binance/kline.yaml +3 -0
  316. package/src/adapters/binance/ticker.yaml +3 -0
  317. package/src/adapters/blender/animation.yaml +3 -0
  318. package/src/adapters/blender/camera.yaml +3 -0
  319. package/src/adapters/blender/convert.yaml +3 -0
  320. package/src/adapters/blender/export.yaml +3 -0
  321. package/src/adapters/blender/import.yaml +3 -0
  322. package/src/adapters/blender/info.yaml +3 -0
  323. package/src/adapters/blender/lighting.yaml +3 -0
  324. package/src/adapters/blender/materials.yaml +3 -0
  325. package/src/adapters/blender/objects.yaml +3 -0
  326. package/src/adapters/blender/render.yaml +3 -0
  327. package/src/adapters/blender/scene.yaml +3 -0
  328. package/src/adapters/blender/screenshot.yaml +3 -0
  329. package/src/adapters/blender/script.yaml +3 -0
  330. package/src/adapters/bloomberg/businessweek.yaml +3 -0
  331. package/src/adapters/bloomberg/economics.yaml +3 -0
  332. package/src/adapters/bloomberg/feeds.yaml +3 -0
  333. package/src/adapters/bloomberg/industries.yaml +3 -0
  334. package/src/adapters/bloomberg/main.yaml +3 -0
  335. package/src/adapters/bloomberg/markets.yaml +3 -0
  336. package/src/adapters/bloomberg/news.yaml +3 -0
  337. package/src/adapters/bloomberg/opinions.yaml +3 -0
  338. package/src/adapters/bloomberg/politics.yaml +3 -0
  339. package/src/adapters/bloomberg/tech.yaml +3 -0
  340. package/src/adapters/bluesky/feeds.test.ts +15 -0
  341. package/src/adapters/bluesky/feeds.yaml +3 -0
  342. package/src/adapters/bluesky/followers.test.ts +15 -0
  343. package/src/adapters/bluesky/followers.yaml +3 -0
  344. package/src/adapters/bluesky/following.test.ts +15 -0
  345. package/src/adapters/bluesky/following.yaml +3 -0
  346. package/src/adapters/bluesky/likes.test.ts +15 -0
  347. package/src/adapters/bluesky/likes.yaml +3 -0
  348. package/src/adapters/bluesky/notifications.test.ts +15 -0
  349. package/src/adapters/bluesky/notifications.yaml +3 -0
  350. package/src/adapters/bluesky/post.yaml +3 -0
  351. package/src/adapters/bluesky/profile.yaml +3 -0
  352. package/src/adapters/bluesky/search.test.ts +15 -0
  353. package/src/adapters/bluesky/search.yaml +3 -0
  354. package/src/adapters/bluesky/starter-packs.test.ts +15 -0
  355. package/src/adapters/bluesky/starter-packs.yaml +3 -0
  356. package/src/adapters/bluesky/thread.test.ts +15 -0
  357. package/src/adapters/bluesky/thread.yaml +3 -0
  358. package/src/adapters/bluesky/trending.test.ts +15 -0
  359. package/src/adapters/bluesky/trending.yaml +3 -0
  360. package/src/adapters/bluesky/user.test.ts +15 -0
  361. package/src/adapters/bluesky/user.yaml +3 -0
  362. package/src/adapters/boss/batchgreet.yaml +3 -0
  363. package/src/adapters/boss/chatlist.yaml +3 -0
  364. package/src/adapters/boss/chatmsg.yaml +3 -0
  365. package/src/adapters/boss/detail.yaml +3 -0
  366. package/src/adapters/boss/exchange.yaml +3 -0
  367. package/src/adapters/boss/greet.yaml +3 -0
  368. package/src/adapters/boss/invite.yaml +3 -0
  369. package/src/adapters/boss/joblist.yaml +3 -0
  370. package/src/adapters/boss/mark.yaml +3 -0
  371. package/src/adapters/boss/recommend.yaml +3 -0
  372. package/src/adapters/boss/resume.yaml +3 -0
  373. package/src/adapters/boss/search.yaml +3 -0
  374. package/src/adapters/boss/send.yaml +3 -0
  375. package/src/adapters/boss/stats.yaml +3 -0
  376. package/src/adapters/chaoxing/assignments.yaml +3 -0
  377. package/src/adapters/chaoxing/exams.yaml +3 -0
  378. package/src/adapters/chrome/bookmarks.yaml +3 -0
  379. package/src/adapters/chrome/tabs.yaml +3 -0
  380. package/src/adapters/claude-code/version.yaml +3 -0
  381. package/src/adapters/cloudcompare/compare.yaml +3 -0
  382. package/src/adapters/cloudcompare/convert.yaml +3 -0
  383. package/src/adapters/cloudcompare/info.yaml +3 -0
  384. package/src/adapters/cloudcompare/subsample.yaml +3 -0
  385. package/src/adapters/cnki/search.yaml +3 -0
  386. package/src/adapters/cnn/technology.yaml +3 -0
  387. package/src/adapters/cnn/top.yaml +3 -0
  388. package/src/adapters/cocoapods/info.yaml +3 -0
  389. package/src/adapters/cocoapods/search.yaml +3 -0
  390. package/src/adapters/codex-cli/version.yaml +3 -0
  391. package/src/adapters/coinbase/prices.yaml +3 -0
  392. package/src/adapters/coinbase/rates.yaml +3 -0
  393. package/src/adapters/comfyui/generate.yaml +3 -0
  394. package/src/adapters/comfyui/history.yaml +3 -0
  395. package/src/adapters/comfyui/nodes.yaml +3 -0
  396. package/src/adapters/comfyui/status.yaml +3 -0
  397. package/src/adapters/coupang/add-to-cart.yaml +3 -0
  398. package/src/adapters/coupang/hot.yaml +3 -0
  399. package/src/adapters/coupang/search.yaml +3 -0
  400. package/src/adapters/crates-io/info.yaml +3 -0
  401. package/src/adapters/crates-io/search.yaml +3 -0
  402. package/src/adapters/crates-io/versions.yaml +3 -0
  403. package/src/adapters/ctrip/hot.yaml +3 -0
  404. package/src/adapters/ctrip/search.yaml +3 -0
  405. package/src/adapters/cua/bench-list.yaml +3 -0
  406. package/src/adapters/cua/bench-run.yaml +3 -0
  407. package/src/adapters/dangdang/hot.yaml +3 -0
  408. package/src/adapters/dangdang/search.yaml +3 -0
  409. package/src/adapters/deepseek/chat.yaml +3 -0
  410. package/src/adapters/deepseek/models.yaml +3 -0
  411. package/src/adapters/devto/latest.yaml +3 -0
  412. package/src/adapters/devto/search.yaml +3 -0
  413. package/src/adapters/devto/tag.yaml +3 -0
  414. package/src/adapters/devto/top.yaml +3 -0
  415. package/src/adapters/devto/user.yaml +3 -0
  416. package/src/adapters/dianping/hot.yaml +3 -0
  417. package/src/adapters/dianping/search.yaml +3 -0
  418. package/src/adapters/dictionary/examples.yaml +3 -0
  419. package/src/adapters/dictionary/search.yaml +3 -0
  420. package/src/adapters/dictionary/synonyms.yaml +3 -0
  421. package/src/adapters/dingtalk/version.yaml +3 -0
  422. package/src/adapters/docker/build.yaml +3 -0
  423. package/src/adapters/docker/images.yaml +3 -0
  424. package/src/adapters/docker/logs.yaml +3 -0
  425. package/src/adapters/docker/networks.yaml +3 -0
  426. package/src/adapters/docker/ps.yaml +3 -0
  427. package/src/adapters/docker/run.yaml +3 -0
  428. package/src/adapters/docker/volumes.yaml +3 -0
  429. package/src/adapters/docker-hub/info.yaml +3 -0
  430. package/src/adapters/docker-hub/search.yaml +3 -0
  431. package/src/adapters/docker-hub/tags.yaml +3 -0
  432. package/src/adapters/doctl/droplets.yaml +3 -0
  433. package/src/adapters/douban/book-hot.yaml +5 -1
  434. package/src/adapters/douban/download.yaml +3 -0
  435. package/src/adapters/douban/group-hot.yaml +5 -1
  436. package/src/adapters/douban/marks.yaml +3 -0
  437. package/src/adapters/douban/movie-hot.test.ts +15 -0
  438. package/src/adapters/douban/movie-hot.yaml +3 -0
  439. package/src/adapters/douban/new-movies.test.ts +15 -0
  440. package/src/adapters/douban/new-movies.yaml +3 -0
  441. package/src/adapters/douban/photos.yaml +3 -0
  442. package/src/adapters/douban/reviews.yaml +3 -0
  443. package/src/adapters/douban/search.test.ts +15 -0
  444. package/src/adapters/douban/search.yaml +3 -0
  445. package/src/adapters/douban/subject.yaml +3 -0
  446. package/src/adapters/douban/top250.yaml +5 -1
  447. package/src/adapters/douban/tv-hot.test.ts +15 -0
  448. package/src/adapters/douban/tv-hot.yaml +3 -0
  449. package/src/adapters/doubao/ask.yaml +3 -0
  450. package/src/adapters/doubao/new.yaml +3 -0
  451. package/src/adapters/doubao/status.yaml +3 -0
  452. package/src/adapters/doubao-web/ask.yaml +3 -0
  453. package/src/adapters/doubao-web/detail.yaml +3 -0
  454. package/src/adapters/doubao-web/history.yaml +3 -0
  455. package/src/adapters/doubao-web/meeting-summary.yaml +3 -0
  456. package/src/adapters/doubao-web/meeting-transcript.yaml +3 -0
  457. package/src/adapters/doubao-web/new.yaml +3 -0
  458. package/src/adapters/doubao-web/read.yaml +3 -0
  459. package/src/adapters/doubao-web/send.yaml +3 -0
  460. package/src/adapters/doubao-web/status.yaml +3 -0
  461. package/src/adapters/douyu/hot.yaml +3 -0
  462. package/src/adapters/douyu/search.yaml +3 -0
  463. package/src/adapters/drawio/export.yaml +3 -0
  464. package/src/adapters/eastmoney/fund.yaml +3 -0
  465. package/src/adapters/eastmoney/hot.yaml +5 -1
  466. package/src/adapters/eastmoney/market.yaml +5 -1
  467. package/src/adapters/eastmoney/search.yaml +3 -0
  468. package/src/adapters/ele/hot.yaml +3 -0
  469. package/src/adapters/ele/search.yaml +3 -0
  470. package/src/adapters/exchangerate/convert.yaml +3 -0
  471. package/src/adapters/exchangerate/list.yaml +5 -1
  472. package/src/adapters/facebook/add-friend.yaml +3 -0
  473. package/src/adapters/facebook/events.yaml +3 -0
  474. package/src/adapters/facebook/feed.yaml +3 -0
  475. package/src/adapters/facebook/friends.yaml +3 -0
  476. package/src/adapters/facebook/groups.yaml +3 -0
  477. package/src/adapters/facebook/join-group.yaml +3 -0
  478. package/src/adapters/facebook/marketplace.yaml +3 -0
  479. package/src/adapters/facebook/memories.yaml +3 -0
  480. package/src/adapters/facebook/notifications.yaml +3 -0
  481. package/src/adapters/facebook/post.yaml +3 -0
  482. package/src/adapters/facebook/profile.yaml +3 -0
  483. package/src/adapters/facebook/search.yaml +3 -0
  484. package/src/adapters/feishu/calendar.yaml +3 -0
  485. package/src/adapters/feishu/docs.yaml +3 -0
  486. package/src/adapters/feishu/send.yaml +3 -0
  487. package/src/adapters/feishu/tasks.yaml +3 -0
  488. package/src/adapters/ffmpeg/compress.yaml +3 -0
  489. package/src/adapters/ffmpeg/concat.yaml +3 -0
  490. package/src/adapters/ffmpeg/convert.yaml +3 -0
  491. package/src/adapters/ffmpeg/extract-audio.yaml +3 -0
  492. package/src/adapters/ffmpeg/gif.yaml +3 -0
  493. package/src/adapters/ffmpeg/normalize.yaml +3 -0
  494. package/src/adapters/ffmpeg/probe.yaml +3 -0
  495. package/src/adapters/ffmpeg/resize.yaml +3 -0
  496. package/src/adapters/ffmpeg/subtitles.yaml +3 -0
  497. package/src/adapters/ffmpeg/thumbnail.yaml +3 -0
  498. package/src/adapters/ffmpeg/trim.yaml +3 -0
  499. package/src/adapters/figma/export-selected.yaml +5 -0
  500. package/src/adapters/flyctl/apps.yaml +3 -0
  501. package/src/adapters/freecad/assembly.yaml +3 -0
  502. package/src/adapters/freecad/bom.yaml +3 -0
  503. package/src/adapters/freecad/boolean.yaml +3 -0
  504. package/src/adapters/freecad/check.yaml +3 -0
  505. package/src/adapters/freecad/convert.yaml +3 -0
  506. package/src/adapters/freecad/export-stl.yaml +3 -0
  507. package/src/adapters/freecad/import.yaml +3 -0
  508. package/src/adapters/freecad/info.yaml +3 -0
  509. package/src/adapters/freecad/macro.yaml +3 -0
  510. package/src/adapters/freecad/measure.yaml +3 -0
  511. package/src/adapters/freecad/mesh.yaml +3 -0
  512. package/src/adapters/freecad/properties.yaml +3 -0
  513. package/src/adapters/freecad/render.yaml +3 -0
  514. package/src/adapters/freecad/section.yaml +3 -0
  515. package/src/adapters/freecad/sketch.yaml +3 -0
  516. package/src/adapters/futu/hot.yaml +3 -0
  517. package/src/adapters/futu/quote.yaml +3 -0
  518. package/src/adapters/gcloud/projects.yaml +5 -1
  519. package/src/adapters/gemini/ask.yaml +3 -0
  520. package/src/adapters/gemini/deep-research-result.yaml +3 -0
  521. package/src/adapters/gemini/deep-research.yaml +3 -0
  522. package/src/adapters/gemini/image.yaml +3 -0
  523. package/src/adapters/gemini/new.yaml +3 -0
  524. package/src/adapters/gh/issue.yaml +3 -0
  525. package/src/adapters/gh/pr.yaml +3 -0
  526. package/src/adapters/gh/release.yaml +3 -0
  527. package/src/adapters/gh/repo.yaml +3 -0
  528. package/src/adapters/gh/run.yaml +3 -0
  529. package/src/adapters/gimp/adjust.yaml +3 -0
  530. package/src/adapters/gimp/batch.yaml +3 -0
  531. package/src/adapters/gimp/convert.yaml +3 -0
  532. package/src/adapters/gimp/crop.yaml +3 -0
  533. package/src/adapters/gimp/filter.yaml +3 -0
  534. package/src/adapters/gimp/flip.yaml +3 -0
  535. package/src/adapters/gimp/info.yaml +3 -0
  536. package/src/adapters/gimp/layers.yaml +3 -0
  537. package/src/adapters/gimp/merge-layers.yaml +3 -0
  538. package/src/adapters/gimp/resize.yaml +3 -0
  539. package/src/adapters/gimp/rotate.yaml +3 -0
  540. package/src/adapters/gimp/text.yaml +3 -0
  541. package/src/adapters/gitee/repos.yaml +3 -0
  542. package/src/adapters/gitee/search.yaml +3 -0
  543. package/src/adapters/gitee/trending.yaml +5 -1
  544. package/src/adapters/github-trending/daily.test.ts +15 -0
  545. package/src/adapters/github-trending/daily.yaml +3 -0
  546. package/src/adapters/github-trending/developers.yaml +5 -1
  547. package/src/adapters/github-trending/weekly.yaml +5 -1
  548. package/src/adapters/gitlab/projects.yaml +3 -0
  549. package/src/adapters/gitlab/search.yaml +3 -0
  550. package/src/adapters/gitlab/trending.yaml +3 -0
  551. package/src/adapters/godot/project-run.yaml +3 -0
  552. package/src/adapters/godot/scene-export.yaml +3 -0
  553. package/src/adapters/google/news.yaml +3 -0
  554. package/src/adapters/google/search.yaml +3 -0
  555. package/src/adapters/google/suggest.yaml +3 -0
  556. package/src/adapters/google/trends.yaml +3 -0
  557. package/src/adapters/grok/ask.yaml +3 -0
  558. package/src/adapters/hackernews/ask.test.ts +15 -0
  559. package/src/adapters/hackernews/ask.yaml +3 -0
  560. package/src/adapters/hackernews/best.test.ts +15 -0
  561. package/src/adapters/hackernews/best.yaml +3 -0
  562. package/src/adapters/hackernews/comments.test.ts +15 -0
  563. package/src/adapters/hackernews/comments.yaml +3 -0
  564. package/src/adapters/hackernews/item.yaml +3 -0
  565. package/src/adapters/hackernews/jobs.test.ts +15 -0
  566. package/src/adapters/hackernews/jobs.yaml +3 -0
  567. package/src/adapters/hackernews/new.test.ts +15 -0
  568. package/src/adapters/hackernews/new.yaml +3 -0
  569. package/src/adapters/hackernews/search.test.ts +15 -0
  570. package/src/adapters/hackernews/search.yaml +3 -0
  571. package/src/adapters/hackernews/show.test.ts +15 -0
  572. package/src/adapters/hackernews/show.yaml +3 -0
  573. package/src/adapters/hackernews/top.test.ts +15 -0
  574. package/src/adapters/hackernews/top.yaml +3 -0
  575. package/src/adapters/hackernews/user.yaml +3 -0
  576. package/src/adapters/hermes/sessions-search.yaml +3 -0
  577. package/src/adapters/hermes/skills-list.yaml +3 -0
  578. package/src/adapters/hermes/skills-read.yaml +3 -0
  579. package/src/adapters/hf/datasets.yaml +3 -0
  580. package/src/adapters/hf/models.yaml +3 -0
  581. package/src/adapters/hf/spaces.yaml +3 -0
  582. package/src/adapters/hf/top.yaml +3 -0
  583. package/src/adapters/homebrew/info.yaml +3 -0
  584. package/src/adapters/homebrew/search.yaml +3 -0
  585. package/src/adapters/huggingface-papers/daily.yaml +3 -0
  586. package/src/adapters/huggingface-papers/search.yaml +3 -0
  587. package/src/adapters/hupu/detail.yaml +3 -0
  588. package/src/adapters/hupu/hot.yaml +3 -0
  589. package/src/adapters/hupu/like.yaml +3 -0
  590. package/src/adapters/hupu/mentions.yaml +3 -0
  591. package/src/adapters/hupu/reply.yaml +3 -0
  592. package/src/adapters/hupu/search.yaml +3 -0
  593. package/src/adapters/hupu/unlike.yaml +3 -0
  594. package/src/adapters/imagemagick/compare.yaml +3 -0
  595. package/src/adapters/imagemagick/composite.yaml +3 -0
  596. package/src/adapters/imagemagick/convert.yaml +3 -0
  597. package/src/adapters/imagemagick/identify.yaml +3 -0
  598. package/src/adapters/imagemagick/montage.yaml +3 -0
  599. package/src/adapters/imagemagick/resize.yaml +3 -0
  600. package/src/adapters/imdb/box-office.yaml +3 -0
  601. package/src/adapters/imdb/person.yaml +3 -0
  602. package/src/adapters/imdb/reviews.yaml +3 -0
  603. package/src/adapters/imdb/search.yaml +3 -0
  604. package/src/adapters/imdb/title.yaml +3 -0
  605. package/src/adapters/imdb/top.yaml +5 -1
  606. package/src/adapters/imdb/trending.yaml +3 -0
  607. package/src/adapters/imessage/contact.yaml +3 -0
  608. package/src/adapters/imessage/recent.yaml +3 -0
  609. package/src/adapters/imessage/search.yaml +3 -0
  610. package/src/adapters/infoq/articles.yaml +3 -0
  611. package/src/adapters/infoq/latest.yaml +3 -0
  612. package/src/adapters/inkscape/convert.yaml +3 -0
  613. package/src/adapters/inkscape/export.yaml +3 -0
  614. package/src/adapters/inkscape/optimize.yaml +3 -0
  615. package/src/adapters/instagram/activity.yaml +3 -0
  616. package/src/adapters/instagram/comment.yaml +3 -0
  617. package/src/adapters/instagram/explore.yaml +3 -0
  618. package/src/adapters/instagram/follow.yaml +3 -0
  619. package/src/adapters/instagram/followers.yaml +3 -0
  620. package/src/adapters/instagram/following.yaml +3 -0
  621. package/src/adapters/instagram/highlights.yaml +3 -0
  622. package/src/adapters/instagram/like.yaml +3 -0
  623. package/src/adapters/instagram/profile.yaml +3 -0
  624. package/src/adapters/instagram/reels-trending.yaml +3 -0
  625. package/src/adapters/instagram/reels.yaml +3 -0
  626. package/src/adapters/instagram/save.yaml +3 -0
  627. package/src/adapters/instagram/saved.yaml +3 -0
  628. package/src/adapters/instagram/search.yaml +3 -0
  629. package/src/adapters/instagram/stories.yaml +3 -0
  630. package/src/adapters/instagram/suggested.yaml +3 -0
  631. package/src/adapters/instagram/tags.yaml +3 -0
  632. package/src/adapters/instagram/unfollow.yaml +3 -0
  633. package/src/adapters/instagram/unlike.yaml +3 -0
  634. package/src/adapters/instagram/unsave.yaml +3 -0
  635. package/src/adapters/instagram/user.yaml +3 -0
  636. package/src/adapters/ip-info/lookup.yaml +3 -0
  637. package/src/adapters/itch-io/popular.yaml +5 -1
  638. package/src/adapters/itch-io/search.yaml +3 -0
  639. package/src/adapters/itch-io/top.yaml +5 -1
  640. package/src/adapters/ithome/hot.yaml +5 -1
  641. package/src/adapters/ithome/latest.yaml +3 -0
  642. package/src/adapters/ithome/news.yaml +3 -0
  643. package/src/adapters/jd/hot.yaml +5 -1
  644. package/src/adapters/jd/item.yaml +3 -0
  645. package/src/adapters/jd/search.yaml +3 -0
  646. package/src/adapters/jianyu/search.yaml +3 -0
  647. package/src/adapters/jike/feed.yaml +3 -0
  648. package/src/adapters/jike/notifications.yaml +3 -0
  649. package/src/adapters/jike/post.yaml +3 -0
  650. package/src/adapters/jike/search.yaml +3 -0
  651. package/src/adapters/jike/topic.yaml +3 -0
  652. package/src/adapters/jike/user.yaml +3 -0
  653. package/src/adapters/jimeng/generate.yaml +3 -0
  654. package/src/adapters/jimeng/history.yaml +3 -0
  655. package/src/adapters/jq/format.yaml +3 -0
  656. package/src/adapters/jq/query.yaml +3 -0
  657. package/src/adapters/kdenlive/effects.yaml +3 -0
  658. package/src/adapters/kdenlive/info.yaml +3 -0
  659. package/src/adapters/kdenlive/render.yaml +3 -0
  660. package/src/adapters/ke/ershoufang.yaml +3 -0
  661. package/src/adapters/ke/xiaoqu.yaml +3 -0
  662. package/src/adapters/krita/batch.yaml +3 -0
  663. package/src/adapters/krita/convert.yaml +3 -0
  664. package/src/adapters/krita/export.yaml +3 -0
  665. package/src/adapters/krita/info.yaml +3 -0
  666. package/src/adapters/kuaishou/hot.yaml +3 -0
  667. package/src/adapters/kuaishou/search.yaml +3 -0
  668. package/src/adapters/lark/version.yaml +3 -0
  669. package/src/adapters/lesswrong/comments.test.ts +15 -0
  670. package/src/adapters/lesswrong/comments.yaml +3 -0
  671. package/src/adapters/lesswrong/curated.test.ts +15 -0
  672. package/src/adapters/lesswrong/curated.yaml +3 -0
  673. package/src/adapters/lesswrong/frontpage.test.ts +15 -0
  674. package/src/adapters/lesswrong/frontpage.yaml +3 -0
  675. package/src/adapters/lesswrong/new.test.ts +15 -0
  676. package/src/adapters/lesswrong/new.yaml +3 -0
  677. package/src/adapters/lesswrong/read.test.ts +23 -0
  678. package/src/adapters/lesswrong/read.yaml +3 -0
  679. package/src/adapters/lesswrong/sequences.test.ts +15 -0
  680. package/src/adapters/lesswrong/sequences.yaml +3 -0
  681. package/src/adapters/lesswrong/shortform.test.ts +15 -0
  682. package/src/adapters/lesswrong/shortform.yaml +3 -0
  683. package/src/adapters/lesswrong/tag.yaml +3 -0
  684. package/src/adapters/lesswrong/tags.test.ts +15 -0
  685. package/src/adapters/lesswrong/tags.yaml +3 -0
  686. package/src/adapters/lesswrong/top-month.test.ts +15 -0
  687. package/src/adapters/lesswrong/top-month.yaml +3 -0
  688. package/src/adapters/lesswrong/top-week.test.ts +15 -0
  689. package/src/adapters/lesswrong/top-week.yaml +3 -0
  690. package/src/adapters/lesswrong/top-year.test.ts +15 -0
  691. package/src/adapters/lesswrong/top-year.yaml +3 -0
  692. package/src/adapters/lesswrong/top.test.ts +15 -0
  693. package/src/adapters/lesswrong/top.yaml +3 -0
  694. package/src/adapters/lesswrong/user-posts.yaml +3 -0
  695. package/src/adapters/lesswrong/user.yaml +3 -0
  696. package/src/adapters/libreoffice/convert.yaml +3 -0
  697. package/src/adapters/libreoffice/print.yaml +3 -0
  698. package/src/adapters/linear/issue-create.test.ts +15 -0
  699. package/src/adapters/linear/issue-create.yaml +3 -0
  700. package/src/adapters/linear/issue-list.yaml +3 -0
  701. package/src/adapters/linear/issue-update.test.ts +15 -0
  702. package/src/adapters/linear/issue-update.yaml +3 -0
  703. package/src/adapters/linkedin/jobs.yaml +3 -0
  704. package/src/adapters/linkedin/profile.yaml +3 -0
  705. package/src/adapters/linkedin/search.yaml +3 -0
  706. package/src/adapters/linkedin/timeline.yaml +3 -0
  707. package/src/adapters/linux-do/categories.yaml +3 -0
  708. package/src/adapters/linux-do/category.yaml +3 -0
  709. package/src/adapters/linux-do/feed.yaml +3 -0
  710. package/src/adapters/linux-do/hot.yaml +3 -0
  711. package/src/adapters/linux-do/latest.yaml +3 -0
  712. package/src/adapters/linux-do/search.yaml +3 -0
  713. package/src/adapters/linux-do/tags.yaml +3 -0
  714. package/src/adapters/linux-do/topic.yaml +3 -0
  715. package/src/adapters/linux-do/user-posts.yaml +3 -0
  716. package/src/adapters/linux-do/user-topics.yaml +3 -0
  717. package/src/adapters/lobsters/active.yaml +3 -0
  718. package/src/adapters/lobsters/hot.yaml +3 -0
  719. package/src/adapters/lobsters/newest.yaml +3 -0
  720. package/src/adapters/lobsters/search.yaml +3 -0
  721. package/src/adapters/lobsters/tag.yaml +3 -0
  722. package/src/adapters/macos/active-app.yaml +3 -0
  723. package/src/adapters/macos/apps-list.yaml +3 -0
  724. package/src/adapters/macos/apps.yaml +3 -0
  725. package/src/adapters/macos/battery.yaml +3 -0
  726. package/src/adapters/macos/bluetooth.yaml +3 -0
  727. package/src/adapters/macos/brightness.yaml +3 -0
  728. package/src/adapters/macos/caffeinate.yaml +3 -0
  729. package/src/adapters/macos/calendar-create.yaml +3 -0
  730. package/src/adapters/macos/calendar-list.yaml +3 -0
  731. package/src/adapters/macos/calendar-today.yaml +3 -0
  732. package/src/adapters/macos/clipboard.yaml +3 -0
  733. package/src/adapters/macos/contacts-search.yaml +3 -0
  734. package/src/adapters/macos/dark-mode.yaml +3 -0
  735. package/src/adapters/macos/disk-info.yaml +3 -0
  736. package/src/adapters/macos/disk-usage.yaml +3 -0
  737. package/src/adapters/macos/do-not-disturb.yaml +3 -0
  738. package/src/adapters/macos/empty-trash.yaml +3 -0
  739. package/src/adapters/macos/finder-copy.yaml +3 -0
  740. package/src/adapters/macos/finder-move.yaml +3 -0
  741. package/src/adapters/macos/finder-new-folder.yaml +3 -0
  742. package/src/adapters/macos/finder-recent.yaml +3 -0
  743. package/src/adapters/macos/finder-selection.yaml +3 -0
  744. package/src/adapters/macos/finder-tags.yaml +3 -0
  745. package/src/adapters/macos/lock-screen.yaml +3 -0
  746. package/src/adapters/macos/mail-send.yaml +3 -0
  747. package/src/adapters/macos/mail-status.yaml +3 -0
  748. package/src/adapters/macos/messages-send.yaml +3 -0
  749. package/src/adapters/macos/music-control.yaml +3 -0
  750. package/src/adapters/macos/music-now.yaml +3 -0
  751. package/src/adapters/macos/notes-list.yaml +3 -0
  752. package/src/adapters/macos/notes-search.yaml +3 -0
  753. package/src/adapters/macos/notification.yaml +3 -0
  754. package/src/adapters/macos/notify.yaml +3 -0
  755. package/src/adapters/macos/open-app.yaml +3 -0
  756. package/src/adapters/macos/open.yaml +3 -0
  757. package/src/adapters/macos/photos-search.yaml +3 -0
  758. package/src/adapters/macos/processes.yaml +3 -0
  759. package/src/adapters/macos/reminder-create.yaml +3 -0
  760. package/src/adapters/macos/reminders-complete.yaml +3 -0
  761. package/src/adapters/macos/reminders-list.yaml +3 -0
  762. package/src/adapters/macos/safari-history.yaml +3 -0
  763. package/src/adapters/macos/safari-tabs.yaml +3 -0
  764. package/src/adapters/macos/safari-url.yaml +3 -0
  765. package/src/adapters/macos/say.yaml +3 -0
  766. package/src/adapters/macos/screen-lock.yaml +3 -0
  767. package/src/adapters/macos/screen-recording.yaml +3 -0
  768. package/src/adapters/macos/screenshot.yaml +3 -0
  769. package/src/adapters/macos/shortcuts-list.yaml +3 -0
  770. package/src/adapters/macos/shortcuts-run.yaml +3 -0
  771. package/src/adapters/macos/sleep.yaml +3 -0
  772. package/src/adapters/macos/spotlight.yaml +3 -0
  773. package/src/adapters/macos/system-info.yaml +3 -0
  774. package/src/adapters/macos/trash.yaml +3 -0
  775. package/src/adapters/macos/uptime.yaml +3 -0
  776. package/src/adapters/macos/volume.yaml +3 -0
  777. package/src/adapters/macos/wallpaper.yaml +3 -0
  778. package/src/adapters/macos/wifi-info.yaml +3 -0
  779. package/src/adapters/macos/wifi.yaml +3 -0
  780. package/src/adapters/maimai/search.yaml +3 -0
  781. package/src/adapters/maoyan/hot.yaml +3 -0
  782. package/src/adapters/maoyan/search.yaml +3 -0
  783. package/src/adapters/mastodon/search.yaml +3 -0
  784. package/src/adapters/mastodon/timeline.yaml +5 -1
  785. package/src/adapters/mastodon/trending.yaml +3 -0
  786. package/src/adapters/mastodon/user.yaml +3 -0
  787. package/src/adapters/medium/article.yaml +3 -0
  788. package/src/adapters/medium/feed.yaml +3 -0
  789. package/src/adapters/medium/search.yaml +3 -0
  790. package/src/adapters/medium/trending.yaml +3 -0
  791. package/src/adapters/medium/user.yaml +3 -0
  792. package/src/adapters/meituan/hot.yaml +3 -0
  793. package/src/adapters/meituan/search.yaml +3 -0
  794. package/src/adapters/mermaid/render.yaml +3 -0
  795. package/src/adapters/minimax/chat.yaml +3 -0
  796. package/src/adapters/minimax/models.yaml +3 -0
  797. package/src/adapters/minimax/tts.yaml +3 -0
  798. package/src/adapters/motion-studio/component-get.yaml +3 -0
  799. package/src/adapters/mubu/list.yaml +3 -0
  800. package/src/adapters/mubu/search.yaml +3 -0
  801. package/src/adapters/musescore/convert.yaml +3 -0
  802. package/src/adapters/musescore/export.yaml +3 -0
  803. package/src/adapters/musescore/info.yaml +3 -0
  804. package/src/adapters/musescore/instruments.yaml +3 -0
  805. package/src/adapters/musescore/transpose.yaml +3 -0
  806. package/src/adapters/neonctl/projects.yaml +3 -0
  807. package/src/adapters/netease-music/hot.yaml +5 -1
  808. package/src/adapters/netease-music/playlist.yaml +3 -0
  809. package/src/adapters/netease-music/search.yaml +3 -0
  810. package/src/adapters/netease-music/top.yaml +5 -1
  811. package/src/adapters/netlify/sites.yaml +3 -0
  812. package/src/adapters/notebooklm/current.yaml +3 -0
  813. package/src/adapters/notebooklm/get.yaml +3 -0
  814. package/src/adapters/notebooklm/history.yaml +3 -0
  815. package/src/adapters/notebooklm/list.yaml +3 -0
  816. package/src/adapters/notebooklm/note-list.yaml +3 -0
  817. package/src/adapters/notebooklm/notes-get.yaml +3 -0
  818. package/src/adapters/notebooklm/open.yaml +3 -0
  819. package/src/adapters/notebooklm/rpc.yaml +3 -0
  820. package/src/adapters/notebooklm/shared.yaml +3 -0
  821. package/src/adapters/notebooklm/source-fulltext.yaml +3 -0
  822. package/src/adapters/notebooklm/source-get.yaml +3 -0
  823. package/src/adapters/notebooklm/source-guide.yaml +3 -0
  824. package/src/adapters/notebooklm/source-list.yaml +3 -0
  825. package/src/adapters/notebooklm/status.yaml +3 -0
  826. package/src/adapters/notebooklm/summary.yaml +3 -0
  827. package/src/adapters/notion/databases.yaml +3 -0
  828. package/src/adapters/notion/pages.yaml +3 -0
  829. package/src/adapters/notion/search.yaml +3 -0
  830. package/src/adapters/novita/generate.yaml +3 -0
  831. package/src/adapters/novita/models.yaml +3 -0
  832. package/src/adapters/novita/status.yaml +3 -0
  833. package/src/adapters/npm/downloads.yaml +3 -0
  834. package/src/adapters/npm/info.yaml +3 -0
  835. package/src/adapters/npm/search.yaml +3 -0
  836. package/src/adapters/npm/versions.yaml +3 -0
  837. package/src/adapters/npm-trends/compare.yaml +3 -0
  838. package/src/adapters/npm-trends/trending.yaml +3 -0
  839. package/src/adapters/nytimes/search.yaml +3 -0
  840. package/src/adapters/nytimes/top.yaml +3 -0
  841. package/src/adapters/obs/record-start.yaml +3 -0
  842. package/src/adapters/obs/record-stop.yaml +3 -0
  843. package/src/adapters/obs/scenes.yaml +3 -0
  844. package/src/adapters/obs/screenshot.yaml +3 -0
  845. package/src/adapters/obs/sources.yaml +3 -0
  846. package/src/adapters/obs/status.yaml +3 -0
  847. package/src/adapters/obs/stream-start.yaml +3 -0
  848. package/src/adapters/obs/stream-stop.yaml +3 -0
  849. package/src/adapters/obsidian/daily.yaml +3 -0
  850. package/src/adapters/obsidian/open.yaml +3 -0
  851. package/src/adapters/obsidian/search.yaml +3 -0
  852. package/src/adapters/ollama/generate.yaml +3 -0
  853. package/src/adapters/ollama/list.yaml +3 -0
  854. package/src/adapters/ollama/models.yaml +5 -1
  855. package/src/adapters/ollama/ps.yaml +3 -0
  856. package/src/adapters/ones/enrich-tasks.yaml +3 -0
  857. package/src/adapters/ones/login.yaml +3 -0
  858. package/src/adapters/ones/logout.yaml +3 -0
  859. package/src/adapters/ones/me.yaml +3 -0
  860. package/src/adapters/ones/my-tasks.yaml +3 -0
  861. package/src/adapters/ones/resolve-labels.yaml +3 -0
  862. package/src/adapters/ones/task-helpers.yaml +3 -0
  863. package/src/adapters/ones/task.yaml +3 -0
  864. package/src/adapters/ones/tasks.yaml +3 -0
  865. package/src/adapters/ones/token-info.yaml +3 -0
  866. package/src/adapters/ones/worklog.yaml +3 -0
  867. package/src/adapters/opencode/version.yaml +3 -0
  868. package/src/adapters/openharness/memory-read.yaml +3 -0
  869. package/src/adapters/openharness/skills-list.yaml +3 -0
  870. package/src/adapters/openrouter/models.yaml +3 -0
  871. package/src/adapters/openrouter/search.yaml +3 -0
  872. package/src/adapters/pandoc/convert.yaml +3 -0
  873. package/src/adapters/paperreview/feedback.yaml +3 -0
  874. package/src/adapters/paperreview/review.yaml +3 -0
  875. package/src/adapters/paperreview/submit.yaml +3 -0
  876. package/src/adapters/perplexity/ask.yaml +3 -0
  877. package/src/adapters/pexels/curated.yaml +3 -0
  878. package/src/adapters/pexels/search.yaml +3 -0
  879. package/src/adapters/pinduoduo/hot.yaml +3 -0
  880. package/src/adapters/pinduoduo/search.yaml +3 -0
  881. package/src/adapters/pixiv/detail.yaml +3 -0
  882. package/src/adapters/pixiv/download.yaml +3 -0
  883. package/src/adapters/pixiv/illusts.yaml +3 -0
  884. package/src/adapters/pixiv/ranking.yaml +3 -0
  885. package/src/adapters/pixiv/search.yaml +3 -0
  886. package/src/adapters/pixiv/user.yaml +3 -0
  887. package/src/adapters/producthunt/browse.yaml +3 -0
  888. package/src/adapters/producthunt/hot.yaml +3 -0
  889. package/src/adapters/producthunt/posts.yaml +3 -0
  890. package/src/adapters/producthunt/search.yaml +3 -0
  891. package/src/adapters/producthunt/today.yaml +3 -0
  892. package/src/adapters/pscale/databases.yaml +3 -0
  893. package/src/adapters/pypi/info.yaml +3 -0
  894. package/src/adapters/pypi/search.yaml +3 -0
  895. package/src/adapters/pypi/versions.yaml +3 -0
  896. package/src/adapters/quark/ls.yaml +3 -0
  897. package/src/adapters/quark/search.yaml +3 -0
  898. package/src/adapters/qweather/forecast.yaml +3 -0
  899. package/src/adapters/qweather/now.yaml +3 -0
  900. package/src/adapters/railway/deploy.yaml +3 -0
  901. package/src/adapters/reddit/comment.yaml +3 -0
  902. package/src/adapters/reddit/comments.test.ts +15 -0
  903. package/src/adapters/reddit/comments.yaml +3 -0
  904. package/src/adapters/reddit/frontpage.test.ts +15 -0
  905. package/src/adapters/reddit/frontpage.yaml +3 -0
  906. package/src/adapters/reddit/hot.test.ts +15 -0
  907. package/src/adapters/reddit/hot.yaml +3 -0
  908. package/src/adapters/reddit/new.test.ts +15 -0
  909. package/src/adapters/reddit/new.yaml +3 -0
  910. package/src/adapters/reddit/popular.test.ts +15 -0
  911. package/src/adapters/reddit/popular.yaml +3 -0
  912. package/src/adapters/reddit/read.test.ts +15 -0
  913. package/src/adapters/reddit/read.yaml +3 -0
  914. package/src/adapters/reddit/rising.test.ts +15 -0
  915. package/src/adapters/reddit/rising.yaml +3 -0
  916. package/src/adapters/reddit/save.yaml +3 -0
  917. package/src/adapters/reddit/saved.test.ts +15 -0
  918. package/src/adapters/reddit/saved.yaml +3 -0
  919. package/src/adapters/reddit/search.test.ts +15 -0
  920. package/src/adapters/reddit/search.yaml +3 -0
  921. package/src/adapters/reddit/subreddit.test.ts +15 -0
  922. package/src/adapters/reddit/subreddit.yaml +3 -0
  923. package/src/adapters/reddit/subscribe.yaml +3 -0
  924. package/src/adapters/reddit/top.test.ts +15 -0
  925. package/src/adapters/reddit/top.yaml +3 -0
  926. package/src/adapters/reddit/trending.test.ts +15 -0
  927. package/src/adapters/reddit/trending.yaml +3 -0
  928. package/src/adapters/reddit/upvote.yaml +3 -0
  929. package/src/adapters/reddit/upvoted.test.ts +15 -0
  930. package/src/adapters/reddit/upvoted.yaml +3 -0
  931. package/src/adapters/reddit/user-comments.test.ts +15 -0
  932. package/src/adapters/reddit/user-comments.yaml +3 -0
  933. package/src/adapters/reddit/user-posts.test.ts +15 -0
  934. package/src/adapters/reddit/user-posts.yaml +3 -0
  935. package/src/adapters/reddit/user.test.ts +21 -0
  936. package/src/adapters/reddit/user.yaml +3 -0
  937. package/src/adapters/renderdoc/capture-list.yaml +3 -0
  938. package/src/adapters/renderdoc/frame-export.yaml +3 -0
  939. package/src/adapters/replicate/run.yaml +3 -0
  940. package/src/adapters/replicate/search.yaml +3 -0
  941. package/src/adapters/replicate/trending.yaml +5 -1
  942. package/src/adapters/reuters/article.yaml +3 -0
  943. package/src/adapters/reuters/latest.yaml +5 -1
  944. package/src/adapters/reuters/search.yaml +3 -0
  945. package/src/adapters/reuters/top.yaml +5 -1
  946. package/src/adapters/shotcut/effects.yaml +3 -0
  947. package/src/adapters/shotcut/info.yaml +3 -0
  948. package/src/adapters/shotcut/render.yaml +3 -0
  949. package/src/adapters/sinablog/article.yaml +3 -0
  950. package/src/adapters/sinablog/hot.yaml +3 -0
  951. package/src/adapters/sinablog/search.yaml +3 -0
  952. package/src/adapters/sinablog/user.yaml +3 -0
  953. package/src/adapters/sinafinance/market.yaml +3 -0
  954. package/src/adapters/sinafinance/news.yaml +3 -0
  955. package/src/adapters/sinafinance/rolling-news.yaml +3 -0
  956. package/src/adapters/sinafinance/stock-rank.yaml +3 -0
  957. package/src/adapters/sinafinance/stock.yaml +3 -0
  958. package/src/adapters/sketch/artboards.yaml +3 -0
  959. package/src/adapters/sketch/export.yaml +3 -0
  960. package/src/adapters/sketch/symbols.yaml +3 -0
  961. package/src/adapters/slack/channels.yaml +3 -0
  962. package/src/adapters/slack/messages.yaml +3 -0
  963. package/src/adapters/slack/post.yaml +3 -0
  964. package/src/adapters/slack/search.yaml +3 -0
  965. package/src/adapters/slack/send.yaml +3 -0
  966. package/src/adapters/slack/status.yaml +3 -0
  967. package/src/adapters/slack/users.yaml +3 -0
  968. package/src/adapters/slay-the-spire-ii/deck.yaml +3 -0
  969. package/src/adapters/slay-the-spire-ii/end-turn.yaml +3 -0
  970. package/src/adapters/slay-the-spire-ii/map.yaml +3 -0
  971. package/src/adapters/slay-the-spire-ii/play-card.yaml +3 -0
  972. package/src/adapters/slay-the-spire-ii/status.yaml +3 -0
  973. package/src/adapters/slay-the-spire-ii/use-potion.yaml +3 -0
  974. package/src/adapters/slock/servers.yaml +3 -0
  975. package/src/adapters/smzdm/article.yaml +3 -0
  976. package/src/adapters/smzdm/hot.yaml +3 -0
  977. package/src/adapters/smzdm/search.yaml +3 -0
  978. package/src/adapters/spotify/now-playing.yaml +3 -0
  979. package/src/adapters/spotify/playlists.yaml +3 -0
  980. package/src/adapters/spotify/search.yaml +3 -0
  981. package/src/adapters/spotify/top-tracks.yaml +3 -0
  982. package/src/adapters/sspai/hot.yaml +3 -0
  983. package/src/adapters/sspai/latest.yaml +3 -0
  984. package/src/adapters/stackoverflow/bounties.yaml +3 -0
  985. package/src/adapters/stackoverflow/hot.yaml +3 -0
  986. package/src/adapters/stackoverflow/question.yaml +3 -0
  987. package/src/adapters/stackoverflow/search.yaml +3 -0
  988. package/src/adapters/stackoverflow/tags.yaml +3 -0
  989. package/src/adapters/stackoverflow/unanswered.yaml +3 -0
  990. package/src/adapters/stagehand/wrap-observe.yaml +3 -0
  991. package/src/adapters/steam/app-details.yaml +3 -0
  992. package/src/adapters/steam/new-releases.yaml +3 -0
  993. package/src/adapters/steam/search.yaml +3 -0
  994. package/src/adapters/steam/specials.yaml +3 -0
  995. package/src/adapters/steam/top-sellers.yaml +3 -0
  996. package/src/adapters/steam/wishlist.yaml +3 -0
  997. package/src/adapters/substack/feed.yaml +3 -0
  998. package/src/adapters/substack/publication.yaml +3 -0
  999. package/src/adapters/substack/search.yaml +3 -0
  1000. package/src/adapters/substack/trending.yaml +3 -0
  1001. package/src/adapters/supabase/projects.yaml +3 -0
  1002. package/src/adapters/taobao/hot.yaml +3 -0
  1003. package/src/adapters/taobao/search.yaml +3 -0
  1004. package/src/adapters/techcrunch/latest.yaml +3 -0
  1005. package/src/adapters/techcrunch/search.yaml +3 -0
  1006. package/src/adapters/theverge/latest.yaml +3 -0
  1007. package/src/adapters/theverge/search.yaml +3 -0
  1008. package/src/adapters/threads/hot.yaml +3 -0
  1009. package/src/adapters/threads/search.yaml +3 -0
  1010. package/src/adapters/tieba/hot.yaml +5 -1
  1011. package/src/adapters/tieba/posts.yaml +3 -0
  1012. package/src/adapters/tieba/read.yaml +3 -0
  1013. package/src/adapters/tieba/search.yaml +3 -0
  1014. package/src/adapters/tiktok/comment.yaml +3 -0
  1015. package/src/adapters/tiktok/explore.yaml +3 -0
  1016. package/src/adapters/tiktok/follow.yaml +3 -0
  1017. package/src/adapters/tiktok/following.yaml +3 -0
  1018. package/src/adapters/tiktok/friends.yaml +3 -0
  1019. package/src/adapters/tiktok/like.yaml +3 -0
  1020. package/src/adapters/tiktok/live.yaml +3 -0
  1021. package/src/adapters/tiktok/notifications.yaml +3 -0
  1022. package/src/adapters/tiktok/profile.yaml +3 -0
  1023. package/src/adapters/tiktok/save.yaml +3 -0
  1024. package/src/adapters/tiktok/search.yaml +3 -0
  1025. package/src/adapters/tiktok/trending.yaml +3 -0
  1026. package/src/adapters/tiktok/unfollow.yaml +3 -0
  1027. package/src/adapters/tiktok/unlike.yaml +3 -0
  1028. package/src/adapters/tiktok/unsave.yaml +3 -0
  1029. package/src/adapters/tiktok/user.yaml +3 -0
  1030. package/src/adapters/toutiao/hot.yaml +3 -0
  1031. package/src/adapters/toutiao/search.yaml +3 -0
  1032. package/src/adapters/twitch/games.yaml +3 -0
  1033. package/src/adapters/twitch/search.yaml +3 -0
  1034. package/src/adapters/twitch/streams.yaml +3 -0
  1035. package/src/adapters/twitch/top.yaml +3 -0
  1036. package/src/adapters/twitter/lists.yaml +3 -0
  1037. package/src/adapters/twitter/media.yaml +3 -0
  1038. package/src/adapters/twitter/mentions.yaml +3 -0
  1039. package/src/adapters/twitter/mute.yaml +3 -0
  1040. package/src/adapters/twitter/pin.yaml +3 -0
  1041. package/src/adapters/twitter/quotes.yaml +3 -0
  1042. package/src/adapters/twitter/retweets.yaml +3 -0
  1043. package/src/adapters/twitter/spaces.yaml +3 -0
  1044. package/src/adapters/twitter/unmute.yaml +3 -0
  1045. package/src/adapters/unsplash/random.yaml +3 -0
  1046. package/src/adapters/unsplash/search.yaml +3 -0
  1047. package/src/adapters/v2ex/daily.yaml +3 -0
  1048. package/src/adapters/v2ex/hot.yaml +3 -0
  1049. package/src/adapters/v2ex/latest.yaml +3 -0
  1050. package/src/adapters/v2ex/me.test.ts +15 -0
  1051. package/src/adapters/v2ex/me.yaml +3 -0
  1052. package/src/adapters/v2ex/member.yaml +3 -0
  1053. package/src/adapters/v2ex/node.yaml +3 -0
  1054. package/src/adapters/v2ex/nodes.yaml +3 -0
  1055. package/src/adapters/v2ex/notifications.test.ts +15 -0
  1056. package/src/adapters/v2ex/notifications.yaml +3 -0
  1057. package/src/adapters/v2ex/replies.yaml +3 -0
  1058. package/src/adapters/v2ex/search.test.ts +15 -0
  1059. package/src/adapters/v2ex/search.yaml +3 -0
  1060. package/src/adapters/v2ex/topic.yaml +3 -0
  1061. package/src/adapters/v2ex/user.yaml +3 -0
  1062. package/src/adapters/vercel/list.yaml +3 -0
  1063. package/src/adapters/vscode/extensions.yaml +3 -0
  1064. package/src/adapters/vscode/install-ext.yaml +3 -0
  1065. package/src/adapters/vscode/open.yaml +3 -0
  1066. package/src/adapters/web/read.yaml +3 -0
  1067. package/src/adapters/wechat-channels/hot.yaml +3 -0
  1068. package/src/adapters/wechat-channels/search.yaml +3 -0
  1069. package/src/adapters/weibo/comments.yaml +3 -0
  1070. package/src/adapters/weibo/feed.yaml +3 -0
  1071. package/src/adapters/weibo/hot.yaml +3 -0
  1072. package/src/adapters/weibo/me.yaml +3 -0
  1073. package/src/adapters/weibo/post.yaml +3 -0
  1074. package/src/adapters/weibo/profile.yaml +3 -0
  1075. package/src/adapters/weibo/search.yaml +3 -0
  1076. package/src/adapters/weibo/timeline.yaml +3 -0
  1077. package/src/adapters/weibo/trending.yaml +3 -0
  1078. package/src/adapters/weibo/user.yaml +3 -0
  1079. package/src/adapters/weixin/article.yaml +3 -0
  1080. package/src/adapters/weixin/download.yaml +3 -0
  1081. package/src/adapters/weixin/hot.yaml +3 -0
  1082. package/src/adapters/weixin/search.yaml +3 -0
  1083. package/src/adapters/weread/book.yaml +3 -0
  1084. package/src/adapters/weread/highlights.yaml +3 -0
  1085. package/src/adapters/weread/notebooks.yaml +3 -0
  1086. package/src/adapters/weread/notes.yaml +3 -0
  1087. package/src/adapters/weread/ranking.yaml +3 -0
  1088. package/src/adapters/weread/search.yaml +3 -0
  1089. package/src/adapters/weread/shelf.yaml +3 -0
  1090. package/src/adapters/wikipedia/random.yaml +3 -0
  1091. package/src/adapters/wikipedia/search.yaml +3 -0
  1092. package/src/adapters/wikipedia/summary.yaml +3 -0
  1093. package/src/adapters/wikipedia/today.yaml +5 -1
  1094. package/src/adapters/wikipedia/trending.yaml +3 -0
  1095. package/src/adapters/wiremock/create-stub.yaml +3 -0
  1096. package/src/adapters/wiremock/delete-stub.yaml +3 -0
  1097. package/src/adapters/wiremock/reset.yaml +3 -0
  1098. package/src/adapters/wiremock/stubs.yaml +3 -0
  1099. package/src/adapters/wiremock/verify.yaml +3 -0
  1100. package/src/adapters/wrangler/list.yaml +3 -0
  1101. package/src/adapters/xianyu/chat.yaml +3 -0
  1102. package/src/adapters/xianyu/item.yaml +3 -0
  1103. package/src/adapters/xianyu/search.yaml +3 -0
  1104. package/src/adapters/xiaoe/catalog.yaml +3 -0
  1105. package/src/adapters/xiaoe/content.yaml +3 -0
  1106. package/src/adapters/xiaoe/courses.yaml +3 -0
  1107. package/src/adapters/xiaoe/detail.yaml +3 -0
  1108. package/src/adapters/xiaoe/play-url.yaml +3 -0
  1109. package/src/adapters/xiaohongshu/feed.yaml +3 -0
  1110. package/src/adapters/xiaohongshu/follow.yaml +3 -0
  1111. package/src/adapters/xiaohongshu/hashtag.yaml +3 -0
  1112. package/src/adapters/xiaohongshu/hot.yaml +3 -0
  1113. package/src/adapters/xiaohongshu/like.yaml +3 -0
  1114. package/src/adapters/xiaohongshu/notifications.yaml +3 -0
  1115. package/src/adapters/xiaohongshu/profile.yaml +3 -0
  1116. package/src/adapters/xiaohongshu/save.yaml +3 -0
  1117. package/src/adapters/xiaohongshu/suggest.yaml +3 -0
  1118. package/src/adapters/xiaohongshu/trending.yaml +3 -0
  1119. package/src/adapters/xiaohongshu/unfollow.yaml +3 -0
  1120. package/src/adapters/xiaoyuzhou/episode.yaml +3 -0
  1121. package/src/adapters/xiaoyuzhou/podcast-episodes.yaml +3 -0
  1122. package/src/adapters/xiaoyuzhou/podcast.yaml +3 -0
  1123. package/src/adapters/xueqiu/comments.yaml +3 -0
  1124. package/src/adapters/xueqiu/earnings-date.yaml +3 -0
  1125. package/src/adapters/xueqiu/feed.yaml +3 -0
  1126. package/src/adapters/xueqiu/fund-holdings.yaml +3 -0
  1127. package/src/adapters/xueqiu/fund-snapshot.yaml +3 -0
  1128. package/src/adapters/xueqiu/hot-stock.yaml +3 -0
  1129. package/src/adapters/xueqiu/hot.yaml +3 -0
  1130. package/src/adapters/xueqiu/market.yaml +3 -0
  1131. package/src/adapters/xueqiu/quote.yaml +3 -0
  1132. package/src/adapters/xueqiu/search.yaml +3 -0
  1133. package/src/adapters/xueqiu/stock.yaml +3 -0
  1134. package/src/adapters/xueqiu/watchlist.yaml +3 -0
  1135. package/src/adapters/yahoo-finance/quote.yaml +3 -0
  1136. package/src/adapters/yahoo-finance/search.yaml +3 -0
  1137. package/src/adapters/yahoo-finance/trending.yaml +5 -1
  1138. package/src/adapters/ycombinator/launches.yaml +3 -0
  1139. package/src/adapters/yollomi/background.yaml +3 -0
  1140. package/src/adapters/yollomi/edit.yaml +3 -0
  1141. package/src/adapters/yollomi/face-swap.yaml +3 -0
  1142. package/src/adapters/yollomi/generate.yaml +3 -0
  1143. package/src/adapters/yollomi/models.yaml +3 -0
  1144. package/src/adapters/yollomi/object-remover.yaml +3 -0
  1145. package/src/adapters/yollomi/remove-bg.yaml +3 -0
  1146. package/src/adapters/yollomi/restore.yaml +3 -0
  1147. package/src/adapters/yollomi/try-on.yaml +3 -0
  1148. package/src/adapters/yollomi/upload.yaml +3 -0
  1149. package/src/adapters/yollomi/upscale.yaml +3 -0
  1150. package/src/adapters/yollomi/video.yaml +3 -0
  1151. package/src/adapters/youtube/playlist.yaml +3 -0
  1152. package/src/adapters/youtube/shorts.yaml +3 -0
  1153. package/src/adapters/youtube/trending.yaml +3 -0
  1154. package/src/adapters/yt-dlp/download.yaml +3 -0
  1155. package/src/adapters/yt-dlp/extract-audio.yaml +3 -0
  1156. package/src/adapters/yt-dlp/info.yaml +3 -0
  1157. package/src/adapters/yt-dlp/search.yaml +3 -0
  1158. package/src/adapters/yuanbao/ask.yaml +3 -0
  1159. package/src/adapters/yuanbao/new.yaml +3 -0
  1160. package/src/adapters/yuanbao/shared.yaml +3 -0
  1161. package/src/adapters/zhihu/answer.yaml +3 -0
  1162. package/src/adapters/zhihu/answers.test.ts +15 -0
  1163. package/src/adapters/zhihu/answers.yaml +3 -0
  1164. package/src/adapters/zhihu/article.yaml +3 -0
  1165. package/src/adapters/zhihu/articles.test.ts +15 -0
  1166. package/src/adapters/zhihu/articles.yaml +3 -0
  1167. package/src/adapters/zhihu/collections.test.ts +15 -0
  1168. package/src/adapters/zhihu/collections.yaml +3 -0
  1169. package/src/adapters/zhihu/columns.test.ts +15 -0
  1170. package/src/adapters/zhihu/columns.yaml +3 -0
  1171. package/src/adapters/zhihu/comment.test.ts +15 -0
  1172. package/src/adapters/zhihu/comment.yaml +3 -0
  1173. package/src/adapters/zhihu/download.yaml +3 -0
  1174. package/src/adapters/zhihu/feed.test.ts +15 -0
  1175. package/src/adapters/zhihu/feed.yaml +3 -0
  1176. package/src/adapters/zhihu/followers.test.ts +15 -0
  1177. package/src/adapters/zhihu/followers.yaml +3 -0
  1178. package/src/adapters/zhihu/following.test.ts +15 -0
  1179. package/src/adapters/zhihu/following.yaml +3 -0
  1180. package/src/adapters/zhihu/hot.test.ts +15 -0
  1181. package/src/adapters/zhihu/hot.yaml +3 -0
  1182. package/src/adapters/zhihu/me.yaml +3 -0
  1183. package/src/adapters/zhihu/notifications.test.ts +15 -0
  1184. package/src/adapters/zhihu/notifications.yaml +3 -0
  1185. package/src/adapters/zhihu/pins.test.ts +15 -0
  1186. package/src/adapters/zhihu/pins.yaml +3 -0
  1187. package/src/adapters/zhihu/question.test.ts +15 -0
  1188. package/src/adapters/zhihu/question.yaml +3 -0
  1189. package/src/adapters/zhihu/search.test.ts +15 -0
  1190. package/src/adapters/zhihu/search.yaml +3 -0
  1191. package/src/adapters/zhihu/topic.test.ts +15 -0
  1192. package/src/adapters/zhihu/topic.yaml +3 -0
  1193. package/src/adapters/zhihu/topics.test.ts +15 -0
  1194. package/src/adapters/zhihu/topics.yaml +3 -0
  1195. package/src/adapters/zhihu/trending.test.ts +15 -0
  1196. package/src/adapters/zhihu/trending.yaml +3 -0
  1197. package/src/adapters/zhihu/user.yaml +3 -0
  1198. package/src/adapters/zoom/join.yaml +3 -0
  1199. package/src/adapters/zoom/start.yaml +3 -0
  1200. package/src/adapters/zoom/toggle-mute.yaml +3 -0
  1201. package/src/adapters/zotero/add-note.yaml +3 -0
  1202. package/src/adapters/zotero/add-tag.yaml +3 -0
  1203. package/src/adapters/zotero/collections.yaml +3 -0
  1204. package/src/adapters/zotero/export.yaml +3 -0
  1205. package/src/adapters/zotero/items.yaml +3 -0
  1206. package/src/adapters/zotero/notes.yaml +3 -0
  1207. package/src/adapters/zotero/search.yaml +3 -0
  1208. package/src/adapters/zotero/tags.yaml +3 -0
  1209. package/src/adapters/zsxq/dynamics.yaml +3 -0
  1210. package/src/adapters/zsxq/groups.yaml +3 -0
  1211. package/src/adapters/zsxq/search.yaml +3 -0
  1212. package/src/adapters/zsxq/topic.yaml +3 -0
  1213. package/src/adapters/zsxq/topics.yaml +3 -0
  1214. package/dist/engine/steps/browser.d.ts +0 -11
  1215. package/dist/engine/steps/browser.d.ts.map +0 -1
  1216. package/dist/engine/steps/browser.js +0 -11
  1217. package/dist/engine/steps/browser.js.map +0 -1
  1218. package/dist/engine/steps/control.d.ts +0 -10
  1219. package/dist/engine/steps/control.d.ts.map +0 -1
  1220. package/dist/engine/steps/control.js +0 -10
  1221. package/dist/engine/steps/control.js.map +0 -1
  1222. package/dist/engine/steps/desktop.d.ts +0 -10
  1223. package/dist/engine/steps/desktop.d.ts.map +0 -1
  1224. package/dist/engine/steps/desktop.js +0 -10
  1225. package/dist/engine/steps/desktop.js.map +0 -1
  1226. package/dist/engine/steps/transform.d.ts +0 -10
  1227. package/dist/engine/steps/transform.d.ts.map +0 -1
  1228. package/dist/engine/steps/transform.js +0 -10
  1229. package/dist/engine/steps/transform.js.map +0 -1
@@ -1,2176 +1,18 @@
1
1
  /**
2
- * YAML Pipeline Execution Engine the Flight Computer.
3
- *
4
- * Executes pipeline steps defined in YAML adapters:
5
- * fetch → HTTP request (GET/POST)
6
- * select → Extract nested field from response
7
- * map → Transform each item using template expressions
8
- * filter → Keep items matching a condition
9
- * limit → Cap the number of results
10
- * html_to_md Convert HTML to Markdown via turndown
11
- * write_temp → Write content to a temp file (cleaned up after pipeline)
12
- * evaluate Run JS expression (for browser adapters, future)
13
- *
14
- * Template syntax: ${{ expression }}
15
- * Available variables: item, index, args, base, temp
16
- */
17
- import { execFile } from "node:child_process";
18
- import { existsSync, mkdirSync, writeFileSync, rmSync, readFileSync, } from "node:fs";
19
- import { stat } from "node:fs/promises";
20
- import { join, resolve } from "node:path";
21
- import { tmpdir, homedir } from "node:os";
22
- import { randomBytes, createHash } from "node:crypto";
23
- import { promisify } from "node:util";
24
- import { runInNewContext } from "node:vm";
25
- import TurndownService from "turndown";
26
- import { USER_AGENT } from "../constants.js";
27
- import { formatCookieHeader, loadCookiesWithCDP } from "./cookies.js";
28
- import { matchSensitivePathRealpath, buildSensitivePathDenial, } from "../permissions/sensitive-paths.js";
29
- import { generateInterceptorJs, generateReadInterceptedJs, } from "./interceptor.js";
30
- import { httpDownload, ytdlpDownload, requiresYtdlp, sanitizeFilename, generateFilename, mapConcurrent, } from "./download.js";
31
- import { executeWebsocket } from "./websocket.js";
32
- import { getProxyAgent } from "./proxy.js";
33
- import { createTransportBus } from "../transport/bus.js";
34
- import { CuaTransport } from "../transport/adapters/cua.js";
35
- import { DesktopAxTransport } from "../transport/adapters/desktop-ax.js";
36
- import { DesktopUiaTransport } from "../transport/adapters/desktop-uia.js";
37
- import { DesktopAtspiTransport } from "../transport/adapters/desktop-atspi.js";
38
- import { HttpTransport } from "../transport/adapters/http.js";
39
- import { SubprocessTransport } from "../transport/adapters/subprocess.js";
40
- import { CdpBrowserTransport } from "../transport/adapters/cdp-browser.js";
41
- import { CUA_STEP_HANDLERS } from "./steps/cua.js";
42
- import { DESKTOP_AX_STEP_HANDLERS, } from "./steps/desktop-ax.js";
43
- const execFileAsync = promisify(execFile);
44
- /**
45
- * Structured pipeline error — designed for AI agent consumption.
46
- * An agent receiving this error can read the adapter YAML, understand
47
- * exactly what failed, and edit the file to fix it.
48
- */
49
- export class PipelineError extends Error {
50
- detail;
51
- constructor(message, detail) {
52
- super(message);
53
- this.detail = detail;
54
- this.name = "PipelineError";
55
- }
56
- /** JSON output for AI agents — includes everything needed to self-repair */
57
- toAgentJSON(adapterPath) {
58
- return {
59
- error: this.message,
60
- adapter: adapterPath,
61
- ...this.detail,
62
- retryable: this.detail.retryable ?? false,
63
- alternatives: this.detail.alternatives ?? [],
64
- };
65
- }
66
- }
67
- /** Reserved sibling keys that are not step action names */
68
- const SIBLING_KEYS = new Set([
69
- "fallback",
70
- "then",
71
- "else",
72
- "merge",
73
- "retry",
74
- "backoff",
75
- ]);
76
- /**
77
- * SSRF defence — reject request URLs that point at non-http(s) schemes or
78
- * reserved local address ranges before we issue the fetch.
79
- *
80
- * The attack shape this blocks: a YAML adapter takes `${{ args.query }}`
81
- * and interpolates it into the request URL. An attacker (or a careless
82
- * template author) feeds a payload like `http://169.254.169.254/latest/meta-data/`
83
- * (AWS IMDS) or `http://127.0.0.1:19825/internal` (Uni-CLI daemon). Without
84
- * this guard the runner happily fetches it and returns the response —
85
- * leaking credentials or driving the daemon.
86
- *
87
- * The check is intentionally conservative: only http/https, and no loopback
88
- * / link-local / private metadata addresses. Set `UNICLI_ALLOW_LOCAL=1` to
89
- * bypass — useful for local dev / testing where a developer intentionally
90
- * targets `127.0.0.1` or a docker compose stack on a private subnet.
91
- */
92
- export function assertSafeRequestUrl(raw) {
93
- let u;
94
- try {
95
- u = new URL(raw);
96
- }
97
- catch {
98
- throw new Error(`invalid URL for pipeline fetch: ${raw.slice(0, 120)}`);
99
- }
100
- if (u.protocol !== "http:" && u.protocol !== "https:") {
101
- throw new Error(`disallowed URL scheme for pipeline fetch: ${u.protocol} (only http/https)`);
102
- }
103
- if (process.env.UNICLI_ALLOW_LOCAL === "1")
104
- return;
105
- // Node's URL.hostname keeps the IPv6 brackets (`[::1]`) around the
106
- // zero-compressed literal; strip them before comparing. This is load-
107
- // bearing — a missing strip means `[::1]` and `[fe80::...]` slip past
108
- // the check.
109
- const hostnameLower = u.hostname.toLowerCase();
110
- const host = hostnameLower.startsWith("[") && hostnameLower.endsWith("]")
111
- ? hostnameLower.slice(1, -1)
112
- : hostnameLower;
113
- // Literal loopback / unspecified / link-local / cloud metadata
114
- if (host === "localhost" ||
115
- host === "0.0.0.0" ||
116
- host === "::" ||
117
- host === "::1" ||
118
- host === "metadata.google.internal" ||
119
- host === "metadata" ||
120
- // IPv6 link-local (fe80::/10) and unique-local (fc00::/7 → fc/fd prefix)
121
- host.startsWith("fe80:") ||
122
- host.startsWith("fc") ||
123
- host.startsWith("fd") ||
124
- // IPv4 CIDR check — crude but covers the most common SSRF vectors.
125
- // Full RFC-6890 enumeration is overkill for adapter fetches; if you
126
- // need to target those ranges, set UNICLI_ALLOW_LOCAL=1.
127
- host.startsWith("127.") ||
128
- host.startsWith("10.") ||
129
- host.startsWith("169.254.") ||
130
- host.startsWith("192.168.") ||
131
- /^172\.(1[6-9]|2\d|3[01])\./.test(host)) {
132
- throw new Error(`blocked fetch to reserved/local address ${host} — set UNICLI_ALLOW_LOCAL=1 to override`);
133
- }
134
- }
135
- /**
136
- * Lazily-created transport bus — the capability registry for the runner.
137
- *
138
- * All seven v0.212 transports are registered so `bus.require(step)` gives
139
- * an honest answer for every pipeline step name in the capability matrix.
140
- * The legacy step executors (stepFetch / stepExec / browser ops) still run
141
- * their own code paths for v0.212 compatibility — registering the HTTP,
142
- * subprocess, and CDP transports here does NOT eagerly open Chrome or
143
- * spawn a process; `open()` is only called when a handler dispatches
144
- * through the bus (currently only the `cua_*` and `ax_*` step families).
145
- *
146
- * This is deliberately lightweight: the bus is the single source of truth
147
- * for capability declarations; full execution routing through the bus is
148
- * the v0.213 destination.
149
- */
150
- let sharedBus;
151
- function getBus() {
152
- if (sharedBus)
153
- return sharedBus;
154
- const bus = createTransportBus();
155
- // Order matters only for fallback lookup — register in the canonical
156
- // order from src/transport/capability.ts:TRANSPORT_KINDS.
157
- bus.register(new HttpTransport());
158
- bus.register(new CdpBrowserTransport());
159
- bus.register(new SubprocessTransport());
160
- bus.register(new DesktopAxTransport());
161
- bus.register(new DesktopUiaTransport());
162
- bus.register(new DesktopAtspiTransport());
163
- bus.register(new CuaTransport());
164
- sharedBus = bus;
165
- return bus;
166
- }
167
- /** Exposed for tests — reset the shared bus between runs. */
168
- export function __resetTransportBusForTests() {
169
- sharedBus = undefined;
170
- }
171
- function buildTransportCtx(ctx) {
172
- return {
173
- cwd: process.cwd(),
174
- env: process.env,
175
- cookieHeader: ctx.cookieHeader,
176
- vars: ctx.vars,
177
- bus: getBus(),
178
- };
179
- }
180
- function isCuaStep(action) {
181
- return action in CUA_STEP_HANDLERS;
182
- }
183
- function isDesktopAxStep(action) {
184
- return action in DESKTOP_AX_STEP_HANDLERS;
185
- }
186
- function getActionEntry(step) {
187
- const entries = Object.entries(step);
188
- return (entries.find(([k]) => !SIBLING_KEYS.has(k)) ?? entries[0]);
189
- }
190
- /** Dispatch a single pipeline step by action name. */
191
- async function executeStep(ctx, action, config, stepIndex, fullStep, depth) {
192
- switch (action) {
193
- case "fetch":
194
- return stepFetch(ctx, config);
195
- case "fetch_text":
196
- return stepFetchText(ctx, config);
197
- case "parse_rss":
198
- return stepParseRss(ctx, config);
199
- case "select":
200
- return stepSelect(ctx, config, stepIndex);
201
- case "map":
202
- return stepMap(ctx, config);
203
- case "filter":
204
- return stepFilter(ctx, config);
205
- case "sort":
206
- return stepSort(ctx, config);
207
- case "limit":
208
- return stepLimit(ctx, config);
209
- case "exec":
210
- return stepExec(ctx, config);
211
- case "html_to_md":
212
- return stepHtmlToMd(ctx);
213
- case "write_temp":
214
- return stepWriteTemp(ctx, config);
215
- case "navigate":
216
- return stepNavigate(ctx, config);
217
- case "evaluate":
218
- return stepEvaluate(ctx, config);
219
- case "click":
220
- return stepClick(ctx, config);
221
- case "type":
222
- return stepType(ctx, config);
223
- case "wait":
224
- return stepWaitBrowser(ctx, config);
225
- case "intercept":
226
- return stepIntercept(ctx, config);
227
- case "press":
228
- return stepPress(ctx, config);
229
- case "scroll":
230
- return stepScroll(ctx, config);
231
- case "snapshot":
232
- return stepSnapshot(ctx, config);
233
- case "tap":
234
- return stepTap(ctx, config);
235
- case "download":
236
- return stepDownload(ctx, config);
237
- case "websocket":
238
- return stepWebsocket(ctx, config);
239
- case "rate_limit": {
240
- const rlConfig = config;
241
- const { waitForToken } = await import("./rate-limiter.js");
242
- await waitForToken(rlConfig.domain, rlConfig.rpm ?? 60);
243
- return ctx;
244
- }
245
- case "assert":
246
- return stepAssert(ctx, config, stepIndex);
247
- case "set":
248
- return stepSet(ctx, config);
249
- case "append":
250
- return stepAppend(ctx, config);
251
- case "if": {
252
- const ifStep = (fullStep ?? { if: config });
253
- return stepIf(ctx, ifStep, stepIndex, (depth ?? 0) + 1);
254
- }
255
- case "each":
256
- return stepEach(ctx, config, stepIndex, depth ?? 0);
257
- case "parallel": {
258
- const mergeStrategy = fullStep?.merge ?? "concat";
259
- return stepParallel(ctx, config, mergeStrategy, stepIndex, depth ?? 0);
260
- }
261
- case "extract":
262
- return stepExtract(ctx, config);
263
- default: {
264
- // CUA family + macOS AX family dispatch through the transport bus.
265
- // `action()` never throws — envelopes surface via ctx.vars.lastEnvelope.
266
- if (isCuaStep(action)) {
267
- const handler = CUA_STEP_HANDLERS[action];
268
- const params = config && typeof config === "object" && !Array.isArray(config)
269
- ? config
270
- : {};
271
- const envelope = await handler({ bus: getBus(), transportCtx: buildTransportCtx(ctx) }, params);
272
- ctx.vars["lastEnvelope"] = envelope;
273
- return { ...ctx, data: envelope.ok ? envelope.data : envelope };
274
- }
275
- if (isDesktopAxStep(action)) {
276
- const handler = DESKTOP_AX_STEP_HANDLERS[action];
277
- const params = config && typeof config === "object" && !Array.isArray(config)
278
- ? config
279
- : {};
280
- const envelope = await handler({ bus: getBus(), transportCtx: buildTransportCtx(ctx) }, params);
281
- ctx.vars["lastEnvelope"] = envelope;
282
- return { ...ctx, data: envelope.ok ? envelope.data : envelope };
283
- }
284
- // Check plugin custom step registry before giving up
285
- const { getCustomStep } = await import("../plugin/step-registry.js");
286
- const customHandler = getCustomStep(action);
287
- if (customHandler) {
288
- const pluginCtx = {
289
- data: ctx.data,
290
- args: ctx.args,
291
- vars: ctx.vars,
292
- base: ctx.base,
293
- cookieHeader: ctx.cookieHeader,
294
- };
295
- const result = await customHandler(pluginCtx, config);
296
- return { ...ctx, data: result.data, vars: result.vars };
297
- }
298
- return ctx;
299
- }
300
- }
301
- }
302
- export async function runPipeline(steps, args, base, options) {
303
- // Load cookies for cookie/header strategy (disk first, CDP fallback)
304
- let cookieHeader;
305
- if ((options?.strategy === "cookie" || options?.strategy === "header") &&
306
- options?.site) {
307
- const cookies = await loadCookiesWithCDP(options.site);
308
- if (!cookies) {
309
- throw new PipelineError(`No cookies found for "${options.site}". Run: unicli auth setup ${options.site}`, {
310
- step: -1,
311
- action: "auth",
312
- config: { site: options.site, strategy: options.strategy },
313
- errorType: "http_error",
314
- suggestion: `Either start Chrome with "unicli browser start" and login to ${options.site}, or create cookie file at ~/.unicli/cookies/${options.site}.json`,
315
- retryable: false,
316
- alternatives: [`unicli auth setup ${options.site}`],
317
- });
318
- }
319
- cookieHeader = formatCookieHeader(cookies);
320
- }
321
- let ctx = { data: null, args, vars: {}, base, cookieHeader };
322
- let tempDir;
323
- try {
324
- for (let i = 0; i < steps.length; i++) {
325
- const step = steps[i];
326
- const [action, config] = getActionEntry(step);
327
- // --- Fallback extraction ---
328
- // Fallback can live inside the step config (object configs like fetch)
329
- // or as a sibling key in the step object (string configs like select).
330
- let stepConfig = config;
331
- let fallbacks;
332
- if (stepConfig &&
333
- typeof stepConfig === "object" &&
334
- !Array.isArray(stepConfig)) {
335
- const configObj = stepConfig;
336
- if ("fallback" in configObj) {
337
- fallbacks = Array.isArray(configObj.fallback)
338
- ? configObj.fallback
339
- : [configObj.fallback];
340
- const { fallback: _, ...rest } = configObj;
341
- stepConfig = rest;
342
- }
343
- }
344
- if (!fallbacks) {
345
- const stepObj = step;
346
- if ("fallback" in stepObj) {
347
- const fb = stepObj.fallback;
348
- fallbacks = Array.isArray(fb) ? fb : [fb];
349
- }
350
- }
351
- // Filter out null/undefined fallback entries (e.g. `fallback:` with no value in YAML)
352
- if (fallbacks) {
353
- fallbacks = fallbacks.filter((fb) => fb != null);
354
- if (fallbacks.length === 0)
355
- fallbacks = undefined;
356
- }
357
- // --- Retry configuration ---
358
- const retryCount = getRetryCount(step, stepConfig);
359
- const backoffMs = getBackoffMs(step, stepConfig);
360
- // Strip retry/backoff from stepConfig to avoid passing them to step implementations
361
- if (stepConfig &&
362
- typeof stepConfig === "object" &&
363
- !Array.isArray(stepConfig)) {
364
- const cfgObj = stepConfig;
365
- if ("retry" in cfgObj || "backoff" in cfgObj) {
366
- const { retry: _r, backoff: _b, ...rest } = cfgObj;
367
- stepConfig = rest;
368
- }
369
- }
370
- try {
371
- if (retryCount > 0) {
372
- // Retry-aware execution: wraps primary + fallback
373
- let lastRetryErr;
374
- let retrySucceeded = false;
375
- for (let attempt = 0; attempt <= retryCount; attempt++) {
376
- try {
377
- // Inner: primary step + fallback
378
- try {
379
- ctx = await executeStep(ctx, action, stepConfig, i, step);
380
- }
381
- catch (primaryErr) {
382
- if (!fallbacks || fallbacks.length === 0)
383
- throw primaryErr;
384
- let lastErr = primaryErr;
385
- let fbOk = false;
386
- for (const fb of fallbacks) {
387
- try {
388
- ctx = await executeStep(ctx, action, fb, i, step);
389
- fbOk = true;
390
- break;
391
- }
392
- catch (fbErr) {
393
- lastErr = fbErr;
394
- }
395
- }
396
- if (!fbOk)
397
- throw lastErr;
398
- }
399
- retrySucceeded = true;
400
- break;
401
- }
402
- catch (retryErr) {
403
- lastRetryErr = retryErr;
404
- if (attempt < retryCount) {
405
- const delay = backoffMs * Math.pow(2, attempt);
406
- await new Promise((r) => setTimeout(r, delay));
407
- }
408
- }
409
- }
410
- if (!retrySucceeded && lastRetryErr)
411
- throw lastRetryErr;
412
- }
413
- else {
414
- // Original execution path (no retry): primary + fallback
415
- try {
416
- ctx = await executeStep(ctx, action, stepConfig, i, step);
417
- }
418
- catch (primaryErr) {
419
- if (!fallbacks || fallbacks.length === 0)
420
- throw primaryErr;
421
- let lastErr = primaryErr;
422
- let succeeded = false;
423
- for (const fb of fallbacks) {
424
- try {
425
- ctx = await executeStep(ctx, action, fb, i, step);
426
- succeeded = true;
427
- break;
428
- }
429
- catch (fbErr) {
430
- lastErr = fbErr;
431
- }
432
- }
433
- if (!succeeded)
434
- throw lastErr;
435
- }
436
- }
437
- }
438
- catch (err) {
439
- // Auto-fix: try alternative select paths when selector_miss
440
- if (action === "select" &&
441
- err instanceof PipelineError &&
442
- err.detail.errorType === "selector_miss" &&
443
- options?.site) {
444
- try {
445
- const { suggestSelectFix } = await import("./auto-fix.js");
446
- const suggestions = suggestSelectFix(ctx.data, stepConfig);
447
- let fixed = false;
448
- for (const suggestion of suggestions) {
449
- try {
450
- ctx = stepSelect(ctx, suggestion, i);
451
- process.stderr.write(`[auto-fix] ${options.site}: select path changed "${String(stepConfig)}" → "${suggestion}"\n`);
452
- fixed = true;
453
- break;
454
- }
455
- catch {
456
- // Try next suggestion
457
- }
458
- }
459
- if (fixed)
460
- continue;
461
- }
462
- catch {
463
- // Auto-fix module not available
464
- }
465
- }
466
- // Emit diagnostic context for agent self-repair
467
- if (process.env.UNICLI_DIAGNOSTIC === "1") {
468
- try {
469
- const { buildRepairContext, emitRepairContext } = await import("./diagnostic.js");
470
- const repairCtx = await buildRepairContext({
471
- error: err instanceof Error ? err : new Error(String(err)),
472
- site: options?.site ?? "unknown",
473
- command: "unknown",
474
- page: ctx.page,
475
- });
476
- emitRepairContext(repairCtx);
477
- }
478
- catch {
479
- // Diagnostic collection failure should never mask the original error
480
- }
481
- }
482
- // Smart cookie refresh on auth failure
483
- if (err instanceof PipelineError &&
484
- (err.detail.statusCode === 401 || err.detail.statusCode === 403) &&
485
- (options?.strategy === "cookie" || options?.strategy === "header") &&
486
- options?.site) {
487
- try {
488
- const { refreshCookies } = await import("./cookie-refresh.js");
489
- const refreshed = await refreshCookies(options.site);
490
- if (refreshed) {
491
- process.stderr.write(`[cookie-refresh] Cookies refreshed for ${options.site}, retry the command.\n`);
492
- }
493
- }
494
- catch {
495
- // Cookie refresh failure is non-fatal
496
- }
497
- }
498
- if (err instanceof PipelineError)
499
- throw err;
500
- const errMsg = err instanceof Error ? err.message : String(err);
501
- const isTransient = /timeout|ETIMEDOUT|ECONNREFUSED|ECONNRESET|socket hang up/i.test(errMsg);
502
- throw new PipelineError(`Step ${i} (${action}) failed: ${errMsg}`, {
503
- step: i,
504
- action,
505
- config,
506
- errorType: isTransient ? "timeout" : "parse_error",
507
- suggestion: `Check the ${action} step at index ${i} in the adapter YAML. The expression or configuration may be invalid.`,
508
- retryable: isTransient,
509
- alternatives: [],
510
- });
511
- }
512
- if (ctx.tempDir)
513
- tempDir = ctx.tempDir;
514
- }
515
- const result = ctx.data;
516
- if (Array.isArray(result))
517
- return result;
518
- if (result !== null && result !== undefined)
519
- return [result];
520
- return [];
521
- }
522
- finally {
523
- if (tempDir) {
524
- try {
525
- rmSync(tempDir, { recursive: true, force: true });
526
- }
527
- catch {
528
- // Best-effort cleanup
529
- }
530
- }
531
- if (ctx.page) {
532
- try {
533
- await ctx.page.close();
534
- }
535
- catch {
536
- // Best-effort cleanup
537
- }
538
- }
539
- }
540
- }
541
- // --- Retry helpers ---
542
- function getRetryCount(step, config) {
543
- const stepObj = step;
544
- if (typeof stepObj.retry === "number")
545
- return stepObj.retry;
546
- if (config &&
547
- typeof config === "object" &&
548
- !Array.isArray(config) &&
549
- "retry" in config) {
550
- return Number(config.retry) || 0;
551
- }
552
- return 0;
553
- }
554
- function getBackoffMs(step, config) {
555
- const stepObj = step;
556
- if (typeof stepObj.backoff === "number")
557
- return stepObj.backoff;
558
- if (config &&
559
- typeof config === "object" &&
560
- !Array.isArray(config) &&
561
- "backoff" in config) {
562
- return Number(config.backoff) || 1000;
563
- }
564
- return 1000;
565
- }
566
- export async function stepAssert(ctx, config, stepIndex) {
567
- const page = ctx.page ? ctx.page : undefined;
568
- // URL assertion
569
- if (config.url) {
570
- if (!page)
571
- throw assertionError("url assertion requires a browser page", config, stepIndex);
572
- const currentUrl = await page.url();
573
- const expected = evalTemplate(config.url, ctx);
574
- if (!currentUrl.includes(expected)) {
575
- throw assertionError(`URL mismatch: expected "${expected}" in "${currentUrl}"`, config, stepIndex);
576
- }
577
- }
578
- // Selector assertion
579
- if (config.selector) {
580
- if (!page)
581
- throw assertionError("selector assertion requires a browser page", config, stepIndex);
582
- const selector = evalTemplate(config.selector, ctx);
583
- const exists = await page.evaluate(`!!document.querySelector(${JSON.stringify(selector)})`);
584
- if (!exists) {
585
- throw assertionError(`Element not found: ${selector}`, config, stepIndex);
586
- }
587
- }
588
- // Text assertion
589
- if (config.text) {
590
- if (!page)
591
- throw assertionError("text assertion requires a browser page", config, stepIndex);
592
- const expected = evalTemplate(config.text, ctx);
593
- const bodyText = (await page.evaluate("document.body?.innerText || ''"));
594
- if (!bodyText.includes(expected)) {
595
- throw assertionError(`Text not found: "${expected}"`, config, stepIndex);
596
- }
597
- }
598
- // Condition assertion (works without browser)
599
- if (config.condition) {
600
- const expr = evalTemplate(config.condition, ctx);
601
- const result = evalExpression(expr, {
602
- data: ctx.data,
603
- args: ctx.args,
604
- vars: ctx.vars,
605
- });
606
- if (!result) {
607
- throw assertionError(`Condition failed: ${expr}`, config, stepIndex);
608
- }
609
- }
610
- return ctx;
611
- }
612
- function assertionError(message, config, stepIndex) {
613
- return new PipelineError(config.message ?? message, {
614
- step: stepIndex,
615
- action: "assert",
616
- config,
617
- errorType: "assertion_failed",
618
- suggestion: "Check the assertion conditions in the adapter YAML. The page state may not match expectations.",
619
- retryable: false,
620
- alternatives: [],
621
- });
622
- }
623
- export async function stepFetch(ctx, config) {
624
- let url = evalTemplate(config.url, ctx);
625
- assertSafeRequestUrl(url);
626
- // If data is an array of items with IDs, fetch each one (fan-out with concurrency limit)
627
- if (Array.isArray(ctx.data)) {
628
- const items = ctx.data;
629
- const concurrency = config
630
- .concurrency
631
- ? Number(config.concurrency)
632
- : 5;
633
- const results = await mapConcurrent(items, concurrency, async (item) => {
634
- const itemCtx = { ...ctx, data: item };
635
- const itemUrl = evalTemplate(config.url, itemCtx);
636
- assertSafeRequestUrl(itemUrl);
637
- const resolvedConfig = config.body
638
- ? { ...config, body: resolveTemplateDeep(config.body, itemCtx) }
639
- : config;
640
- return fetchJson(itemUrl, resolvedConfig, ctx.cookieHeader);
641
- });
642
- return { ...ctx, data: results };
643
- }
644
- // Append query params
645
- if (config.params) {
646
- const params = new URLSearchParams();
647
- for (const [k, v] of Object.entries(config.params)) {
648
- const val = evalTemplate(String(v), ctx);
649
- params.set(k, val);
650
- }
651
- url += (url.includes("?") ? "&" : "?") + params.toString();
652
- }
653
- const resolvedConfig = config.body
654
- ? { ...config, body: resolveTemplateDeep(config.body, ctx) }
655
- : config;
656
- // Strategy fallback: if no cookie and fetch returns 401/403, try with cookies
657
- try {
658
- const data = await fetchJson(url, resolvedConfig, ctx.cookieHeader);
659
- return { ...ctx, data };
660
- }
661
- catch (err) {
662
- if (err instanceof PipelineError &&
663
- (err.detail.statusCode === 401 || err.detail.statusCode === 403) &&
664
- !ctx.cookieHeader) {
665
- // Attempt cookie fallback — try loading cookies for the domain
666
- try {
667
- const hostname = new URL(url).hostname;
668
- const siteName = hostname
669
- .replace(/^www\./, "")
670
- .split(".")
671
- .slice(0, -1)
672
- .join("-");
673
- const cookies = await loadCookiesWithCDP(siteName);
674
- if (cookies) {
675
- const fallbackCookie = formatCookieHeader(cookies);
676
- const data = await fetchJson(url, resolvedConfig, fallbackCookie);
677
- return { ...ctx, data, cookieHeader: fallbackCookie };
678
- }
679
- }
680
- catch {
681
- // Cookie fallback also failed — throw original
682
- }
683
- }
684
- throw err;
685
- }
686
- }
687
- // --- Fetch response cache ---
688
- const CACHE_DIR = join(homedir(), ".unicli", "cache");
689
- function fetchCacheKey(url, method) {
690
- return createHash("sha256")
691
- .update(`${method}:${url}`)
692
- .digest("hex")
693
- .slice(0, 16);
694
- }
695
- function readFetchCache(url, method, ttlSeconds) {
696
- const key = fetchCacheKey(url, method);
697
- const filePath = join(CACHE_DIR, `${key}.json`);
698
- if (!existsSync(filePath))
699
- return null;
700
- try {
701
- const raw = readFileSync(filePath, "utf-8");
702
- const entry = JSON.parse(raw);
703
- if (Date.now() - entry.ts > ttlSeconds * 1000)
704
- return null;
705
- return entry.data;
706
- }
707
- catch {
708
- return null;
709
- }
710
- }
711
- const MAX_CACHE_ENTRY_BYTES = 10 * 1024 * 1024; // 10MB per entry
712
- function writeFetchCache(url, method, data) {
713
- try {
714
- const payload = JSON.stringify({ ts: Date.now(), url, data });
715
- if (payload.length > MAX_CACHE_ENTRY_BYTES)
716
- return; // reject oversized responses
717
- mkdirSync(CACHE_DIR, { recursive: true });
718
- const key = fetchCacheKey(url, method);
719
- writeFileSync(join(CACHE_DIR, `${key}.json`), payload);
720
- }
721
- catch {
722
- /* cache write failure is non-fatal */
723
- }
724
- }
725
- async function fetchJson(url, config, cookieHeader) {
726
- const method = config.method ?? "GET";
727
- // Check cache before making network request
728
- if (config.cache && config.cache > 0) {
729
- const cached = readFetchCache(url, method, config.cache);
730
- if (cached !== null)
731
- return cached;
732
- }
733
- const headers = {
734
- Accept: "application/json",
735
- "User-Agent": USER_AGENT,
736
- ...config.headers,
737
- };
738
- if (cookieHeader) {
739
- headers["Cookie"] = cookieHeader;
740
- }
741
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dispatcher from undici not in standard RequestInit
742
- const init = { method, headers };
743
- if (config.body && method !== "GET") {
744
- headers["Content-Type"] = "application/json";
745
- init.body = JSON.stringify(config.body);
746
- }
747
- const proxyAgent = getProxyAgent();
748
- if (proxyAgent)
749
- init.dispatcher = proxyAgent;
750
- const maxAttempts = config.retry ?? 1;
751
- const baseDelay = config.backoff ?? 1000;
752
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
753
- const resp = await fetch(url, init);
754
- if (resp.ok) {
755
- const data = await resp.json();
756
- if (config.cache && config.cache > 0)
757
- writeFetchCache(url, method, data);
758
- return data;
759
- }
760
- const isRetryable = resp.status === 429 || resp.status >= 500;
761
- const isLastAttempt = attempt === maxAttempts;
762
- if (isRetryable && !isLastAttempt) {
763
- await new Promise((r) => setTimeout(r, baseDelay * 2 ** (attempt - 1)));
764
- continue;
765
- }
766
- // Non-retryable error or last attempt — throw
767
- let preview = "";
768
- try {
769
- preview = (await resp.text()).slice(0, 200);
770
- }
771
- catch {
772
- /* ignore */
773
- }
774
- const isRetryableStatus = resp.status === 429 ||
775
- resp.status === 500 ||
776
- resp.status === 502 ||
777
- resp.status === 503;
778
- throw new PipelineError(`HTTP ${resp.status} ${resp.statusText} from ${url}`, {
779
- step: -1, // will be overwritten by caller
780
- action: "fetch",
781
- config: { url, method },
782
- errorType: "http_error",
783
- url,
784
- statusCode: resp.status,
785
- responsePreview: preview,
786
- suggestion: resp.status === 403
787
- ? "The API is blocking requests. The endpoint may require authentication (cookie strategy) or the User-Agent may need updating."
788
- : resp.status === 404
789
- ? "The API endpoint was not found. The URL path may have changed — check the target site for the current API."
790
- : resp.status === 429
791
- ? "Rate limited. Add a delay between requests or reduce the limit parameter."
792
- : `HTTP ${resp.status} error. Check if the API endpoint is still valid.`,
793
- retryable: isRetryableStatus,
794
- alternatives: resp.status === 401 || resp.status === 403
795
- ? ["unicli auth setup <site>"]
796
- : [],
797
- });
798
- }
799
- // Unreachable — loop always returns or throws — but satisfies TypeScript
800
- throw new Error("fetchJson: unreachable");
801
- }
802
- export function stepSelect(ctx, path, stepIndex) {
803
- const resolved = evalTemplate(path, ctx);
804
- const data = getNestedValue(ctx.data, resolved);
805
- if (data === undefined || data === null) {
806
- throw new PipelineError(`Select "${resolved}" returned nothing — the response structure may have changed`, {
807
- step: stepIndex,
808
- action: "select",
809
- config: path,
810
- errorType: "selector_miss",
811
- suggestion: `The path "${resolved}" does not exist in the API response. Inspect the actual response JSON to find the correct path, then update the "select" step in the adapter YAML.`,
812
- retryable: false,
813
- alternatives: [],
814
- });
815
- }
816
- return { ...ctx, data };
817
- }
818
- export function stepMap(ctx, template) {
819
- if (!Array.isArray(ctx.data))
820
- return ctx;
821
- const items = ctx.data;
822
- const mapped = items.map((item, index) => {
823
- const row = {};
824
- for (const [key, expr] of Object.entries(template)) {
825
- row[key] = evalTemplate(String(expr), {
826
- ...ctx,
827
- data: { item, index },
828
- });
829
- }
830
- return row;
831
- });
832
- return { ...ctx, data: mapped };
833
- }
834
- export function stepFilter(ctx, expr) {
835
- if (!Array.isArray(ctx.data))
836
- return ctx;
837
- const items = ctx.data;
838
- const filtered = items.filter((item, index) => {
839
- const result = evalExpression(expr, { item, index, args: ctx.args });
840
- return Boolean(result);
841
- });
842
- return { ...ctx, data: filtered };
843
- }
844
- export function stepLimit(ctx, config) {
845
- if (!Array.isArray(ctx.data))
846
- return ctx;
847
- let n;
848
- if (typeof config === "number") {
849
- n = config;
850
- }
851
- else {
852
- const val = evalTemplate(String(config), ctx);
853
- n = parseInt(val, 10) || 20;
854
- }
855
- return { ...ctx, data: ctx.data.slice(0, n) };
856
- }
857
- // --- fetch_text: like fetch but returns raw text (for XML/RSS/HTML) ---
858
- export async function stepFetchText(ctx, config) {
859
- let url = evalTemplate(config.url, ctx);
860
- assertSafeRequestUrl(url);
861
- if (config.params) {
862
- const params = new URLSearchParams();
863
- for (const [k, v] of Object.entries(config.params)) {
864
- params.set(k, evalTemplate(String(v), ctx));
865
- }
866
- url += (url.includes("?") ? "&" : "?") + params.toString();
867
- }
868
- const method = config.method ?? "GET";
869
- const headers = {
870
- "User-Agent": USER_AGENT,
871
- ...config.headers,
872
- };
873
- if (ctx.cookieHeader) {
874
- headers["Cookie"] = ctx.cookieHeader;
875
- }
876
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dispatcher from undici not in standard RequestInit
877
- const fetchInit = { method, headers };
878
- const ftAgent = getProxyAgent();
879
- if (ftAgent)
880
- fetchInit.dispatcher = ftAgent;
881
- const resp = await fetch(url, fetchInit);
882
- if (!resp.ok) {
883
- throw new PipelineError(`HTTP ${resp.status} ${resp.statusText} from ${url}`, {
884
- step: -1,
885
- action: "fetch_text",
886
- config: { url, method },
887
- errorType: "http_error",
888
- url,
889
- statusCode: resp.status,
890
- suggestion: `Check if the URL is still valid: ${url}`,
891
- retryable: resp.status === 429 ||
892
- resp.status === 500 ||
893
- resp.status === 502 ||
894
- resp.status === 503,
895
- alternatives: resp.status === 401 || resp.status === 403
896
- ? ["unicli auth setup <site>"]
897
- : [],
898
- });
899
- }
900
- const text = await resp.text();
901
- return { ...ctx, data: text };
902
- }
903
- export function stepParseRss(ctx, config) {
904
- const xml = String(ctx.data ?? "");
905
- const items = [];
906
- // Support both RSS 2.0 (<item>) and Atom (<entry>) formats
907
- const isAtom = xml.includes("<entry>");
908
- const itemRegex = isAtom
909
- ? /<entry>([\s\S]*?)<\/entry>/g
910
- : /<item>([\s\S]*?)<\/item>/g;
911
- let match;
912
- while ((match = itemRegex.exec(xml)) !== null) {
913
- const block = match[1];
914
- if (config?.fields) {
915
- const row = {};
916
- for (const [key, tag] of Object.entries(config.fields)) {
917
- row[key] = extractXmlTag(block, tag);
918
- }
919
- items.push(row);
920
- }
921
- else if (isAtom) {
922
- // Atom format: <title>, <link href="...">, <published>, <summary>/<content>
923
- const linkMatch = block.match(/<link[^>]*rel=["']alternate["'][^>]*href=["']([^"']+)["']/);
924
- const linkHref = linkMatch?.[1] ??
925
- block.match(/<link[^>]*href=["']([^"']+)["']/)?.[1] ??
926
- "";
927
- items.push({
928
- title: extractXmlCdata(block, "title"),
929
- description: extractXmlCdata(block, "content") ||
930
- extractXmlCdata(block, "summary"),
931
- link: linkHref,
932
- pubDate: extractXmlTag(block, "published") || extractXmlTag(block, "updated"),
933
- guid: extractXmlTag(block, "id"),
934
- });
935
- }
936
- else {
937
- items.push({
938
- title: extractXmlCdata(block, "title"),
939
- description: extractXmlCdata(block, "description"),
940
- link: extractXmlTag(block, "link"),
941
- pubDate: extractXmlTag(block, "pubDate"),
942
- guid: extractXmlTag(block, "guid"),
943
- });
944
- }
945
- }
946
- return { ...ctx, data: items };
947
- }
948
- function extractXmlCdata(xml, tag) {
949
- const cdataMatch = xml.match(new RegExp(`<${tag}><!\\[CDATA\\[([\\s\\S]*?)\\]\\]></${tag}>`));
950
- if (cdataMatch)
951
- return cdataMatch[1].trim();
952
- return extractXmlTag(xml, tag);
953
- }
954
- function extractXmlTag(xml, tag) {
955
- const m = xml.match(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)</${tag}>`));
956
- return m ? m[1].trim() : "";
957
- }
958
- export function stepSort(ctx, config) {
959
- if (!Array.isArray(ctx.data))
960
- return ctx;
961
- const items = [...ctx.data];
962
- const desc = config.order === "desc";
963
- items.sort((a, b) => {
964
- const va = a[config.by];
965
- const vb = b[config.by];
966
- const na = Number(va);
967
- const nb = Number(vb);
968
- if (!isNaN(na) && !isNaN(nb))
969
- return desc ? nb - na : na - nb;
970
- return desc
971
- ? String(vb ?? "").localeCompare(String(va ?? ""))
972
- : String(va ?? "").localeCompare(String(vb ?? ""));
973
- });
974
- return { ...ctx, data: items };
975
- }
976
- export async function stepExec(ctx, config) {
977
- const cmd = evalTemplate(config.command, ctx);
978
- const execArgs = (config.args ?? []).map((a) => evalTemplate(String(a), ctx));
979
- const timeout = config.timeout ?? 30000;
980
- // Sensitive-path deny list — scan every arg that looks like a path before
981
- // touching subprocess. Cannot be overridden by permission mode. Defends
982
- // against prompt-injection that smuggles a credential path into args.
983
- // Uses the realpath-aware variant so `ln -s ~/.ssh/id_rsa /tmp/x.txt` is
984
- // still blocked.
985
- for (const arg of execArgs) {
986
- if (typeof arg !== "string" || arg.length === 0)
987
- continue;
988
- if (!arg.startsWith("/") && !arg.startsWith("~/"))
989
- continue;
990
- const expanded = arg.startsWith("~/") ? join(homedir(), arg.slice(2)) : arg;
991
- const matched = matchSensitivePathRealpath(expanded);
992
- if (matched) {
993
- const denial = buildSensitivePathDenial(expanded);
994
- // The error message is the canonical `sensitive_path_denied` string so
995
- // agents can pattern-match the same identifier regardless of whether
996
- // the block fires in operate upload or the exec pipeline step. The
997
- // full denial payload (path, pattern, hint) is inlined into `config`
998
- // for `toAgentJSON()` to surface.
999
- throw new PipelineError("sensitive_path_denied", {
1000
- step: -1,
1001
- action: "exec",
1002
- config: {
1003
- command: cmd,
1004
- args: execArgs,
1005
- denial_path: denial.path,
1006
- denial_pattern: denial.pattern,
1007
- },
1008
- errorType: "assertion_failed",
1009
- suggestion: denial.hint,
1010
- retryable: false,
1011
- alternatives: [],
1012
- });
1013
- }
1014
- }
1015
- // Resolve env vars (merge with process.env)
1016
- let envOption;
1017
- if (config.env) {
1018
- const resolved = {};
1019
- for (const [k, v] of Object.entries(config.env)) {
1020
- resolved[k] = evalTemplate(String(v), ctx);
1021
- }
1022
- envOption = { ...process.env, ...resolved };
1023
- }
1024
- // Resolve stdin content
1025
- const stdinContent = config.stdin
1026
- ? evalTemplate(config.stdin, ctx)
1027
- : undefined;
1028
- // Resolve output_file path
1029
- const outputFile = config.output_file
1030
- ? evalTemplate(config.output_file, ctx)
1031
- : undefined;
1032
- try {
1033
- let stdout;
1034
- if (stdinContent !== undefined) {
1035
- // Use spawn to pipe stdin
1036
- const { spawn } = await import("node:child_process");
1037
- stdout = await new Promise((resolve, reject) => {
1038
- const child = spawn(cmd, execArgs, {
1039
- timeout,
1040
- env: envOption,
1041
- stdio: ["pipe", "pipe", "pipe"],
1042
- });
1043
- const chunks = [];
1044
- const errChunks = [];
1045
- child.stdout.on("data", (c) => chunks.push(c));
1046
- child.stderr.on("data", (c) => errChunks.push(c));
1047
- child.on("error", (err) => reject(err));
1048
- child.on("close", (code) => {
1049
- if (code !== 0) {
1050
- const stderr = Buffer.concat(errChunks).toString("utf8");
1051
- reject(new Error(`Process exited with code ${code}${stderr ? `: ${stderr}` : ""}`));
1052
- }
1053
- else {
1054
- resolve(Buffer.concat(chunks).toString("utf8"));
1055
- }
1056
- });
1057
- child.stdin.write(stdinContent);
1058
- child.stdin.end();
1059
- });
1060
- }
1061
- else {
1062
- // Use execFileAsync (original path) with optional env
1063
- const opts = {
1064
- timeout,
1065
- maxBuffer: 10 * 1024 * 1024,
1066
- };
1067
- if (envOption)
1068
- opts.env = envOption;
1069
- ({ stdout } = await execFileAsync(cmd, execArgs, opts));
1070
- }
1071
- // If output_file is specified, return file info instead of stdout
1072
- if (outputFile) {
1073
- const { stat } = await import("node:fs/promises");
1074
- try {
1075
- const info = await stat(outputFile);
1076
- return { ...ctx, data: { file: outputFile, size: info.size } };
1077
- }
1078
- catch {
1079
- throw new PipelineError(`exec "${cmd}" did not produce expected output file: ${outputFile}`, {
1080
- step: -1,
1081
- action: "exec",
1082
- config: { command: cmd, args: execArgs },
1083
- errorType: "parse_error",
1084
- suggestion: `Check that the command writes to "${outputFile}". Verify the path is correct.`,
1085
- retryable: false,
1086
- alternatives: [],
1087
- });
1088
- }
1089
- }
1090
- let data;
1091
- switch (config.parse ?? "lines") {
1092
- case "json":
1093
- data = JSON.parse(stdout);
1094
- break;
1095
- case "lines":
1096
- data = stdout
1097
- .split("\n")
1098
- .filter(Boolean)
1099
- .map((line) => ({ line }));
1100
- break;
1101
- case "csv": {
1102
- const lines = stdout.split("\n").filter(Boolean);
1103
- if (lines.length < 2) {
1104
- data = [];
1105
- break;
1106
- }
1107
- const headers = lines[0].split(",").map((h) => h.trim());
1108
- data = lines.slice(1).map((line) => {
1109
- const vals = line.split(",");
1110
- const row = {};
1111
- headers.forEach((h, i) => {
1112
- row[h] = (vals[i] ?? "").trim();
1113
- });
1114
- return row;
1115
- });
1116
- break;
1117
- }
1118
- case "text":
1119
- default:
1120
- data = stdout;
1121
- }
1122
- return { ...ctx, data };
1123
- }
1124
- catch (err) {
1125
- if (err instanceof PipelineError)
1126
- throw err;
1127
- const msg = err instanceof Error ? err.message : String(err);
1128
- const isExecTransient = /timeout|ETIMEDOUT|ECONNREFUSED|ECONNRESET/i.test(msg);
1129
- throw new PipelineError(`exec "${cmd}" failed: ${msg}`, {
1130
- step: -1,
1131
- action: "exec",
1132
- config: { command: cmd, args: execArgs },
1133
- errorType: isExecTransient ? "timeout" : "parse_error",
1134
- suggestion: `Check that "${cmd}" is installed and accessible. Run: which ${cmd}`,
1135
- retryable: isExecTransient,
1136
- alternatives: [],
1137
- });
1138
- }
1139
- }
1140
- // --- HTML to Markdown ---
1141
- export function stepHtmlToMd(ctx) {
1142
- const html = String(ctx.data ?? "");
1143
- const turndown = new TurndownService({
1144
- headingStyle: "atx",
1145
- codeBlockStyle: "fenced",
1146
- });
1147
- const md = turndown.turndown(html);
1148
- return { ...ctx, data: md };
1149
- }
1150
- // --- Template engine ---
1151
- /**
1152
- * Evaluate ${{ expression }} templates in a string.
1153
- * Returns the raw value if the entire string is a single expression,
1154
- * otherwise returns a string with interpolated values.
1155
- */
1156
- function evalTemplate(template, ctx) {
1157
- const fullMatch = template.match(/^\$\{\{\s*(.+?)\s*\}\}$/);
1158
- if (fullMatch) {
1159
- const result = evalExpression(fullMatch[1], buildScope(ctx));
1160
- return String(result ?? "");
1161
- }
1162
- return template.replace(/\$\{\{\s*(.+?)\s*\}\}/g, (_match, expr) => {
1163
- const result = evalExpression(expr, buildScope(ctx));
1164
- return String(result ?? "");
1165
- });
1166
- }
1167
- function buildScope(ctx) {
1168
- const scope = {
1169
- args: ctx.args,
1170
- vars: ctx.vars ?? {},
1171
- base: ctx.base,
1172
- temp: ctx.temp ?? {},
1173
- };
1174
- if (ctx.data &&
1175
- typeof ctx.data === "object" &&
1176
- "item" in ctx.data) {
1177
- const d = ctx.data;
1178
- scope.item = d.item;
1179
- scope.index = d.index;
1180
- }
1181
- else {
1182
- scope.item = ctx.data;
1183
- }
1184
- return scope;
1185
- }
1186
- // --- Set step (store pipeline variables) ---
1187
- export function stepSet(ctx, config) {
1188
- if (!config || typeof config !== "object" || Array.isArray(config))
1189
- return ctx;
1190
- const resolved = {};
1191
- for (const [key, value] of Object.entries(config)) {
1192
- resolved[key] = resolveTemplateDeep(value, ctx);
1193
- }
1194
- return { ...ctx, vars: { ...ctx.vars, ...resolved } };
1195
- }
1196
- // --- Append step (accumulate data into vars array) ---
1197
- export function stepAppend(ctx, key) {
1198
- if (typeof key !== "string" || !key)
1199
- return ctx;
1200
- const existing = ctx.vars[key];
1201
- const arr = Array.isArray(existing)
1202
- ? [...existing]
1203
- : existing !== undefined
1204
- ? [existing]
1205
- : [];
1206
- if (Array.isArray(ctx.data)) {
1207
- arr.push(...ctx.data);
1208
- }
1209
- else if (ctx.data !== null && ctx.data !== undefined) {
1210
- arr.push(ctx.data);
1211
- }
1212
- return { ...ctx, vars: { ...ctx.vars, [key]: arr } };
1213
- }
1214
- // --- If/else step (conditional branching) ---
1215
- export async function stepIf(ctx, config, stepIndex, depth = 0) {
1216
- if (depth > 10) {
1217
- throw new PipelineError("if step recursion depth exceeded (max 10)", {
1218
- step: stepIndex,
1219
- action: "if",
1220
- config,
1221
- errorType: "parse_error",
1222
- suggestion: "Reduce nesting depth of if/else steps. Maximum is 10 levels.",
1223
- retryable: false,
1224
- alternatives: [],
1225
- });
1226
- }
1227
- const conditionStr = typeof config.if === "string" ? config.if : String(config.if);
1228
- // Strip ${{ }} wrapper if present
1229
- const exprMatch = conditionStr.match(/^\$\{\{\s*(.+?)\s*\}\}$/);
1230
- const expr = exprMatch ? exprMatch[1] : conditionStr;
1231
- const result = evalExpression(expr, buildScope(ctx));
1232
- const branch = result ? config.then : config.else;
1233
- if (!branch || !Array.isArray(branch) || branch.length === 0)
1234
- return ctx;
1235
- // Execute sub-pipeline steps sequentially
1236
- for (let j = 0; j < branch.length; j++) {
1237
- const subStep = branch[j];
1238
- const [subAction, subConfig] = getActionEntry(subStep);
1239
- ctx = await executeStep(ctx, subAction, subConfig, stepIndex, subStep, depth);
1240
- }
1241
- return ctx;
1242
- }
1243
- export async function stepEach(ctx, config, stepIndex, depth) {
1244
- if (depth > 10) {
1245
- throw new PipelineError("each step recursion depth exceeded (max 10)", {
1246
- step: stepIndex,
1247
- action: "each",
1248
- config,
1249
- errorType: "parse_error",
1250
- suggestion: "Reduce nesting depth of loop steps. Maximum is 10 levels.",
1251
- retryable: false,
1252
- alternatives: [],
1253
- });
1254
- }
1255
- const maxIterations = Math.max(config.max ?? 100, 1);
1256
- const body = config.do;
1257
- if (!body || !Array.isArray(body) || body.length === 0)
1258
- return ctx;
1259
- for (let iteration = 0; iteration < maxIterations; iteration++) {
1260
- // Reset data at start of each iteration to prevent fetch fan-out
1261
- // from previous iteration's array data. State is carried via ctx.vars.
1262
- ctx = { ...ctx, data: null };
1263
- // Execute body sub-pipeline
1264
- for (const subStep of body) {
1265
- const [subAction, subConfig] = getActionEntry(subStep);
1266
- ctx = await executeStep(ctx, subAction, subConfig, stepIndex, subStep, depth + 1);
1267
- }
1268
- // Check until condition (after body execution — do-while semantics)
1269
- if (config.until) {
1270
- const condStr = typeof config.until === "string" ? config.until : String(config.until);
1271
- // Strip ${{ }} wrapper if present
1272
- const exprMatch = condStr.match(/^\$\{\{\s*(.+?)\s*\}\}$/);
1273
- const expr = exprMatch ? exprMatch[1] : condStr;
1274
- // Build scope with data alias for until condition evaluation
1275
- const scope = buildScope(ctx);
1276
- scope.data = ctx.data;
1277
- const result = evalExpression(expr, scope);
1278
- if (result)
1279
- break;
1280
- }
1281
- }
1282
- return ctx;
1283
- }
1284
- // --- Parallel step (concurrent branch execution with merge strategies) ---
1285
- export async function stepParallel(ctx, branches, merge, stepIndex, depth) {
1286
- if (!Array.isArray(branches) || branches.length === 0)
1287
- return ctx;
1288
- if (depth > 10) {
1289
- throw new PipelineError("parallel step recursion depth exceeded (max 10)", {
1290
- step: stepIndex,
1291
- action: "parallel",
1292
- config: branches,
1293
- errorType: "parse_error",
1294
- suggestion: "Reduce nesting depth of parallel steps. Maximum is 10 levels.",
1295
- retryable: false,
1296
- alternatives: [],
1297
- });
1298
- }
1299
- // Parallel concurrency cap — bound simultaneous branch execution so a
1300
- // pipeline with 100 parallel `fetch` steps doesn't exhaust the socket
1301
- // pool or trip per-host rate limiters. Default 5 mirrors `stepFetch`
1302
- // fan-out; overridable via `concurrency:` on the parallel step config
1303
- // (read when the runner dispatches; plumbing goes through executeStep).
1304
- const results = await mapConcurrent(branches, 5, async (branch) => {
1305
- const branchCtx = {
1306
- ...ctx,
1307
- vars: { ...ctx.vars },
1308
- };
1309
- const [action, config] = getActionEntry(branch);
1310
- const result = await executeStep(branchCtx, action, config, stepIndex, branch, depth + 1);
1311
- return result.data;
1312
- });
1313
- let merged;
1314
- switch (merge) {
1315
- case "zip": {
1316
- const first = results[0];
1317
- if (Array.isArray(first)) {
1318
- merged = first.map((_, i) => results.map((r) => (Array.isArray(r) ? r[i] : r)));
1319
- }
1320
- else {
1321
- merged = results;
1322
- }
1323
- break;
1324
- }
1325
- case "object":
1326
- merged = Object.fromEntries(results.map((r, i) => [String(i), r]));
1327
- break;
1328
- case "concat":
1329
- default:
1330
- merged = results.flatMap((r) => (Array.isArray(r) ? r : [r]));
1331
- break;
1332
- }
1333
- return { ...ctx, data: merged };
1334
- }
1335
- /**
1336
- * Recursively resolve ${{ }} templates in nested objects, arrays, and strings.
1337
- * Non-string primitives (numbers, booleans, null) pass through unchanged.
1338
- */
1339
- function resolveTemplateDeep(value, ctx) {
1340
- if (typeof value === "string") {
1341
- return evalTemplate(value, ctx);
1342
- }
1343
- if (Array.isArray(value)) {
1344
- return value.map((v) => resolveTemplateDeep(v, ctx));
1345
- }
1346
- if (value !== null && typeof value === "object") {
1347
- const result = {};
1348
- for (const [k, v] of Object.entries(value)) {
1349
- result[k] = resolveTemplateDeep(v, ctx);
1350
- }
1351
- return result;
1352
- }
1353
- return value;
1354
- }
1355
- /**
1356
- * Built-in pipe filters — used in template expressions like:
1357
- * ${{ item.tags | join(', ') }}
1358
- * ${{ args.word | urlencode }}
1359
- * ${{ item.text | slice(0, 200) }}
1360
- */
1361
- const PIPE_FILTERS = {
1362
- join: (val, sep) => Array.isArray(val) ? val.join(String(sep ?? ", ")) : String(val ?? ""),
1363
- urlencode: (val) => encodeURIComponent(String(val ?? "")),
1364
- slice: (val, start, end) => {
1365
- const s = String(val ?? "");
1366
- return s.slice(Number(start) || 0, end !== undefined ? Number(end) : undefined);
1367
- },
1368
- replace: (val, search, replacement) => String(val ?? "").replace(new RegExp(String(search), "g"), String(replacement ?? "")),
1369
- lowercase: (val) => String(val ?? "").toLowerCase(),
1370
- uppercase: (val) => String(val ?? "").toUpperCase(),
1371
- trim: (val) => String(val ?? "").trim(),
1372
- default: (val, fallback) => val == null || val === "" ? fallback : val,
1373
- split: (val, sep) => String(val ?? "").split(String(sep ?? ",")),
1374
- first: (val) => (Array.isArray(val) ? val[0] : val),
1375
- last: (val) => (Array.isArray(val) ? val[val.length - 1] : val),
1376
- length: (val) => Array.isArray(val) ? val.length : String(val ?? "").length,
1377
- strip_html: (val) => String(val ?? "").replace(/<[^>]+>/g, ""),
1378
- truncate: (val, max) => {
1379
- const s = String(val ?? "");
1380
- const n = Number(max) || 100;
1381
- return s.length > n ? s.slice(0, n) + "..." : s;
1382
- },
1383
- slugify: (val) => {
1384
- return String(val ?? "")
1385
- .normalize("NFKD")
1386
- .replace(/[\u0300-\u036f]/g, "")
1387
- .toLowerCase()
1388
- .replace(/[^a-z0-9]+/g, "-")
1389
- .replace(/^-+|-+$/g, "");
1390
- },
1391
- sanitize: (val) => String(val ?? "")
1392
- .replace(/[<>:"/\\|?*\x00-\x1f]/g, "_")
1393
- .replace(/^\.+/, "")
1394
- .trim() || "download",
1395
- ext: (val) => {
1396
- try {
1397
- const pathname = new URL(String(val)).pathname;
1398
- const dot = pathname.lastIndexOf(".");
1399
- return dot > 0 ? pathname.slice(dot + 1) : "";
1400
- }
1401
- catch {
1402
- const s = String(val ?? "");
1403
- const dot = s.lastIndexOf(".");
1404
- return dot > 0 ? s.slice(dot + 1).split(/[?#]/)[0] : "";
1405
- }
1406
- },
1407
- basename: (val) => {
1408
- try {
1409
- const pathname = new URL(String(val)).pathname;
1410
- return pathname.split("/").pop() ?? "";
1411
- }
1412
- catch {
1413
- return (String(val ?? "")
1414
- .split("/")
1415
- .pop() ?? "");
1416
- }
1417
- },
1418
- keys: (val) => val && typeof val === "object" && !Array.isArray(val)
1419
- ? Object.keys(val)
1420
- : [],
1421
- json: (val) => JSON.stringify(val),
1422
- abs: (val) => Math.abs(Number(val) || 0),
1423
- round: (val) => Math.round(Number(val) || 0),
1424
- ceil: (val) => Math.ceil(Number(val) || 0),
1425
- floor: (val) => Math.floor(Number(val) || 0),
1426
- int: (val) => parseInt(String(val), 10) || 0,
1427
- float: (val) => parseFloat(String(val)) || 0,
1428
- str: (val) => String(val ?? ""),
1429
- reverse: (val) => Array.isArray(val)
1430
- ? [...val].reverse()
1431
- : String(val ?? "")
1432
- .split("")
1433
- .reverse()
1434
- .join(""),
1435
- unique: (val) => (Array.isArray(val) ? [...new Set(val)] : val),
1436
- };
1437
- /**
1438
- * Parse pipe filters from expression: "expr | filter1(arg) | filter2"
1439
- * Returns { baseExpr, filters: [{ name, args }] }
1440
- */
1441
- function parsePipes(expr) {
1442
- // Only split on | that is NOT inside parentheses, quotes, or array syntax
1443
- const parts = [];
1444
- let current = "";
1445
- let depth = 0;
1446
- let inStr = null;
1447
- for (let i = 0; i < expr.length; i++) {
1448
- const ch = expr[i];
1449
- if (inStr) {
1450
- current += ch;
1451
- if (ch === inStr && expr[i - 1] !== "\\")
1452
- inStr = null;
1453
- continue;
1454
- }
1455
- if (ch === '"' || ch === "'") {
1456
- inStr = ch;
1457
- current += ch;
1458
- continue;
1459
- }
1460
- if (ch === "(" || ch === "[") {
1461
- depth++;
1462
- current += ch;
1463
- continue;
1464
- }
1465
- if (ch === ")" || ch === "]") {
1466
- depth--;
1467
- current += ch;
1468
- continue;
1469
- }
1470
- if (ch === "|" && depth === 0 && expr[i + 1] !== "|") {
1471
- // Check it's not || (logical OR)
1472
- parts.push(current.trim());
1473
- current = "";
1474
- continue;
1475
- }
1476
- current += ch;
1477
- }
1478
- parts.push(current.trim());
1479
- if (parts.length <= 1)
1480
- return { baseExpr: expr, filters: [] };
1481
- const baseExpr = parts[0];
1482
- const filters = parts.slice(1).map((f) => {
1483
- const m = f.match(/^(\w+)\((.*)\)$/s);
1484
- if (m) {
1485
- // Parse args — simple comma split respecting strings
1486
- const rawArgs = m[2].trim();
1487
- const args = rawArgs ? splitFilterArgs(rawArgs) : [];
1488
- return { name: m[1], args };
1489
- }
1490
- return { name: f.trim(), args: [] };
1491
- });
1492
- return { baseExpr, filters };
1493
- }
1494
- function splitFilterArgs(raw) {
1495
- const args = [];
1496
- let current = "";
1497
- let inStr = null;
1498
- let depth = 0;
1499
- for (const ch of raw) {
1500
- if (inStr) {
1501
- current += ch;
1502
- if (ch === inStr)
1503
- inStr = null;
1504
- continue;
1505
- }
1506
- if (ch === '"' || ch === "'") {
1507
- inStr = ch;
1508
- current += ch;
1509
- continue;
1510
- }
1511
- if (ch === "(") {
1512
- depth++;
1513
- current += ch;
1514
- continue;
1515
- }
1516
- if (ch === ")") {
1517
- depth--;
1518
- current += ch;
1519
- continue;
1520
- }
1521
- if (ch === "," && depth === 0) {
1522
- args.push(current.trim());
1523
- current = "";
1524
- continue;
1525
- }
1526
- current += ch;
1527
- }
1528
- args.push(current.trim());
1529
- return args;
1530
- }
1531
- /** Patterns that must never appear in evaluated expressions. */
1532
- const FORBIDDEN_EXPR = /constructor|__proto__|prototype|globalThis|process|require|import\s*\(|eval\s*\(/;
1533
- /**
1534
- * Safe expression evaluator using Node.js VM sandbox.
1535
- * Provides stronger isolation than `new Function()` with a 50ms timeout
1536
- * to prevent DoS. Simple dotted access (the most common case) uses a
1537
- * fast path that avoids the VM overhead entirely.
1538
- *
1539
- * Supports pipe filters: ${{ expr | join(', ') | slice(0, 100) }}
1540
- */
1541
- function evalExpression(expr, scope) {
1542
- try {
1543
- // Security: reject dangerous patterns
1544
- if (FORBIDDEN_EXPR.test(expr))
1545
- return undefined;
1546
- const { baseExpr, filters } = parsePipes(expr);
1547
- // Fast path: simple dotted access like "item.title" or "args.query"
1548
- if (/^[a-zA-Z_][\w.]*(\[\d+\])?$/.test(baseExpr)) {
1549
- let result = resolveDottedPath(baseExpr, scope);
1550
- for (const filter of filters) {
1551
- const filterFn = PIPE_FILTERS[filter.name];
1552
- if (!filterFn)
1553
- continue;
1554
- const evaledArgs = filter.args.map((a) => resolveFilterArg(a, scope));
1555
- result = filterFn(result, ...evaledArgs);
1556
- }
1557
- return result;
1558
- }
1559
- // VM sandbox evaluation with 50ms timeout.
1560
- // SECURITY: Create a null-prototype sandbox to prevent prototype chain escape.
1561
- // Node.js vm is NOT a security boundary — host objects leak constructors.
1562
- // We mitigate by: (1) null-prototype sandbox, (2) frozen copies of built-ins,
1563
- // (3) contextCodeGeneration restriction, (4) FORBIDDEN_EXPR pre-check.
1564
- const sandbox = Object.create(null);
1565
- // Copy scope values (args, item, index, etc.) — shallow copy with null prototype
1566
- for (const [k, v] of Object.entries(scope)) {
1567
- sandbox[k] = v;
1568
- }
1569
- // Add safe built-ins as frozen copies (prevents constructor chain traversal)
1570
- sandbox.encodeURIComponent = encodeURIComponent;
1571
- sandbox.decodeURIComponent = decodeURIComponent;
1572
- sandbox.JSON = { parse: JSON.parse, stringify: JSON.stringify };
1573
- sandbox.Math = Object.freeze({ ...Math });
1574
- sandbox.parseInt = parseInt;
1575
- sandbox.parseFloat = parseFloat;
1576
- sandbox.isNaN = isNaN;
1577
- sandbox.isFinite = isFinite;
1578
- let result;
1579
- try {
1580
- result = runInNewContext(`(${baseExpr})`, sandbox, {
1581
- timeout: 50,
1582
- contextCodeGeneration: { strings: false, wasm: false },
1583
- });
1584
- }
1585
- catch {
1586
- return undefined;
1587
- }
1588
- // Apply pipe filters
1589
- for (const filter of filters) {
1590
- const filterFn = PIPE_FILTERS[filter.name];
1591
- if (!filterFn)
1592
- continue;
1593
- const evaledArgs = filter.args.map((a) => resolveFilterArg(a, scope));
1594
- result = filterFn(result, ...evaledArgs);
1595
- }
1596
- return result;
1597
- }
1598
- catch {
1599
- return undefined;
1600
- }
1601
- }
1602
- /** Resolve a dotted path like "item.tags[0]" against the scope object. */
1603
- function resolveDottedPath(path, scope) {
1604
- // Handle array index: "item.tags[0]"
1605
- const cleanPath = path.replace(/\[(\d+)\]/g, ".$1");
1606
- const parts = cleanPath.split(".");
1607
- let current = scope[parts[0]];
1608
- for (let i = 1; i < parts.length; i++) {
1609
- if (current == null || typeof current !== "object")
1610
- return undefined;
1611
- current = current[parts[i]];
1612
- }
1613
- return current;
1614
- }
1615
- /** Resolve a single filter argument — string literal, number, or expression. */
1616
- function resolveFilterArg(a, scope) {
1617
- // String literal
1618
- if ((a.startsWith("'") && a.endsWith("'")) ||
1619
- (a.startsWith('"') && a.endsWith('"'))) {
1620
- return a.slice(1, -1);
1621
- }
1622
- // Number
1623
- if (/^-?\d+(\.\d+)?$/.test(a))
1624
- return Number(a);
1625
- // Security check
1626
- if (FORBIDDEN_EXPR.test(a))
1627
- return a;
1628
- // Expression via VM (same hardened sandbox as evalExpression)
1629
- try {
1630
- const sandbox = Object.create(null);
1631
- for (const [k, v] of Object.entries(scope))
1632
- sandbox[k] = v;
1633
- sandbox.JSON = { parse: JSON.parse, stringify: JSON.stringify };
1634
- sandbox.Math = Object.freeze({ ...Math });
1635
- sandbox.parseInt = parseInt;
1636
- sandbox.parseFloat = parseFloat;
1637
- return runInNewContext(`(${a})`, sandbox, {
1638
- timeout: 50,
1639
- contextCodeGeneration: { strings: false, wasm: false },
1640
- });
1641
- }
1642
- catch {
1643
- return a;
1644
- }
1645
- }
1646
- export function stepWriteTemp(ctx, config) {
1647
- const td = ctx.tempDir ?? join(tmpdir(), `unicli-${randomBytes(6).toString("hex")}`);
1648
- mkdirSync(td, { recursive: true });
1649
- const filename = evalTemplate(config.filename, ctx);
1650
- const content = evalTemplate(config.content, ctx);
1651
- const filePath = join(td, filename);
1652
- writeFileSync(filePath, content, "utf-8");
1653
- const key = filename.replace(/[^a-zA-Z0-9]/g, "_");
1654
- const temp = { ...(ctx.temp ?? {}), [key]: filePath };
1655
- return { ...ctx, temp, tempDir: td };
1656
- }
1657
- // --- Browser step implementations ---
1658
- /**
1659
- * Lazily acquire a BrowserPage. Connects on first use and caches on ctx.
1660
- */
1661
- async function acquirePage(ctx) {
1662
- if (ctx.page)
1663
- return ctx.page;
1664
- let port = 9222;
1665
- const rawPort = process.env.UNICLI_CDP_PORT;
1666
- if (rawPort) {
1667
- const p = parseInt(rawPort, 10);
1668
- if (Number.isInteger(p) && p >= 1 && p <= 65535) {
1669
- port = p;
1670
- }
1671
- }
1672
- // 1. Try direct CDP first (fastest, no daemon overhead)
1673
- try {
1674
- const { BrowserPage: BP } = await import("../browser/page.js");
1675
- const { injectStealth } = await import("../browser/stealth.js");
1676
- const page = await BP.connect(port);
1677
- await injectStealth(page.sendCDP.bind(page));
1678
- return page;
1679
- }
1680
- catch {
1681
- // CDP not available — try daemon
1682
- }
1683
- // 2. Fallback: daemon (reuses Chrome login sessions via extension)
1684
- try {
1685
- const { checkDaemonStatus } = await import("../browser/discover.js");
1686
- const status = await checkDaemonStatus({ timeout: 300 });
1687
- if (status.running && status.extensionConnected) {
1688
- const { BrowserBridge } = await import("../browser/bridge.js");
1689
- const bridge = new BrowserBridge();
1690
- const page = await bridge.connect({ timeout: 5000 });
1691
- return page;
1692
- }
1693
- }
1694
- catch {
1695
- // Daemon not available either
1696
- }
1697
- // 3. Last resort: auto-launch Chrome with debug port
1698
- try {
1699
- const { launchChrome } = await import("../browser/launcher.js");
1700
- const { BrowserPage: BP } = await import("../browser/page.js");
1701
- const { injectStealth } = await import("../browser/stealth.js");
1702
- await launchChrome(port);
1703
- // Poll for connection (5 attempts, 500ms intervals)
1704
- let page;
1705
- for (let attempt = 0; attempt < 5; attempt++) {
1706
- try {
1707
- page = await BP.connect(port);
1708
- break;
1709
- }
1710
- catch {
1711
- if (attempt < 4)
1712
- await new Promise((r) => setTimeout(r, 500));
1713
- }
1714
- }
1715
- if (!page)
1716
- throw new Error("Chrome launched but no page target available");
1717
- await injectStealth(page.sendCDP.bind(page));
1718
- return page;
1719
- }
1720
- catch (err) {
1721
- throw new Error(`Cannot connect to Chrome. Run "unicli browser start" first. (${err instanceof Error ? err.message : String(err)})`);
1722
- }
1723
- }
1724
- /**
1725
- * Wait until no new network requests occur for quietMs.
1726
- * Uses polling — checks page.networkRequests() count stability.
1727
- */
1728
- async function waitForNetworkIdle(page, maxMs = 5000, quietMs = 500) {
1729
- const start = Date.now();
1730
- let lastCount = -1;
1731
- let stableSince = Date.now();
1732
- while (Date.now() - start < maxMs) {
1733
- const requests = await page.networkRequests();
1734
- const currentCount = requests.length;
1735
- if (currentCount !== lastCount) {
1736
- lastCount = currentCount;
1737
- stableSince = Date.now();
1738
- }
1739
- else if (Date.now() - stableSince >= quietMs) {
1740
- return;
1741
- }
1742
- await page.waitFor(100);
1743
- }
1744
- }
1745
- export async function stepNavigate(ctx, config) {
1746
- const page = await acquirePage(ctx);
1747
- const url = evalTemplate(config.url, ctx);
1748
- const settleMs = config.settleMs ?? 0;
1749
- await page.goto(url, { settleMs, waitUntil: config.waitUntil });
1750
- if (config.waitUntil === "networkidle") {
1751
- await waitForNetworkIdle(page, 5000, 500);
1752
- }
1753
- return { ...ctx, page };
1754
- }
1755
- export async function stepEvaluate(ctx, config) {
1756
- const page = await acquirePage(ctx);
1757
- const expr = typeof config === "string"
1758
- ? evalTemplate(config, ctx)
1759
- : evalTemplate(config.expression, ctx);
1760
- const result = await page.evaluate(expr);
1761
- return { ...ctx, data: result, page };
1762
- }
1763
- export async function stepClick(ctx, config) {
1764
- const page = await acquirePage(ctx);
1765
- // String shorthand: just a CSS selector
1766
- if (typeof config === "string") {
1767
- const selector = evalTemplate(config, ctx);
1768
- await page.click(selector);
1769
- return { ...ctx, page };
1770
- }
1771
- // Coordinate-based click
1772
- if (config.x !== undefined && config.y !== undefined) {
1773
- await page.nativeClick(config.x, config.y);
1774
- return { ...ctx, page };
1775
- }
1776
- // Selector-based click
1777
- if (config.selector) {
1778
- const selector = evalTemplate(config.selector, ctx);
1779
- await page.click(selector);
1780
- return { ...ctx, page };
1781
- }
1782
- throw new PipelineError("click step requires either selector or x/y coordinates", {
1783
- step: -1,
1784
- action: "click",
1785
- config,
1786
- errorType: "expression_error",
1787
- suggestion: 'Provide either a CSS selector string, {selector: "..."}, or {x: N, y: N} for coordinate click.',
1788
- retryable: false,
1789
- alternatives: [],
1790
- });
1791
- }
1792
- export async function stepType(ctx, config) {
1793
- const page = await acquirePage(ctx);
1794
- const text = evalTemplate(config.text, ctx);
1795
- if (config.selector) {
1796
- const selector = evalTemplate(config.selector, ctx);
1797
- await page.type(selector, text);
1798
- }
1799
- else {
1800
- // No selector — type into currently focused element via CDP
1801
- await page.sendCDP("Input.insertText", { text });
1802
- }
1803
- if (config.submit)
1804
- await page.press("Enter");
1805
- return { ...ctx, page };
1806
- }
1807
- export async function stepWaitBrowser(ctx, config) {
1808
- const page = await acquirePage(ctx);
1809
- if (typeof config === "number") {
1810
- await page.waitFor(config);
1811
- }
1812
- else if (config.selector) {
1813
- await page.waitFor(config.selector, config.timeout ?? 10000);
1814
- }
1815
- else if (config.ms) {
1816
- await page.waitFor(config.ms);
1817
- }
1818
- return { ...ctx, page };
1819
- }
1820
- export async function stepIntercept(ctx, config) {
1821
- const page = await acquirePage(ctx);
1822
- const capturePattern = evalTemplate(config.capture, ctx);
1823
- const timeout = config.timeout ?? 10000;
1824
- // Install interceptor: patch fetch + XHR to capture matching responses
1825
- await page.evaluate(generateInterceptorJs(capturePattern, {
1826
- regex: config.regex,
1827
- captureAll: config.all,
1828
- captureText: config.captureText,
1829
- }));
1830
- // Execute trigger action
1831
- const trigger = evalTemplate(config.trigger, ctx);
1832
- if (trigger.startsWith("navigate:")) {
1833
- await page.goto(trigger.slice(9), { settleMs: 2000 });
1834
- }
1835
- else if (trigger.startsWith("click:")) {
1836
- await page.click(trigger.slice(6));
1837
- }
1838
- else if (trigger === "scroll") {
1839
- await page.scroll("down");
1840
- }
1841
- else if (trigger.startsWith("evaluate:")) {
1842
- await page.evaluate(trigger.slice(9));
1843
- }
1844
- // Poll for captured response
1845
- const startTime = Date.now();
1846
- let captured = null;
1847
- while (Date.now() - startTime < timeout) {
1848
- const result = await page.evaluate(generateReadInterceptedJs());
1849
- const arr = JSON.parse(result);
1850
- if (arr.length > 0) {
1851
- if (config.all) {
1852
- captured = arr.map((item) => item.data);
1853
- }
1854
- else {
1855
- captured = arr[arr.length - 1].data;
1856
- }
1857
- break;
1858
- }
1859
- await page.waitFor(200);
1860
- }
1861
- if (!captured) {
1862
- throw new PipelineError(`Intercept timeout: no request matching "${capturePattern}" captured within ${String(timeout)}ms`, {
1863
- step: -1,
1864
- action: "intercept",
1865
- config: { capture: capturePattern, trigger },
1866
- errorType: "timeout",
1867
- suggestion: `No network request matching "${capturePattern}" was observed. Verify the capture pattern matches the target API URL and that the trigger action causes the request.`,
1868
- retryable: true,
1869
- alternatives: [],
1870
- });
1871
- }
1872
- // Apply optional dot-path selector to captured data
1873
- let data = captured;
1874
- if (config.select) {
1875
- const segments = config.select.split(".");
1876
- for (const key of segments) {
1877
- if (data !== null && data !== undefined && typeof data === "object") {
1878
- data = data[key];
1879
- }
1880
- }
1881
- }
1882
- return { ...ctx, data, page };
1883
- }
1884
- // --- press: keyboard event dispatch ---
1885
- export async function stepPress(ctx, config) {
1886
- const page = await acquirePage(ctx);
1887
- if (typeof config === "string") {
1888
- await page.press(evalTemplate(config, ctx));
1889
- }
1890
- else {
1891
- const cfg = config;
1892
- const key = evalTemplate(cfg.key, ctx);
1893
- if (cfg.modifiers && cfg.modifiers.length > 0) {
1894
- await page.nativeKeyPress(key, cfg.modifiers);
1895
- }
1896
- else {
1897
- await page.press(key);
1898
- }
1899
- }
1900
- return { ...ctx, page };
1901
- }
1902
- // --- scroll: page scrolling ---
1903
- export async function stepScroll(ctx, config) {
1904
- const page = await acquirePage(ctx);
1905
- if (typeof config === "string") {
1906
- await page.scroll(config);
1907
- }
1908
- else {
1909
- const cfg = config;
1910
- if (cfg.auto) {
1911
- await page.autoScroll({ maxScrolls: cfg.max, delay: cfg.delay });
1912
- }
1913
- else if (cfg.selector) {
1914
- const sel = evalTemplate(cfg.selector, ctx);
1915
- const escaped = sel.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
1916
- await page.evaluate(`document.querySelector('${escaped}')?.scrollIntoView({ behavior: 'smooth', block: 'center' })`);
1917
- }
1918
- else if (cfg.to) {
1919
- await page.scroll(cfg.to);
1920
- }
1921
- }
1922
- return { ...ctx, page };
1923
- }
1924
- // --- snapshot: DOM accessibility tree ---
1925
- export async function stepSnapshot(ctx, config) {
1926
- const page = await acquirePage(ctx);
1927
- const opts = typeof config === "object" && config !== null
1928
- ? config
1929
- : {};
1930
- // Normalize max_depth to maxDepth for BrowserPage.snapshot
1931
- const normalizedOpts = {
1932
- interactive: opts.interactive,
1933
- compact: opts.compact,
1934
- maxDepth: opts.max_depth,
1935
- raw: opts.raw,
1936
- };
1937
- const result = await page.snapshot(normalizedOpts);
1938
- return { ...ctx, data: result, page };
1939
- }
1940
- export async function stepTap(ctx, config) {
1941
- const page = await acquirePage(ctx);
1942
- const { generateTapInterceptorJs } = await import("./interceptor.js");
1943
- const capturePattern = evalTemplate(config.capture, ctx);
1944
- const timeout = (config.timeout ?? 5) * 1000;
1945
- const storeName = evalTemplate(config.store, ctx);
1946
- const actionName = evalTemplate(config.action, ctx);
1947
- // Sanitize store/action names to prevent JS injection in page context
1948
- if (!/^[a-zA-Z_$][\w$]*$/.test(storeName)) {
1949
- throw new PipelineError(`Invalid store name: "${storeName}"`, {
1950
- step: -1,
1951
- action: "tap",
1952
- config,
1953
- errorType: "expression_error",
1954
- suggestion: "Store name must be a valid JavaScript identifier.",
1955
- retryable: false,
1956
- alternatives: [],
1957
- });
1958
- }
1959
- if (!/^[a-zA-Z_$][\w$]*$/.test(actionName)) {
1960
- throw new PipelineError(`Invalid action name: "${actionName}"`, {
1961
- step: -1,
1962
- action: "tap",
1963
- config,
1964
- errorType: "expression_error",
1965
- suggestion: "Action name must be a valid JavaScript identifier.",
1966
- retryable: false,
1967
- alternatives: [],
1968
- });
1969
- }
1970
- const framework = config.framework ?? "auto";
1971
- const actionArgs = config.args
1972
- ? config.args.map((a) => JSON.stringify(a)).join(", ")
1973
- : "";
1974
- const tap = generateTapInterceptorJs(capturePattern);
1975
- // Build optional select chain (escape keys to prevent JS injection)
1976
- const selectChain = config.select
1977
- ? config.select
1978
- .split(".")
1979
- .map((k) => `?.[${JSON.stringify(k)}]`)
1980
- .join("")
1981
- : "";
1982
- // Store discovery based on framework
1983
- const piniaDiscovery = `
1984
- const pinia = document.querySelector('#app')?.__vue_app__?.config?.globalProperties?.$pinia;
1985
- if (!pinia) throw new Error('Pinia not found');
1986
- const store = pinia._s.get('${storeName}');
1987
- if (!store) throw new Error('Store "${storeName}" not found');
1988
- await store['${actionName}'](${actionArgs});
1989
- `;
1990
- const vuexDiscovery = `
1991
- const vStore = document.querySelector('#app')?.__vue_app__?.config?.globalProperties?.$store;
1992
- if (!vStore) throw new Error('Vuex store not found');
1993
- await vStore.dispatch('${storeName}/${actionName}'${actionArgs ? ", " + actionArgs : ""});
1994
- `;
1995
- const autoDiscovery = `
1996
- const app = document.querySelector('#app')?.__vue_app__;
1997
- if (!app) throw new Error('No Vue app found');
1998
- const pinia = app.config?.globalProperties?.$pinia;
1999
- if (pinia && pinia._s.has('${storeName}')) {
2000
- const store = pinia._s.get('${storeName}');
2001
- await store['${actionName}'](${actionArgs});
2002
- } else {
2003
- const vStore = app.config?.globalProperties?.$store;
2004
- if (vStore) {
2005
- await vStore.dispatch('${storeName}/${actionName}'${actionArgs ? ", " + actionArgs : ""});
2006
- } else {
2007
- throw new Error('No Pinia or Vuex store found');
2008
- }
2009
- }
2010
- `;
2011
- const storeCode = framework === "pinia"
2012
- ? piniaDiscovery
2013
- : framework === "vuex"
2014
- ? vuexDiscovery
2015
- : autoDiscovery;
2016
- const script = `(async () => {
2017
- ${tap.setupVar}
2018
- ${tap.fetchPatch}
2019
- ${tap.xhrPatch}
2020
- try {
2021
- ${storeCode}
2022
- const result = await Promise.race([
2023
- ${tap.promiseVar},
2024
- new Promise((_, reject) => setTimeout(() => reject(new Error('tap timeout')), ${timeout})),
2025
- ]);
2026
- return JSON.stringify(result${selectChain});
2027
- } finally {
2028
- ${tap.restorePatch}
2029
- }
2030
- })()`;
2031
- const raw = await page.evaluate(script);
2032
- let data;
2033
- if (typeof raw === "string") {
2034
- try {
2035
- data = JSON.parse(raw);
2036
- }
2037
- catch {
2038
- data = raw;
2039
- }
2040
- }
2041
- else {
2042
- data = raw;
2043
- }
2044
- return { ...ctx, data, page };
2045
- }
2046
- /**
2047
- * Navigate nested object by dot-path: "data.list[].title"
2048
- */
2049
- function getNestedValue(obj, path) {
2050
- const parts = path.split(".");
2051
- let current = obj;
2052
- for (const part of parts) {
2053
- if (current == null)
2054
- return undefined;
2055
- if (part.endsWith("[]")) {
2056
- const key = part.slice(0, -2);
2057
- if (key) {
2058
- current = current[key];
2059
- }
2060
- // current should now be an array — continue traversing
2061
- }
2062
- else {
2063
- current = current[part];
2064
- }
2065
- }
2066
- return current;
2067
- }
2068
- export async function stepDownload(ctx, config) {
2069
- const dir = resolve(config.dir ?? "./downloads");
2070
- mkdirSync(dir, { recursive: true });
2071
- const concurrency = config.concurrency ?? 3;
2072
- const skipExisting = config.skip_existing !== false; // default true
2073
- const cookieHeader = ctx.cookieHeader;
2074
- async function downloadOne(item, index) {
2075
- const itemCtx = { ...ctx, data: { item, index } };
2076
- const url = evalTemplate(config.url, itemCtx);
2077
- const filename = config.filename
2078
- ? evalTemplate(config.filename, itemCtx)
2079
- : generateFilename(url, index);
2080
- const destPath = join(dir, sanitizeFilename(filename));
2081
- if (skipExisting && existsSync(destPath)) {
2082
- return { ...item, _download: { status: "skipped", path: destPath } };
2083
- }
2084
- const useYtdlp = config.use_ytdlp ?? (config.type === "video" && requiresYtdlp(url));
2085
- let result;
2086
- if (config.type === "document" && config.content) {
2087
- const content = evalTemplate(config.content, itemCtx);
2088
- writeFileSync(destPath, content, "utf-8");
2089
- const info = await stat(destPath);
2090
- result = {
2091
- status: "success",
2092
- path: destPath,
2093
- size: info.size,
2094
- duration: 0,
2095
- };
2096
- }
2097
- else if (useYtdlp) {
2098
- result = await ytdlpDownload(url, dir);
2099
- }
2100
- else {
2101
- const headers = {};
2102
- if (cookieHeader)
2103
- headers["Cookie"] = cookieHeader;
2104
- result = await httpDownload(url, destPath, headers);
2105
- }
2106
- return { ...item, _download: result };
2107
- }
2108
- if (Array.isArray(ctx.data)) {
2109
- const items = ctx.data;
2110
- const results = await mapConcurrent(items, concurrency, downloadOne);
2111
- return { ...ctx, data: results };
2112
- }
2113
- else {
2114
- const item = (ctx.data ?? {});
2115
- const result = await downloadOne(item, 0);
2116
- return { ...ctx, data: [result] };
2117
- }
2118
- }
2119
- export async function stepWebsocket(ctx, config) {
2120
- const resolvedConfig = {
2121
- ...config,
2122
- url: evalTemplate(config.url, ctx),
2123
- send: evalTemplate(config.send, ctx),
2124
- };
2125
- const data = await executeWebsocket(resolvedConfig);
2126
- return { ...ctx, data };
2127
- }
2128
- export async function stepExtract(ctx, config) {
2129
- const page = await acquirePage(ctx);
2130
- const containerSelector = evalTemplate(config.from, ctx);
2131
- // Build a JS expression that extracts structured data
2132
- const fieldEntries = Object.entries(config.fields);
2133
- const fieldJs = fieldEntries
2134
- .map(([key, def]) => {
2135
- const sel = JSON.stringify(def.selector);
2136
- const attr = def.attribute ? JSON.stringify(def.attribute) : null;
2137
- const pattern = def.pattern ? JSON.stringify(def.pattern) : null;
2138
- const type = def.type ?? "text";
2139
- if (type === "attribute" || attr) {
2140
- return `${JSON.stringify(key)}: (() => { const el = item.querySelector(${sel}); return el ? el.getAttribute(${attr ?? JSON.stringify("href")}) : null; })()`;
2141
- }
2142
- else if (type === "number") {
2143
- return `${JSON.stringify(key)}: (() => { const el = item.querySelector(${sel}); if (!el) return null; const txt = el.textContent || ''; ${pattern ? `const m = txt.match(new RegExp(${pattern})); return m ? parseFloat(m[0]) : null;` : `return parseFloat(txt.replace(/[^\\d.-]/g, '')) || null;`} })()`;
2144
- }
2145
- else if (type === "html") {
2146
- return `${JSON.stringify(key)}: (() => { const el = item.querySelector(${sel}); return el ? el.innerHTML : null; })()`;
2147
- }
2148
- else {
2149
- // text (default)
2150
- if (pattern) {
2151
- return `${JSON.stringify(key)}: (() => { const el = item.querySelector(${sel}); if (!el) return null; const txt = el.textContent || ''; const m = txt.match(new RegExp(${pattern})); return m ? (m[1] || m[0]) : txt.trim(); })()`;
2152
- }
2153
- return `${JSON.stringify(key)}: (() => { const el = item.querySelector(${sel}); return el ? el.textContent.trim() : null; })()`;
2154
- }
2155
- })
2156
- .join(",\n ");
2157
- const extractJs = `
2158
- JSON.stringify(
2159
- Array.from(document.querySelectorAll(${JSON.stringify(containerSelector)})).map(item => ({
2160
- ${fieldJs}
2161
- }))
2162
- )
2163
- `;
2164
- const resultStr = (await page.evaluate(extractJs));
2165
- let data;
2166
- try {
2167
- data = JSON.parse(resultStr);
2168
- }
2169
- catch {
2170
- data = [];
2171
- }
2172
- return { ...ctx, data, page };
2
+ * @deprecated since v0.213import from ./executor.js (runPipeline,
3
+ * PipelineError, assertSafeRequestUrl), ./step-registry.js (registerStep,
4
+ * StepHandler), ./template.js (evalExpression, PIPE_FILTERS), or the
5
+ * per-step file under ./steps/. This shim is removed in v0.214.
6
+ */
7
+ // prettier-ignore
8
+ export { runPipeline, PipelineError, assertSafeRequestUrl, _resetTransportBusForTests } from "./executor.js";
9
+ // prettier-ignore
10
+ export { registerStep, getStep, listSteps } from "./step-registry.js";
11
+ // prettier-ignore
12
+ export { PIPE_FILTERS, evalExpression, buildScope } from "./template.js";
13
+ // prettier-ignore
14
+ export { stepFetch, stepFetchText, stepParseRss, stepHtmlToMd, stepSelect, stepMap, stepFilter, stepSort, stepLimit, stepExec, stepWriteTemp, stepNavigate, stepEvaluate, stepClick, stepType, stepWaitBrowser, stepIntercept, stepPress, stepScroll, stepSnapshot, stepTap, stepExtract, stepSet, stepAppend, stepIf, stepEach, stepParallel, stepAssert, stepDownload, stepWebsocket } from "./steps/index.js";
15
+ if (process.env.UNICLI_DEBUG === "1") {
16
+ process.stderr.write("[unicli] yaml-runner.ts is deprecated; import from ./executor.js or ./steps/\n");
2173
17
  }
2174
- // Exported for unit testing — not part of public API
2175
- export { PIPE_FILTERS, evalExpression, buildScope };
2176
18
  //# sourceMappingURL=yaml-runner.js.map