@zenalexa/unicli 0.211.2 → 0.213.0-beta.2

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