@sovovs/bycli 1.8.0 → 2.0.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 (509) hide show
  1. package/cli-manifest.json +0 -61
  2. package/dist/src/adapter-shadow.js +2 -2
  3. package/dist/src/browser/analyze.d.ts +10 -99
  4. package/dist/src/browser/analyze.js +10 -226
  5. package/dist/src/browser/cdp.js +64 -7
  6. package/dist/src/browser/navigation-guard.d.ts +78 -0
  7. package/dist/src/browser/navigation-guard.js +142 -0
  8. package/dist/src/browser/navigation-guard.test.d.ts +1 -0
  9. package/dist/src/browser/url-policy.d.ts +89 -0
  10. package/dist/src/browser/url-policy.js +311 -0
  11. package/dist/src/browser/url-policy.test.d.ts +1 -0
  12. package/dist/src/browser/verify-fixture.js +2 -2
  13. package/dist/src/cli.js +10 -13
  14. package/dist/src/config-paths.d.ts +19 -0
  15. package/dist/src/config-paths.js +31 -0
  16. package/dist/src/daemon.js +240 -0
  17. package/dist/src/discovery.d.ts +5 -3
  18. package/dist/src/discovery.js +8 -6
  19. package/dist/src/execution.js +8 -3
  20. package/dist/src/main.js +18 -2
  21. package/dist/src/plugin.js +11 -8
  22. package/dist/src/recorder/highlevel/analyze.d.ts +29 -0
  23. package/dist/src/recorder/highlevel/analyze.js +91 -0
  24. package/dist/src/recorder/highlevel/init.d.ts +125 -0
  25. package/dist/src/recorder/highlevel/init.js +279 -0
  26. package/dist/src/recorder/highlevel/init.test.d.ts +1 -0
  27. package/dist/src/recorder/highlevel/verify.d.ts +73 -0
  28. package/dist/src/recorder/highlevel/verify.js +74 -0
  29. package/dist/src/recorder/http/analyze-runner.d.ts +28 -0
  30. package/dist/src/recorder/http/analyze-runner.js +78 -0
  31. package/dist/src/recorder/http/analyze-runner.test.d.ts +1 -0
  32. package/dist/src/recorder/http/handlers.d.ts +51 -0
  33. package/dist/src/recorder/http/handlers.js +219 -0
  34. package/dist/src/recorder/http/wrapper-config.d.ts +20 -0
  35. package/dist/src/recorder/http/wrapper-config.js +82 -0
  36. package/dist/src/recorder/http/wrapper-envelope.d.ts +31 -0
  37. package/dist/src/recorder/http/wrapper-envelope.js +51 -0
  38. package/dist/src/recorder/http/wrapper-gates.d.ts +16 -0
  39. package/dist/src/recorder/http/wrapper-gates.js +28 -0
  40. package/dist/src/recorder/http/wrapper-metrics.d.ts +1 -0
  41. package/dist/src/recorder/http/wrapper-metrics.js +5 -0
  42. package/dist/src/recorder/http/wrapper-registry.d.ts +50 -0
  43. package/dist/src/recorder/http/wrapper-registry.js +98 -0
  44. package/dist/src/recorder/http/wrapper-server.d.ts +59 -0
  45. package/dist/src/recorder/http/wrapper-server.js +240 -0
  46. package/dist/src/recorder/http/wrapper.test.d.ts +1 -0
  47. package/dist/src/recorder/observability/logger.d.ts +5 -0
  48. package/dist/src/recorder/observability/logger.js +13 -0
  49. package/dist/src/recorder/runner/config.d.ts +34 -0
  50. package/dist/src/recorder/runner/config.js +84 -0
  51. package/dist/src/recorder/runner/config.test.d.ts +1 -0
  52. package/dist/src/recorder/runner/reap.d.ts +79 -0
  53. package/dist/src/recorder/runner/reap.js +230 -0
  54. package/dist/src/recorder/runner/reap.test.d.ts +1 -0
  55. package/dist/src/recorder/runner/runner-port.d.ts +95 -0
  56. package/dist/src/recorder/runner/runner-port.js +518 -0
  57. package/dist/src/recorder/runner/runner-port.test.d.ts +1 -0
  58. package/dist/src/recorder/runner/session-keys.d.ts +28 -0
  59. package/dist/src/recorder/runner/session-keys.js +72 -0
  60. package/dist/src/recorder/runner/session-keys.test.d.ts +1 -0
  61. package/dist/src/recorder/runner/verify-runner-main.d.ts +95 -0
  62. package/dist/src/recorder/runner/verify-runner-main.js +275 -0
  63. package/dist/src/recorder/runner/verify-runner-main.test.d.ts +1 -0
  64. package/package.json +8 -5
  65. package/scripts/m1-daemon-navigate-drive.mjs +138 -0
  66. package/scripts/m1-extension-spike.mjs +237 -0
  67. package/scripts/m1-live-spike.mjs +156 -0
  68. package/scripts/m6b-connect-back-spike.sh +31 -0
  69. package/scripts/recorder.sh +186 -0
  70. package/clis/12306/utils.test.js +0 -331
  71. package/clis/1688/assets.test.js +0 -39
  72. package/clis/1688/download.test.js +0 -31
  73. package/clis/1688/item.test.js +0 -67
  74. package/clis/1688/search.test.js +0 -75
  75. package/clis/1688/shared.test.js +0 -57
  76. package/clis/1688/store.test.js +0 -62
  77. package/clis/36kr/article.test.js +0 -46
  78. package/clis/36kr/hot.test.js +0 -15
  79. package/clis/36kr/news.test.js +0 -85
  80. package/clis/51job/utils.test.js +0 -69
  81. package/clis/_atlassian/shared.test.js +0 -170
  82. package/clis/aibase/news.test.js +0 -59
  83. package/clis/amazon/bestsellers.test.js +0 -29
  84. package/clis/amazon/discussion.test.js +0 -124
  85. package/clis/amazon/offer.test.js +0 -29
  86. package/clis/amazon/product.test.js +0 -24
  87. package/clis/amazon/rankings.test.js +0 -41
  88. package/clis/amazon/search.test.js +0 -22
  89. package/clis/amazon/shared.test.js +0 -44
  90. package/clis/apple-podcasts/commands.test.js +0 -119
  91. package/clis/apple-podcasts/utils.test.js +0 -57
  92. package/clis/arxiv/arxiv.test.js +0 -112
  93. package/clis/baidu-scholar/search.test.js +0 -23
  94. package/clis/barchart/greeks.test.js +0 -138
  95. package/clis/bilibili/comment.test.js +0 -153
  96. package/clis/bilibili/comments.test.js +0 -142
  97. package/clis/bilibili/dynamic.test.js +0 -68
  98. package/clis/bilibili/hot.test.js +0 -17
  99. package/clis/bilibili/subtitle.test.js +0 -196
  100. package/clis/bilibili/summary.test.js +0 -210
  101. package/clis/bilibili/utils.test.js +0 -61
  102. package/clis/bilibili/video.test.js +0 -132
  103. package/clis/binance/commands.test.js +0 -70
  104. package/clis/bloomberg/utils.test.js +0 -129
  105. package/clis/booking/booking.test.js +0 -356
  106. package/clis/boss/chatlist.test.js +0 -211
  107. package/clis/boss/chatmsg.test.js +0 -230
  108. package/clis/boss/search.test.js +0 -78
  109. package/clis/boss/utils.test.js +0 -34
  110. package/clis/brave/search.test.js +0 -76
  111. package/clis/chaoxing/utils.test.js +0 -45
  112. package/clis/chatgpt/commands.test.js +0 -45
  113. package/clis/chatgpt/envelope.test.js +0 -108
  114. package/clis/chatgpt/image.test.js +0 -188
  115. package/clis/chatgpt/utils.test.js +0 -330
  116. package/clis/chatgpt-app/ax.test.js +0 -35
  117. package/clis/chatwise/composer.test.js +0 -186
  118. package/clis/chess/analyze.test.js +0 -79
  119. package/clis/chess/game.test.js +0 -178
  120. package/clis/chess/games.test.js +0 -164
  121. package/clis/chess/stats.test.js +0 -79
  122. package/clis/chess/utils.test.js +0 -230
  123. package/clis/claude/ask.test.js +0 -338
  124. package/clis/claude/commands.test.js +0 -118
  125. package/clis/claude/utils.test.js +0 -148
  126. package/clis/cnki/search.test.js +0 -18
  127. package/clis/codex/sidebar.test.js +0 -329
  128. package/clis/coingecko/coingecko.test.js +0 -109
  129. package/clis/confluence/commands.test.js +0 -195
  130. package/clis/coupang/coupang.test.js +0 -159
  131. package/clis/coupang/utils.test.js +0 -62
  132. package/clis/ctrip/ctrip.test.js +0 -719
  133. package/clis/dblp/dblp.test.js +0 -397
  134. package/clis/deepseek/ask.test.js +0 -312
  135. package/clis/deepseek/detail.test.js +0 -81
  136. package/clis/deepseek/send.test.js +0 -107
  137. package/clis/deepseek/utils.test.js +0 -370
  138. package/clis/defillama/defillama.test.js +0 -99
  139. package/clis/devto/devto.test.js +0 -236
  140. package/clis/dianping/dianping.test.js +0 -578
  141. package/clis/douban/download.test.js +0 -170
  142. package/clis/douban/movie-hot.test.js +0 -14
  143. package/clis/douban/search.test.js +0 -11
  144. package/clis/douban/subject.test.js +0 -11
  145. package/clis/douban/utils.test.js +0 -438
  146. package/clis/doubao/detail.test.js +0 -42
  147. package/clis/doubao/history.test.js +0 -37
  148. package/clis/doubao/utils.test.js +0 -344
  149. package/clis/douyin/_shared/browser-fetch.test.js +0 -64
  150. package/clis/douyin/_shared/creation-id.test.js +0 -22
  151. package/clis/douyin/_shared/imagex-upload.test.js +0 -87
  152. package/clis/douyin/_shared/sts2.test.js +0 -27
  153. package/clis/douyin/_shared/text-extra.test.js +0 -37
  154. package/clis/douyin/_shared/timing.test.js +0 -28
  155. package/clis/douyin/_shared/tos-upload-short-read.test.js +0 -83
  156. package/clis/douyin/_shared/tos-upload.test.js +0 -229
  157. package/clis/douyin/_shared/transcode.test.js +0 -91
  158. package/clis/douyin/_shared/vod-upload.test.js +0 -38
  159. package/clis/douyin/activities.test.js +0 -55
  160. package/clis/douyin/collections.test.js +0 -45
  161. package/clis/douyin/delete.test.js +0 -100
  162. package/clis/douyin/draft.test.js +0 -335
  163. package/clis/douyin/drafts.test.js +0 -11
  164. package/clis/douyin/hashtag.test.js +0 -171
  165. package/clis/douyin/location.test.js +0 -23
  166. package/clis/douyin/profile.test.js +0 -46
  167. package/clis/douyin/publish-upload-id.test.js +0 -170
  168. package/clis/douyin/publish.test.js +0 -38
  169. package/clis/douyin/stats.test.js +0 -22
  170. package/clis/douyin/update.test.js +0 -11
  171. package/clis/douyin/user-videos.test.js +0 -151
  172. package/clis/douyin/videos.test.js +0 -54
  173. package/clis/duckduckgo/search.test.js +0 -128
  174. package/clis/duckduckgo/suggest.test.js +0 -66
  175. package/clis/eastmoney/hot-rank.test.js +0 -59
  176. package/clis/endoflife/endoflife.test.js +0 -51
  177. package/clis/facebook/feed.test.js +0 -169
  178. package/clis/facebook/marketplace.test.js +0 -91
  179. package/clis/facebook/notifications.test.js +0 -458
  180. package/clis/facebook/search.test.js +0 -55
  181. package/clis/flathub/flathub.test.js +0 -90
  182. package/clis/flomo/memos.test.js +0 -144
  183. package/clis/gemini/ask.test.js +0 -100
  184. package/clis/gemini/deep-research-result.test.js +0 -155
  185. package/clis/gemini/deep-research.test.js +0 -181
  186. package/clis/gemini/reply-state.test.js +0 -671
  187. package/clis/gemini/utils.test.js +0 -198
  188. package/clis/geogebra/geogebra.test.js +0 -175
  189. package/clis/gitee/search.test.js +0 -65
  190. package/clis/gitee/user.test.js +0 -63
  191. package/clis/google/utils.test.js +0 -75
  192. package/clis/google-scholar/cite.test.js +0 -47
  193. package/clis/google-scholar/profile.test.js +0 -49
  194. package/clis/google-scholar/search.test.js +0 -71
  195. package/clis/goproxy/goproxy.test.js +0 -103
  196. package/clis/gov-law/commands.test.js +0 -39
  197. package/clis/gov-policy/gov-policy.test.js +0 -224
  198. package/clis/grok/ask.test.js +0 -29
  199. package/clis/grok/utils.test.js +0 -103
  200. package/clis/hackernews/hackernews.test.js +0 -132
  201. package/clis/hf/hf.test.js +0 -16
  202. package/clis/hupu/hot.test.js +0 -224
  203. package/clis/imdb/utils.test.js +0 -88
  204. package/clis/indeed/indeed.test.js +0 -375
  205. package/clis/instagram/_shared/private-publish.test.js +0 -705
  206. package/clis/instagram/_shared/protocol-capture.test.js +0 -114
  207. package/clis/instagram/download.test.js +0 -118
  208. package/clis/instagram/note.test.js +0 -53
  209. package/clis/instagram/post.test.js +0 -1647
  210. package/clis/instagram/reel.test.js +0 -167
  211. package/clis/instagram/story.test.js +0 -134
  212. package/clis/jd/commands.test.js +0 -41
  213. package/clis/jd/item.test.js +0 -354
  214. package/clis/jianyu/search.test.js +0 -153
  215. package/clis/jianyu/shared/procurement-contract.test.js +0 -72
  216. package/clis/jianyu/shared/procurement-detail.test.js +0 -84
  217. package/clis/jike/read.test.js +0 -86
  218. package/clis/jira/commands.test.js +0 -287
  219. package/clis/juejin/search.js +0 -123
  220. package/clis/juejin/search.test.js +0 -98
  221. package/clis/lesswrong/frontpage.test.js +0 -37
  222. package/clis/lichess/lichess.test.js +0 -85
  223. package/clis/linkedin/connect.test.js +0 -213
  224. package/clis/linkedin/inbox.test.js +0 -152
  225. package/clis/linkedin/job-detail.test.js +0 -38
  226. package/clis/linkedin/jobs-preferences.test.js +0 -43
  227. package/clis/linkedin/people-search.test.js +0 -216
  228. package/clis/linkedin/post-analytics.test.js +0 -40
  229. package/clis/linkedin/posts.test.js +0 -40
  230. package/clis/linkedin/profile-analytics.test.js +0 -67
  231. package/clis/linkedin/profile-experience.test.js +0 -152
  232. package/clis/linkedin/profile-projects.test.js +0 -111
  233. package/clis/linkedin/profile-read.test.js +0 -77
  234. package/clis/linkedin/safe-send.test.js +0 -204
  235. package/clis/linkedin/salesnav-inbox.test.js +0 -113
  236. package/clis/linkedin/salesnav-message.test.js +0 -172
  237. package/clis/linkedin/salesnav-search.test.js +0 -76
  238. package/clis/linkedin/salesnav-thread.test.js +0 -79
  239. package/clis/linkedin/search.test.js +0 -222
  240. package/clis/linkedin/sent-invitations.test.js +0 -62
  241. package/clis/linkedin/services-read.test.js +0 -105
  242. package/clis/linkedin/thread-snapshot.test.js +0 -89
  243. package/clis/linkedin/timeline.test.js +0 -81
  244. package/clis/linkedin-learning/course.test.js +0 -114
  245. package/clis/linkedin-learning/search.test.js +0 -144
  246. package/clis/linkedin-learning/trending.test.js +0 -123
  247. package/clis/linux-do/feed.test.js +0 -153
  248. package/clis/linux-do/topic-content.test.js +0 -59
  249. package/clis/lobsters/lobsters.test.js +0 -169
  250. package/clis/notebooklm/add-source.test.js +0 -97
  251. package/clis/notebooklm/compat.test.js +0 -16
  252. package/clis/notebooklm/create.test.js +0 -58
  253. package/clis/notebooklm/generate-audio.test.js +0 -63
  254. package/clis/notebooklm/generate-slides.test.js +0 -75
  255. package/clis/notebooklm/history.test.js +0 -58
  256. package/clis/notebooklm/note-list.test.js +0 -56
  257. package/clis/notebooklm/notes-get.test.js +0 -72
  258. package/clis/notebooklm/open.test.js +0 -63
  259. package/clis/notebooklm/rpc.test.js +0 -131
  260. package/clis/notebooklm/source-fulltext.test.js +0 -106
  261. package/clis/notebooklm/source-get.test.js +0 -84
  262. package/clis/notebooklm/source-guide.test.js +0 -104
  263. package/clis/notebooklm/summary.test.js +0 -78
  264. package/clis/notebooklm/utils.test.js +0 -449
  265. package/clis/notebooklm/write-note.test.js +0 -70
  266. package/clis/nuget/nuget.test.js +0 -111
  267. package/clis/oeis/oeis.test.js +0 -88
  268. package/clis/ones/task-helpers.test.js +0 -12
  269. package/clis/ones/worklog.test.js +0 -20
  270. package/clis/openfda/openfda.test.js +0 -114
  271. package/clis/openreview/openreview.test.js +0 -427
  272. package/clis/osv/osv.test.js +0 -97
  273. package/clis/paperreview/commands.test.js +0 -243
  274. package/clis/paperreview/utils.test.js +0 -49
  275. package/clis/pixiv/download.test.js +0 -87
  276. package/clis/pixiv/illusts.test.js +0 -99
  277. package/clis/pixiv/search.test.js +0 -83
  278. package/clis/powerchina/search.test.js +0 -93
  279. package/clis/producthunt/utils.test.js +0 -64
  280. package/clis/pubmed/pubmed.test.js +0 -276
  281. package/clis/quark/utils.test.js +0 -58
  282. package/clis/qwen/utils.test.js +0 -45
  283. package/clis/reddit/extract-media.test.js +0 -149
  284. package/clis/reddit/frontpage.test.js +0 -34
  285. package/clis/reddit/home.test.js +0 -170
  286. package/clis/reddit/hot.test.js +0 -32
  287. package/clis/reddit/popular.test.js +0 -26
  288. package/clis/reddit/read.test.js +0 -335
  289. package/clis/reddit/reply.test.js +0 -89
  290. package/clis/reddit/search.test.js +0 -26
  291. package/clis/reddit/subreddit-info.test.js +0 -163
  292. package/clis/reddit/subreddit.test.js +0 -31
  293. package/clis/reddit/subscribed.test.js +0 -168
  294. package/clis/reddit/whoami.test.js +0 -105
  295. package/clis/rednote/navigation.test.js +0 -26
  296. package/clis/rednote/rednote.test.js +0 -222
  297. package/clis/rest-countries/rest-countries.test.js +0 -83
  298. package/clis/reuters/reuters.test.js +0 -299
  299. package/clis/rfc/rfc.test.js +0 -74
  300. package/clis/sinafinance/stock.test.js +0 -59
  301. package/clis/spotify/utils.test.js +0 -67
  302. package/clis/stackoverflow/stackoverflow.test.js +0 -404
  303. package/clis/steam/steam.test.js +0 -46
  304. package/clis/substack/utils.test.js +0 -44
  305. package/clis/suno/commands.test.js +0 -188
  306. package/clis/suno/download.test.js +0 -151
  307. package/clis/suno/generate.test.js +0 -252
  308. package/clis/suno/utils.test.js +0 -329
  309. package/clis/taobao/commands.test.js +0 -41
  310. package/clis/tdx/hot-rank.test.js +0 -59
  311. package/clis/ths/hot-rank.test.js +0 -64
  312. package/clis/tieba/commands.test.js +0 -79
  313. package/clis/tieba/utils.test.js +0 -290
  314. package/clis/tiktok/creator-videos.test.js +0 -113
  315. package/clis/tiktok/refactor.test.js +0 -389
  316. package/clis/tiktok/write-refactor.test.js +0 -370
  317. package/clis/toutiao/toutiao.test.js +0 -378
  318. package/clis/tvmaze/tvmaze.test.js +0 -93
  319. package/clis/twitter/bookmark-folder.test.js +0 -395
  320. package/clis/twitter/bookmark-folders.test.js +0 -152
  321. package/clis/twitter/bookmark.test.js +0 -74
  322. package/clis/twitter/bookmarks.test.js +0 -205
  323. package/clis/twitter/delete.test.js +0 -85
  324. package/clis/twitter/device-follow.test.js +0 -287
  325. package/clis/twitter/download.test.js +0 -457
  326. package/clis/twitter/followers.test.js +0 -62
  327. package/clis/twitter/following.test.js +0 -338
  328. package/clis/twitter/hide-reply.test.js +0 -76
  329. package/clis/twitter/like.test.js +0 -73
  330. package/clis/twitter/likes.test.js +0 -220
  331. package/clis/twitter/list-add.test.js +0 -133
  332. package/clis/twitter/list-create.test.js +0 -169
  333. package/clis/twitter/list-remove.test.js +0 -110
  334. package/clis/twitter/list-tweets.test.js +0 -196
  335. package/clis/twitter/lists.test.js +0 -253
  336. package/clis/twitter/post.test.js +0 -258
  337. package/clis/twitter/profile.test.js +0 -39
  338. package/clis/twitter/quote.test.js +0 -194
  339. package/clis/twitter/reply.test.js +0 -237
  340. package/clis/twitter/retweet.test.js +0 -73
  341. package/clis/twitter/search.test.js +0 -349
  342. package/clis/twitter/shared.test.js +0 -794
  343. package/clis/twitter/thread.test.js +0 -58
  344. package/clis/twitter/timeline.test.js +0 -104
  345. package/clis/twitter/trending.test.js +0 -15
  346. package/clis/twitter/tweets.test.js +0 -362
  347. package/clis/twitter/unbookmark.test.js +0 -73
  348. package/clis/twitter/unlike.test.js +0 -75
  349. package/clis/twitter/unretweet.test.js +0 -73
  350. package/clis/twitter/utils.test.js +0 -169
  351. package/clis/uisdc/news.test.js +0 -66
  352. package/clis/uiverse/_shared.test.js +0 -62
  353. package/clis/uiverse/navigation.test.js +0 -12
  354. package/clis/upwork/upwork.test.js +0 -566
  355. package/clis/wanfang/search.test.js +0 -23
  356. package/clis/web/read.test.js +0 -392
  357. package/clis/weibo/delete.test.js +0 -94
  358. package/clis/weibo/envelope.test.js +0 -85
  359. package/clis/weibo/favorites.test.js +0 -114
  360. package/clis/weibo/publish.test.js +0 -192
  361. package/clis/weibo/user-posts.test.js +0 -92
  362. package/clis/weibo/utils.test.js +0 -36
  363. package/clis/weixin/drafts.test.js +0 -69
  364. package/clis/weixin/search.test.js +0 -227
  365. package/clis/weread/ai-outline.test.js +0 -83
  366. package/clis/weread/book-search.test.js +0 -242
  367. package/clis/weread/commands.test.js +0 -398
  368. package/clis/weread/private-api-regression.test.js +0 -369
  369. package/clis/weread/search-regression.test.js +0 -494
  370. package/clis/weread/utils.test.js +0 -128
  371. package/clis/weread-official/commands.test.js +0 -385
  372. package/clis/weread-official/utils.test.js +0 -242
  373. package/clis/wikidata/wikidata.test.js +0 -83
  374. package/clis/wikipedia/trending.test.js +0 -57
  375. package/clis/wttr/wttr.test.js +0 -84
  376. package/clis/xianyu/chat.test.js +0 -84
  377. package/clis/xianyu/im.test.js +0 -253
  378. package/clis/xianyu/item.test.js +0 -56
  379. package/clis/xianyu/publish.test.js +0 -220
  380. package/clis/xianyu/search.test.js +0 -17
  381. package/clis/xiaoe/xiaoe.test.js +0 -486
  382. package/clis/xiaohongshu/comments.test.js +0 -258
  383. package/clis/xiaohongshu/creator-note-detail.test.js +0 -451
  384. package/clis/xiaohongshu/creator-notes-summary.test.js +0 -94
  385. package/clis/xiaohongshu/creator-notes.test.js +0 -281
  386. package/clis/xiaohongshu/creator-stats.test.js +0 -24
  387. package/clis/xiaohongshu/delete-note.test.js +0 -172
  388. package/clis/xiaohongshu/download.test.js +0 -316
  389. package/clis/xiaohongshu/navigation.test.js +0 -34
  390. package/clis/xiaohongshu/note.test.js +0 -249
  391. package/clis/xiaohongshu/publish.test.js +0 -608
  392. package/clis/xiaohongshu/search.test.js +0 -325
  393. package/clis/xiaohongshu/user-helpers.test.js +0 -160
  394. package/clis/xiaoyuzhou/auth.test.js +0 -124
  395. package/clis/xiaoyuzhou/download.test.js +0 -135
  396. package/clis/xiaoyuzhou/podcast-episodes.test.js +0 -78
  397. package/clis/xiaoyuzhou/transcript.test.js +0 -195
  398. package/clis/xiaoyuzhou/utils.test.js +0 -39
  399. package/clis/xueqiu/comments.test.js +0 -696
  400. package/clis/xueqiu/danjuan-utils.test.js +0 -41
  401. package/clis/xueqiu/utils.test.js +0 -26
  402. package/clis/yahoo/search.test.js +0 -94
  403. package/clis/youdao/note.test.js +0 -99
  404. package/clis/youtube/channel.test.js +0 -59
  405. package/clis/youtube/feed.test.js +0 -131
  406. package/clis/youtube/transcript-group.test.js +0 -99
  407. package/clis/youtube/transcript.test.js +0 -296
  408. package/clis/youtube/utils.test.js +0 -84
  409. package/clis/yuanbao/ask.test.js +0 -124
  410. package/clis/yuanbao/new.test.js +0 -30
  411. package/clis/yuanbao/shared.test.js +0 -80
  412. package/clis/zhihu/answer-comments.test.js +0 -287
  413. package/clis/zhihu/answer-detail.test.js +0 -338
  414. package/clis/zhihu/answer.test.js +0 -54
  415. package/clis/zhihu/collection.test.js +0 -337
  416. package/clis/zhihu/collections.test.js +0 -182
  417. package/clis/zhihu/comment.test.js +0 -50
  418. package/clis/zhihu/download.test.js +0 -12
  419. package/clis/zhihu/favorite.test.js +0 -59
  420. package/clis/zhihu/follow.test.js +0 -42
  421. package/clis/zhihu/like.test.js +0 -41
  422. package/clis/zhihu/question.test.js +0 -251
  423. package/clis/zhihu/recommend.test.js +0 -143
  424. package/clis/zhihu/search.test.js +0 -198
  425. package/clis/zhihu/target.test.js +0 -77
  426. package/clis/zhihu/text.test.js +0 -24
  427. package/clis/zhihu/write-shared.test.js +0 -176
  428. package/clis/zlibrary/commands.test.js +0 -65
  429. package/clis/zsxq/search.test.js +0 -24
  430. package/clis/zsxq/topic.test.js +0 -30
  431. package/clis/zsxq/topics.test.js +0 -24
  432. package/clis/zsxq/utils.test.js +0 -31
  433. package/dist/src/adapter-shadow.test.js +0 -49
  434. package/dist/src/adapter-source.test.js +0 -29
  435. package/dist/src/browser/analyze.test.js +0 -166
  436. package/dist/src/browser/article-extract.e2e.test.js +0 -105
  437. package/dist/src/browser/article-extract.test.js +0 -94
  438. package/dist/src/browser/ax-snapshot.test.js +0 -91
  439. package/dist/src/browser/base-page.test.js +0 -758
  440. package/dist/src/browser/cdp-click-fixture.test.js +0 -87
  441. package/dist/src/browser/cdp.test.js +0 -79
  442. package/dist/src/browser/compound.test.js +0 -175
  443. package/dist/src/browser/daemon-client.test.js +0 -199
  444. package/dist/src/browser/dom-helpers.test.js +0 -106
  445. package/dist/src/browser/dom-snapshot.test.js +0 -313
  446. package/dist/src/browser/errors.test.js +0 -60
  447. package/dist/src/browser/extract.test.js +0 -129
  448. package/dist/src/browser/find.test.js +0 -180
  449. package/dist/src/browser/html-tree.test.js +0 -181
  450. package/dist/src/browser/network-cache.test.js +0 -58
  451. package/dist/src/browser/network-key.test.js +0 -49
  452. package/dist/src/browser/page.test.js +0 -378
  453. package/dist/src/browser/shape-filter.test.js +0 -101
  454. package/dist/src/browser/shape.test.js +0 -82
  455. package/dist/src/browser/stealth.test.js +0 -134
  456. package/dist/src/browser/target-errors.test.js +0 -94
  457. package/dist/src/browser/target-resolver.test.js +0 -118
  458. package/dist/src/browser/utils.test.js +0 -29
  459. package/dist/src/browser/verify-fixture.test.js +0 -219
  460. package/dist/src/browser.test.js +0 -232
  461. package/dist/src/build-manifest.test.js +0 -341
  462. package/dist/src/capabilityRouting.test.js +0 -63
  463. package/dist/src/cli-argv-preprocess.test.js +0 -209
  464. package/dist/src/cli.test.js +0 -2803
  465. package/dist/src/commanderAdapter.test.js +0 -352
  466. package/dist/src/commands/daemon.test.js +0 -219
  467. package/dist/src/completion.test.js +0 -24
  468. package/dist/src/convention-audit.test.js +0 -247
  469. package/dist/src/daemon.test.js +0 -60
  470. package/dist/src/doctor.test.js +0 -263
  471. package/dist/src/download/article-download.test.js +0 -235
  472. package/dist/src/download/index.test.js +0 -140
  473. package/dist/src/download/media-download.test.js +0 -112
  474. package/dist/src/download/progress.test.js +0 -36
  475. package/dist/src/electron-apps.test.js +0 -49
  476. package/dist/src/engine.test.js +0 -312
  477. package/dist/src/errors.test.js +0 -109
  478. package/dist/src/execution.test.js +0 -623
  479. package/dist/src/extension-manifest-regression.test.js +0 -12
  480. package/dist/src/external.test.js +0 -146
  481. package/dist/src/help.test.js +0 -58
  482. package/dist/src/hooks.test.js +0 -92
  483. package/dist/src/interceptor.test.js +0 -81
  484. package/dist/src/launcher.test.js +0 -138
  485. package/dist/src/node-network.test.js +0 -55
  486. package/dist/src/observation/artifact.test.js +0 -121
  487. package/dist/src/observation/manager.test.js +0 -13
  488. package/dist/src/observation/redaction.test.js +0 -32
  489. package/dist/src/observation/retention.test.js +0 -118
  490. package/dist/src/observation/ring-buffer.test.js +0 -22
  491. package/dist/src/output.test.js +0 -53
  492. package/dist/src/package-exports.test.js +0 -112
  493. package/dist/src/pipeline/executor.test.js +0 -182
  494. package/dist/src/pipeline/steps/download.test.js +0 -102
  495. package/dist/src/pipeline/steps/fetch.test.js +0 -117
  496. package/dist/src/pipeline/template.test.js +0 -175
  497. package/dist/src/pipeline/transform.test.js +0 -133
  498. package/dist/src/plugin-manifest.test.js +0 -179
  499. package/dist/src/plugin-scaffold.test.js +0 -83
  500. package/dist/src/plugin.test.js +0 -1324
  501. package/dist/src/registry.test.js +0 -209
  502. package/dist/src/runtime-detect.test.js +0 -39
  503. package/dist/src/serialization.test.js +0 -78
  504. package/dist/src/snapshotFormatter.test.js +0 -521
  505. package/dist/src/tui.test.js +0 -19
  506. package/dist/src/update-check.test.js +0 -116
  507. package/dist/src/utils.test.js +0 -155
  508. package/dist/src/validate.test.js +0 -90
  509. package/dist/src/weixin-download.test.js +0 -45
@@ -0,0 +1,98 @@
1
+ const TERMINAL = ['succeeded', 'failed', 'timeout', 'cancelled'];
2
+ function project(rec) {
3
+ const { runnerId: _runnerId, ...status } = rec;
4
+ return status;
5
+ }
6
+ export class WrapperRegistry {
7
+ opts;
8
+ requests = new Map();
9
+ now;
10
+ constructor(opts) {
11
+ this.opts = opts;
12
+ this.now = opts.now ?? Date.now;
13
+ }
14
+ /** 铸一条 in-flight 记录。init 同步跑完会立刻 finalize;verify/analyze 留 running 待轮询/后台收尾。 */
15
+ createRequest(input) {
16
+ const t = this.now();
17
+ const rec = {
18
+ requestId: input.requestId,
19
+ type: input.type,
20
+ status: input.status ?? 'running',
21
+ startedAt: t,
22
+ updatedAt: t,
23
+ expiresAt: null,
24
+ pollAfterMs: this.opts.pollAfterMs,
25
+ queueReason: null,
26
+ progress: null,
27
+ result: null,
28
+ error: null,
29
+ runnerId: input.runnerId ?? input.requestId,
30
+ };
31
+ this.requests.set(input.requestId, rec);
32
+ return project(rec);
33
+ }
34
+ /** 合并一条非终态 status/result/error/queueReason 更新;终态记录不可变。 */
35
+ updateRequest(requestId, patch) {
36
+ const rec = this.requests.get(requestId);
37
+ if (!rec)
38
+ return;
39
+ if (TERMINAL.includes(rec.status))
40
+ return;
41
+ if (patch.status)
42
+ rec.status = patch.status;
43
+ if (patch.result !== undefined)
44
+ rec.result = patch.result;
45
+ if (patch.error !== undefined)
46
+ rec.error = patch.error;
47
+ if (patch.queueReason !== undefined)
48
+ rec.queueReason = patch.queueReason;
49
+ rec.updatedAt = this.now();
50
+ }
51
+ /** 置终态 + 武装 TTL(终态过期 → getRequest 返 undefined → 404)。已终态则幂等 no-op。 */
52
+ finalizeRequest(requestId, patch) {
53
+ const rec = this.requests.get(requestId);
54
+ if (!rec)
55
+ return;
56
+ if (TERMINAL.includes(rec.status))
57
+ return;
58
+ rec.status = patch.status;
59
+ if (patch.result !== undefined)
60
+ rec.result = patch.result;
61
+ if (patch.error !== undefined)
62
+ rec.error = patch.error;
63
+ const t = this.now();
64
+ rec.updatedAt = t;
65
+ rec.expiresAt = t + this.opts.terminalTtlMs;
66
+ }
67
+ /** 面向客户端的 RequestStatus(TTL 校验,wrapper-only 字段投影掉)。 */
68
+ getRequest(id) {
69
+ const rec = this.getRecord(id);
70
+ return rec ? project(rec) : undefined;
71
+ }
72
+ /** 内部记录(含 runnerId;handler 用,不返客户端)。TTL 过期即删并返 undefined。 */
73
+ getRecord(id) {
74
+ const r = this.requests.get(id);
75
+ if (!r)
76
+ return undefined;
77
+ if (r.expiresAt !== null && this.now() > r.expiresAt) {
78
+ this.requests.delete(id);
79
+ return undefined; // TTL 过期 → request_not_found
80
+ }
81
+ return r;
82
+ }
83
+ /**
84
+ * 周期清理:删除所有已过 TTL 的终态记录(getRecord 只在"被访问"时删,abandoned 的终态记录否则常驻
85
+ * 内存)。non-terminal 记录 expiresAt=null,绝不被清。返回清理条数。
86
+ */
87
+ sweepExpired() {
88
+ const t = this.now();
89
+ let removed = 0;
90
+ for (const [id, rec] of this.requests) {
91
+ if (rec.expiresAt !== null && t > rec.expiresAt) {
92
+ this.requests.delete(id);
93
+ removed += 1;
94
+ }
95
+ }
96
+ return removed;
97
+ }
98
+ }
@@ -0,0 +1,59 @@
1
+ import { createServer } from 'node:http';
2
+ import { type WrapperConfig } from './wrapper-config.js';
3
+ import { WrapperRegistry } from './wrapper-registry.js';
4
+ import { type Metrics } from './wrapper-metrics.js';
5
+ import { type AnalyzeRunner } from './analyze-runner.js';
6
+ import { createAdapterDraft } from '../highlevel/init.js';
7
+ import { type RunnerPortWithLifecycle } from '../runner/runner-port.js';
8
+ /** 结构化日志允许字段(09;forbidden=token/body/seed 类型上就进不来)。 */
9
+ interface LogFields {
10
+ status: string;
11
+ errorCode?: string;
12
+ durationMs?: number;
13
+ requestId?: string;
14
+ }
15
+ export interface WrapperDeps {
16
+ /** 注入时钟(测试 TTL)。 */
17
+ now?: () => number;
18
+ /** 注入 init 实现(测试无需写盘)。默认真实 createAdapterDraft。 */
19
+ createDraft?: typeof createAdapterDraft;
20
+ /** 结构化日志 sink(默认 stderr 单行 JSON)。 */
21
+ logSink?: (line: string) => void;
22
+ /** 注入 metrics(测试断言)。默认 createMetrics()。 */
23
+ metrics?: Metrics;
24
+ /** 注入 verify runner(测试 fake;production = defaultRunnerPort,连回 daemon 拿 Page 跑 browser adapter)。 */
25
+ runner?: RunnerPortWithLifecycle;
26
+ /** 注入 sessionId→HMAC key(测试)。默认进程级 defaultSessionKeyRegistry。 */
27
+ sessionKeyFor?: (sessionId?: string) => string;
28
+ /** 注入 analyze runner(测试 fake;production = daemon-backed Page)。 */
29
+ analyzeRunner?: AnalyzeRunner;
30
+ }
31
+ export interface WrapperCtx {
32
+ cfg: WrapperConfig;
33
+ registry: WrapperRegistry;
34
+ createDraft: typeof createAdapterDraft;
35
+ runner: RunnerPortWithLifecycle;
36
+ sessionKeyFor?: (sessionId?: string) => string;
37
+ analyzeRunner: AnalyzeRunner;
38
+ /** 在飞后台作业(analyze in-process Page);shutdown drain 用(Codex M9 Med)。 */
39
+ inflight: Set<Promise<unknown>>;
40
+ log: (operation: string, fields: LogFields) => void;
41
+ metrics: Metrics;
42
+ }
43
+ /** Shutdown 时等在飞作业跑完(其 finally 释放 Page lease),最多 graceMs;超时则放弃(lease 靠 daemon
44
+ * idle-expire 兜底)。返回时 inflight 要么空、要么已超 grace。 */
45
+ export declare function drainInflight(inflight: Set<Promise<unknown>>, graceMs: number): Promise<void>;
46
+ /**
47
+ * 装配 wrapper app(工厂 + 可注入 deps 供测试)。返回 { server, ctx };调用方负责 server.listen。
48
+ */
49
+ export declare function createWrapperApp(cfg: WrapperConfig, deps?: WrapperDeps): {
50
+ server: ReturnType<typeof createServer>;
51
+ ctx: WrapperCtx;
52
+ };
53
+ /**
54
+ * `bycli internal highlevel-http` 入口:load config → 装配 → bind 127.0.0.1。默认关闭(opt-in)。
55
+ * 绝不打印 token(只打印 listening 行)。**在 server 存活期间不 resolve**(等 'close'),这样 main.ts
56
+ * 的 `await runWrapperCli()` 会一直挂起、绝不 fall-through 到完整 CLI discovery。
57
+ */
58
+ export declare function runWrapperCli(): Promise<void>;
59
+ export {};
@@ -0,0 +1,240 @@
1
+ // M9 High-Level HTTP wrapper —— 独立 loopback HTTP server(07 · Optional HTTP Wrapper)。
2
+ // 形态镜像 dashboard-be/src/server.ts 的 createApp/createServer/finish-logger,但**主仓侧**(直调
3
+ // high-level 模块,不经 daemon bridge,不 import be)。与 daemon 的 /v1/*(daemon-high-level.v1)是
4
+ // 独立 server / 独立路由族(ADR-0007,严禁混淆)。默认关闭,经 `bycli internal highlevel-http` 启动。
5
+ import { createServer } from 'node:http';
6
+ import { loadWrapperConfig } from './wrapper-config.js';
7
+ import { WrapperRegistry } from './wrapper-registry.js';
8
+ import { checkWrapperGates } from './wrapper-gates.js';
9
+ import { errorBody } from './wrapper-envelope.js';
10
+ import { handleHealth, handleInit, handleVerify, handleAnalyze, handleRequestStatus, } from './handlers.js';
11
+ import { createMetrics } from './wrapper-metrics.js';
12
+ import { createAnalyzeRunner } from './analyze-runner.js';
13
+ import { createAdapterDraft } from '../highlevel/init.js';
14
+ import { defaultRunnerPort, setDefaultRunnerDaemonPort, } from '../runner/runner-port.js';
15
+ /** Shutdown 时等在飞作业跑完(其 finally 释放 Page lease),最多 graceMs;超时则放弃(lease 靠 daemon
16
+ * idle-expire 兜底)。返回时 inflight 要么空、要么已超 grace。 */
17
+ export async function drainInflight(inflight, graceMs) {
18
+ if (inflight.size === 0)
19
+ return;
20
+ let timer;
21
+ const deadline = new Promise((r) => { timer = setTimeout(r, graceMs); });
22
+ try {
23
+ await Promise.race([Promise.allSettled([...inflight]).then(() => undefined), deadline]);
24
+ }
25
+ finally {
26
+ if (timer)
27
+ clearTimeout(timer);
28
+ }
29
+ }
30
+ const REQUESTS_PREFIX = '/v1/requests/';
31
+ /** Shutdown 时等在飞 analyze 释放 Page lease 的最长宽限(Codex M9 Med)。 */
32
+ const SHUTDOWN_GRACE_MS = 5000;
33
+ function sendOutcome(res, outcome) {
34
+ if (outcome.errorCode)
35
+ res.__errorCode = outcome.errorCode;
36
+ const payload = JSON.stringify(outcome.body);
37
+ // 04 章:不回 CORS 头(no-CORS);nosniff。
38
+ res.writeHead(outcome.status, { 'Content-Type': 'application/json', 'X-Content-Type-Options': 'nosniff' });
39
+ res.end(payload);
40
+ }
41
+ async function readJson(req, maxBytes) {
42
+ const chunks = [];
43
+ let size = 0;
44
+ for await (const c of req) {
45
+ size += c.length;
46
+ if (size > maxBytes)
47
+ throw new Error('payload too large');
48
+ chunks.push(c);
49
+ }
50
+ if (!chunks.length)
51
+ return {};
52
+ return JSON.parse(Buffer.concat(chunks).toString('utf8'));
53
+ }
54
+ function clampWaitMs(raw, max) {
55
+ if (!raw)
56
+ return 0;
57
+ const n = Number(raw);
58
+ if (!Number.isFinite(n) || n <= 0)
59
+ return 0;
60
+ return Math.min(Math.floor(n), max);
61
+ }
62
+ /** 路由分发:gate(全端点)→ 已注册端点;未匹配 404。 */
63
+ async function route(ctx, req, res) {
64
+ const url = new URL(req.url ?? '/', 'http://127.0.0.1');
65
+ const pathname = url.pathname;
66
+ const method = req.method ?? 'GET';
67
+ // 门禁:所有端点(含 /health)统一过 X-byCLI + Origin + token。
68
+ const gate = checkWrapperGates(req, { allowedOrigins: ctx.cfg.ALLOWED_ORIGINS, token: ctx.cfg.TOKEN });
69
+ if (!gate.ok) {
70
+ sendOutcome(res, {
71
+ status: 403,
72
+ body: { error: errorBody(gate.code ?? 'auth_failed', gate.message ?? 'forbidden') },
73
+ errorCode: gate.code ?? 'auth_failed',
74
+ });
75
+ return;
76
+ }
77
+ if (method === 'GET' && pathname === '/health') {
78
+ sendOutcome(res, handleHealth());
79
+ return;
80
+ }
81
+ // GET /metrics —— loopback metrics scrape(09 · M8 复审遗留,gated 同其它端点)。返回 snapshot JSON
82
+ // (counters/histograms,标签只非敏感 enum)。不入 finish-logger 计数(避免 scrape 自计/噪声)。
83
+ if (method === 'GET' && pathname === '/metrics') {
84
+ sendOutcome(res, { status: 200, body: ctx.metrics.snapshot() });
85
+ return;
86
+ }
87
+ if (method === 'POST' && pathname === '/v1/adapters/init') {
88
+ let body;
89
+ try {
90
+ body = await readJson(req, ctx.cfg.BODY_LIMIT_BYTES);
91
+ }
92
+ catch {
93
+ sendOutcome(res, {
94
+ status: 400,
95
+ body: { error: errorBody('validation_failed', 'request body too large or invalid JSON') },
96
+ errorCode: 'validation_failed',
97
+ });
98
+ return;
99
+ }
100
+ sendOutcome(res, handleInit(ctx, body));
101
+ return;
102
+ }
103
+ if (method === 'POST' && pathname === '/v1/adapters/verify') {
104
+ let body;
105
+ try {
106
+ body = await readJson(req, ctx.cfg.BODY_LIMIT_BYTES);
107
+ }
108
+ catch {
109
+ sendOutcome(res, {
110
+ status: 400,
111
+ body: { error: errorBody('validation_failed', 'request body too large or invalid JSON') },
112
+ errorCode: 'validation_failed',
113
+ });
114
+ return;
115
+ }
116
+ sendOutcome(res, await handleVerify(ctx, body));
117
+ return;
118
+ }
119
+ if (method === 'POST' && pathname === '/v1/browser/analyze') {
120
+ let body;
121
+ try {
122
+ body = await readJson(req, ctx.cfg.BODY_LIMIT_BYTES);
123
+ }
124
+ catch {
125
+ sendOutcome(res, {
126
+ status: 400,
127
+ body: { error: errorBody('validation_failed', 'request body too large or invalid JSON') },
128
+ errorCode: 'validation_failed',
129
+ });
130
+ return;
131
+ }
132
+ sendOutcome(res, handleAnalyze(ctx, body));
133
+ return;
134
+ }
135
+ if (method === 'GET' && pathname.startsWith(REQUESTS_PREFIX)) {
136
+ const requestId = pathname.slice(REQUESTS_PREFIX.length);
137
+ const waitMs = clampWaitMs(url.searchParams.get('waitMs'), ctx.cfg.REQUEST_WAIT_MS_MAX);
138
+ sendOutcome(res, await handleRequestStatus(ctx, requestId, waitMs));
139
+ return;
140
+ }
141
+ // 未注册路由(含 M9b/c 待接的 verify/analyze)→ 404。
142
+ sendOutcome(res, { status: 404, body: { error: { code: 'request_not_found', message: 'not found' } } });
143
+ }
144
+ /**
145
+ * 装配 wrapper app(工厂 + 可注入 deps 供测试)。返回 { server, ctx };调用方负责 server.listen。
146
+ */
147
+ export function createWrapperApp(cfg, deps = {}) {
148
+ const logSink = deps.logSink ?? ((line) => process.stderr.write(line + '\n'));
149
+ // production runner = defaultRunnerPort();browser-verify 子进程经 BYCLI_DAEMON_PORT 连回 daemon 拿 Page,
150
+ // 故必须在首次 defaultRunnerPort() 前注入 daemon 端口(同 daemon.ts:49)。测试注入 deps.runner 绕开全局单例。
151
+ if (!deps.runner)
152
+ setDefaultRunnerDaemonPort(cfg.DAEMON_PORT);
153
+ const ctx = {
154
+ cfg,
155
+ registry: new WrapperRegistry({
156
+ terminalTtlMs: cfg.REQUEST_TERMINAL_STATUS_TTL_MS,
157
+ pollAfterMs: cfg.REQUEST_POLL_AFTER_MS,
158
+ now: deps.now,
159
+ }),
160
+ createDraft: deps.createDraft ?? createAdapterDraft,
161
+ runner: deps.runner ?? defaultRunnerPort(),
162
+ sessionKeyFor: deps.sessionKeyFor,
163
+ analyzeRunner: deps.analyzeRunner ?? createAnalyzeRunner({ timeoutMs: cfg.ANALYZE_TIMEOUT_MS }),
164
+ inflight: new Set(),
165
+ log: (operation, fields) => {
166
+ if (cfg.LOG_LEVEL === 'error' && fields.status === 'ok')
167
+ return; // 静音成功日志(error level)
168
+ logSink(JSON.stringify({ time: Date.now(), level: 'info', operation, ...fields }));
169
+ },
170
+ metrics: deps.metrics ?? createMetrics(),
171
+ };
172
+ const server = createServer((req, res) => {
173
+ // 单点 finish-logger + metrics(09 Structured Logging/Metrics):operation + outcome + latency。只
174
+ // path/method/status/errorCode/durationMs —— 永不含 header/token/body/seed。
175
+ const started = Date.now();
176
+ res.on('finish', () => {
177
+ const path = (req.url ?? '/').split('?')[0] ?? '/';
178
+ if (path === '/metrics')
179
+ return; // 不给 /metrics scrape 自计/记日志
180
+ // 把唯一动态段(GET /v1/requests/{id})collapse 成稳定模板,operation 基数有界。
181
+ const operation = path.startsWith(REQUESTS_PREFIX)
182
+ ? 'highlevel.requests'
183
+ : `highlevel.${path.replace(/^\/+/, '').replace(/\//g, '.') || 'root'}`;
184
+ const status = res.statusCode < 400 ? 'ok' : 'failed';
185
+ const errorCode = res.__errorCode;
186
+ const durationMs = Date.now() - started;
187
+ ctx.log(operation, { status, errorCode, durationMs });
188
+ ctx.metrics.inc('highlevel_requests_total', { operation, status, errorCode });
189
+ ctx.metrics.observe('highlevel_request_duration_ms', durationMs);
190
+ });
191
+ route(ctx, req, res).catch(() => {
192
+ if (!res.headersSent) {
193
+ sendOutcome(res, {
194
+ status: 500,
195
+ body: { error: errorBody('network_error', 'internal error') },
196
+ errorCode: 'network_error',
197
+ });
198
+ }
199
+ });
200
+ });
201
+ // 周期 sweep:回收已过 TTL 的终态记录(getRecord 只在访问时删,abandoned 终态否则常驻内存)。
202
+ // 间隔绑 TTL(Codex M9 Low:固定 5min 对 1min TTL 会多滞留 ~4min)——取 min(5min, max(30s, TTL/2))。
203
+ // unref → 不吊住进程;server 'close' 清掉(测试关 server 即停 timer,不泄漏)。
204
+ const sweepMs = Math.min(300_000, Math.max(30_000, Math.floor(cfg.REQUEST_TERMINAL_STATUS_TTL_MS / 2)));
205
+ const sweepTimer = setInterval(() => ctx.registry.sweepExpired(), sweepMs);
206
+ sweepTimer.unref?.();
207
+ server.on('close', () => clearInterval(sweepTimer));
208
+ return { server, ctx };
209
+ }
210
+ /**
211
+ * `bycli internal highlevel-http` 入口:load config → 装配 → bind 127.0.0.1。默认关闭(opt-in)。
212
+ * 绝不打印 token(只打印 listening 行)。**在 server 存活期间不 resolve**(等 'close'),这样 main.ts
213
+ * 的 `await runWrapperCli()` 会一直挂起、绝不 fall-through 到完整 CLI discovery。
214
+ */
215
+ export async function runWrapperCli() {
216
+ const cfg = loadWrapperConfig();
217
+ // analyze 的 daemon-backed Page 经 daemon-client(模块加载时读 process.env.BYCLI_DAEMON_PORT)连回 daemon;
218
+ // 显式钉到 cfg.DAEMON_PORT,保证 Page 连的是 wrapper 配置的同一 daemon(Page 在首次 analyze 才懒加载)。
219
+ process.env.BYCLI_DAEMON_PORT = String(cfg.DAEMON_PORT);
220
+ const { server, ctx } = createWrapperApp(cfg);
221
+ // 在 server 存活期间不 resolve(阻止 main.ts fall-through);仅在 SIGTERM/SIGINT 优雅关闭后 resolve。
222
+ await new Promise((resolve, reject) => {
223
+ let closing = false;
224
+ const shutdown = () => {
225
+ if (closing)
226
+ return;
227
+ closing = true;
228
+ server.close(); // 停止接新连接
229
+ // 先 drain 在飞 analyze(其 finally closeWindow 释放 daemon Page lease),最多 SHUTDOWN_GRACE_MS,
230
+ // 再 resolve → main.ts process.exit;不再被硬切(Codex M9 Med)。超 grace 则放弃,靠 lease idle-expire。
231
+ void drainInflight(ctx.inflight, SHUTDOWN_GRACE_MS).finally(() => resolve());
232
+ };
233
+ server.once('error', reject);
234
+ process.once('SIGTERM', shutdown);
235
+ process.once('SIGINT', shutdown);
236
+ server.listen(cfg.PORT, cfg.HOST, () => {
237
+ process.stderr.write(JSON.stringify({ time: Date.now(), level: 'info', operation: 'highlevel.listening', status: 'ok', port: cfg.PORT }) + '\n');
238
+ });
239
+ });
240
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { type Logger, type LogLevel } from '@sovovs/bycli-recorder-core';
2
+ export type { Logger, LogLevel } from '@sovovs/bycli-recorder-core';
3
+ export type { LogFields } from '@sovovs/bycli-recorder-core';
4
+ /** daemon/runner structured logger: single-line JSON to stderr. Inject `sink` in tests. */
5
+ export declare function createRecorderLogger(level: LogLevel, sink?: (line: string) => void): Logger;
@@ -0,0 +1,13 @@
1
+ // Structured logger for the daemon/runner recording path (#1a · 09 Structured Logging).
2
+ //
3
+ // The main repo keeps TWO loggers on purpose: `src/logger.ts` (human-facing emoji singleton,
4
+ // 17 CLI consumers, unchanged) and THIS one — single-line JSON for the daemon/runner SERVICE path,
5
+ // whose output goes to a detached process's stderr captured to daemon logs, never to an interactive
6
+ // user. The formatter + by-construction redaction (LogFields = only 09 allowed fields) live in
7
+ // recorder-core; this thin factory supplies the stderr sink that core deliberately omits to stay
8
+ // IO-free. Reuses the exact same engine as dashboard-be's logger so daemon/be log shapes match.
9
+ import { createLogger as createCoreLogger } from '@sovovs/bycli-recorder-core';
10
+ /** daemon/runner structured logger: single-line JSON to stderr. Inject `sink` in tests. */
11
+ export function createRecorderLogger(level, sink = (line) => process.stderr.write(line + '\n')) {
12
+ return createCoreLogger(level, { sink });
13
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Verify-runner config — read VERIFY_RUNNER_* from the environment (09 HighLevelConfig).
3
+ *
4
+ * The static range validation is the pure `validateRunnerConfig` in recorder-core (no
5
+ * process.env access, unit-tested there). This thin wrapper reads the env keys and applies
6
+ * the one env-side rule the pure layer can't: clamp `maxConcurrency` to the CPU count
7
+ * (09: "int, 1-CPU count"). A range error is surfaced as a thrown ConfigError (fail closed,
8
+ * 08: unsupported config fails before launch).
9
+ */
10
+ import { type RunnerConfig, type TempCapacity } from '@sovovs/bycli-recorder-core';
11
+ /**
12
+ * Resolve the verify-runner config from `env` (defaults to process.env). Missing/empty →
13
+ * default; out-of-range/non-integer → ConfigError. `maxConcurrency` is additionally clamped
14
+ * down to the CPU count (never below 1).
15
+ */
16
+ export declare function resolveRunnerConfig(env?: NodeJS.ProcessEnv): RunnerConfig;
17
+ /**
18
+ * Temp-store reap policy (M7b · 09:27-29 RecorderConfig). Read on the daemon side (the process
19
+ * that owns verify temp dirs) to drive startup reap + the periodic TTL sweep.
20
+ */
21
+ export interface TempPolicy {
22
+ /** Periodic sweep threshold: a temp dir older than this is a leak (09: default 1h). */
23
+ tempTtlMs: number;
24
+ /** Startup reap age backstop: clean dirs older than this regardless of owner (09: default 24h). */
25
+ startupReapMaxAgeMs: number;
26
+ /** SIGTERM→SIGKILL grace when terminating a reaped child (09: default 1500ms). */
27
+ orphanKillGraceMs: number;
28
+ }
29
+ export declare function resolveTempCapacity(env?: NodeJS.ProcessEnv): TempCapacity;
30
+ /**
31
+ * Resolve the temp-store reap policy from `env` (defaults to process.env). Missing/empty → default;
32
+ * out-of-range / non-integer → ConfigError (fail closed, consistent with resolveRunnerConfig).
33
+ */
34
+ export declare function resolveTempPolicy(env?: NodeJS.ProcessEnv): TempPolicy;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Verify-runner config — read VERIFY_RUNNER_* from the environment (09 HighLevelConfig).
3
+ *
4
+ * The static range validation is the pure `validateRunnerConfig` in recorder-core (no
5
+ * process.env access, unit-tested there). This thin wrapper reads the env keys and applies
6
+ * the one env-side rule the pure layer can't: clamp `maxConcurrency` to the CPU count
7
+ * (09: "int, 1-CPU count"). A range error is surfaced as a thrown ConfigError (fail closed,
8
+ * 08: unsupported config fails before launch).
9
+ */
10
+ import * as os from 'node:os';
11
+ import { validateRunnerConfig, validateTempCapacity } from '@sovovs/bycli-recorder-core';
12
+ import { ConfigError } from '../../errors.js';
13
+ const ENV_KEYS = {
14
+ maxConcurrency: 'VERIFY_RUNNER_MAX_CONCURRENCY',
15
+ queueLimit: 'HIGH_LEVEL_QUEUE_LIMIT',
16
+ stdoutLimitBytes: 'VERIFY_RUNNER_STDOUT_LIMIT_BYTES',
17
+ stderrLimitBytes: 'VERIFY_RUNNER_STDERR_LIMIT_BYTES',
18
+ jsonlLineLimit: 'VERIFY_RUNNER_JSONL_LINE_LIMIT',
19
+ timeoutMs: 'VERIFY_RUNNER_TIMEOUT_MS',
20
+ killGraceMs: 'VERIFY_RUNNER_KILL_GRACE_MS',
21
+ };
22
+ /**
23
+ * Resolve the verify-runner config from `env` (defaults to process.env). Missing/empty →
24
+ * default; out-of-range/non-integer → ConfigError. `maxConcurrency` is additionally clamped
25
+ * down to the CPU count (never below 1).
26
+ */
27
+ export function resolveRunnerConfig(env = process.env) {
28
+ const raw = {};
29
+ for (const key of Object.keys(ENV_KEYS)) {
30
+ raw[key] = env[ENV_KEYS[key]];
31
+ }
32
+ const r = validateRunnerConfig(raw);
33
+ if (!r.ok)
34
+ throw new ConfigError(r.reason, 'Fix the VERIFY_RUNNER_* environment variable and retry.');
35
+ const cpus = Math.max(1, os.cpus().length || 1);
36
+ return { ...r.config, maxConcurrency: Math.min(r.config.maxConcurrency, cpus) };
37
+ }
38
+ const TEMP_POLICY_FIELDS = {
39
+ tempTtlMs: { key: 'RECORDER_TEMP_TTL_MS', def: 3_600_000, min: 60_000, max: 86_400_000 },
40
+ startupReapMaxAgeMs: { key: 'RECORDER_STARTUP_REAP_MAX_AGE_MS', def: 86_400_000, min: 60_000, max: 86_400_000 },
41
+ orphanKillGraceMs: { key: 'RECORDER_ORPHAN_KILL_GRACE_MS', def: 1_500, min: 100, max: 30_000 },
42
+ };
43
+ /**
44
+ * Temp-store capacity env keys (#1d · 09:33-36). The pure range/band-order validation is
45
+ * `validateTempCapacity` in recorder-core; this maps env key names → field names (mirroring
46
+ * resolveRunnerConfig) and fail-closes via ConfigError. The temp ROOT is restart-only (09:182):
47
+ * these are read at daemon startup, not hot-reloaded.
48
+ */
49
+ const TEMP_CAPACITY_ENV_KEYS = {
50
+ maxBytes: 'RECORDER_TEMP_MAX_BYTES',
51
+ highWatermarkRatio: 'RECORDER_TEMP_HIGH_WATERMARK_RATIO',
52
+ lowWatermarkRatio: 'RECORDER_TEMP_LOW_WATERMARK_RATIO',
53
+ };
54
+ export function resolveTempCapacity(env = process.env) {
55
+ const raw = {};
56
+ for (const k of Object.keys(TEMP_CAPACITY_ENV_KEYS)) {
57
+ raw[k] = env[TEMP_CAPACITY_ENV_KEYS[k]];
58
+ }
59
+ const r = validateTempCapacity(raw);
60
+ if (!r.ok)
61
+ throw new ConfigError(r.reason, 'Fix the RECORDER_TEMP_* environment variable and retry.');
62
+ return r.capacity;
63
+ }
64
+ /**
65
+ * Resolve the temp-store reap policy from `env` (defaults to process.env). Missing/empty → default;
66
+ * out-of-range / non-integer → ConfigError (fail closed, consistent with resolveRunnerConfig).
67
+ */
68
+ export function resolveTempPolicy(env = process.env) {
69
+ const out = {};
70
+ for (const k of Object.keys(TEMP_POLICY_FIELDS)) {
71
+ const f = TEMP_POLICY_FIELDS[k];
72
+ const raw = env[f.key];
73
+ if (raw === undefined || raw === '') {
74
+ out[k] = f.def;
75
+ continue;
76
+ }
77
+ const n = Number(raw);
78
+ if (!Number.isInteger(n) || n < f.min || n > f.max) {
79
+ throw new ConfigError(`${f.key}=${raw} is out of range [${f.min}, ${f.max}]`, `Fix the ${f.key} environment variable and retry.`);
80
+ }
81
+ out[k] = n;
82
+ }
83
+ return out;
84
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,79 @@
1
+ export interface VerifyMarker {
2
+ requestId: string;
3
+ /** The runner child pid (backfilled into marker.json after spawn). */
4
+ pid: number | null;
5
+ /** The process that spawned the runner (daemon/CLI); stands in for 08's localServiceRunId. */
6
+ ownerPid: number | null;
7
+ startedAt: number;
8
+ tempRoot: string;
9
+ operation: 'verify';
10
+ }
11
+ /** Parse + validate marker.json. Returns null unless it is a well-formed verify marker. */
12
+ export declare function parseVerifyMarker(raw: string): VerifyMarker | null;
13
+ /** Does a live process's command line belong to THIS marker's runner? (pid-reuse guard) */
14
+ export declare function cmdlineMatchesMarker(cmdline: string, marker: VerifyMarker): boolean;
15
+ /** Containment: the dir must sit directly under tmpRoot and carry our prefix. */
16
+ export declare function isReapableTempDir(dir: string, tmpRoot: string): boolean;
17
+ /**
18
+ * True orphan iff the spawning owner is gone. An unknown ownerPid (legacy marker without the
19
+ * field) is treated as live → skipped, so we never kill a run we cannot attribute to a dead owner.
20
+ */
21
+ export declare function isOrphan(marker: VerifyMarker, ownerAlive: boolean): boolean;
22
+ /**
23
+ * Aged-out leak (M7b · RECORDER_TEMP_TTL_MS / RECORDER_STARTUP_REAP_MAX_AGE_MS, 09:27-28):
24
+ * a temp dir older than `maxAgeMs` is reaped regardless of owner liveness. CALLERS MUST pass the
25
+ * floored threshold from `effectiveLeakThresholdMs` (never the raw config) — `maxAgeMs` here is
26
+ * assumed to already exceed the run's hard deadline, so a dir older than it is a definite leak,
27
+ * never a live run. An undatable legacy marker (startedAt<=0) is never aged-out (we cannot
28
+ * attribute its age). maxAgeMs<=0 disables age-based reaping (orphan-only, the pre-M7b behavior).
29
+ */
30
+ export declare function isAgedOut(marker: VerifyMarker, now: number, maxAgeMs: number): boolean;
31
+ /**
32
+ * Effective age-reap threshold: the configured `maxAgeMs` floored at the run's hard deadline
33
+ * (`minLeakAgeMs` = timeoutMs + killGraceMs). A threshold below the max legitimate run lifetime would
34
+ * let the sweep kill a still-running verify — a live run can outlive a short TTL but never its own
35
+ * kill deadline, so flooring closes that hole. A 0/negative configured value keeps age-reaping
36
+ * disabled (orphan-only).
37
+ */
38
+ export declare function effectiveLeakThresholdMs(maxAgeMs: number, minLeakAgeMs: number): number;
39
+ export interface ProcProbe {
40
+ isAlive(pid: number): boolean;
41
+ cmdline(pid: number): string | null;
42
+ /** Process start time (epoch ms) for the live pid, or null if undeterminable (win32 / ps failure).
43
+ * Used for the owner pid-reuse guard (#6); optional so injected test probes need not implement it. */
44
+ startTime?(pid: number): number | null;
45
+ kill(pid: number, signal: NodeJS.Signals): void;
46
+ }
47
+ /**
48
+ * Owner pid-reuse guard (#6): the spawning owner necessarily started BEFORE it created this run, so a
49
+ * process now occupying `ownerPid` whose start time is meaningfully AFTER the run was created cannot be
50
+ * that owner — the real owner died and the pid was recycled, making this a true orphan. Undeterminable
51
+ * start time (win32 / ps failure → null) → NOT reused: we never escalate to a kill on a guess, so a
52
+ * live owner is never misclassified (its start time is ≤ runStartedAt, far below the threshold).
53
+ */
54
+ export declare function isOwnerPidReused(liveStartTime: number | null, runStartedAt: number, toleranceMs: number): boolean;
55
+ export interface ReapResult {
56
+ scanned: number;
57
+ orphans: number;
58
+ agedOut: number;
59
+ killed: number;
60
+ deleted: number;
61
+ }
62
+ /**
63
+ * Scan tmpRoot for orphaned / aged-out verify temp dirs and clean them up. Synchronous (runs at
64
+ * daemon startup and on a periodic sweep); the SIGKILL backstop is scheduled async + unref'd so it
65
+ * never blocks. `maxAgeMs` (M7b) enables age-based reaping; 0/undefined keeps orphan-only behavior.
66
+ */
67
+ export declare function reapOrphanedVerifyRuns(opts?: {
68
+ tmpRoot?: string;
69
+ log?: (msg: string) => void;
70
+ graceMs?: number;
71
+ /** Age-based leak threshold (M7b). 0/undefined → orphan-only (pre-M7b behavior). */
72
+ maxAgeMs?: number;
73
+ /** Run hard-deadline floor (timeoutMs + killGraceMs); the age threshold is never lower than this
74
+ * so a still-running verify is never age-reaped (08 / M7b safety floor). */
75
+ minLeakAgeMs?: number;
76
+ /** Injectable clock (tests). */
77
+ now?: number;
78
+ probe?: ProcProbe;
79
+ }): ReapResult;