@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,31 @@
1
+ import * as os from 'node:os';
2
+ import * as path from 'node:path';
3
+ /**
4
+ * Resolvers for the byCLI user-data root and its well-known subdirectories.
5
+ *
6
+ * The root is `BYCLI_CONFIG_DIR` when set, else `~/.bycli`. These are FUNCTIONS,
7
+ * not module-level constants, on purpose: `BYCLI_CONFIG_DIR` is set dynamically
8
+ * (tests, the verify child sandbox in runner-port.ts) — a const captured at import
9
+ * time would freeze the wrong root and leak across tests. Always resolve on call.
10
+ *
11
+ * Existing duplicate idioms this consolidates: observation/artifact.ts, browser/profile.ts.
12
+ */
13
+ export function getConfigDir() {
14
+ return process.env.BYCLI_CONFIG_DIR || path.join(os.homedir(), '.bycli');
15
+ }
16
+ /** User adapter root: `$CONFIG_DIR/clis`. Adapters live at `<clis>/<site>/<command>.js`. */
17
+ export function getUserClisDir() {
18
+ return path.join(getConfigDir(), 'clis');
19
+ }
20
+ /** Per-site memory/recorder root: `$CONFIG_DIR/sites`. */
21
+ export function getSitesDir() {
22
+ return path.join(getConfigDir(), 'sites');
23
+ }
24
+ /** A single site's directory: `$CONFIG_DIR/sites/<site>`. */
25
+ export function getSiteDir(site) {
26
+ return path.join(getSitesDir(), site);
27
+ }
28
+ /** A site's recorder artifact dir: `$CONFIG_DIR/sites/<site>/recorder`. */
29
+ export function getSiteRecorderDir(site) {
30
+ return path.join(getSiteDir(site), 'recorder');
31
+ }
@@ -26,9 +26,34 @@ import { EXIT_CODES } from './errors.js';
26
26
  import { log } from './logger.js';
27
27
  import { PKG_VERSION } from './version.js';
28
28
  import { DEFAULT_CONTEXT_ID } from './browser/profile.js';
29
+ import { createAdapterDraft, saveAdapterSource, recoverInitTransactions } from './recorder/highlevel/init.js';
30
+ import { verifyAdapter } from './recorder/highlevel/verify.js';
31
+ import { defaultRunnerPort, setDefaultRunnerDaemonPort, setDefaultRunnerObservability, setDefaultRunnerTempGuard } from './recorder/runner/runner-port.js';
32
+ import { createMetrics } from '@sovovs/bycli-recorder-core';
33
+ import { createRecorderLogger } from './recorder/observability/logger.js';
34
+ import { reapOrphanedVerifyRuns } from './recorder/runner/reap.js';
35
+ import { resolveTempPolicy, resolveRunnerConfig, resolveTempCapacity } from './recorder/runner/config.js';
36
+ import { defaultSessionKeyRegistry } from './recorder/runner/session-keys.js';
29
37
  import { recordExtensionVersion } from './update-check.js';
30
38
  import { buildCommandDispatchFailure, buildExtensionDisconnectFailure, getResponseCorsHeaders, } from './daemon-utils.js';
31
39
  const PORT = parseInt(process.env.BYCLI_DAEMON_PORT ?? String(DEFAULT_DAEMON_PORT), 10);
40
+ // The verify runner (M6b) spawns child processes that connect back to THIS daemon for a
41
+ // browser Page. Hand them our port (→ BYCLI_DAEMON_PORT in the child env) so the child's
42
+ // Page reaches us, not a freshly-spawned daemon. Must run before the first /v1/verify
43
+ // builds the default runner singleton.
44
+ setDefaultRunnerDaemonPort(PORT);
45
+ // ─── Observability (#1b · 09) ────────────────────────────────────────
46
+ // daemon/runner-side structured logs + metrics (M8 only did dashboard-be). The metrics instance is
47
+ // a process singleton, shared by reference into the runner port (#1c) so GET /metrics shows runner
48
+ // counters too. LOG_LEVEL is read once at startup — the daemon has no ConfigPort, so no hot reload
49
+ // here (that's a larger, separate milestone); a malformed value falls back to 'info'.
50
+ const LOG_LEVELS = ['error', 'warn', 'info', 'debug'];
51
+ const envLogLevel = process.env.LOG_LEVEL ?? '';
52
+ const metrics = createMetrics();
53
+ const logger = createRecorderLogger(LOG_LEVELS.includes(envLogLevel) ? envLogLevel : 'info');
54
+ // Share these singletons with the verify runner (#1c) BEFORE the first defaultRunnerPort() use, so
55
+ // runner counters surface on GET /metrics and runner logs share the daemon's level.
56
+ setDefaultRunnerObservability(metrics, logger);
32
57
  const extensionProfiles = new Map();
33
58
  const pending = new Map();
34
59
  let commandResultUnknownCount = 0;
@@ -119,6 +144,7 @@ function unregisterExtensionConnection(ws) {
119
144
  });
120
145
  if (failure.countAsCommandResultUnknown) {
121
146
  commandResultUnknownCount++;
147
+ metrics.inc('daemon_command_result_unknown_total'); // #1b: structured metric alongside the /status field
122
148
  log.warn(`[daemon] Command result unknown after extension disconnect (id=${id}, action=${p.action}, context=${contextId})`);
123
149
  }
124
150
  p.reject(new DaemonCommandFailure(failure.message, failure.errorCode, failure.errorHint, failure.status));
@@ -150,6 +176,11 @@ function readBody(req) {
150
176
  });
151
177
  }
152
178
  function jsonResponse(res, status, data, extraHeaders) {
179
+ // Stash any errorCode for the request-completion metrics choke point (#1b), so handlers don't
180
+ // each have to wire it through. Only the bounded ErrorCode enum reaches a label/log field.
181
+ const ec = data?.errorCode;
182
+ if (typeof ec === 'string')
183
+ res.__errorCode = ec;
153
184
  res.writeHead(status, { 'Content-Type': 'application/json', ...extraHeaders });
154
185
  res.end(JSON.stringify(data));
155
186
  }
@@ -174,6 +205,32 @@ async function handleRequest(req, res) {
174
205
  }
175
206
  const url = req.url ?? '/';
176
207
  const pathname = url.split('?')[0];
208
+ // Request-completion metrics choke point (#1b · 09): one place records type/status/errorCode +
209
+ // latency, no per-handler scatter. Only the high-level surface (/v1/*) and the /command bridge are
210
+ // counted; the dynamic /v1/requests/{id} segment collapses to a bounded route template so the
211
+ // `operation` label can't grow unboundedly. /v1/* operations also get a structured log line
212
+ // (low volume, the recording ops); /command is counted but not logged (per-command WS bridge is
213
+ // high volume). Never logs headers/token/body — only operation/status/errorCode/durationMs.
214
+ const reqStarted = Date.now();
215
+ res.on('finish', () => {
216
+ let operation = null;
217
+ if (pathname.startsWith('/v1/requests/'))
218
+ operation = 'v1.requests';
219
+ else if (pathname.startsWith('/v1/'))
220
+ operation = `v1${pathname.slice(3).replace(/\//g, '.')}`; // /v1/init → v1.init
221
+ else if (pathname === '/command')
222
+ operation = 'command';
223
+ if (!operation)
224
+ return;
225
+ const status = res.statusCode < 400 ? 'ok' : 'failed';
226
+ const errorCode = res.__errorCode;
227
+ const durationMs = Date.now() - reqStarted;
228
+ metrics.inc('daemon_requests_total', { operation, status, errorCode });
229
+ if (operation === 'v1.verify')
230
+ metrics.observe('daemon_verify_duration_ms', durationMs);
231
+ if (operation.startsWith('v1'))
232
+ logger.info(`daemon.${operation}`, { status, errorCode, durationMs });
233
+ });
177
234
  // Health-check endpoint — no X-byCLI header required.
178
235
  // Used by the extension to silently probe daemon reachability before
179
236
  // attempting a WebSocket connection (avoids uncatchable ERR_CONNECTION_REFUSED).
@@ -193,6 +250,102 @@ async function handleRequest(req, res) {
193
250
  jsonResponse(res, 403, { ok: false, error: 'Forbidden: missing X-byCLI header' });
194
251
  return;
195
252
  }
253
+ // High-level init (M5b · A'): daemon hosts the FS-writing init capability; the
254
+ // logic lives in the main-repo init module (createAdapterDraft). dashboard-be
255
+ // forwards here rather than writing main-repo adapter paths itself. Synchronous
256
+ // (init is a short FS op, not a long-running runner); requestId/async is deferred.
257
+ if (req.method === 'POST' && pathname === '/v1/init') {
258
+ try {
259
+ const body = JSON.parse(await readBody(req));
260
+ if (typeof body.name !== 'string' || !body.name) {
261
+ jsonResponse(res, 400, { ok: false, errorCode: 'validation_failed', error: 'name required' });
262
+ return;
263
+ }
264
+ const result = createAdapterDraft(body);
265
+ if (!result.ok) {
266
+ // init failures are all client-fixable 400s (validation_failed / responsible_use_required).
267
+ jsonResponse(res, 400, { ok: false, errorCode: result.errorCode, error: result.reason });
268
+ return;
269
+ }
270
+ jsonResponse(res, 200, { ok: true, data: { report: result.report, dryRun: result.dryRun, generatedSource: result.generatedSource } });
271
+ }
272
+ catch (err) {
273
+ jsonResponse(res, 400, { ok: false, errorCode: 'validation_failed', error: err instanceof Error ? err.message : String(err) });
274
+ }
275
+ return;
276
+ }
277
+ // N4: save a reviewed, full LLM-generated adapter source to clis/ (verify-then-save flow).
278
+ if (req.method === 'POST' && pathname === '/v1/save-adapter') {
279
+ try {
280
+ const body = JSON.parse(await readBody(req));
281
+ if (typeof body.name !== 'string' || !body.name || typeof body.source !== 'string' || !body.source) {
282
+ jsonResponse(res, 400, { ok: false, errorCode: 'validation_failed', error: 'name and source required' });
283
+ return;
284
+ }
285
+ const result = saveAdapterSource(body);
286
+ if (!result.ok) {
287
+ jsonResponse(res, 400, { ok: false, errorCode: result.errorCode, error: result.reason });
288
+ return;
289
+ }
290
+ jsonResponse(res, 200, { ok: true, data: { adapterPath: result.adapterPath, reportPath: result.reportPath } });
291
+ }
292
+ catch (err) {
293
+ jsonResponse(res, 400, { ok: false, errorCode: 'validation_failed', error: err instanceof Error ? err.message : String(err) });
294
+ }
295
+ return;
296
+ }
297
+ // High-level verify (A'): interface + delegation seam. The adapter JS runs in the
298
+ // M6 child-process runner (08) — verifyAdapter delegates to the real spawn-based
299
+ // RunnerPort (defaultRunnerPort), so runner_protocol_error now signals an actual
300
+ // runner fault, not "not implemented". The evidence HMAC is keyed by a per-session salt
301
+ // held only in daemon memory (M7a · 04:111): be forwards the non-secret sessionId, the
302
+ // daemon mints + holds the salt, and it never crosses the wire.
303
+ if (req.method === 'POST' && pathname === '/v1/verify') {
304
+ try {
305
+ const body = JSON.parse(await readBody(req));
306
+ if (typeof body.name !== 'string' || !body.name) {
307
+ jsonResponse(res, 400, { ok: false, errorCode: 'validation_failed', error: 'name required' });
308
+ return;
309
+ }
310
+ const sessionHmacKey = defaultSessionKeyRegistry().keyFor(body.sessionId);
311
+ const result = await verifyAdapter(body, sessionHmacKey);
312
+ if (!result.ok) {
313
+ const status = result.errorCode === 'validation_failed' ? 400
314
+ : result.errorCode === 'queue_full' ? 429 // 03: queue/concurrency exceeded
315
+ : 500;
316
+ // requestId/sessionId correlation the choke point (res-only) can't see (09:89).
317
+ logger.warn('daemon.verify', { sessionId: body.sessionId, status: 'failed', errorCode: result.errorCode });
318
+ jsonResponse(res, status, { ok: false, errorCode: result.errorCode, error: result.reason });
319
+ return;
320
+ }
321
+ logger.info('daemon.verify', { requestId: result.requestId, sessionId: body.sessionId, stage: 'accepted' });
322
+ jsonResponse(res, 200, { ok: true, data: { requestId: result.requestId } });
323
+ }
324
+ catch (err) {
325
+ jsonResponse(res, 400, { ok: false, errorCode: 'validation_failed', error: err instanceof Error ? err.message : String(err) });
326
+ }
327
+ return;
328
+ }
329
+ // High-level request status (M6 · ADR-0007): be polls verify status here. The runner
330
+ // registry is keyed by the canonical requestId (be ↔ daemon ↔ runner share one id);
331
+ // returns summary-only VerifySummary (raw seed/stdout/stderr/trace path never included).
332
+ if (req.method === 'GET' && pathname.startsWith('/v1/requests/')) {
333
+ const requestId = decodeURIComponent(pathname.slice('/v1/requests/'.length));
334
+ if (!requestId) {
335
+ jsonResponse(res, 400, { ok: false, errorCode: 'validation_failed', error: 'requestId required' });
336
+ return;
337
+ }
338
+ const runStatus = defaultRunnerPort().getRunStatus(requestId);
339
+ if (!runStatus) {
340
+ jsonResponse(res, 404, { ok: false, errorCode: 'request_not_found', error: 'unknown requestId' });
341
+ return;
342
+ }
343
+ // RunStatus (queued|running|succeeded|failed|timeout|cancelled) maps 1:1 to request status;
344
+ // summary is null while queued/running, a VerifySummary once terminal (M6c adds queued).
345
+ logger.debug('daemon.requests', { requestId, status: runStatus.status });
346
+ jsonResponse(res, 200, { ok: true, data: { requestId, status: runStatus.status, result: runStatus.summary } });
347
+ return;
348
+ }
196
349
  if (req.method === 'GET' && pathname === '/status') {
197
350
  const uptime = process.uptime();
198
351
  const mem = process.memoryUsage();
@@ -226,6 +379,13 @@ async function handleRequest(req, res) {
226
379
  });
227
380
  return;
228
381
  }
382
+ // Metrics scrape (#1b · 09): loopback diagnostic, same X-byCLI posture as /status (no token).
383
+ // The counter/histogram registry is pure data in recorder-core; this endpoint (http-bound) stays
384
+ // in the daemon transport layer. Values are non-sensitive enums/counts only (09).
385
+ if (req.method === 'GET' && pathname === '/metrics') {
386
+ jsonResponse(res, 200, { ok: true, ...metrics.snapshot() });
387
+ return;
388
+ }
229
389
  if (req.method === 'GET' && pathname === '/logs') {
230
390
  const params = new URL(url, `http://localhost:${PORT}`).searchParams;
231
391
  const level = params.get('level');
@@ -411,6 +571,86 @@ wss.on('connection', (ws) => {
411
571
  // ─── Start ───────────────────────────────────────────────────────────
412
572
  httpServer.listen(PORT, '127.0.0.1', () => {
413
573
  log.info(`[daemon] Listening on http://127.0.0.1:${PORT}`);
574
+ // Temp-store reap policy (M7b · 09:27-29). Resolved once at startup; out-of-range env → throws,
575
+ // but we keep the daemon alive by falling back to the (validated-elsewhere) defaults on error.
576
+ let tempPolicy;
577
+ try {
578
+ tempPolicy = resolveTempPolicy();
579
+ }
580
+ catch (err) {
581
+ log.warn(`[daemon] temp policy config invalid, using defaults: ${err instanceof Error ? err.message : String(err)}`);
582
+ tempPolicy = { tempTtlMs: 3_600_000, startupReapMaxAgeMs: 86_400_000, orphanKillGraceMs: 1_500 };
583
+ }
584
+ // Age-reap safety floor (08 / M7b): a verify cannot legitimately outlive timeoutMs + killGraceMs,
585
+ // so the age threshold must never drop below it — otherwise a short RECORDER_TEMP_TTL_MS (min 60s)
586
+ // would let the sweep kill a still-running verify (timeoutMs reaches 600s). Fall back to the
587
+ // absolute config ceiling (max timeoutMs + max killGraceMs) if runner config is unreadable.
588
+ let minLeakAgeMs = 600_000 + 30_000;
589
+ try {
590
+ const rc = resolveRunnerConfig();
591
+ minLeakAgeMs = rc.timeoutMs + rc.killGraceMs;
592
+ }
593
+ catch { /* keep the absolute ceiling (safest, highest floor) */ }
594
+ // #1d temp-store capacity guard: the runner refuses a new verify (temp_store_full) past the high
595
+ // watermark rather than risk filling the disk. Resolved at startup (temp root is restart-only,
596
+ // 09:182); bad env falls back to defaults (consistent with temp policy above). Registered BEFORE the
597
+ // first defaultRunnerPort() use (no request is served until after this listen callback returns). The
598
+ // on-pressure sweep reuses the same age-bounded reap as the periodic sweep (never kills a live verify).
599
+ let tempCapacity;
600
+ try {
601
+ tempCapacity = resolveTempCapacity();
602
+ }
603
+ catch (err) {
604
+ log.warn(`[daemon] temp capacity config invalid, using defaults: ${err instanceof Error ? err.message : String(err)}`);
605
+ tempCapacity = resolveTempCapacity({}); // empty env → validated defaults
606
+ }
607
+ setDefaultRunnerTempGuard(tempCapacity, () => {
608
+ reapOrphanedVerifyRuns({ log: (m) => log.info(m), maxAgeMs: tempPolicy.tempTtlMs, minLeakAgeMs, graceMs: tempPolicy.orphanKillGraceMs });
609
+ });
610
+ // Startup reap (08:37): clean up verify temp dirs orphaned by a previously-crashed daemon
611
+ // (ownerPid-dead), plus any dir older than the startup age backstop (M7b leak sweep). A live
612
+ // sibling's in-flight verify (young + owner alive, or within the hard-deadline floor) is never touched.
613
+ try {
614
+ const r = reapOrphanedVerifyRuns({ log: (m) => log.info(m), maxAgeMs: tempPolicy.startupReapMaxAgeMs, minLeakAgeMs, graceMs: tempPolicy.orphanKillGraceMs });
615
+ // #1c · 09 startup reap count (orphan runners killed, temp dirs swept)
616
+ if (r.orphans)
617
+ metrics.inc('startup_reap_orphans_total', undefined, r.orphans);
618
+ if (r.killed)
619
+ metrics.inc('startup_reap_killed_total', undefined, r.killed);
620
+ if (r.deleted)
621
+ metrics.inc('startup_reap_deleted_total', undefined, r.deleted);
622
+ }
623
+ catch (err) {
624
+ log.warn(`[daemon] verify startup reap failed: ${err instanceof Error ? err.message : String(err)}`);
625
+ }
626
+ // Periodic TTL sweep (M7b): while the daemon runs, reap dirs older than the temp TTL — but never
627
+ // below the run hard-deadline floor (minLeakAgeMs), so an aggressive TTL can't kill a live verify.
628
+ // Unref'd so it never keeps the process alive on its own.
629
+ const sweep = setInterval(() => {
630
+ try {
631
+ const r = reapOrphanedVerifyRuns({ log: (m) => log.info(m), maxAgeMs: tempPolicy.tempTtlMs, minLeakAgeMs, graceMs: tempPolicy.orphanKillGraceMs });
632
+ if (r.killed)
633
+ metrics.inc('temp_sweep_killed_total', undefined, r.killed);
634
+ if (r.deleted)
635
+ metrics.inc('temp_sweep_deleted_total', undefined, r.deleted);
636
+ // Drop salts for abandoned sessions (M7a TTL backstop; daemon restart already rotates all).
637
+ const dropped = defaultSessionKeyRegistry().sweepExpired();
638
+ if (dropped)
639
+ metrics.inc('session_keys_dropped_total', undefined, dropped);
640
+ }
641
+ catch (err) {
642
+ log.warn(`[daemon] verify temp sweep failed: ${err instanceof Error ? err.message : String(err)}`);
643
+ }
644
+ }, tempPolicy.tempTtlMs);
645
+ sweep.unref?.();
646
+ // Init write-transaction crash recovery (07:106-117): resolve any adapter draft interrupted
647
+ // mid-write by a previous crash (roll-forward marker / quarantine unprovenanced / roll back).
648
+ try {
649
+ recoverInitTransactions({ log: (m) => log.info(m) });
650
+ }
651
+ catch (err) {
652
+ log.warn(`[daemon] init startup recovery failed: ${err instanceof Error ? err.message : String(err)}`);
653
+ }
414
654
  });
415
655
  httpServer.on('error', (err) => {
416
656
  if (err.code === 'EADDRINUSE') {
@@ -7,11 +7,13 @@
7
7
  * when their command is executed.
8
8
  * 2. FALLBACK (filesystem scan): Traditional runtime discovery for development.
9
9
  */
10
- /** User runtime directory: ~/.bycli */
10
+ /** User runtime directory: BYCLI_CONFIG_DIR or ~/.bycli.
11
+ * Resolved once at module load; the entry process fixes BYCLI_CONFIG_DIR before import. */
11
12
  export declare const USER_BYCLI_DIR: string;
12
- /** User CLIs directory: ~/.bycli/clis */
13
+ /** User CLIs directory: <config>/clis */
13
14
  export declare const USER_CLIS_DIR: string;
14
- /** Plugins directory: ~/.bycli/plugins/ */
15
+ /** Plugins directory: <config>/plugins/ (plugins are out of scope for the clis/sites
16
+ * relocation; kept as a const that many plugin.ts/tests consumers rely on). */
15
17
  export declare const PLUGINS_DIR: string;
16
18
  /**
17
19
  * Ensure ~/.bycli/node_modules/@sovovs/bycli symlink exists so that
@@ -8,18 +8,20 @@
8
8
  * 2. FALLBACK (filesystem scan): Traditional runtime discovery for development.
9
9
  */
10
10
  import * as fs from 'node:fs';
11
- import * as os from 'node:os';
12
11
  import * as path from 'node:path';
13
12
  import { fileURLToPath, pathToFileURL } from 'node:url';
14
13
  import { Strategy, registerCommand } from './registry.js';
15
14
  import { getErrorMessage } from './errors.js';
16
15
  import { log } from './logger.js';
17
16
  import { findPackageRoot, getCliManifestPath } from './package-paths.js';
18
- /** User runtime directory: ~/.bycli */
19
- export const USER_BYCLI_DIR = path.join(os.homedir(), '.bycli');
20
- /** User CLIs directory: ~/.bycli/clis */
21
- export const USER_CLIS_DIR = path.join(USER_BYCLI_DIR, 'clis');
22
- /** Plugins directory: ~/.bycli/plugins/ */
17
+ import { getConfigDir, getUserClisDir } from './config-paths.js';
18
+ /** User runtime directory: BYCLI_CONFIG_DIR or ~/.bycli.
19
+ * Resolved once at module load; the entry process fixes BYCLI_CONFIG_DIR before import. */
20
+ export const USER_BYCLI_DIR = getConfigDir();
21
+ /** User CLIs directory: <config>/clis */
22
+ export const USER_CLIS_DIR = getUserClisDir();
23
+ /** Plugins directory: <config>/plugins/ (plugins are out of scope for the clis/sites
24
+ * relocation; kept as a const that many plugin.ts/tests consumers rely on). */
23
25
  export const PLUGINS_DIR = path.join(USER_BYCLI_DIR, 'plugins');
24
26
  /** Matches files that register commands via cli() or lifecycle hooks */
25
27
  const PLUGIN_MODULE_PATTERN = /\b(?:cli|onStartup|onBeforeExecute|onAfterExecute)\s*\(/;
@@ -13,7 +13,8 @@ import { getRegistry, fullName, } from './registry.js';
13
13
  import { pathToFileURL } from 'node:url';
14
14
  import * as crypto from 'node:crypto';
15
15
  import * as fs from 'node:fs';
16
- import * as os from 'node:os';
16
+ import * as path from 'node:path';
17
+ import { getUserClisDir } from './config-paths.js';
17
18
  import { executePipeline } from './pipeline/index.js';
18
19
  import { adapterLoadError, ArgumentError, CommandExecutionError, attachTraceReceipt, getErrorMessage } from './errors.js';
19
20
  import { shouldUseBrowserSession } from './capabilityRouting.js';
@@ -28,7 +29,11 @@ import { resolveAdapterSourcePath } from './adapter-source.js';
28
29
  const _loadedModules = new Map();
29
30
  /** Track mtime of loaded user adapter files for hot-reload in daemon mode. */
30
31
  const _moduleMtimes = new Map();
31
- const _userClisDir = `${os.homedir()}/.bycli/clis/`;
32
+ /** User adapter dir with a trailing separator, so `startsWith` can't match a sibling
33
+ * like `clis-foo`. Resolved per-call (config-paths.ts rationale) — no import-time freeze. */
34
+ function userClisPrefix() {
35
+ return getUserClisDir() + path.sep;
36
+ }
32
37
  function normalizeTraceMode(raw) {
33
38
  if (raw === undefined || raw === null || raw === '' || raw === 'off')
34
39
  return 'off';
@@ -83,7 +88,7 @@ async function runCommand(cmd, page, kwargs, debug) {
83
88
  if (internal._lazy && internal._modulePath) {
84
89
  const modulePath = internal._modulePath;
85
90
  // Hot-reload: if a user adapter's file has changed on disk, invalidate cache
86
- const isUserAdapter = modulePath.startsWith(_userClisDir);
91
+ const isUserAdapter = modulePath.startsWith(userClisPrefix());
87
92
  if (isUserAdapter && _loadedModules.has(modulePath)) {
88
93
  try {
89
94
  const stat = fs.statSync(modulePath);
package/dist/src/main.js CHANGED
@@ -13,8 +13,8 @@ if (process.platform !== 'win32') {
13
13
  process.env.PATH = [...cur].join(':');
14
14
  }
15
15
  import * as fs from 'node:fs';
16
- import * as os from 'node:os';
17
16
  import * as path from 'node:path';
17
+ import { getUserClisDir } from './config-paths.js';
18
18
  import { fileURLToPath } from 'node:url';
19
19
  import { getCompletionsFromManifest, hasAllManifests, printCompletionScriptFast } from './completion-fast.js';
20
20
  import { findPackageRoot, getCliManifestPath } from './package-paths.js';
@@ -26,7 +26,7 @@ const __dirname = path.dirname(__filename);
26
26
  // Adapters are JS-first and live at <package-root>/clis/.
27
27
  // Use findPackageRoot so the path works both in dev (src/main.ts) and prod (dist/src/main.js).
28
28
  const BUILTIN_CLIS = path.join(findPackageRoot(__filename), 'clis');
29
- const USER_CLIS = path.join(os.homedir(), '.bycli', 'clis');
29
+ const USER_CLIS = getUserClisDir();
30
30
  // ── Ultra-fast path: lightweight commands bypass full discovery ──────────
31
31
  // These are high-frequency or trivial paths that must not pay the startup tax.
32
32
  const argv = process.argv.slice(2);
@@ -52,6 +52,22 @@ if (argv[0] === 'completion' && argv.length >= 2) {
52
52
  }
53
53
  // Unknown shell — fall through to full path for proper error handling
54
54
  }
55
+ // Fast path: internal verify-runner — the child process spawned by the verify RunnerPort
56
+ // (08). It loads ONLY the target adapter (by path, from input.json) and emits JSONL; it must
57
+ // never run full adapter discovery, and its stdout must stay pure JSONL.
58
+ if (argv[0] === 'internal' && argv[1] === 'verify-runner') {
59
+ const { runVerifyRunnerCli } = await import('./recorder/runner/verify-runner-main.js');
60
+ await runVerifyRunnerCli(argv.slice(2));
61
+ process.exit(process.exitCode ?? EXIT_CODES.SUCCESS);
62
+ }
63
+ // Fast path: internal highlevel-http — the OPTIONAL standalone High-Level HTTP wrapper (M9,
64
+ // 07 · Optional HTTP Wrapper). Default off; opt-in only via this command. Binds 127.0.0.1 and
65
+ // keeps running, so it must skip full adapter discovery. Distinct from the daemon /v1/* family.
66
+ if (argv[0] === 'internal' && argv[1] === 'highlevel-http') {
67
+ const { runWrapperCli } = await import('./recorder/http/wrapper-server.js');
68
+ await runWrapperCli(); // blocks while the server is alive; resolves on close (SIGTERM/SIGINT)
69
+ process.exit(process.exitCode ?? EXIT_CODES.SUCCESS); // never fall through to CLI discovery
70
+ }
55
71
  // Fast path: --get-completions — read from manifest, skip discovery
56
72
  const getCompIdx = process.argv.indexOf('--get-completions');
57
73
  if (getCompIdx !== -1) {
@@ -1013,13 +1013,16 @@ function getPluginSource(dir) {
1013
1013
  }
1014
1014
  /** Parse a plugin source string into clone URL, repo name, and optional sub-plugin. */
1015
1015
  function parseSource(source) {
1016
+ // Strip either the legacy `bycli-plugin-` or the current `opencli-plugin-`
1017
+ // repo-name prefix when deriving the local plugin name.
1018
+ const PLUGIN_NAME_PREFIX = /^(?:bycli|opencli)-plugin-/;
1016
1019
  if (source.startsWith('file://')) {
1017
1020
  try {
1018
1021
  const localPath = path.resolve(fileURLToPath(source));
1019
1022
  return {
1020
1023
  type: 'local',
1021
1024
  localPath,
1022
- name: path.basename(localPath).replace(/^bycli-plugin-/, ''),
1025
+ name: path.basename(localPath).replace(PLUGIN_NAME_PREFIX, ''),
1023
1026
  };
1024
1027
  }
1025
1028
  catch {
@@ -1031,14 +1034,14 @@ function parseSource(source) {
1031
1034
  return {
1032
1035
  type: 'local',
1033
1036
  localPath,
1034
- name: path.basename(localPath).replace(/^bycli-plugin-/, ''),
1037
+ name: path.basename(localPath).replace(PLUGIN_NAME_PREFIX, ''),
1035
1038
  };
1036
1039
  }
1037
1040
  // github:user/repo/subplugin (monorepo specific sub-plugin)
1038
1041
  const githubSubMatch = source.match(/^github:([\w.-]+)\/([\w.-]+)\/([\w.-]+)$/);
1039
1042
  if (githubSubMatch) {
1040
1043
  const [, user, repo, sub] = githubSubMatch;
1041
- const name = repo.replace(/^bycli-plugin-/, '');
1044
+ const name = repo.replace(PLUGIN_NAME_PREFIX, '');
1042
1045
  return {
1043
1046
  type: 'git',
1044
1047
  cloneUrl: `https://github.com/${user}/${repo}.git`,
@@ -1050,7 +1053,7 @@ function parseSource(source) {
1050
1053
  const githubMatch = source.match(/^github:([\w.-]+)\/([\w.-]+)$/);
1051
1054
  if (githubMatch) {
1052
1055
  const [, user, repo] = githubMatch;
1053
- const name = repo.replace(/^bycli-plugin-/, '');
1056
+ const name = repo.replace(PLUGIN_NAME_PREFIX, '');
1054
1057
  return {
1055
1058
  type: 'git',
1056
1059
  cloneUrl: `https://github.com/${user}/${repo}.git`,
@@ -1061,7 +1064,7 @@ function parseSource(source) {
1061
1064
  const urlMatch = source.match(/^https?:\/\/github\.com\/([\w.-]+)\/([\w.-]+?)(?:\.git)?$/);
1062
1065
  if (urlMatch) {
1063
1066
  const [, user, repo] = urlMatch;
1064
- const name = repo.replace(/^bycli-plugin-/, '');
1067
+ const name = repo.replace(PLUGIN_NAME_PREFIX, '');
1065
1068
  return {
1066
1069
  type: 'git',
1067
1070
  cloneUrl: `https://github.com/${user}/${repo}.git`,
@@ -1075,7 +1078,7 @@ function parseSource(source) {
1075
1078
  const pathPart = sshUrlMatch[1];
1076
1079
  const segments = pathPart.split('/');
1077
1080
  const repoSegment = segments.pop();
1078
- const name = repoSegment.replace(/^bycli-plugin-/, '');
1081
+ const name = repoSegment.replace(PLUGIN_NAME_PREFIX, '');
1079
1082
  return { type: 'git', cloneUrl: source, name };
1080
1083
  }
1081
1084
  // git@host:user/repo.git (SCP-style)
@@ -1084,7 +1087,7 @@ function parseSource(source) {
1084
1087
  const pathPart = scpMatch[1];
1085
1088
  const segments = pathPart.split('/');
1086
1089
  const repoSegment = segments.pop();
1087
- const name = repoSegment.replace(/^bycli-plugin-/, '');
1090
+ const name = repoSegment.replace(PLUGIN_NAME_PREFIX, '');
1088
1091
  return { type: 'git', cloneUrl: source, name };
1089
1092
  }
1090
1093
  // Generic https/http git URL (non-GitHub hosts)
@@ -1093,7 +1096,7 @@ function parseSource(source) {
1093
1096
  const pathPart = genericHttpMatch[1];
1094
1097
  const segments = pathPart.split('/');
1095
1098
  const repoSegment = segments.pop();
1096
- const name = repoSegment.replace(/^bycli-plugin-/, '');
1099
+ const name = repoSegment.replace(PLUGIN_NAME_PREFIX, '');
1097
1100
  // Ensure clone URL ends with .git
1098
1101
  const cloneUrl = source.endsWith('.git') ? source : `${source}.git`;
1099
1102
  return { type: 'git', cloneUrl, name };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * High-Level analyze module (M5a, 07-high-level-services.md · analyzeBrowser).
3
+ *
4
+ * Module-first (ADR-0004): drives the Page abstraction through the multi-step
5
+ * site probe (goto / wait / network capture / window-global probe / cookies) to
6
+ * assemble PageSignals, then calls the PURE analyzeSite() classifier. In-process
7
+ * async, no child process (07:50). Hosted by the main-repo daemon high-level
8
+ * surface (Codex A' verdict); dashboard-be only forwards.
9
+ *
10
+ * Timeout surfaces as `analyze_timeout` (07:52), never a runner error.
11
+ */
12
+ import type { IPage } from '../../types.js';
13
+ import { type AdapterRef, type AnalyzeReport } from '../../browser/analyze.js';
14
+ export interface AnalyzeInput {
15
+ url: string;
16
+ /** site-level settle window after load (ms); default 2000 (07 input). */
17
+ settleMs?: number;
18
+ }
19
+ export declare class AnalyzeTimeoutError extends Error {
20
+ readonly code = "analyze_timeout";
21
+ constructor(ms: number);
22
+ }
23
+ /**
24
+ * Collect PageSignals by driving the page, then classify with the pure analyzeSite.
25
+ * `registry` is the adapter registry used for nearest-adapter matching.
26
+ */
27
+ export declare function analyzeBrowser(page: IPage, registry: Map<string, AdapterRef>, input: AnalyzeInput): Promise<AnalyzeReport>;
28
+ /** Wrap analyzeBrowser with a deadline → AnalyzeTimeoutError (07:52 analyze_timeout). */
29
+ export declare function analyzeBrowserWithTimeout(page: IPage, registry: Map<string, AdapterRef>, input: AnalyzeInput, timeoutMs: number): Promise<AnalyzeReport>;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * High-Level analyze module (M5a, 07-high-level-services.md · analyzeBrowser).
3
+ *
4
+ * Module-first (ADR-0004): drives the Page abstraction through the multi-step
5
+ * site probe (goto / wait / network capture / window-global probe / cookies) to
6
+ * assemble PageSignals, then calls the PURE analyzeSite() classifier. In-process
7
+ * async, no child process (07:50). Hosted by the main-repo daemon high-level
8
+ * surface (Codex A' verdict); dashboard-be only forwards.
9
+ *
10
+ * Timeout surfaces as `analyze_timeout` (07:52), never a runner error.
11
+ */
12
+ import { analyzeSite } from '../../browser/analyze.js';
13
+ import { generateInterceptorJs } from '../../interceptor.js';
14
+ export class AnalyzeTimeoutError extends Error {
15
+ code = 'analyze_timeout';
16
+ constructor(ms) {
17
+ super(`analyze timed out after ${ms}ms`);
18
+ this.name = 'AnalyzeTimeoutError';
19
+ }
20
+ }
21
+ const DEFAULT_SETTLE_MS = 2000;
22
+ /** Window-global + cookie + finalUrl probe (mirrors the CLI analyze probe). */
23
+ const PROBE_JS = `(function(){
24
+ return {
25
+ cookieNames: (document.cookie || '').split(';').map(function(c){ return c.trim().split('=')[0]; }).filter(Boolean),
26
+ initialState: {
27
+ __INITIAL_STATE__: typeof window.__INITIAL_STATE__ !== 'undefined',
28
+ __NUXT__: typeof window.__NUXT__ !== 'undefined',
29
+ __NEXT_DATA__: typeof window.__NEXT_DATA__ !== 'undefined',
30
+ __APOLLO_STATE__: typeof window.__APOLLO_STATE__ !== 'undefined',
31
+ },
32
+ title: document.title || '',
33
+ finalUrl: location.href,
34
+ };
35
+ })()`;
36
+ /**
37
+ * Collect PageSignals by driving the page, then classify with the pure analyzeSite.
38
+ * `registry` is the adapter registry used for nearest-adapter matching.
39
+ */
40
+ export async function analyzeBrowser(page, registry, input) {
41
+ const settleMs = input.settleMs ?? DEFAULT_SETTLE_MS;
42
+ // Prefer the session-level CDP network capture; fall back to an injected interceptor.
43
+ const hasSessionCapture = (await page.startNetworkCapture?.()) ?? false;
44
+ await page.goto(input.url);
45
+ await page.wait(Math.max(1, Math.round(settleMs / 1000)));
46
+ if (!hasSessionCapture) {
47
+ // match-all: analyze wants every json/text response, not a filtered subset.
48
+ try {
49
+ await page.evaluate(generateInterceptorJs('true'));
50
+ }
51
+ catch { /* non-fatal */ }
52
+ }
53
+ await page.wait(1);
54
+ const rawItems = ((await page.readNetworkCapture?.()) ?? []);
55
+ const networkEntries = rawItems.map((e) => ({
56
+ url: e.url ?? '',
57
+ status: e.status ?? 0,
58
+ contentType: e.ct ?? '',
59
+ bodyPreview: typeof e.body === 'string'
60
+ ? e.body.slice(0, 2000)
61
+ : (e.body ? JSON.stringify(e.body).slice(0, 2000) : null),
62
+ }));
63
+ const probe = await page.evaluate(PROBE_JS);
64
+ const browserCookieNames = (await page.getCookies({ url: probe.finalUrl || input.url }).catch(() => []))
65
+ .map((c) => c.name)
66
+ .filter(Boolean);
67
+ const cookieNames = [...new Set([...probe.cookieNames, ...browserCookieNames])];
68
+ const signals = {
69
+ requestedUrl: input.url,
70
+ finalUrl: probe.finalUrl,
71
+ cookieNames,
72
+ networkEntries,
73
+ initialState: probe.initialState,
74
+ title: probe.title,
75
+ };
76
+ return analyzeSite(signals, registry);
77
+ }
78
+ /** Wrap analyzeBrowser with a deadline → AnalyzeTimeoutError (07:52 analyze_timeout). */
79
+ export async function analyzeBrowserWithTimeout(page, registry, input, timeoutMs) {
80
+ let timer;
81
+ const timeout = new Promise((_, reject) => {
82
+ timer = setTimeout(() => reject(new AnalyzeTimeoutError(timeoutMs)), timeoutMs);
83
+ });
84
+ try {
85
+ return await Promise.race([analyzeBrowser(page, registry, input), timeout]);
86
+ }
87
+ finally {
88
+ if (timer)
89
+ clearTimeout(timer);
90
+ }
91
+ }