@jackwener/opencli 1.6.9 → 1.7.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 (531) hide show
  1. package/README.md +128 -59
  2. package/README.zh-CN.md +134 -78
  3. package/dist/clis/_shared/common.d.ts +3 -0
  4. package/dist/clis/_shared/common.js +22 -0
  5. package/dist/clis/bilibili/hot.js +35 -0
  6. package/dist/clis/bluesky/feeds.d.ts +1 -0
  7. package/dist/clis/bluesky/feeds.js +27 -0
  8. package/dist/clis/bluesky/followers.d.ts +1 -0
  9. package/dist/clis/bluesky/followers.js +27 -0
  10. package/dist/clis/bluesky/following.d.ts +1 -0
  11. package/dist/clis/bluesky/following.js +27 -0
  12. package/dist/clis/bluesky/profile.d.ts +1 -0
  13. package/dist/clis/bluesky/profile.js +29 -0
  14. package/dist/clis/bluesky/search.d.ts +1 -0
  15. package/dist/clis/bluesky/search.js +28 -0
  16. package/dist/clis/bluesky/starter-packs.d.ts +1 -0
  17. package/dist/clis/bluesky/starter-packs.js +28 -0
  18. package/dist/clis/bluesky/thread.d.ts +1 -0
  19. package/dist/clis/bluesky/thread.js +30 -0
  20. package/dist/clis/bluesky/trending.d.ts +1 -0
  21. package/dist/clis/bluesky/trending.js +19 -0
  22. package/dist/clis/bluesky/user.d.ts +1 -0
  23. package/dist/clis/bluesky/user.js +33 -0
  24. package/dist/clis/cnki/search.d.ts +1 -0
  25. package/dist/clis/cnki/search.js +60 -0
  26. package/dist/clis/cnki/search.test.d.ts +1 -0
  27. package/dist/clis/cnki/search.test.js +18 -0
  28. package/dist/clis/devto/tag.d.ts +1 -0
  29. package/dist/clis/devto/tag.js +32 -0
  30. package/dist/clis/devto/top.d.ts +1 -0
  31. package/dist/clis/devto/top.js +26 -0
  32. package/dist/clis/devto/user.d.ts +1 -0
  33. package/dist/clis/devto/user.js +31 -0
  34. package/dist/clis/dictionary/examples.d.ts +1 -0
  35. package/dist/clis/dictionary/examples.js +27 -0
  36. package/dist/clis/dictionary/search.d.ts +1 -0
  37. package/dist/clis/dictionary/search.js +29 -0
  38. package/dist/clis/dictionary/synonyms.d.ts +1 -0
  39. package/dist/clis/dictionary/synonyms.js +27 -0
  40. package/dist/clis/douban/subject.d.ts +1 -0
  41. package/dist/clis/douban/subject.js +118 -0
  42. package/dist/clis/douban/top250.d.ts +1 -0
  43. package/dist/clis/douban/top250.js +67 -0
  44. package/dist/clis/facebook/add-friend.d.ts +1 -0
  45. package/dist/clis/facebook/add-friend.js +43 -0
  46. package/dist/clis/facebook/events.d.ts +1 -0
  47. package/dist/clis/facebook/events.js +40 -0
  48. package/dist/clis/facebook/feed.d.ts +1 -0
  49. package/dist/clis/facebook/feed.js +59 -0
  50. package/dist/clis/facebook/friends.d.ts +1 -0
  51. package/dist/clis/facebook/friends.js +38 -0
  52. package/dist/clis/facebook/groups.d.ts +1 -0
  53. package/dist/clis/facebook/groups.js +46 -0
  54. package/dist/clis/facebook/join-group.d.ts +1 -0
  55. package/dist/clis/facebook/join-group.js +44 -0
  56. package/dist/clis/facebook/memories.d.ts +1 -0
  57. package/dist/clis/facebook/memories.js +35 -0
  58. package/dist/clis/facebook/notifications.d.ts +1 -0
  59. package/dist/clis/facebook/notifications.js +36 -0
  60. package/dist/clis/facebook/profile.d.ts +1 -0
  61. package/dist/clis/facebook/profile.js +37 -0
  62. package/dist/clis/facebook/search.d.ts +1 -0
  63. package/dist/clis/facebook/search.js +38 -0
  64. package/dist/clis/facebook/search.test.d.ts +1 -1
  65. package/dist/clis/facebook/search.test.js +6 -9
  66. package/dist/clis/gitee/index.d.ts +3 -0
  67. package/dist/clis/gitee/index.js +3 -0
  68. package/dist/clis/gitee/search.d.ts +1 -0
  69. package/dist/clis/gitee/search.js +136 -0
  70. package/dist/clis/gitee/trending.d.ts +1 -0
  71. package/dist/clis/gitee/trending.js +567 -0
  72. package/dist/clis/gitee/user.d.ts +1 -0
  73. package/dist/clis/gitee/user.js +199 -0
  74. package/dist/clis/gitee/user.test.d.ts +1 -0
  75. package/dist/clis/gitee/user.test.js +63 -0
  76. package/dist/clis/hackernews/ask.d.ts +1 -0
  77. package/dist/clis/hackernews/ask.js +29 -0
  78. package/dist/clis/hackernews/best.d.ts +1 -0
  79. package/dist/clis/hackernews/best.js +29 -0
  80. package/dist/clis/hackernews/jobs.d.ts +1 -0
  81. package/dist/clis/hackernews/jobs.js +27 -0
  82. package/dist/clis/hackernews/new.d.ts +1 -0
  83. package/dist/clis/hackernews/new.js +29 -0
  84. package/dist/clis/hackernews/search.d.ts +1 -0
  85. package/dist/clis/hackernews/search.js +36 -0
  86. package/dist/clis/hackernews/show.d.ts +1 -0
  87. package/dist/clis/hackernews/show.js +29 -0
  88. package/dist/clis/hackernews/top.d.ts +1 -0
  89. package/dist/clis/hackernews/top.js +29 -0
  90. package/dist/clis/hackernews/user.d.ts +1 -0
  91. package/dist/clis/hackernews/user.js +22 -0
  92. package/dist/clis/hupu/hot.d.ts +1 -0
  93. package/dist/clis/hupu/hot.js +40 -0
  94. package/dist/clis/instagram/comment.d.ts +1 -0
  95. package/dist/clis/instagram/comment.js +47 -0
  96. package/dist/clis/instagram/explore.d.ts +1 -0
  97. package/dist/clis/instagram/explore.js +41 -0
  98. package/dist/clis/instagram/follow.d.ts +1 -0
  99. package/dist/clis/instagram/follow.js +43 -0
  100. package/dist/clis/instagram/followers.d.ts +1 -0
  101. package/dist/clis/instagram/followers.js +45 -0
  102. package/dist/clis/instagram/following.d.ts +1 -0
  103. package/dist/clis/instagram/following.js +45 -0
  104. package/dist/clis/instagram/like.d.ts +1 -0
  105. package/dist/clis/instagram/like.js +45 -0
  106. package/dist/clis/instagram/profile.d.ts +1 -0
  107. package/dist/clis/instagram/profile.js +39 -0
  108. package/dist/clis/instagram/save.d.ts +1 -0
  109. package/dist/clis/instagram/save.js +45 -0
  110. package/dist/clis/instagram/saved.d.ts +1 -0
  111. package/dist/clis/instagram/saved.js +38 -0
  112. package/dist/clis/instagram/search.d.ts +1 -0
  113. package/dist/clis/instagram/search.js +38 -0
  114. package/dist/clis/instagram/unfollow.d.ts +1 -0
  115. package/dist/clis/instagram/unfollow.js +40 -0
  116. package/dist/clis/instagram/unlike.d.ts +1 -0
  117. package/dist/clis/instagram/unlike.js +45 -0
  118. package/dist/clis/instagram/unsave.d.ts +1 -0
  119. package/dist/clis/instagram/unsave.js +45 -0
  120. package/dist/clis/instagram/user.d.ts +1 -0
  121. package/dist/clis/instagram/user.js +48 -0
  122. package/dist/clis/jd/add-cart.d.ts +1 -0
  123. package/dist/clis/jd/add-cart.js +71 -0
  124. package/dist/clis/jd/cart.d.ts +1 -0
  125. package/dist/clis/jd/cart.js +79 -0
  126. package/dist/clis/jd/commands.test.d.ts +5 -0
  127. package/dist/clis/jd/commands.test.js +64 -0
  128. package/dist/clis/jd/detail.d.ts +1 -0
  129. package/dist/clis/jd/detail.js +62 -0
  130. package/dist/clis/jd/reviews.d.ts +1 -0
  131. package/dist/clis/jd/reviews.js +54 -0
  132. package/dist/clis/jd/search.d.ts +1 -0
  133. package/dist/clis/jd/search.js +65 -0
  134. package/dist/clis/jianyu/detail.d.ts +1 -0
  135. package/dist/clis/jianyu/detail.js +20 -0
  136. package/dist/clis/jianyu/search.d.ts +41 -4
  137. package/dist/clis/jianyu/search.js +458 -96
  138. package/dist/clis/jianyu/search.test.js +105 -0
  139. package/dist/clis/jianyu/shared/china-bid-search.d.ts +12 -0
  140. package/dist/clis/jianyu/shared/china-bid-search.js +165 -0
  141. package/dist/clis/jianyu/shared/procurement-contract.d.ts +68 -0
  142. package/dist/clis/jianyu/shared/procurement-contract.js +324 -0
  143. package/dist/clis/jianyu/shared/procurement-contract.test.d.ts +1 -0
  144. package/dist/clis/jianyu/shared/procurement-contract.test.js +72 -0
  145. package/dist/clis/jianyu/shared/procurement-detail.d.ts +6 -0
  146. package/dist/clis/jianyu/shared/procurement-detail.js +92 -0
  147. package/dist/clis/jianyu/shared/procurement-detail.test.d.ts +1 -0
  148. package/dist/clis/jianyu/shared/procurement-detail.test.js +72 -0
  149. package/dist/clis/jike/post.d.ts +1 -0
  150. package/dist/clis/jike/post.js +61 -0
  151. package/dist/clis/jike/topic.d.ts +1 -0
  152. package/dist/clis/jike/topic.js +51 -0
  153. package/dist/clis/jike/user.d.ts +1 -0
  154. package/dist/clis/jike/user.js +50 -0
  155. package/dist/clis/jimeng/generate.d.ts +1 -0
  156. package/dist/clis/jimeng/generate.js +83 -0
  157. package/dist/clis/jimeng/history.d.ts +1 -0
  158. package/dist/clis/jimeng/history.js +47 -0
  159. package/dist/clis/jimeng/new.d.ts +1 -0
  160. package/dist/clis/jimeng/new.js +43 -0
  161. package/dist/clis/jimeng/workspaces.d.ts +1 -0
  162. package/dist/clis/jimeng/workspaces.js +41 -0
  163. package/dist/clis/linux-do/categories.d.ts +1 -0
  164. package/dist/clis/linux-do/categories.js +65 -0
  165. package/dist/clis/linux-do/search.d.ts +1 -0
  166. package/dist/clis/linux-do/search.js +41 -0
  167. package/dist/clis/linux-do/tags.d.ts +1 -0
  168. package/dist/clis/linux-do/tags.js +39 -0
  169. package/dist/clis/linux-do/topic-content.test.js +5 -5
  170. package/dist/clis/linux-do/topic.d.ts +1 -0
  171. package/dist/clis/linux-do/topic.js +56 -0
  172. package/dist/clis/linux-do/user-posts.d.ts +1 -0
  173. package/dist/clis/linux-do/user-posts.js +61 -0
  174. package/dist/clis/linux-do/user-topics.d.ts +1 -0
  175. package/dist/clis/linux-do/user-topics.js +48 -0
  176. package/dist/clis/lobsters/active.d.ts +1 -0
  177. package/dist/clis/lobsters/active.js +26 -0
  178. package/dist/clis/lobsters/hot.d.ts +1 -0
  179. package/dist/clis/lobsters/hot.js +26 -0
  180. package/dist/clis/lobsters/newest.d.ts +1 -0
  181. package/dist/clis/lobsters/newest.js +26 -0
  182. package/dist/clis/lobsters/tag.d.ts +1 -0
  183. package/dist/clis/lobsters/tag.js +32 -0
  184. package/dist/clis/pixiv/detail.d.ts +1 -0
  185. package/dist/clis/pixiv/detail.js +58 -0
  186. package/dist/clis/pixiv/ranking.d.ts +1 -0
  187. package/dist/clis/pixiv/ranking.js +59 -0
  188. package/dist/clis/pixiv/user.d.ts +1 -0
  189. package/dist/clis/pixiv/user.js +52 -0
  190. package/dist/clis/reddit/frontpage.d.ts +1 -0
  191. package/dist/clis/reddit/frontpage.js +31 -0
  192. package/dist/clis/reddit/hot.d.ts +1 -0
  193. package/dist/clis/reddit/hot.js +45 -0
  194. package/dist/clis/reddit/popular.d.ts +1 -0
  195. package/dist/clis/reddit/popular.js +41 -0
  196. package/dist/clis/reddit/search.d.ts +1 -0
  197. package/dist/clis/reddit/search.js +65 -0
  198. package/dist/clis/reddit/subreddit.d.ts +1 -0
  199. package/dist/clis/reddit/subreddit.js +52 -0
  200. package/dist/clis/reddit/user-comments.d.ts +1 -0
  201. package/dist/clis/reddit/user-comments.js +44 -0
  202. package/dist/clis/reddit/user-posts.d.ts +1 -0
  203. package/dist/clis/reddit/user-posts.js +42 -0
  204. package/dist/clis/reddit/user.d.ts +1 -0
  205. package/dist/clis/reddit/user.js +37 -0
  206. package/dist/clis/stackoverflow/bounties.d.ts +1 -0
  207. package/dist/clis/stackoverflow/bounties.js +27 -0
  208. package/dist/clis/stackoverflow/hot.d.ts +1 -0
  209. package/dist/clis/stackoverflow/hot.js +24 -0
  210. package/dist/clis/stackoverflow/search.d.ts +1 -0
  211. package/dist/clis/stackoverflow/search.js +27 -0
  212. package/dist/clis/stackoverflow/unanswered.d.ts +1 -0
  213. package/dist/clis/stackoverflow/unanswered.js +26 -0
  214. package/dist/clis/steam/top-sellers.d.ts +1 -0
  215. package/dist/clis/steam/top-sellers.js +25 -0
  216. package/dist/clis/taobao/add-cart.d.ts +1 -0
  217. package/dist/clis/taobao/add-cart.js +149 -0
  218. package/dist/clis/taobao/cart.d.ts +1 -0
  219. package/dist/clis/taobao/cart.js +95 -0
  220. package/dist/clis/taobao/commands.test.d.ts +5 -0
  221. package/dist/clis/taobao/commands.test.js +64 -0
  222. package/dist/clis/taobao/detail.d.ts +1 -0
  223. package/dist/clis/taobao/detail.js +70 -0
  224. package/dist/clis/taobao/reviews.d.ts +1 -0
  225. package/dist/clis/taobao/reviews.js +76 -0
  226. package/dist/clis/taobao/search.d.ts +1 -0
  227. package/dist/clis/taobao/search.js +96 -0
  228. package/dist/clis/tiktok/comment.d.ts +1 -0
  229. package/dist/clis/tiktok/comment.js +57 -0
  230. package/dist/clis/tiktok/explore.d.ts +1 -0
  231. package/dist/clis/tiktok/explore.js +35 -0
  232. package/dist/clis/tiktok/follow.d.ts +1 -0
  233. package/dist/clis/tiktok/follow.js +39 -0
  234. package/dist/clis/tiktok/following.d.ts +1 -0
  235. package/dist/clis/tiktok/following.js +42 -0
  236. package/dist/clis/tiktok/friends.d.ts +1 -0
  237. package/dist/clis/tiktok/friends.js +43 -0
  238. package/dist/clis/tiktok/like.d.ts +1 -0
  239. package/dist/clis/tiktok/like.js +33 -0
  240. package/dist/clis/tiktok/live.d.ts +1 -0
  241. package/dist/clis/tiktok/live.js +47 -0
  242. package/dist/clis/tiktok/notifications.d.ts +1 -0
  243. package/dist/clis/tiktok/notifications.js +49 -0
  244. package/dist/clis/tiktok/profile.d.ts +1 -0
  245. package/dist/clis/tiktok/profile.js +54 -0
  246. package/dist/clis/tiktok/save.d.ts +1 -0
  247. package/dist/clis/tiktok/save.js +29 -0
  248. package/dist/clis/tiktok/search.d.ts +1 -0
  249. package/dist/clis/tiktok/search.js +39 -0
  250. package/dist/clis/tiktok/unfollow.d.ts +1 -0
  251. package/dist/clis/tiktok/unfollow.js +44 -0
  252. package/dist/clis/tiktok/unlike.d.ts +1 -0
  253. package/dist/clis/tiktok/unlike.js +33 -0
  254. package/dist/clis/tiktok/unsave.d.ts +1 -0
  255. package/dist/clis/tiktok/unsave.js +31 -0
  256. package/dist/clis/tiktok/user.d.ts +1 -0
  257. package/dist/clis/tiktok/user.js +41 -0
  258. package/dist/clis/v2ex/hot.d.ts +1 -0
  259. package/dist/clis/v2ex/hot.js +25 -0
  260. package/dist/clis/v2ex/latest.d.ts +1 -0
  261. package/dist/clis/v2ex/latest.js +25 -0
  262. package/dist/clis/v2ex/member.d.ts +1 -0
  263. package/dist/clis/v2ex/member.js +27 -0
  264. package/dist/clis/v2ex/node.d.ts +1 -0
  265. package/dist/clis/v2ex/node.js +38 -0
  266. package/dist/clis/v2ex/nodes.d.ts +1 -0
  267. package/dist/clis/v2ex/nodes.js +25 -0
  268. package/dist/clis/v2ex/replies.d.ts +1 -0
  269. package/dist/clis/v2ex/replies.js +26 -0
  270. package/dist/clis/v2ex/topic.d.ts +1 -0
  271. package/dist/clis/v2ex/topic.js +30 -0
  272. package/dist/clis/v2ex/user.d.ts +1 -0
  273. package/dist/clis/v2ex/user.js +33 -0
  274. package/dist/clis/xiaoe/catalog.d.ts +1 -0
  275. package/dist/clis/xiaoe/catalog.js +161 -0
  276. package/dist/clis/xiaoe/content.d.ts +1 -0
  277. package/dist/clis/xiaoe/content.js +39 -0
  278. package/dist/clis/xiaoe/courses.d.ts +1 -0
  279. package/dist/clis/xiaoe/courses.js +69 -0
  280. package/dist/clis/xiaoe/detail.d.ts +1 -0
  281. package/dist/clis/xiaoe/detail.js +35 -0
  282. package/dist/clis/xiaoe/play-url.d.ts +1 -0
  283. package/dist/clis/xiaoe/play-url.js +120 -0
  284. package/dist/clis/xiaohongshu/feed.d.ts +1 -0
  285. package/dist/clis/xiaohongshu/feed.js +32 -0
  286. package/dist/clis/xiaohongshu/notifications.d.ts +1 -0
  287. package/dist/clis/xiaohongshu/notifications.js +38 -0
  288. package/dist/clis/xueqiu/earnings-date.d.ts +1 -0
  289. package/dist/clis/xueqiu/earnings-date.js +61 -0
  290. package/dist/clis/xueqiu/feed.d.ts +1 -0
  291. package/dist/clis/xueqiu/feed.js +48 -0
  292. package/dist/clis/xueqiu/groups.d.ts +1 -0
  293. package/dist/clis/xueqiu/groups.js +25 -0
  294. package/dist/clis/xueqiu/hot-stock.d.ts +1 -0
  295. package/dist/clis/xueqiu/hot-stock.js +44 -0
  296. package/dist/clis/xueqiu/hot.d.ts +1 -0
  297. package/dist/clis/xueqiu/hot.js +44 -0
  298. package/dist/clis/xueqiu/kline.d.ts +1 -0
  299. package/dist/clis/xueqiu/kline.js +64 -0
  300. package/dist/clis/xueqiu/search.d.ts +1 -0
  301. package/dist/clis/xueqiu/search.js +49 -0
  302. package/dist/clis/xueqiu/stock.d.ts +1 -0
  303. package/dist/clis/xueqiu/stock.js +72 -0
  304. package/dist/clis/xueqiu/watchlist.d.ts +1 -0
  305. package/dist/clis/xueqiu/watchlist.js +45 -0
  306. package/dist/clis/zhihu/hot.d.ts +1 -0
  307. package/dist/clis/zhihu/hot.js +43 -0
  308. package/dist/clis/zhihu/search.d.ts +1 -0
  309. package/dist/clis/zhihu/search.js +52 -0
  310. package/dist/src/browser/bridge.js +1 -1
  311. package/dist/src/browser/daemon-client.d.ts +16 -4
  312. package/dist/src/browser/daemon-client.js +33 -15
  313. package/dist/src/browser/daemon-client.test.js +0 -3
  314. package/dist/src/browser/dom-helpers.test.js +3 -2
  315. package/dist/src/browser/errors.d.ts +26 -1
  316. package/dist/src/browser/errors.js +40 -7
  317. package/dist/src/browser/errors.test.d.ts +1 -0
  318. package/dist/src/browser/errors.test.js +51 -0
  319. package/dist/src/browser/page.d.ts +9 -8
  320. package/dist/src/browser/page.js +33 -31
  321. package/dist/src/browser.test.js +25 -6
  322. package/dist/src/build-manifest.d.ts +5 -11
  323. package/dist/src/build-manifest.js +6 -75
  324. package/dist/src/build-manifest.test.js +1 -39
  325. package/dist/src/cascade.js +3 -2
  326. package/dist/src/cli.d.ts +3 -3
  327. package/dist/src/cli.js +71 -71
  328. package/dist/src/cli.test.js +20 -15
  329. package/dist/src/clis/binance/asks.d.ts +1 -0
  330. package/dist/src/clis/binance/asks.js +20 -0
  331. package/dist/src/clis/binance/commands.test.d.ts +3 -1
  332. package/dist/src/clis/binance/commands.test.js +10 -6
  333. package/dist/src/clis/binance/depth.d.ts +1 -0
  334. package/dist/src/clis/binance/depth.js +20 -0
  335. package/dist/src/clis/binance/gainers.d.ts +1 -0
  336. package/dist/src/clis/binance/gainers.js +21 -0
  337. package/dist/src/clis/binance/klines.d.ts +1 -0
  338. package/dist/src/clis/binance/klines.js +20 -0
  339. package/dist/src/clis/binance/losers.d.ts +1 -0
  340. package/dist/src/clis/binance/losers.js +21 -0
  341. package/dist/src/clis/binance/pairs.d.ts +1 -0
  342. package/dist/src/clis/binance/pairs.js +20 -0
  343. package/dist/src/clis/binance/price.d.ts +1 -0
  344. package/dist/src/clis/binance/price.js +17 -0
  345. package/dist/src/clis/binance/prices.d.ts +1 -0
  346. package/dist/src/clis/binance/prices.js +18 -0
  347. package/dist/src/clis/binance/ticker.d.ts +1 -0
  348. package/dist/src/clis/binance/ticker.js +20 -0
  349. package/dist/src/clis/binance/top.d.ts +1 -0
  350. package/dist/src/clis/binance/top.js +20 -0
  351. package/dist/src/clis/binance/trades.d.ts +1 -0
  352. package/dist/src/clis/binance/trades.js +19 -0
  353. package/dist/src/commands/daemon.d.ts +2 -6
  354. package/dist/src/commands/daemon.js +2 -58
  355. package/dist/src/commands/daemon.test.js +24 -120
  356. package/dist/src/completion-fast.d.ts +25 -0
  357. package/dist/src/completion-fast.js +140 -0
  358. package/dist/src/completion.d.ts +1 -0
  359. package/dist/src/completion.js +1 -0
  360. package/dist/src/constants.d.ts +0 -2
  361. package/dist/src/constants.js +0 -2
  362. package/dist/src/daemon.d.ts +1 -1
  363. package/dist/src/daemon.js +2 -15
  364. package/dist/src/diagnostic.test.js +2 -2
  365. package/dist/src/discovery.d.ts +3 -3
  366. package/dist/src/discovery.js +34 -97
  367. package/dist/src/download/index.d.ts +1 -1
  368. package/dist/src/engine.test.js +4 -19
  369. package/dist/src/execution.js +5 -1
  370. package/dist/src/generate-verified.d.ts +105 -0
  371. package/dist/src/generate-verified.js +696 -0
  372. package/dist/src/generate-verified.test.d.ts +1 -0
  373. package/dist/src/generate-verified.test.js +925 -0
  374. package/dist/src/generate.d.ts +9 -1
  375. package/dist/src/generate.js +2 -2
  376. package/dist/src/main.js +65 -12
  377. package/dist/src/pipeline/steps/download.d.ts +1 -17
  378. package/dist/src/pipeline/steps/download.js +20 -31
  379. package/dist/src/pipeline/steps/intercept.d.ts +1 -1
  380. package/dist/src/pipeline/steps/intercept.js +1 -1
  381. package/dist/src/pipeline/steps/tap.d.ts +1 -1
  382. package/dist/src/pipeline/steps/tap.js +1 -1
  383. package/dist/src/plugin-scaffold.d.ts +2 -2
  384. package/dist/src/plugin-scaffold.js +24 -21
  385. package/dist/src/plugin-scaffold.test.js +1 -1
  386. package/dist/src/plugin.d.ts +1 -1
  387. package/dist/src/plugin.js +4 -6
  388. package/dist/src/plugin.test.js +31 -31
  389. package/dist/src/record.js +26 -25
  390. package/dist/src/runtime-detect.js +3 -7
  391. package/dist/src/scripts/framework.d.ts +3 -0
  392. package/dist/src/scripts/framework.js +8 -4
  393. package/dist/src/scripts/store.d.ts +5 -1
  394. package/dist/src/scripts/store.js +5 -1
  395. package/dist/src/skill-generate.d.ts +30 -0
  396. package/dist/src/skill-generate.js +75 -0
  397. package/dist/src/skill-generate.test.d.ts +1 -0
  398. package/dist/src/skill-generate.test.js +173 -0
  399. package/dist/src/synthesize.d.ts +1 -1
  400. package/dist/src/synthesize.js +7 -8
  401. package/dist/src/types.d.ts +3 -1
  402. package/package.json +4 -3
  403. package/dist/clis/bilibili/hot.yaml +0 -38
  404. package/dist/clis/bluesky/feeds.yaml +0 -29
  405. package/dist/clis/bluesky/followers.yaml +0 -33
  406. package/dist/clis/bluesky/following.yaml +0 -33
  407. package/dist/clis/bluesky/profile.yaml +0 -27
  408. package/dist/clis/bluesky/search.yaml +0 -34
  409. package/dist/clis/bluesky/starter-packs.yaml +0 -34
  410. package/dist/clis/bluesky/thread.yaml +0 -32
  411. package/dist/clis/bluesky/trending.yaml +0 -27
  412. package/dist/clis/bluesky/user.yaml +0 -34
  413. package/dist/clis/devto/tag.yaml +0 -34
  414. package/dist/clis/devto/top.yaml +0 -29
  415. package/dist/clis/devto/user.yaml +0 -33
  416. package/dist/clis/dictionary/examples.yaml +0 -25
  417. package/dist/clis/dictionary/search.yaml +0 -27
  418. package/dist/clis/dictionary/synonyms.yaml +0 -25
  419. package/dist/clis/douban/subject.yaml +0 -107
  420. package/dist/clis/douban/top250.yaml +0 -70
  421. package/dist/clis/facebook/add-friend.yaml +0 -43
  422. package/dist/clis/facebook/events.yaml +0 -44
  423. package/dist/clis/facebook/feed.yaml +0 -63
  424. package/dist/clis/facebook/friends.yaml +0 -42
  425. package/dist/clis/facebook/groups.yaml +0 -50
  426. package/dist/clis/facebook/join-group.yaml +0 -44
  427. package/dist/clis/facebook/memories.yaml +0 -39
  428. package/dist/clis/facebook/notifications.yaml +0 -40
  429. package/dist/clis/facebook/profile.yaml +0 -37
  430. package/dist/clis/facebook/search.yaml +0 -47
  431. package/dist/clis/hackernews/ask.yaml +0 -38
  432. package/dist/clis/hackernews/best.yaml +0 -38
  433. package/dist/clis/hackernews/jobs.yaml +0 -36
  434. package/dist/clis/hackernews/new.yaml +0 -38
  435. package/dist/clis/hackernews/search.yaml +0 -44
  436. package/dist/clis/hackernews/show.yaml +0 -38
  437. package/dist/clis/hackernews/top.yaml +0 -38
  438. package/dist/clis/hackernews/user.yaml +0 -25
  439. package/dist/clis/hupu/hot.yaml +0 -43
  440. package/dist/clis/instagram/comment.yaml +0 -52
  441. package/dist/clis/instagram/explore.yaml +0 -43
  442. package/dist/clis/instagram/follow.yaml +0 -41
  443. package/dist/clis/instagram/followers.yaml +0 -51
  444. package/dist/clis/instagram/following.yaml +0 -51
  445. package/dist/clis/instagram/like.yaml +0 -46
  446. package/dist/clis/instagram/profile.yaml +0 -42
  447. package/dist/clis/instagram/save.yaml +0 -46
  448. package/dist/clis/instagram/saved.yaml +0 -40
  449. package/dist/clis/instagram/search.yaml +0 -44
  450. package/dist/clis/instagram/unfollow.yaml +0 -38
  451. package/dist/clis/instagram/unlike.yaml +0 -46
  452. package/dist/clis/instagram/unsave.yaml +0 -46
  453. package/dist/clis/instagram/user.yaml +0 -54
  454. package/dist/clis/jike/post.yaml +0 -59
  455. package/dist/clis/jike/topic.yaml +0 -53
  456. package/dist/clis/jike/user.yaml +0 -52
  457. package/dist/clis/jimeng/generate.yaml +0 -85
  458. package/dist/clis/jimeng/history.yaml +0 -46
  459. package/dist/clis/linux-do/categories.yaml +0 -70
  460. package/dist/clis/linux-do/search.yaml +0 -48
  461. package/dist/clis/linux-do/tags.yaml +0 -41
  462. package/dist/clis/linux-do/topic.yaml +0 -62
  463. package/dist/clis/linux-do/user-posts.yaml +0 -67
  464. package/dist/clis/linux-do/user-topics.yaml +0 -54
  465. package/dist/clis/lobsters/active.yaml +0 -29
  466. package/dist/clis/lobsters/hot.yaml +0 -29
  467. package/dist/clis/lobsters/newest.yaml +0 -29
  468. package/dist/clis/lobsters/tag.yaml +0 -34
  469. package/dist/clis/pixiv/detail.yaml +0 -49
  470. package/dist/clis/pixiv/ranking.yaml +0 -53
  471. package/dist/clis/pixiv/user.yaml +0 -46
  472. package/dist/clis/reddit/frontpage.yaml +0 -30
  473. package/dist/clis/reddit/hot.yaml +0 -47
  474. package/dist/clis/reddit/popular.yaml +0 -40
  475. package/dist/clis/reddit/search.yaml +0 -61
  476. package/dist/clis/reddit/subreddit.yaml +0 -50
  477. package/dist/clis/reddit/user-comments.yaml +0 -46
  478. package/dist/clis/reddit/user-posts.yaml +0 -44
  479. package/dist/clis/reddit/user.yaml +0 -40
  480. package/dist/clis/stackoverflow/bounties.yaml +0 -29
  481. package/dist/clis/stackoverflow/hot.yaml +0 -28
  482. package/dist/clis/stackoverflow/search.yaml +0 -33
  483. package/dist/clis/stackoverflow/unanswered.yaml +0 -28
  484. package/dist/clis/steam/top-sellers.yaml +0 -29
  485. package/dist/clis/tiktok/comment.yaml +0 -66
  486. package/dist/clis/tiktok/explore.yaml +0 -39
  487. package/dist/clis/tiktok/follow.yaml +0 -39
  488. package/dist/clis/tiktok/following.yaml +0 -46
  489. package/dist/clis/tiktok/friends.yaml +0 -47
  490. package/dist/clis/tiktok/like.yaml +0 -38
  491. package/dist/clis/tiktok/live.yaml +0 -51
  492. package/dist/clis/tiktok/notifications.yaml +0 -52
  493. package/dist/clis/tiktok/profile.yaml +0 -45
  494. package/dist/clis/tiktok/save.yaml +0 -34
  495. package/dist/clis/tiktok/search.yaml +0 -47
  496. package/dist/clis/tiktok/unfollow.yaml +0 -44
  497. package/dist/clis/tiktok/unlike.yaml +0 -38
  498. package/dist/clis/tiktok/unsave.yaml +0 -36
  499. package/dist/clis/tiktok/user.yaml +0 -44
  500. package/dist/clis/v2ex/hot.yaml +0 -28
  501. package/dist/clis/v2ex/latest.yaml +0 -28
  502. package/dist/clis/v2ex/member.yaml +0 -29
  503. package/dist/clis/v2ex/node.yaml +0 -34
  504. package/dist/clis/v2ex/nodes.yaml +0 -31
  505. package/dist/clis/v2ex/replies.yaml +0 -32
  506. package/dist/clis/v2ex/topic.yaml +0 -33
  507. package/dist/clis/v2ex/user.yaml +0 -34
  508. package/dist/clis/xiaoe/catalog.yaml +0 -129
  509. package/dist/clis/xiaoe/content.yaml +0 -43
  510. package/dist/clis/xiaoe/courses.yaml +0 -73
  511. package/dist/clis/xiaoe/detail.yaml +0 -39
  512. package/dist/clis/xiaoe/play-url.yaml +0 -124
  513. package/dist/clis/xiaohongshu/feed.yaml +0 -31
  514. package/dist/clis/xiaohongshu/notifications.yaml +0 -37
  515. package/dist/clis/xueqiu/earnings-date.yaml +0 -69
  516. package/dist/clis/xueqiu/feed.yaml +0 -53
  517. package/dist/clis/xueqiu/groups.yaml +0 -23
  518. package/dist/clis/xueqiu/hot-stock.yaml +0 -49
  519. package/dist/clis/xueqiu/hot.yaml +0 -46
  520. package/dist/clis/xueqiu/kline.yaml +0 -65
  521. package/dist/clis/xueqiu/search.yaml +0 -55
  522. package/dist/clis/xueqiu/stock.yaml +0 -69
  523. package/dist/clis/xueqiu/watchlist.yaml +0 -46
  524. package/dist/clis/zhihu/hot.yaml +0 -46
  525. package/dist/clis/zhihu/search.yaml +0 -59
  526. package/dist/src/daemon.test.js +0 -65
  527. package/dist/src/idle-manager.d.ts +0 -19
  528. package/dist/src/idle-manager.js +0 -54
  529. package/dist/src/yaml-schema.d.ts +0 -29
  530. package/dist/src/yaml-schema.js +0 -22
  531. /package/dist/{src/daemon.test.d.ts → clis/bilibili/hot.d.ts} +0 -0
@@ -1,6 +1,13 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import { __test__ } from './search.js';
3
3
  describe('jianyu search helpers', () => {
4
+ it('builds candidate URLs with supsearch as preferred entry', () => {
5
+ const candidates = __test__.buildSearchCandidates('procurement');
6
+ expect(candidates[0]).toContain('/jylab/supsearch/index.html');
7
+ expect(candidates[0]).toContain('keywords=procurement');
8
+ expect(candidates[0]).toContain('selectType=title');
9
+ expect(candidates[0]).toContain('searchGroup=1');
10
+ });
4
11
  it('builds supsearch URL with required query params', () => {
5
12
  const url = __test__.buildSearchUrl('procurement');
6
13
  expect(url).toContain('keywords=procurement');
@@ -20,4 +27,102 @@ describe('jianyu search helpers', () => {
20
27
  ]);
21
28
  expect(deduped).toHaveLength(2);
22
29
  });
30
+ it('filters obvious navigation rows before quality gate', () => {
31
+ const filtered = __test__.filterNavigationRows('电梯', [
32
+ { title: '招标公告', url: 'https://www.jianyu360.cn/list/stype/ZBGG.html', date: '' },
33
+ { title: '帮助中心', url: 'https://www.jianyu360.cn/helpCenter/index', date: '' },
34
+ { title: '某项目电梯采购公告', url: 'https://www.jianyu360.cn/notice/detail/123', date: '2026-04-07' },
35
+ ]);
36
+ expect(filtered).toHaveLength(1);
37
+ expect(filtered[0].title).toContain('电梯采购公告');
38
+ });
39
+ it('rejects procurement rows that do not contain query evidence', () => {
40
+ const filtered = __test__.filterNavigationRows('电梯', [
41
+ {
42
+ title: '某项目采购公告',
43
+ url: 'https://www.jianyu360.cn/notice/detail/123',
44
+ date: '2026-04-07',
45
+ contextText: '招标公告 项目编号:ABC-123',
46
+ },
47
+ ]);
48
+ expect(filtered).toHaveLength(0);
49
+ });
50
+ it('parses search-index markdown headings', () => {
51
+ const rows = __test__.parseSearchIndexMarkdown(`
52
+ ## [标题一](http://duckduckgo.com/l/?uddg=https%3A%2F%2Fbeijing.jianyu360.cn%2Fjybx%2F20260401_26033143187897.html)
53
+ ## [标题二](https://www.jianyu360.cn/nologin/content/ABC.html)
54
+ `);
55
+ expect(rows).toHaveLength(2);
56
+ expect(rows[0].title).toBe('标题一');
57
+ expect(rows[1].url).toContain('jianyu360.cn/nologin/content');
58
+ });
59
+ it('unwraps duckduckgo redirect links', () => {
60
+ const direct = __test__.unwrapDuckDuckGoUrl('https://duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.jianyu360.cn%2Fnologin%2Fcontent%2FXYZ.html');
61
+ expect(direct).toBe('https://www.jianyu360.cn/nologin/content/XYZ.html');
62
+ });
63
+ it('extracts publish date from jianyu jybx urls', () => {
64
+ const date = __test__.extractDateFromJianyuUrl('https://shandong.jianyu360.cn/jybx/20260310_26030938267551.html');
65
+ expect(date).toBe('2026-03-10');
66
+ });
67
+ it('normalizes api payload rows with fallback url/title fields', () => {
68
+ const normalized = __test__.normalizeApiRow({
69
+ noticeTitle: '某项目电梯采购公告',
70
+ detailUrl: '/jybx/20260310_26030938267551.html',
71
+ publishTime: '2026-03-10 09:00:00',
72
+ buyer: '测试单位',
73
+ });
74
+ expect(normalized).toBeTruthy();
75
+ expect(normalized?.title).toContain('电梯采购公告');
76
+ expect(normalized?.url).toContain('/jybx/20260310_26030938267551.html');
77
+ expect(normalized?.date).toBe('2026-03-10');
78
+ });
79
+ it('keeps later bucket unique api rows before final filtering and slice', async () => {
80
+ const page = {
81
+ goto: async () => { },
82
+ wait: async () => { },
83
+ evaluate: async () => ({
84
+ challenge: false,
85
+ responses: [
86
+ {
87
+ type: 'fType',
88
+ ok: true,
89
+ status: 200,
90
+ payload: {
91
+ list: [
92
+ {
93
+ noticeTitle: '某项目电梯采购公告',
94
+ detailUrl: '/jybx/20260310_26030938267551.html',
95
+ publishTime: '2026-03-10 09:00:00',
96
+ },
97
+ {
98
+ noticeTitle: '某项目电梯采购公告',
99
+ detailUrl: '/jybx/20260310_26030938267551.html',
100
+ publishTime: '2026-03-10 09:00:00',
101
+ },
102
+ ],
103
+ },
104
+ },
105
+ {
106
+ type: 'eType',
107
+ ok: true,
108
+ status: 200,
109
+ payload: {
110
+ list: [
111
+ {
112
+ noticeTitle: '另一条电梯采购公告',
113
+ detailUrl: '/jybx/20260311_26030938267552.html',
114
+ publishTime: '2026-03-11 09:00:00',
115
+ },
116
+ ],
117
+ },
118
+ },
119
+ ],
120
+ }),
121
+ };
122
+ const result = await __test__.fetchJianyuApiRows(page, '电梯', 1);
123
+ expect(result.challenge).toBe(false);
124
+ expect(result.rows).toHaveLength(2);
125
+ expect(result.rows[0].title).toContain('电梯采购公告');
126
+ expect(result.rows[1].title).toContain('另一条电梯采购公告');
127
+ });
23
128
  });
@@ -0,0 +1,12 @@
1
+ import { type ProcurementSearchCandidateRaw, cleanText, normalizeDate } from './procurement-contract.js';
2
+ export type BidSearchCandidate = ProcurementSearchCandidateRaw;
3
+ export { cleanText, normalizeDate };
4
+ export declare function dedupeCandidates(items: BidSearchCandidate[]): BidSearchCandidate[];
5
+ export declare function buildSearchCandidates(query: string, baseEntries: string[], queryKeys?: string[]): string[];
6
+ export declare function detectAuthPrompt(page: any): Promise<boolean>;
7
+ export declare function searchRowsFromEntries(page: any, { query, candidateUrls, allowedHostFragments, limit, }: {
8
+ query: string;
9
+ candidateUrls: string[];
10
+ allowedHostFragments: string[];
11
+ limit: number;
12
+ }): Promise<BidSearchCandidate[]>;
@@ -0,0 +1,165 @@
1
+ import { cleanText, normalizeDate, } from './procurement-contract.js';
2
+ export { cleanText, normalizeDate };
3
+ export function dedupeCandidates(items) {
4
+ const deduped = [];
5
+ const seen = new Set();
6
+ for (const item of items) {
7
+ const key = `${cleanText(item.title)}\t${cleanText(item.url)}`;
8
+ if (seen.has(key))
9
+ continue;
10
+ seen.add(key);
11
+ deduped.push(item);
12
+ }
13
+ return deduped;
14
+ }
15
+ function withQuery(baseUrl, key, query) {
16
+ try {
17
+ const url = new URL(baseUrl);
18
+ url.searchParams.set(key, query);
19
+ return url.toString();
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ export function buildSearchCandidates(query, baseEntries, queryKeys = ['keyword', 'keywords', 'q', 'search', 'title']) {
26
+ const keyword = cleanText(query);
27
+ const candidates = [];
28
+ if (keyword) {
29
+ for (const entry of baseEntries) {
30
+ for (const key of queryKeys) {
31
+ const withKeyword = withQuery(entry, key, keyword);
32
+ if (withKeyword)
33
+ candidates.push(withKeyword);
34
+ }
35
+ }
36
+ }
37
+ candidates.push(...baseEntries);
38
+ const ordered = [];
39
+ const seen = new Set();
40
+ for (const item of candidates) {
41
+ const value = cleanText(item);
42
+ if (!value || seen.has(value))
43
+ continue;
44
+ seen.add(value);
45
+ ordered.push(value);
46
+ }
47
+ return ordered;
48
+ }
49
+ export async function detectAuthPrompt(page) {
50
+ const pageText = cleanText(await page.evaluate('document.body ? document.body.innerText : ""'));
51
+ return /(请先登录|未登录|登录后|验证码|人机验证|权限不足|无权限|请完善信息后访问)/.test(pageText);
52
+ }
53
+ export async function searchRowsFromEntries(page, { query, candidateUrls, allowedHostFragments, limit, }) {
54
+ const queryText = cleanText(query);
55
+ const rows = [];
56
+ for (const targetUrl of candidateUrls) {
57
+ await page.goto(targetUrl);
58
+ await page.wait(2);
59
+ const payload = await page.evaluate(`
60
+ (() => {
61
+ const clean = (value) => (value || '').replace(/\\s+/g, ' ').trim();
62
+ const parseDate = (text) => {
63
+ const normalized = clean(text);
64
+ const match = normalized.match(/(20\\d{2})[.\\-/年](\\d{1,2})[.\\-/月](\\d{1,2})/);
65
+ if (!match) return '';
66
+ return match[1] + '-' + String(match[2]).padStart(2, '0') + '-' + String(match[3]).padStart(2, '0');
67
+ };
68
+ const toAbsolute = (href) => {
69
+ if (!href) return '';
70
+ if (href.startsWith('http://') || href.startsWith('https://')) return href;
71
+ if (href.startsWith('/')) return new URL(href, window.location.origin).toString();
72
+ return '';
73
+ };
74
+
75
+ const token = ${JSON.stringify(queryText)};
76
+ const tokenParts = token.split(/\\s+/).filter(Boolean).map((part) => part.toLowerCase());
77
+ const allowedHosts = ${JSON.stringify(allowedHostFragments.map((item) => item.toLowerCase()))};
78
+ const procurementHints = ['招标', '采购', '公告', '项目', '中标', '成交', '询价', '竞价', '比选', '投标', 'notice', 'tender', 'procurement', 'bidding'];
79
+ const rowSelectors = [
80
+ 'table tbody tr',
81
+ 'table tr',
82
+ 'ul li',
83
+ 'ol li',
84
+ 'article',
85
+ 'section',
86
+ '.list li',
87
+ '.notice li',
88
+ '[class*="list"] li',
89
+ '[class*="notice"] li',
90
+ '[class*="item"]',
91
+ '[class*="row"]',
92
+ ];
93
+
94
+ const rowNodes = [];
95
+ const rowSeen = new Set();
96
+ for (const selector of rowSelectors) {
97
+ const nodes = Array.from(document.querySelectorAll(selector));
98
+ for (const node of nodes) {
99
+ const text = clean(node.innerText || node.textContent || '');
100
+ if (!text || text.length < 8) continue;
101
+ const lowerText = text.toLowerCase();
102
+ const hasDate = /(20\\d{2})[.\\-/年](\\d{1,2})[.\\-/月](\\d{1,2})/.test(text);
103
+ const hasHint = procurementHints.some((hint) => lowerText.includes(hint));
104
+ const hasQuery = tokenParts.length === 0 || tokenParts.some((part) => lowerText.includes(part));
105
+ if (!hasDate && !hasHint && !hasQuery) continue;
106
+ if (rowSeen.has(node)) continue;
107
+ rowSeen.add(node);
108
+ rowNodes.push(node);
109
+ }
110
+ }
111
+
112
+ const rows = [];
113
+ const seen = new Set();
114
+ for (const node of rowNodes) {
115
+ const contextText = clean(node.innerText || node.textContent || '');
116
+ const contextLower = contextText.toLowerCase();
117
+ const hasHint = procurementHints.some((hint) => contextLower.includes(hint));
118
+ const hasQuery = tokenParts.length === 0 || tokenParts.some((part) => contextLower.includes(part));
119
+ if (!hasHint && !hasQuery) continue;
120
+
121
+ const anchors = Array.from(node.querySelectorAll('a[href]'));
122
+ for (const anchor of anchors) {
123
+ const title = clean(anchor.textContent || '');
124
+ if (!title || title.length < 4) continue;
125
+ const url = toAbsolute(anchor.getAttribute('href') || anchor.href || '');
126
+ if (!url) continue;
127
+ const lowerUrl = url.toLowerCase();
128
+ const hostMatched = allowedHosts.length === 0 || allowedHosts.some((item) => lowerUrl.includes(item));
129
+ if (!hostMatched) continue;
130
+
131
+ const key = title + '\\t' + url;
132
+ if (seen.has(key)) continue;
133
+ seen.add(key);
134
+ rows.push({
135
+ title,
136
+ url,
137
+ date: parseDate(contextText),
138
+ contextText,
139
+ });
140
+ }
141
+ }
142
+ return rows;
143
+ })()
144
+ `);
145
+ if (Array.isArray(payload)) {
146
+ for (const item of payload) {
147
+ if (!item || typeof item !== 'object')
148
+ continue;
149
+ const row = item;
150
+ const candidate = {
151
+ title: cleanText(row.title),
152
+ url: cleanText(row.url),
153
+ date: normalizeDate(cleanText(row.date)),
154
+ contextText: cleanText(row.contextText),
155
+ };
156
+ if (!candidate.title || !candidate.url)
157
+ continue;
158
+ rows.push(candidate);
159
+ }
160
+ }
161
+ if (rows.length >= limit)
162
+ break;
163
+ }
164
+ return dedupeCandidates(rows).slice(0, limit);
165
+ }
@@ -0,0 +1,68 @@
1
+ export type ProcurementContentType = 'notice' | 'result' | 'news' | 'navigation' | 'unknown';
2
+ export type ProcurementTaxonomy = 'network_dns' | 'relay_unavailable' | 'selector_drift' | 'timeout' | 'empty_result' | 'extraction_drift';
3
+ export interface ProcurementSearchCandidateRaw {
4
+ title: string;
5
+ url: string;
6
+ date?: string;
7
+ contextText?: string;
8
+ }
9
+ interface ProcurementCoreRecord {
10
+ title: string;
11
+ url: string;
12
+ date: string;
13
+ publish_time: string;
14
+ source_site: string;
15
+ is_detail_page: boolean;
16
+ content_type: ProcurementContentType;
17
+ project_owner: string;
18
+ project_code: string;
19
+ budget_or_limit: string;
20
+ deadline_or_open_time: string;
21
+ snippet: string;
22
+ summary: string;
23
+ quality_flags: string[];
24
+ }
25
+ export interface ProcurementSearchRecord extends ProcurementCoreRecord {
26
+ rank: number;
27
+ }
28
+ export interface ProcurementDetailRecord extends ProcurementCoreRecord {
29
+ detail_text: string;
30
+ evidence_blocks: string[];
31
+ }
32
+ export declare function cleanText(value: unknown): string;
33
+ export declare function normalizeDate(raw: string): string;
34
+ declare function splitEvidenceBlocks(text: string, query: string): string[];
35
+ declare function classifyContentType(title: string, url: string, contextText: string): ProcurementContentType;
36
+ declare function isDetailPage(url: string): boolean;
37
+ declare function qualityRejectReason(core: ProcurementCoreRecord, query: string): string | null;
38
+ export declare function formatTaxonomyError(taxonomy: ProcurementTaxonomy, { site, command, detail, }: {
39
+ site: string;
40
+ command: 'search' | 'detail';
41
+ detail: string;
42
+ }): string;
43
+ export declare function taxonomyError(taxonomy: ProcurementTaxonomy, context: {
44
+ site: string;
45
+ command: 'search' | 'detail';
46
+ detail: string;
47
+ }): Error;
48
+ export declare function toProcurementSearchRecords(rows: ProcurementSearchCandidateRaw[], { site, query, limit, }: {
49
+ site: string;
50
+ query: string;
51
+ limit: number;
52
+ }): ProcurementSearchRecord[];
53
+ export declare function toProcurementDetailRecord({ title, url, contextText, publishTime, }: {
54
+ title: string;
55
+ url: string;
56
+ contextText: string;
57
+ publishTime?: string;
58
+ }, { site, query, }: {
59
+ site: string;
60
+ query?: string;
61
+ }): ProcurementDetailRecord;
62
+ export declare const __test__: {
63
+ classifyContentType: typeof classifyContentType;
64
+ isDetailPage: typeof isDetailPage;
65
+ splitEvidenceBlocks: typeof splitEvidenceBlocks;
66
+ qualityRejectReason: typeof qualityRejectReason;
67
+ };
68
+ export {};
@@ -0,0 +1,324 @@
1
+ const PROCUREMENT_HINTS = [
2
+ '招标',
3
+ '采购',
4
+ '公告',
5
+ '项目',
6
+ '中标',
7
+ '成交',
8
+ '流标',
9
+ '终止',
10
+ '询价',
11
+ '竞价',
12
+ '比选',
13
+ '投标',
14
+ 'tender',
15
+ 'procurement',
16
+ 'bidding',
17
+ 'bid',
18
+ 'notice',
19
+ ];
20
+ const RESULT_HINTS = [
21
+ '中标',
22
+ '成交',
23
+ '结果',
24
+ '候选人',
25
+ '中选',
26
+ '定标',
27
+ '评标',
28
+ 'award',
29
+ 'winner',
30
+ ];
31
+ const NOTICE_HINTS = [
32
+ '招标',
33
+ '采购',
34
+ '询价',
35
+ '比选',
36
+ '公告',
37
+ '竞争性',
38
+ '邀请',
39
+ '投标',
40
+ 'tender',
41
+ 'procurement',
42
+ 'notice',
43
+ ];
44
+ const NEWS_HINTS = [
45
+ '新闻',
46
+ '资讯',
47
+ '动态',
48
+ '政策',
49
+ '简讯',
50
+ 'news',
51
+ 'article',
52
+ ];
53
+ const NAVIGATION_HINTS = [
54
+ '首页',
55
+ '官网',
56
+ '网站地图',
57
+ '联系我们',
58
+ '帮助中心',
59
+ 'english',
60
+ 'login',
61
+ '注册',
62
+ '导航',
63
+ '法规',
64
+ '政策文件',
65
+ '服务平台',
66
+ '信用中国',
67
+ ];
68
+ const DETAIL_URL_HINTS = [
69
+ '/detail',
70
+ '/content',
71
+ '/jybx/',
72
+ '/notice',
73
+ '/article',
74
+ '/view',
75
+ '/project',
76
+ '/bid',
77
+ 'detail=',
78
+ 'id=',
79
+ ];
80
+ const LIST_URL_HINTS = [
81
+ '/search',
82
+ '/list',
83
+ '/index',
84
+ '/home',
85
+ '/portal',
86
+ '/channel',
87
+ 'page=',
88
+ ];
89
+ const OWNER_PATTERNS = [
90
+ /(?:招标人|采购人|业主|建设单位|项目单位)\s*[::]\s*([^\n,。;]{2,60})/i,
91
+ ];
92
+ const CODE_PATTERNS = [
93
+ /(?:项目编号|招标编号|采购编号|项目编码|项目代码|编号)\s*[::]\s*([A-Za-z0-9\-_/]{4,60})/i,
94
+ ];
95
+ const BUDGET_PATTERNS = [
96
+ /(?:预算(?:金额)?|控制价|最高限价|限价|采购金额|合同估算价)\s*[::]\s*([^\n,。;]{2,80})/i,
97
+ ];
98
+ const DEADLINE_PATTERNS = [
99
+ /(?:报名截止时间|投标截止时间|开标时间|响应文件递交截止时间|截止时间|开标日期)\s*[::]\s*([^\n,。;]{2,80})/i,
100
+ ];
101
+ const DATE_PATTERN = /(20\d{2})[.\-/年](\d{1,2})[.\-/月](\d{1,2})/;
102
+ export function cleanText(value) {
103
+ return typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
104
+ }
105
+ export function normalizeDate(raw) {
106
+ const normalized = cleanText(raw);
107
+ const match = normalized.match(DATE_PATTERN);
108
+ if (!match)
109
+ return '';
110
+ const year = match[1];
111
+ const month = match[2].padStart(2, '0');
112
+ const day = match[3].padStart(2, '0');
113
+ return `${year}-${month}-${day}`;
114
+ }
115
+ function uniqueInOrder(values) {
116
+ const ordered = [];
117
+ const seen = new Set();
118
+ for (const value of values) {
119
+ const text = cleanText(value);
120
+ if (!text || seen.has(text))
121
+ continue;
122
+ seen.add(text);
123
+ ordered.push(text);
124
+ }
125
+ return ordered;
126
+ }
127
+ function containsAny(haystack, needles) {
128
+ return needles.some((needle) => haystack.includes(needle.toLowerCase()));
129
+ }
130
+ function extractByPatterns(text, patterns) {
131
+ for (const pattern of patterns) {
132
+ const matched = text.match(pattern);
133
+ if (matched?.[1])
134
+ return cleanText(matched[1]);
135
+ }
136
+ return '';
137
+ }
138
+ function deriveSnippet(text) {
139
+ const normalized = cleanText(text);
140
+ if (!normalized)
141
+ return '';
142
+ return normalized.slice(0, 220);
143
+ }
144
+ function splitEvidenceBlocks(text, query) {
145
+ const normalized = cleanText(text);
146
+ if (!normalized)
147
+ return [];
148
+ const queryTokens = query
149
+ .split(/\s+/)
150
+ .map((item) => item.toLowerCase().trim())
151
+ .filter(Boolean);
152
+ const chunks = normalized
153
+ .split(/[。!?;\n]/)
154
+ .map((chunk) => cleanText(chunk))
155
+ .filter(Boolean);
156
+ const ranked = chunks
157
+ .map((chunk) => {
158
+ const lower = chunk.toLowerCase();
159
+ const tokenScore = queryTokens.length === 0
160
+ ? 0
161
+ : queryTokens.reduce((score, token) => (lower.includes(token) ? score + 2 : score), 0);
162
+ const procurementScore = containsAny(lower, PROCUREMENT_HINTS) ? 1 : 0;
163
+ return {
164
+ chunk,
165
+ score: tokenScore + procurementScore,
166
+ };
167
+ })
168
+ .sort((a, b) => b.score - a.score || b.chunk.length - a.chunk.length)
169
+ .slice(0, 5)
170
+ .map((item) => item.chunk);
171
+ return uniqueInOrder(ranked);
172
+ }
173
+ function classifyContentType(title, url, contextText) {
174
+ const haystack = `${title} ${contextText} ${url}`.toLowerCase();
175
+ if (containsAny(haystack, RESULT_HINTS))
176
+ return 'result';
177
+ if (containsAny(haystack, NOTICE_HINTS))
178
+ return 'notice';
179
+ if (containsAny(haystack, NEWS_HINTS))
180
+ return 'news';
181
+ if (containsAny(haystack, NAVIGATION_HINTS))
182
+ return 'navigation';
183
+ return 'unknown';
184
+ }
185
+ function isDetailPage(url) {
186
+ const lower = cleanText(url).toLowerCase();
187
+ if (!lower)
188
+ return false;
189
+ const hasDetailToken = DETAIL_URL_HINTS.some((hint) => lower.includes(hint));
190
+ if (!hasDetailToken)
191
+ return false;
192
+ const hasListToken = LIST_URL_HINTS.some((hint) => lower.includes(hint));
193
+ return !hasListToken;
194
+ }
195
+ function buildQualityFlags(core) {
196
+ const flags = [];
197
+ if (!core.project_owner)
198
+ flags.push('missing_project_owner');
199
+ if (!core.project_code)
200
+ flags.push('missing_project_code');
201
+ if (!core.budget_or_limit)
202
+ flags.push('missing_budget');
203
+ if (!core.deadline_or_open_time)
204
+ flags.push('missing_deadline');
205
+ if (core.content_type === 'navigation')
206
+ flags.push('navigation_risk');
207
+ if (!core.is_detail_page)
208
+ flags.push('list_page_url');
209
+ return flags;
210
+ }
211
+ function queryMatched(text, query) {
212
+ const tokenParts = query
213
+ .split(/\s+/)
214
+ .map((part) => part.toLowerCase().trim())
215
+ .filter(Boolean);
216
+ if (tokenParts.length === 0)
217
+ return true;
218
+ const lower = text.toLowerCase();
219
+ return tokenParts.some((part) => lower.includes(part));
220
+ }
221
+ function normalizeCoreRecord(row, { sourceSite, }) {
222
+ const title = cleanText(row.title);
223
+ const url = cleanText(row.url);
224
+ const contextText = cleanText(row.contextText);
225
+ const date = normalizeDate(cleanText(row.date || contextText));
226
+ const publishTime = date;
227
+ const contentType = classifyContentType(title, url, contextText);
228
+ const projectOwner = extractByPatterns(contextText, OWNER_PATTERNS);
229
+ const projectCode = extractByPatterns(contextText, CODE_PATTERNS);
230
+ const budget = extractByPatterns(contextText, BUDGET_PATTERNS);
231
+ const deadline = extractByPatterns(contextText, DEADLINE_PATTERNS);
232
+ const snippet = deriveSnippet(contextText || title);
233
+ const core = {
234
+ title,
235
+ url,
236
+ date,
237
+ publish_time: publishTime,
238
+ source_site: sourceSite,
239
+ is_detail_page: isDetailPage(url),
240
+ content_type: contentType,
241
+ project_owner: projectOwner,
242
+ project_code: projectCode,
243
+ budget_or_limit: budget,
244
+ deadline_or_open_time: deadline,
245
+ snippet,
246
+ summary: snippet,
247
+ quality_flags: [],
248
+ };
249
+ core.quality_flags = buildQualityFlags(core);
250
+ return core;
251
+ }
252
+ function qualityRejectReason(core, query) {
253
+ if (!core.title || !core.url)
254
+ return 'missing_identity';
255
+ if (core.content_type === 'navigation')
256
+ return 'navigation_only';
257
+ const searchable = `${core.title} ${core.snippet} ${core.url}`.toLowerCase();
258
+ const hasQuery = queryMatched(searchable, query);
259
+ if (!hasQuery)
260
+ return 'query_mismatch';
261
+ return null;
262
+ }
263
+ function dedupeByTitleUrl(items) {
264
+ const deduped = [];
265
+ const seen = new Set();
266
+ for (const item of items) {
267
+ const key = `${item.title}\t${item.url}`;
268
+ if (seen.has(key))
269
+ continue;
270
+ seen.add(key);
271
+ deduped.push(item);
272
+ }
273
+ return deduped;
274
+ }
275
+ export function formatTaxonomyError(taxonomy, { site, command, detail, }) {
276
+ return `[taxonomy=${taxonomy}] site=${site} command=${command} ${cleanText(detail)}`;
277
+ }
278
+ export function taxonomyError(taxonomy, context) {
279
+ return new Error(formatTaxonomyError(taxonomy, context));
280
+ }
281
+ export function toProcurementSearchRecords(rows, { site, query, limit, }) {
282
+ const normalizedRows = dedupeByTitleUrl(rows.map((row) => normalizeCoreRecord(row, { sourceSite: site })));
283
+ const accepted = [];
284
+ for (const row of normalizedRows) {
285
+ const rejectReason = qualityRejectReason(row, query);
286
+ if (rejectReason)
287
+ continue;
288
+ accepted.push(row);
289
+ }
290
+ if (normalizedRows.length > 0 && accepted.length === 0) {
291
+ throw taxonomyError('extraction_drift', {
292
+ site,
293
+ command: 'search',
294
+ detail: `all rows rejected by quality gate (raw=${normalizedRows.length})`,
295
+ });
296
+ }
297
+ return accepted
298
+ .slice(0, Math.max(1, limit))
299
+ .map((row, index) => ({
300
+ rank: index + 1,
301
+ ...row,
302
+ }));
303
+ }
304
+ export function toProcurementDetailRecord({ title, url, contextText, publishTime, }, { site, query = '', }) {
305
+ const core = normalizeCoreRecord({
306
+ title,
307
+ url,
308
+ date: publishTime,
309
+ contextText,
310
+ }, { sourceSite: site });
311
+ const detailText = cleanText(contextText).slice(0, 6000);
312
+ const evidenceBlocks = splitEvidenceBlocks(detailText, query);
313
+ return {
314
+ ...core,
315
+ detail_text: detailText,
316
+ evidence_blocks: evidenceBlocks,
317
+ };
318
+ }
319
+ export const __test__ = {
320
+ classifyContentType,
321
+ isDetailPage,
322
+ splitEvidenceBlocks,
323
+ qualityRejectReason,
324
+ };