@zenalexa/unicli 0.219.0 → 0.220.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (430) hide show
  1. package/AGENTS.md +9 -9
  2. package/README.md +55 -249
  3. package/README.zh-CN.md +55 -249
  4. package/dist/adapters/1point3acres/forum.d.ts +58 -0
  5. package/dist/adapters/1point3acres/forum.d.ts.map +1 -0
  6. package/dist/adapters/1point3acres/forum.js +708 -0
  7. package/dist/adapters/1point3acres/forum.js.map +1 -0
  8. package/dist/adapters/aibase/news.d.ts +21 -0
  9. package/dist/adapters/aibase/news.d.ts.map +1 -0
  10. package/dist/adapters/aibase/news.js +96 -0
  11. package/dist/adapters/aibase/news.js.map +1 -0
  12. package/dist/adapters/arxiv/papers.d.ts +27 -0
  13. package/dist/adapters/arxiv/papers.d.ts.map +1 -0
  14. package/dist/adapters/arxiv/papers.js +193 -0
  15. package/dist/adapters/arxiv/papers.js.map +1 -0
  16. package/dist/adapters/bbc/topic.d.ts +24 -0
  17. package/dist/adapters/bbc/topic.d.ts.map +1 -0
  18. package/dist/adapters/bbc/topic.js +122 -0
  19. package/dist/adapters/bbc/topic.js.map +1 -0
  20. package/dist/adapters/chatgpt/web.d.ts +24 -0
  21. package/dist/adapters/chatgpt/web.d.ts.map +1 -0
  22. package/dist/adapters/chatgpt/web.js +242 -0
  23. package/dist/adapters/chatgpt/web.js.map +1 -0
  24. package/dist/adapters/claude/web.d.ts +24 -0
  25. package/dist/adapters/claude/web.d.ts.map +1 -0
  26. package/dist/adapters/claude/web.js +575 -0
  27. package/dist/adapters/claude/web.js.map +1 -0
  28. package/dist/adapters/codex/projects.d.ts +27 -0
  29. package/dist/adapters/codex/projects.d.ts.map +1 -0
  30. package/dist/adapters/codex/projects.js +147 -0
  31. package/dist/adapters/codex/projects.js.map +1 -0
  32. package/dist/adapters/coingecko/markets.d.ts +19 -0
  33. package/dist/adapters/coingecko/markets.d.ts.map +1 -0
  34. package/dist/adapters/coingecko/markets.js +474 -0
  35. package/dist/adapters/coingecko/markets.js.map +1 -0
  36. package/dist/adapters/coupang/product.d.ts +27 -0
  37. package/dist/adapters/coupang/product.d.ts.map +1 -0
  38. package/dist/adapters/coupang/product.js +211 -0
  39. package/dist/adapters/coupang/product.js.map +1 -0
  40. package/dist/adapters/crates/registry.d.ts +44 -0
  41. package/dist/adapters/crates/registry.d.ts.map +1 -0
  42. package/dist/adapters/crates/registry.js +186 -0
  43. package/dist/adapters/crates/registry.js.map +1 -0
  44. package/dist/adapters/ctrip/travel.d.ts +83 -0
  45. package/dist/adapters/ctrip/travel.d.ts.map +1 -0
  46. package/dist/adapters/ctrip/travel.js +630 -0
  47. package/dist/adapters/ctrip/travel.js.map +1 -0
  48. package/dist/adapters/dblp/publications.d.ts +41 -0
  49. package/dist/adapters/dblp/publications.d.ts.map +1 -0
  50. package/dist/adapters/dblp/publications.js +409 -0
  51. package/dist/adapters/dblp/publications.js.map +1 -0
  52. package/dist/adapters/deepseek/web.d.ts +1 -1
  53. package/dist/adapters/deepseek/web.d.ts.map +1 -1
  54. package/dist/adapters/deepseek/web.js +66 -1
  55. package/dist/adapters/deepseek/web.js.map +1 -1
  56. package/dist/adapters/defillama/protocols.d.ts +13 -0
  57. package/dist/adapters/defillama/protocols.d.ts.map +1 -0
  58. package/dist/adapters/defillama/protocols.js +218 -0
  59. package/dist/adapters/defillama/protocols.js.map +1 -0
  60. package/dist/adapters/devto/read.d.ts +26 -0
  61. package/dist/adapters/devto/read.d.ts.map +1 -0
  62. package/dist/adapters/devto/read.js +110 -0
  63. package/dist/adapters/devto/read.js.map +1 -0
  64. package/dist/adapters/dianping/shop.d.ts +38 -0
  65. package/dist/adapters/dianping/shop.d.ts.map +1 -0
  66. package/dist/adapters/dianping/shop.js +194 -0
  67. package/dist/adapters/dianping/shop.js.map +1 -0
  68. package/dist/adapters/dockerhub/registry.d.ts +36 -0
  69. package/dist/adapters/dockerhub/registry.d.ts.map +1 -0
  70. package/dist/adapters/dockerhub/registry.js +172 -0
  71. package/dist/adapters/dockerhub/registry.js.map +1 -0
  72. package/dist/adapters/endoflife/product.d.ts +11 -0
  73. package/dist/adapters/endoflife/product.d.ts.map +1 -0
  74. package/dist/adapters/endoflife/product.js +113 -0
  75. package/dist/adapters/endoflife/product.js.map +1 -0
  76. package/dist/adapters/facebook/marketplace-extra.d.ts +9 -0
  77. package/dist/adapters/facebook/marketplace-extra.d.ts.map +1 -0
  78. package/dist/adapters/facebook/marketplace-extra.js +170 -0
  79. package/dist/adapters/facebook/marketplace-extra.js.map +1 -0
  80. package/dist/adapters/flathub/apps.d.ts +17 -0
  81. package/dist/adapters/flathub/apps.d.ts.map +1 -0
  82. package/dist/adapters/flathub/apps.js +220 -0
  83. package/dist/adapters/flathub/apps.js.map +1 -0
  84. package/dist/adapters/goproxy/module.d.ts +24 -0
  85. package/dist/adapters/goproxy/module.d.ts.map +1 -0
  86. package/dist/adapters/goproxy/module.js +221 -0
  87. package/dist/adapters/goproxy/module.js.map +1 -0
  88. package/dist/adapters/grok/web.d.ts +29 -0
  89. package/dist/adapters/grok/web.d.ts.map +1 -0
  90. package/dist/adapters/grok/web.js +553 -0
  91. package/dist/adapters/grok/web.js.map +1 -0
  92. package/dist/adapters/hackernews/read.d.ts +31 -0
  93. package/dist/adapters/hackernews/read.d.ts.map +1 -0
  94. package/dist/adapters/hackernews/read.js +201 -0
  95. package/dist/adapters/hackernews/read.js.map +1 -0
  96. package/dist/adapters/hf/paper.d.ts +22 -0
  97. package/dist/adapters/hf/paper.d.ts.map +1 -0
  98. package/dist/adapters/hf/paper.js +112 -0
  99. package/dist/adapters/hf/paper.js.map +1 -0
  100. package/dist/adapters/homebrew/packages.d.ts +52 -0
  101. package/dist/adapters/homebrew/packages.d.ts.map +1 -0
  102. package/dist/adapters/homebrew/packages.js +240 -0
  103. package/dist/adapters/homebrew/packages.js.map +1 -0
  104. package/dist/adapters/indeed/jobs.d.ts +38 -0
  105. package/dist/adapters/indeed/jobs.d.ts.map +1 -0
  106. package/dist/adapters/indeed/jobs.js +300 -0
  107. package/dist/adapters/indeed/jobs.js.map +1 -0
  108. package/dist/adapters/instagram/collections.d.ts +9 -0
  109. package/dist/adapters/instagram/collections.d.ts.map +1 -0
  110. package/dist/adapters/instagram/collections.js +174 -0
  111. package/dist/adapters/instagram/collections.js.map +1 -0
  112. package/dist/adapters/lichess/players.d.ts +46 -0
  113. package/dist/adapters/lichess/players.d.ts.map +1 -0
  114. package/dist/adapters/lichess/players.js +221 -0
  115. package/dist/adapters/lichess/players.js.map +1 -0
  116. package/dist/adapters/lobsters/read-domain.d.ts +35 -0
  117. package/dist/adapters/lobsters/read-domain.d.ts.map +1 -0
  118. package/dist/adapters/lobsters/read-domain.js +306 -0
  119. package/dist/adapters/lobsters/read-domain.js.map +1 -0
  120. package/dist/adapters/maven/artifact.d.ts +30 -0
  121. package/dist/adapters/maven/artifact.d.ts.map +1 -0
  122. package/dist/adapters/maven/artifact.js +121 -0
  123. package/dist/adapters/maven/artifact.js.map +1 -0
  124. package/dist/adapters/mdn/search.d.ts +11 -0
  125. package/dist/adapters/mdn/search.d.ts.map +1 -0
  126. package/dist/adapters/mdn/search.js +115 -0
  127. package/dist/adapters/mdn/search.js.map +1 -0
  128. package/dist/adapters/medium/tag.d.ts +15 -0
  129. package/dist/adapters/medium/tag.d.ts.map +1 -0
  130. package/dist/adapters/medium/tag.js +148 -0
  131. package/dist/adapters/medium/tag.js.map +1 -0
  132. package/dist/adapters/npm/package.d.ts +32 -0
  133. package/dist/adapters/npm/package.d.ts.map +1 -0
  134. package/dist/adapters/npm/package.js +141 -0
  135. package/dist/adapters/npm/package.js.map +1 -0
  136. package/dist/adapters/nuget/package.d.ts +34 -0
  137. package/dist/adapters/nuget/package.d.ts.map +1 -0
  138. package/dist/adapters/nuget/package.js +135 -0
  139. package/dist/adapters/nuget/package.js.map +1 -0
  140. package/dist/adapters/nvd/cve.d.ts +42 -0
  141. package/dist/adapters/nvd/cve.d.ts.map +1 -0
  142. package/dist/adapters/nvd/cve.js +132 -0
  143. package/dist/adapters/nvd/cve.js.map +1 -0
  144. package/dist/adapters/oeis/sequences.d.ts +14 -0
  145. package/dist/adapters/oeis/sequences.d.ts.map +1 -0
  146. package/dist/adapters/oeis/sequences.js +219 -0
  147. package/dist/adapters/oeis/sequences.js.map +1 -0
  148. package/dist/adapters/openalex/works.d.ts +43 -0
  149. package/dist/adapters/openalex/works.d.ts.map +1 -0
  150. package/dist/adapters/openalex/works.js +267 -0
  151. package/dist/adapters/openalex/works.js.map +1 -0
  152. package/dist/adapters/openfda/records.d.ts +18 -0
  153. package/dist/adapters/openfda/records.d.ts.map +1 -0
  154. package/dist/adapters/openfda/records.js +209 -0
  155. package/dist/adapters/openfda/records.js.map +1 -0
  156. package/dist/adapters/openreview/papers.d.ts +34 -0
  157. package/dist/adapters/openreview/papers.d.ts.map +1 -0
  158. package/dist/adapters/openreview/papers.js +463 -0
  159. package/dist/adapters/openreview/papers.js.map +1 -0
  160. package/dist/adapters/osv/security.d.ts +36 -0
  161. package/dist/adapters/osv/security.d.ts.map +1 -0
  162. package/dist/adapters/osv/security.js +247 -0
  163. package/dist/adapters/osv/security.js.map +1 -0
  164. package/dist/adapters/packagist/package.d.ts +31 -0
  165. package/dist/adapters/packagist/package.d.ts.map +1 -0
  166. package/dist/adapters/packagist/package.js +108 -0
  167. package/dist/adapters/packagist/package.js.map +1 -0
  168. package/dist/adapters/pubmed/articles.d.ts +31 -0
  169. package/dist/adapters/pubmed/articles.d.ts.map +1 -0
  170. package/dist/adapters/pubmed/articles.js +385 -0
  171. package/dist/adapters/pubmed/articles.js.map +1 -0
  172. package/dist/adapters/pypi/package.d.ts +38 -0
  173. package/dist/adapters/pypi/package.d.ts.map +1 -0
  174. package/dist/adapters/pypi/package.js +235 -0
  175. package/dist/adapters/pypi/package.js.map +1 -0
  176. package/dist/adapters/qwen/web.d.ts +26 -0
  177. package/dist/adapters/qwen/web.d.ts.map +1 -0
  178. package/dist/adapters/qwen/web.js +672 -0
  179. package/dist/adapters/qwen/web.js.map +1 -0
  180. package/dist/adapters/reddit/account.d.ts +12 -0
  181. package/dist/adapters/reddit/account.d.ts.map +1 -0
  182. package/dist/adapters/reddit/account.js +409 -0
  183. package/dist/adapters/reddit/account.js.map +1 -0
  184. package/dist/adapters/rednote/web.d.ts +30 -0
  185. package/dist/adapters/rednote/web.d.ts.map +1 -0
  186. package/dist/adapters/rednote/web.js +858 -0
  187. package/dist/adapters/rednote/web.js.map +1 -0
  188. package/dist/adapters/rest-countries/countries.d.ts +14 -0
  189. package/dist/adapters/rest-countries/countries.d.ts.map +1 -0
  190. package/dist/adapters/rest-countries/countries.js +231 -0
  191. package/dist/adapters/rest-countries/countries.js.map +1 -0
  192. package/dist/adapters/reuters/article-detail.d.ts +37 -0
  193. package/dist/adapters/reuters/article-detail.d.ts.map +1 -0
  194. package/dist/adapters/reuters/article-detail.js +139 -0
  195. package/dist/adapters/reuters/article-detail.js.map +1 -0
  196. package/dist/adapters/rfc/rfc.d.ts +11 -0
  197. package/dist/adapters/rfc/rfc.d.ts.map +1 -0
  198. package/dist/adapters/rfc/rfc.js +121 -0
  199. package/dist/adapters/rfc/rfc.js.map +1 -0
  200. package/dist/adapters/rubygems/gem.d.ts +26 -0
  201. package/dist/adapters/rubygems/gem.d.ts.map +1 -0
  202. package/dist/adapters/rubygems/gem.js +96 -0
  203. package/dist/adapters/rubygems/gem.js.map +1 -0
  204. package/dist/adapters/stackoverflow/questions.d.ts +79 -0
  205. package/dist/adapters/stackoverflow/questions.d.ts.map +1 -0
  206. package/dist/adapters/stackoverflow/questions.js +504 -0
  207. package/dist/adapters/stackoverflow/questions.js.map +1 -0
  208. package/dist/adapters/steam/app.d.ts +39 -0
  209. package/dist/adapters/steam/app.d.ts.map +1 -0
  210. package/dist/adapters/steam/app.js +165 -0
  211. package/dist/adapters/steam/app.js.map +1 -0
  212. package/dist/adapters/tiktok/creator-videos.d.ts +52 -0
  213. package/dist/adapters/tiktok/creator-videos.d.ts.map +1 -0
  214. package/dist/adapters/tiktok/creator-videos.js +267 -0
  215. package/dist/adapters/tiktok/creator-videos.js.map +1 -0
  216. package/dist/adapters/tvmaze/shows.d.ts +13 -0
  217. package/dist/adapters/tvmaze/shows.d.ts.map +1 -0
  218. package/dist/adapters/tvmaze/shows.js +240 -0
  219. package/dist/adapters/tvmaze/shows.js.map +1 -0
  220. package/dist/adapters/twitter/bookmark-folders.d.ts +33 -0
  221. package/dist/adapters/twitter/bookmark-folders.d.ts.map +1 -0
  222. package/dist/adapters/twitter/bookmark-folders.js +290 -0
  223. package/dist/adapters/twitter/bookmark-folders.js.map +1 -0
  224. package/dist/adapters/twitter/quote.d.ts +18 -0
  225. package/dist/adapters/twitter/quote.d.ts.map +1 -0
  226. package/dist/adapters/twitter/quote.js +285 -0
  227. package/dist/adapters/twitter/quote.js.map +1 -0
  228. package/dist/adapters/twitter/tweet-actions.d.ts +12 -0
  229. package/dist/adapters/twitter/tweet-actions.d.ts.map +1 -0
  230. package/dist/adapters/twitter/tweet-actions.js +145 -0
  231. package/dist/adapters/twitter/tweet-actions.js.map +1 -0
  232. package/dist/adapters/twitter/tweet-url.d.ts +15 -0
  233. package/dist/adapters/twitter/tweet-url.d.ts.map +1 -0
  234. package/dist/adapters/twitter/tweet-url.js +57 -0
  235. package/dist/adapters/twitter/tweet-url.js.map +1 -0
  236. package/dist/adapters/uisdc/news.d.ts +22 -0
  237. package/dist/adapters/uisdc/news.d.ts.map +1 -0
  238. package/dist/adapters/uisdc/news.js +91 -0
  239. package/dist/adapters/uisdc/news.js.map +1 -0
  240. package/dist/adapters/weibo/favorites-publish.d.ts +28 -0
  241. package/dist/adapters/weibo/favorites-publish.d.ts.map +1 -0
  242. package/dist/adapters/weibo/favorites-publish.js +356 -0
  243. package/dist/adapters/weibo/favorites-publish.js.map +1 -0
  244. package/dist/adapters/wikidata/entities.d.ts +15 -0
  245. package/dist/adapters/wikidata/entities.d.ts.map +1 -0
  246. package/dist/adapters/wikidata/entities.js +219 -0
  247. package/dist/adapters/wikidata/entities.js.map +1 -0
  248. package/dist/adapters/wikipedia/page.d.ts +21 -0
  249. package/dist/adapters/wikipedia/page.d.ts.map +1 -0
  250. package/dist/adapters/wikipedia/page.js +116 -0
  251. package/dist/adapters/wikipedia/page.js.map +1 -0
  252. package/dist/adapters/wttr/weather.d.ts +12 -0
  253. package/dist/adapters/wttr/weather.d.ts.map +1 -0
  254. package/dist/adapters/wttr/weather.js +207 -0
  255. package/dist/adapters/wttr/weather.js.map +1 -0
  256. package/dist/adapters/xianyu/publish.d.ts +31 -0
  257. package/dist/adapters/xianyu/publish.d.ts.map +1 -0
  258. package/dist/adapters/xianyu/publish.js +349 -0
  259. package/dist/adapters/xianyu/publish.js.map +1 -0
  260. package/dist/adapters/xiaohongshu/user-helpers.d.ts +2 -2
  261. package/dist/adapters/xiaohongshu/user-helpers.d.ts.map +1 -1
  262. package/dist/adapters/xiaohongshu/user-helpers.js +5 -4
  263. package/dist/adapters/xiaohongshu/user-helpers.js.map +1 -1
  264. package/dist/adapters/yuanbao/web.d.ts +27 -0
  265. package/dist/adapters/yuanbao/web.d.ts.map +1 -0
  266. package/dist/adapters/yuanbao/web.js +365 -0
  267. package/dist/adapters/yuanbao/web.js.map +1 -0
  268. package/dist/adapters/zhihu/collection.d.ts +33 -0
  269. package/dist/adapters/zhihu/collection.d.ts.map +1 -0
  270. package/dist/adapters/zhihu/collection.js +185 -0
  271. package/dist/adapters/zhihu/collection.js.map +1 -0
  272. package/dist/adapters/zlibrary/web.d.ts +19 -0
  273. package/dist/adapters/zlibrary/web.d.ts.map +1 -0
  274. package/dist/adapters/zlibrary/web.js +153 -0
  275. package/dist/adapters/zlibrary/web.js.map +1 -0
  276. package/dist/browser/daemon-client.js +2 -2
  277. package/dist/browser/daemon-client.js.map +1 -1
  278. package/dist/discovery/macos-dynamic.d.ts.map +1 -1
  279. package/dist/discovery/macos-dynamic.js +17 -3
  280. package/dist/discovery/macos-dynamic.js.map +1 -1
  281. package/dist/manifest-compact.txt +10 -10
  282. package/dist/manifest-search.json +1 -1
  283. package/dist/manifest.json +5130 -112
  284. package/dist/transport/refs.d.ts +5 -3
  285. package/dist/transport/refs.d.ts.map +1 -1
  286. package/dist/transport/refs.js +8 -1
  287. package/dist/transport/refs.js.map +1 -1
  288. package/dist/transport/sidecar-binary.d.ts +7 -0
  289. package/dist/transport/sidecar-binary.d.ts.map +1 -1
  290. package/dist/transport/sidecar-binary.js +28 -8
  291. package/dist/transport/sidecar-binary.js.map +1 -1
  292. package/package.json +4 -3
  293. package/server.json +3 -3
  294. package/skills/unicli/SKILL.md +1 -1
  295. package/skills/unicli-claude-code/SKILL.md +1 -1
  296. package/skills/unicli-hermes/SKILL.md +1 -1
  297. package/src/adapters/1point3acres/forum.test.ts +300 -0
  298. package/src/adapters/1point3acres/forum.ts +852 -0
  299. package/src/adapters/aibase/news.test.ts +42 -0
  300. package/src/adapters/aibase/news.ts +118 -0
  301. package/src/adapters/arxiv/papers.test.ts +59 -0
  302. package/src/adapters/arxiv/papers.ts +226 -0
  303. package/src/adapters/bbc/topic.test.ts +52 -0
  304. package/src/adapters/bbc/topic.ts +149 -0
  305. package/src/adapters/chatgpt/web.test.ts +121 -0
  306. package/src/adapters/chatgpt/web.ts +286 -0
  307. package/src/adapters/claude/web.test.ts +206 -0
  308. package/src/adapters/claude/web.ts +684 -0
  309. package/src/adapters/codex/projects.test.ts +77 -0
  310. package/src/adapters/codex/projects.ts +178 -0
  311. package/src/adapters/coingecko/markets.test.ts +156 -0
  312. package/src/adapters/coingecko/markets.ts +574 -0
  313. package/src/adapters/coupang/product.test.ts +111 -0
  314. package/src/adapters/coupang/product.ts +256 -0
  315. package/src/adapters/crates/registry.test.ts +89 -0
  316. package/src/adapters/crates/registry.ts +247 -0
  317. package/src/adapters/ctrip/travel.test.ts +359 -0
  318. package/src/adapters/ctrip/travel.ts +792 -0
  319. package/src/adapters/dblp/publications.test.ts +123 -0
  320. package/src/adapters/dblp/publications.ts +494 -0
  321. package/src/adapters/deepseek/web.test.ts +69 -0
  322. package/src/adapters/deepseek/web.ts +78 -1
  323. package/src/adapters/defillama/protocols.test.ts +75 -0
  324. package/src/adapters/defillama/protocols.ts +253 -0
  325. package/src/adapters/devto/read.test.ts +49 -0
  326. package/src/adapters/devto/read.ts +145 -0
  327. package/src/adapters/dianping/shop.test.ts +134 -0
  328. package/src/adapters/dianping/shop.ts +261 -0
  329. package/src/adapters/dockerhub/registry.test.ts +97 -0
  330. package/src/adapters/dockerhub/registry.ts +223 -0
  331. package/src/adapters/endoflife/product.test.ts +50 -0
  332. package/src/adapters/endoflife/product.ts +128 -0
  333. package/src/adapters/facebook/marketplace-extra.test.ts +132 -0
  334. package/src/adapters/facebook/marketplace-extra.ts +213 -0
  335. package/src/adapters/flathub/apps.test.ts +85 -0
  336. package/src/adapters/flathub/apps.ts +254 -0
  337. package/src/adapters/goproxy/module.test.ts +72 -0
  338. package/src/adapters/goproxy/module.ts +258 -0
  339. package/src/adapters/grok/web.test.ts +181 -0
  340. package/src/adapters/grok/web.ts +640 -0
  341. package/src/adapters/hackernews/read.test.ts +68 -0
  342. package/src/adapters/hackernews/read.ts +265 -0
  343. package/src/adapters/hf/paper.test.ts +48 -0
  344. package/src/adapters/hf/paper.ts +138 -0
  345. package/src/adapters/homebrew/packages.test.ts +109 -0
  346. package/src/adapters/homebrew/packages.ts +304 -0
  347. package/src/adapters/indeed/jobs.test.ts +230 -0
  348. package/src/adapters/indeed/jobs.ts +375 -0
  349. package/src/adapters/instagram/collections.test.ts +94 -0
  350. package/src/adapters/instagram/collections.ts +206 -0
  351. package/src/adapters/lichess/players.test.ts +99 -0
  352. package/src/adapters/lichess/players.ts +277 -0
  353. package/src/adapters/lobsters/read-domain.test.ts +121 -0
  354. package/src/adapters/lobsters/read-domain.ts +400 -0
  355. package/src/adapters/maven/artifact.test.ts +67 -0
  356. package/src/adapters/maven/artifact.ts +155 -0
  357. package/src/adapters/mdn/search.test.ts +39 -0
  358. package/src/adapters/mdn/search.ts +133 -0
  359. package/src/adapters/medium/tag.test.ts +64 -0
  360. package/src/adapters/medium/tag.ts +164 -0
  361. package/src/adapters/npm/package.test.ts +53 -0
  362. package/src/adapters/npm/package.ts +177 -0
  363. package/src/adapters/nuget/package.test.ts +102 -0
  364. package/src/adapters/nuget/package.ts +193 -0
  365. package/src/adapters/nvd/cve.test.ts +66 -0
  366. package/src/adapters/nvd/cve.ts +182 -0
  367. package/src/adapters/oeis/sequences.test.ts +71 -0
  368. package/src/adapters/oeis/sequences.ts +234 -0
  369. package/src/adapters/openalex/works.test.ts +99 -0
  370. package/src/adapters/openalex/works.ts +319 -0
  371. package/src/adapters/openfda/records.test.ts +90 -0
  372. package/src/adapters/openfda/records.ts +239 -0
  373. package/src/adapters/openreview/papers.test.ts +139 -0
  374. package/src/adapters/openreview/papers.ts +560 -0
  375. package/src/adapters/osv/security.test.ts +91 -0
  376. package/src/adapters/osv/security.ts +298 -0
  377. package/src/adapters/packagist/package.test.ts +62 -0
  378. package/src/adapters/packagist/package.ts +146 -0
  379. package/src/adapters/pubmed/articles.test.ts +96 -0
  380. package/src/adapters/pubmed/articles.ts +497 -0
  381. package/src/adapters/pypi/package.test.ts +131 -0
  382. package/src/adapters/pypi/package.ts +297 -0
  383. package/src/adapters/qwen/web.test.ts +176 -0
  384. package/src/adapters/qwen/web.ts +758 -0
  385. package/src/adapters/reddit/account.test.ts +56 -0
  386. package/src/adapters/reddit/account.ts +493 -0
  387. package/src/adapters/rednote/web.test.ts +354 -0
  388. package/src/adapters/rednote/web.ts +968 -0
  389. package/src/adapters/rest-countries/countries.test.ts +80 -0
  390. package/src/adapters/rest-countries/countries.ts +271 -0
  391. package/src/adapters/reuters/article-detail.test.ts +65 -0
  392. package/src/adapters/reuters/article-detail.ts +186 -0
  393. package/src/adapters/rfc/rfc.test.ts +37 -0
  394. package/src/adapters/rfc/rfc.ts +133 -0
  395. package/src/adapters/rubygems/gem.test.ts +43 -0
  396. package/src/adapters/rubygems/gem.ts +126 -0
  397. package/src/adapters/stackoverflow/questions.test.ts +207 -0
  398. package/src/adapters/stackoverflow/questions.ts +765 -0
  399. package/src/adapters/steam/app.test.ts +68 -0
  400. package/src/adapters/steam/app.ts +218 -0
  401. package/src/adapters/tiktok/creator-videos.test.ts +158 -0
  402. package/src/adapters/tiktok/creator-videos.ts +370 -0
  403. package/src/adapters/tvmaze/shows.test.ts +93 -0
  404. package/src/adapters/tvmaze/shows.ts +271 -0
  405. package/src/adapters/twitter/bookmark-folders.test.ts +164 -0
  406. package/src/adapters/twitter/bookmark-folders.ts +366 -0
  407. package/src/adapters/twitter/quote.test.ts +157 -0
  408. package/src/adapters/twitter/quote.ts +332 -0
  409. package/src/adapters/twitter/tweet-actions.test.ts +51 -0
  410. package/src/adapters/twitter/tweet-actions.ts +187 -0
  411. package/src/adapters/twitter/tweet-url.ts +65 -0
  412. package/src/adapters/uisdc/news.test.ts +46 -0
  413. package/src/adapters/uisdc/news.ts +111 -0
  414. package/src/adapters/weibo/favorites-publish.test.ts +177 -0
  415. package/src/adapters/weibo/favorites-publish.ts +426 -0
  416. package/src/adapters/wikidata/entities.test.ts +103 -0
  417. package/src/adapters/wikidata/entities.ts +253 -0
  418. package/src/adapters/wikipedia/page.test.ts +49 -0
  419. package/src/adapters/wikipedia/page.ts +158 -0
  420. package/src/adapters/wttr/weather.test.ts +99 -0
  421. package/src/adapters/wttr/weather.ts +239 -0
  422. package/src/adapters/xianyu/publish.test.ts +210 -0
  423. package/src/adapters/xianyu/publish.ts +420 -0
  424. package/src/adapters/xiaohongshu/user-helpers.ts +5 -2
  425. package/src/adapters/yuanbao/web.test.ts +144 -0
  426. package/src/adapters/yuanbao/web.ts +435 -0
  427. package/src/adapters/zhihu/collection.test.ts +96 -0
  428. package/src/adapters/zhihu/collection.ts +239 -0
  429. package/src/adapters/zlibrary/web.test.ts +104 -0
  430. package/src/adapters/zlibrary/web.ts +192 -0
@@ -0,0 +1,239 @@
1
+ /**
2
+ * @owner src/adapters/zhihu/collection.ts
3
+ * @does Register agent-facing Zhihu collection item reader.
4
+ * @needs Logged-in Zhihu browser session, collection item API, bounded pagination.
5
+ * @feeds surface coverage ledger, Zhihu collection content workflows, answer/article/pin rows.
6
+ * @breaks Zhihu collection API shape drift or weak content mapping can hide saved items.
7
+ */
8
+
9
+ import { cli, Strategy } from "../../registry.js";
10
+
11
+ interface ZhihuCollectionItem {
12
+ content?: {
13
+ type?: unknown;
14
+ id?: unknown;
15
+ url?: unknown;
16
+ content?: unknown;
17
+ question?: { id?: unknown; title?: unknown };
18
+ title?: unknown;
19
+ author?: { name?: unknown };
20
+ voteup_count?: unknown;
21
+ reaction_count?: unknown;
22
+ };
23
+ }
24
+
25
+ interface ZhihuPage {
26
+ data?: ZhihuCollectionItem[];
27
+ paging?: {
28
+ totals?: unknown;
29
+ is_end?: unknown;
30
+ next?: unknown;
31
+ };
32
+ __httpError?: unknown;
33
+ }
34
+
35
+ function stringField(value: unknown): string {
36
+ return value === undefined || value === null ? "" : String(value);
37
+ }
38
+
39
+ function numberField(value: unknown): number {
40
+ const n = Number(value);
41
+ return Number.isFinite(n) ? n : 0;
42
+ }
43
+
44
+ export function stripZhihuHtml(value: unknown): string {
45
+ return stringField(value)
46
+ .replace(/<[^>]+>/g, "")
47
+ .replace(/&nbsp;/g, " ")
48
+ .replace(/&lt;/g, "<")
49
+ .replace(/&gt;/g, ">")
50
+ .replace(/&amp;/g, "&")
51
+ .replace(/&quot;/g, '"')
52
+ .trim();
53
+ }
54
+
55
+ export function requireZhihuCollectionId(value: unknown): string {
56
+ const id = String(value ?? "").trim();
57
+ if (!/^\d+$/.test(id))
58
+ throw new Error("Zhihu collection ID must be numeric.");
59
+ return id;
60
+ }
61
+
62
+ export function requireZhihuPositiveInt(value: unknown, label: string): number {
63
+ const n = Number(value);
64
+ if (!Number.isInteger(n) || n <= 0) {
65
+ throw new Error(`zhihu collection ${label} must be a positive integer.`);
66
+ }
67
+ return n;
68
+ }
69
+
70
+ export function requireZhihuNonNegativeInt(
71
+ value: unknown,
72
+ label: string,
73
+ ): number {
74
+ const n = Number(value);
75
+ if (!Number.isInteger(n) || n < 0) {
76
+ throw new Error(
77
+ `zhihu collection ${label} must be a non-negative integer.`,
78
+ );
79
+ }
80
+ return n;
81
+ }
82
+
83
+ export function zhihuCollectionItemKey(item: ZhihuCollectionItem): string {
84
+ const content = item.content ?? {};
85
+ return `${stringField(content.type) || "unknown"}:${stringField(
86
+ content.id || content.url || JSON.stringify(content).slice(0, 80),
87
+ )}`;
88
+ }
89
+
90
+ export function mapZhihuCollectionItem(
91
+ item: ZhihuCollectionItem,
92
+ rank: number,
93
+ ): Record<string, unknown> {
94
+ const content = item.content ?? {};
95
+ const type = stringField(content.type) || "unknown";
96
+ let title = "";
97
+ let excerpt = "";
98
+ let url = "";
99
+ let author = "";
100
+ let votes = 0;
101
+ if (type === "answer") {
102
+ const question = content.question ?? {};
103
+ title = stringField(question.title);
104
+ excerpt = stripZhihuHtml(content.content).slice(0, 150);
105
+ url =
106
+ stringField(content.url) ||
107
+ `https://www.zhihu.com/question/${stringField(question.id)}/answer/${stringField(content.id)}`;
108
+ author = stringField(content.author?.name) || "匿名用户";
109
+ votes = numberField(content.voteup_count);
110
+ } else if (type === "article") {
111
+ title = stringField(content.title);
112
+ excerpt = stripZhihuHtml(content.content).slice(0, 150);
113
+ url =
114
+ stringField(content.url) ||
115
+ `https://zhuanlan.zhihu.com/p/${stringField(content.id)}`;
116
+ author = stringField(content.author?.name) || "匿名用户";
117
+ votes = numberField(content.voteup_count);
118
+ } else if (type === "pin") {
119
+ title = "想法";
120
+ const blocks = Array.isArray(content.content) ? content.content : [];
121
+ excerpt = stripZhihuHtml(
122
+ blocks
123
+ .map((block) =>
124
+ typeof block === "object" && block !== null && "content" in block
125
+ ? stringField((block as { content?: unknown }).content)
126
+ : "",
127
+ )
128
+ .join(" "),
129
+ ).slice(0, 150);
130
+ url =
131
+ stringField(content.url) ||
132
+ `https://www.zhihu.com/pin/${stringField(content.id)}`;
133
+ author = stringField(content.author?.name) || "匿名用户";
134
+ votes = numberField(content.reaction_count);
135
+ }
136
+ return {
137
+ rank,
138
+ type,
139
+ title: title.slice(0, 100),
140
+ author,
141
+ votes,
142
+ excerpt,
143
+ url,
144
+ };
145
+ }
146
+
147
+ function nextOffsetFromPaging(page: ZhihuPage, fallback: number): number {
148
+ const next = stringField(page.paging?.next);
149
+ if (next) {
150
+ try {
151
+ const url = new URL(next);
152
+ const parsed = Number(url.searchParams.get("offset"));
153
+ if (Number.isInteger(parsed) && parsed > fallback) return parsed;
154
+ } catch {
155
+ return fallback;
156
+ }
157
+ }
158
+ return fallback;
159
+ }
160
+
161
+ cli({
162
+ site: "zhihu",
163
+ name: "collection",
164
+ description: "List items in a Zhihu collection",
165
+ domain: "www.zhihu.com",
166
+ strategy: Strategy.COOKIE,
167
+ browser: true,
168
+ args: [
169
+ {
170
+ name: "id",
171
+ type: "str",
172
+ required: true,
173
+ positional: true,
174
+ description: "Collection ID",
175
+ },
176
+ { name: "offset", type: "int", default: 0, description: "Starting offset" },
177
+ { name: "limit", type: "int", default: 20, description: "Number of items" },
178
+ ],
179
+ columns: ["rank", "type", "title", "author", "votes", "excerpt", "url"],
180
+ func: async (page, kwargs) => {
181
+ const browserPage = page as {
182
+ goto: (url: string) => Promise<unknown>;
183
+ evaluate: (script: string) => Promise<ZhihuPage>;
184
+ };
185
+ const id = requireZhihuCollectionId(kwargs.id);
186
+ const offset = requireZhihuNonNegativeInt(kwargs.offset ?? 0, "offset");
187
+ const requestedLimit = requireZhihuPositiveInt(kwargs.limit ?? 20, "limit");
188
+ const pageLimit = Math.min(requestedLimit, 20);
189
+ await browserPage.goto("https://www.zhihu.com");
190
+ const collected: ZhihuCollectionItem[] = [];
191
+ const seen = new Set<string>();
192
+ let nextOffset = offset;
193
+ const maxPages = Math.ceil(requestedLimit / pageLimit) + 2;
194
+ for (
195
+ let pageIndex = 0;
196
+ pageIndex < maxPages && collected.length < requestedLimit;
197
+ pageIndex += 1
198
+ ) {
199
+ const fetchLimit = Math.min(pageLimit, requestedLimit - collected.length);
200
+ const url = `https://www.zhihu.com/api/v4/collections/${id}/items?offset=${nextOffset}&limit=${fetchLimit}`;
201
+ const data = await browserPage.evaluate(`(async () => {
202
+ const response = await fetch(${JSON.stringify(url)}, { credentials: 'include' });
203
+ if (!response.ok) return { __httpError: response.status };
204
+ return await response.json();
205
+ })()`);
206
+ if (!data || data.__httpError) {
207
+ throw new Error(
208
+ `Zhihu collection request failed${data?.__httpError ? ` (HTTP ${String(data.__httpError)})` : ""}.`,
209
+ );
210
+ }
211
+ const items = Array.isArray(data.data) ? data.data : [];
212
+ for (const item of items) {
213
+ const key = zhihuCollectionItemKey(item);
214
+ if (!seen.has(key)) {
215
+ seen.add(key);
216
+ collected.push(item);
217
+ }
218
+ if (collected.length >= requestedLimit) break;
219
+ }
220
+ if (
221
+ items.length === 0 ||
222
+ data.paging?.is_end ||
223
+ collected.length >= requestedLimit
224
+ )
225
+ break;
226
+ const parsedNextOffset = nextOffsetFromPaging(data, nextOffset);
227
+ nextOffset =
228
+ parsedNextOffset > nextOffset
229
+ ? parsedNextOffset
230
+ : nextOffset + items.length;
231
+ }
232
+ if (collected.length === 0) {
233
+ throw new Error(`No items found for Zhihu collection ${id}.`);
234
+ }
235
+ return collected
236
+ .slice(0, requestedLimit)
237
+ .map((item, index) => mapZhihuCollectionItem(item, offset + index + 1));
238
+ },
239
+ });
@@ -0,0 +1,104 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { resolveCommand } from "../../registry.js";
3
+ import {
4
+ buildZlibrarySearchUrl,
5
+ mapZlibrarySearchRows,
6
+ normalizeZlibraryBookUrl,
7
+ requireZlibraryLimit,
8
+ } from "./web.js";
9
+
10
+ function pageMock(evaluateResults: unknown[]) {
11
+ return {
12
+ goto: vi.fn().mockResolvedValue(undefined),
13
+ wait: vi.fn().mockResolvedValue(undefined),
14
+ evaluate: vi.fn(async () => evaluateResults.shift()),
15
+ };
16
+ }
17
+
18
+ describe("zlibrary agent-facing commands", () => {
19
+ it("validates URLs, queries, limits, and maps rows", () => {
20
+ expect(buildZlibrarySearchUrl(" test book ")).toBe(
21
+ "https://z-library.im/s/test%20book",
22
+ );
23
+ expect(() => buildZlibrarySearchUrl(" ")).toThrow(
24
+ "Z-Library search query cannot be empty.",
25
+ );
26
+ expect(normalizeZlibraryBookUrl("https://z-library.im/book/demo")).toBe(
27
+ "https://z-library.im/book/demo",
28
+ );
29
+ expect(() =>
30
+ normalizeZlibraryBookUrl("https://example.com/book/demo"),
31
+ ).toThrow("Unsupported Z-Library URL host");
32
+ expect(requireZlibraryLimit("25")).toBe(25);
33
+ expect(() => requireZlibraryLimit(26)).toThrow(
34
+ "Z-Library search limit must be <= 25.",
35
+ );
36
+ expect(
37
+ mapZlibrarySearchRows([
38
+ {
39
+ rank: 1,
40
+ title: " Book ",
41
+ author: " Author ",
42
+ url: " https://z-library.im/book/demo ",
43
+ },
44
+ ]),
45
+ ).toEqual([
46
+ {
47
+ rank: 1,
48
+ title: "Book",
49
+ author: "Author",
50
+ url: "https://z-library.im/book/demo",
51
+ },
52
+ ]);
53
+ });
54
+
55
+ it("searches Z-Library results", async () => {
56
+ const command = resolveCommand("zlibrary", "search")?.command;
57
+ const page = pageMock([
58
+ [
59
+ {
60
+ rank: 1,
61
+ title: "Book",
62
+ author: "Author",
63
+ url: "https://z-library.im/book/demo",
64
+ },
65
+ ],
66
+ ]);
67
+ await expect(
68
+ command!.func!(page, { query: "book", limit: 10 }),
69
+ ).resolves.toEqual([
70
+ {
71
+ rank: 1,
72
+ title: "Book",
73
+ author: "Author",
74
+ url: "https://z-library.im/book/demo",
75
+ },
76
+ ]);
77
+ expect(page.goto).toHaveBeenCalledWith("https://z-library.im/s/book", {
78
+ waitUntil: "load",
79
+ settleMs: 3000,
80
+ });
81
+ expect(page.wait).toHaveBeenCalledWith(5);
82
+ });
83
+
84
+ it("extracts Z-Library book info and formats", async () => {
85
+ const command = resolveCommand("zlibrary", "info")?.command;
86
+ const page = pageMock([
87
+ "Demo Book",
88
+ undefined,
89
+ { pdf: "https://z-library.im/dl/pdf", epub: "" },
90
+ ]);
91
+ await expect(
92
+ command!.func!(page, { url: "https://z-library.im/book/demo" }),
93
+ ).resolves.toEqual([
94
+ {
95
+ title: "Demo Book",
96
+ pdf: "https://z-library.im/dl/pdf",
97
+ epub: "",
98
+ url: "https://z-library.im/book/demo",
99
+ },
100
+ ]);
101
+ expect(page.wait).toHaveBeenCalledWith(5);
102
+ expect(page.wait).toHaveBeenCalledWith(3);
103
+ });
104
+ });
@@ -0,0 +1,192 @@
1
+ /**
2
+ * @owner src/adapters/zlibrary/web.ts
3
+ * @does Register agent-facing Z-Library search and info commands implemented with site-specific safety checks.
4
+ * @needs Logged-in z-library.im browser session and Z-Library search/book page DOM.
5
+ * @feeds surface coverage ledger, book search rows, and book detail/download-format discovery.
6
+ * @breaks Z-Library host changes, login redirects, or z-bookcard shadow DOM drift can hide results.
7
+ */
8
+
9
+ import { cli, Strategy } from "../../registry.js";
10
+ import type { IPage } from "../../types.js";
11
+ import { str } from "../_shared/browser-tools.js";
12
+
13
+ const ZLIBRARY_DOMAIN = "z-library.im";
14
+ const ZLIBRARY_ORIGIN = `https://${ZLIBRARY_DOMAIN}`;
15
+ const ALLOWED_HOSTS = new Set([ZLIBRARY_DOMAIN, `www.${ZLIBRARY_DOMAIN}`]);
16
+
17
+ interface ZlibraryFormatLinks {
18
+ pdf?: unknown;
19
+ epub?: unknown;
20
+ }
21
+
22
+ interface ZlibrarySearchRow {
23
+ rank?: unknown;
24
+ title?: unknown;
25
+ author?: unknown;
26
+ url?: unknown;
27
+ }
28
+
29
+ export function normalizeZlibraryBookUrl(value: unknown): string {
30
+ const raw = str(value).trim();
31
+ let url: URL;
32
+ try {
33
+ url = new URL(raw);
34
+ } catch {
35
+ throw new Error(
36
+ `Z-Library book URL must be a valid http(s) URL under ${ZLIBRARY_DOMAIN}.`,
37
+ );
38
+ }
39
+ if (
40
+ !["http:", "https:"].includes(url.protocol) ||
41
+ !ALLOWED_HOSTS.has(url.hostname)
42
+ ) {
43
+ throw new Error(
44
+ `Unsupported Z-Library URL host: ${url.hostname}. Expected ${ZLIBRARY_DOMAIN}.`,
45
+ );
46
+ }
47
+ return url.toString();
48
+ }
49
+
50
+ export function buildZlibrarySearchUrl(value: unknown): string {
51
+ const query = str(value).trim();
52
+ if (!query) throw new Error("Z-Library search query cannot be empty.");
53
+ return `${ZLIBRARY_ORIGIN}/s/${encodeURIComponent(query)}`;
54
+ }
55
+
56
+ export function requireZlibraryLimit(value: unknown, fallback = 10): number {
57
+ const raw = value ?? fallback;
58
+ const limit = Number(raw);
59
+ if (!Number.isInteger(limit) || limit < 1) {
60
+ throw new Error("Z-Library search limit must be a positive integer.");
61
+ }
62
+ if (limit > 25) throw new Error("Z-Library search limit must be <= 25.");
63
+ return limit;
64
+ }
65
+
66
+ export function mapZlibrarySearchRows(
67
+ rows: ZlibrarySearchRow[],
68
+ ): Record<string, unknown>[] {
69
+ return rows
70
+ .map((row, index) => ({
71
+ rank: Number(row.rank) || index + 1,
72
+ title: str(row.title).trim(),
73
+ author: str(row.author).trim(),
74
+ url: str(row.url).trim(),
75
+ }))
76
+ .filter((row) => row.title && row.url);
77
+ }
78
+
79
+ async function extractBookTitle(page: IPage): Promise<string> {
80
+ const title = await page.evaluate(`(() => {
81
+ const card = document.querySelector('z-bookcard');
82
+ if (card?.shadowRoot) {
83
+ const el = card.shadowRoot.querySelector('[class*="title"], h1, a');
84
+ if (el) return el.textContent.trim().split('\\n')[0].trim();
85
+ }
86
+ return document.title.replace(/\\s*[-|].*$/, '').trim();
87
+ })()`);
88
+ return str(title).trim();
89
+ }
90
+
91
+ async function extractFormats(
92
+ page: IPage,
93
+ ): Promise<{ pdf: string; epub: string }> {
94
+ await page.evaluate(`(() => {
95
+ const button = document.querySelector('button[aria-label*="more" i], [class*="dots" i], [class*="more" i]');
96
+ if (button instanceof HTMLElement) button.click();
97
+ })()`);
98
+ await page.wait(3);
99
+ const formats = await page.evaluate(`(() => {
100
+ const result = { pdf: '', epub: '' };
101
+ document.querySelectorAll('a[href]').forEach((anchor) => {
102
+ const href = anchor.href || '';
103
+ const text = (anchor.textContent || '').toUpperCase();
104
+ if (href.includes('/dl/') && text.includes('PDF')) result.pdf = href;
105
+ if (href.includes('/dl/') && text.includes('EPUB')) result.epub = href;
106
+ });
107
+ return result;
108
+ })()`);
109
+ const row =
110
+ typeof formats === "object" && formats !== null
111
+ ? (formats as ZlibraryFormatLinks)
112
+ : {};
113
+ return { pdf: str(row.pdf), epub: str(row.epub) };
114
+ }
115
+
116
+ async function extractSearchResults(
117
+ page: IPage,
118
+ limit: number,
119
+ ): Promise<Record<string, unknown>[]> {
120
+ const result = await page.evaluate(`(() => {
121
+ return Array.from(document.querySelectorAll('z-bookcard'))
122
+ .slice(0, ${JSON.stringify(limit)})
123
+ .map((card, index) => {
124
+ const text = (card.textContent || '').trim();
125
+ const lines = text.split('\\n').map((line) => line.trim()).filter(Boolean);
126
+ const title = lines[0] || '';
127
+ const author = lines[1] || '';
128
+ let url = '';
129
+ if (card.shadowRoot) {
130
+ const link = card.shadowRoot.querySelector('a');
131
+ if (link) url = link.href || '';
132
+ }
133
+ return { rank: index + 1, title, author, url };
134
+ });
135
+ })()`);
136
+ return mapZlibrarySearchRows(
137
+ Array.isArray(result) ? (result as ZlibrarySearchRow[]) : [],
138
+ );
139
+ }
140
+
141
+ cli({
142
+ site: "zlibrary",
143
+ name: "search",
144
+ description: "Search Z-Library for books by title, author, ISBN, or keyword",
145
+ domain: ZLIBRARY_DOMAIN,
146
+ strategy: Strategy.COOKIE,
147
+ browser: true,
148
+ args: [
149
+ { name: "query", type: "str", required: true, positional: true },
150
+ { name: "limit", type: "int", default: 10 },
151
+ ],
152
+ columns: ["rank", "title", "author", "url"],
153
+ func: async (page, kwargs) => {
154
+ const p = page as IPage;
155
+ const limit = requireZlibraryLimit(kwargs.limit);
156
+ await p.goto(buildZlibrarySearchUrl(kwargs.query), {
157
+ waitUntil: "load",
158
+ settleMs: 3000,
159
+ });
160
+ await p.wait(5);
161
+ const rows = await extractSearchResults(p, limit);
162
+ if (!rows.length)
163
+ throw new Error("No Z-Library books found for the query.");
164
+ return rows;
165
+ },
166
+ });
167
+
168
+ cli({
169
+ site: "zlibrary",
170
+ name: "info",
171
+ description:
172
+ "Get book details and available download formats from a Z-Library book page",
173
+ domain: ZLIBRARY_DOMAIN,
174
+ strategy: Strategy.COOKIE,
175
+ browser: true,
176
+ args: [{ name: "url", type: "str", required: true, positional: true }],
177
+ columns: ["title", "pdf", "epub", "url"],
178
+ func: async (page, kwargs) => {
179
+ const p = page as IPage;
180
+ const url = normalizeZlibraryBookUrl(kwargs.url);
181
+ await p.goto(url, { waitUntil: "load", settleMs: 3000 });
182
+ await p.wait(5);
183
+ const title = await extractBookTitle(p);
184
+ const formats = await extractFormats(p);
185
+ if (!title || (!formats.pdf && !formats.epub)) {
186
+ throw new Error(
187
+ "Could not extract Z-Library title and download formats.",
188
+ );
189
+ }
190
+ return [{ title, pdf: formats.pdf, epub: formats.epub, url }];
191
+ },
192
+ });