@sovovs/bycli 1.8.0 → 2.1.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 (539) hide show
  1. package/cli-manifest.json +0 -61
  2. package/clis/twitter/search.js +3 -3
  3. package/dist/src/adapter-shadow.js +2 -2
  4. package/dist/src/browser/analyze.d.ts +10 -99
  5. package/dist/src/browser/analyze.js +10 -226
  6. package/dist/src/browser/cdp.js +67 -7
  7. package/dist/src/browser/daemon-client.d.ts +2 -0
  8. package/dist/src/browser/daemon-client.js +1 -1
  9. package/dist/src/browser/extension-capabilities.d.ts +13 -0
  10. package/dist/src/browser/extension-capabilities.js +22 -0
  11. package/dist/src/browser/extension-capabilities.test.d.ts +1 -0
  12. package/dist/src/browser/extension-version-metadata.test.d.ts +1 -0
  13. package/dist/src/browser/navigation-guard.d.ts +78 -0
  14. package/dist/src/browser/navigation-guard.js +142 -0
  15. package/dist/src/browser/navigation-guard.test.d.ts +1 -0
  16. package/dist/src/browser/page.d.ts +1 -0
  17. package/dist/src/browser/page.js +20 -1
  18. package/dist/src/browser/url-policy.d.ts +89 -0
  19. package/dist/src/browser/url-policy.js +311 -0
  20. package/dist/src/browser/url-policy.test.d.ts +1 -0
  21. package/dist/src/browser/verify-fixture.js +2 -2
  22. package/dist/src/build-manifest.js +4 -2
  23. package/dist/src/capabilityRouting.d.ts +3 -2
  24. package/dist/src/capabilityRouting.js +10 -2
  25. package/dist/src/cli.js +11 -14
  26. package/dist/src/commanderAdapter.js +5 -5
  27. package/dist/src/config-paths.d.ts +19 -0
  28. package/dist/src/config-paths.js +31 -0
  29. package/dist/src/daemon.js +256 -0
  30. package/dist/src/discovery.d.ts +10 -3
  31. package/dist/src/discovery.js +20 -10
  32. package/dist/src/discovery.test.d.ts +1 -0
  33. package/dist/src/execution.d.ts +5 -0
  34. package/dist/src/execution.js +272 -48
  35. package/dist/src/help.js +8 -8
  36. package/dist/src/main.js +18 -2
  37. package/dist/src/manifest-schema.d.ts +9 -0
  38. package/dist/src/manifest-schema.js +162 -0
  39. package/dist/src/manifest-schema.test.d.ts +1 -0
  40. package/dist/src/manifest-types.d.ts +1 -1
  41. package/dist/src/observation/redaction.js +10 -4
  42. package/dist/src/plugin.js +11 -8
  43. package/dist/src/recorder/highlevel/analyze.d.ts +29 -0
  44. package/dist/src/recorder/highlevel/analyze.js +91 -0
  45. package/dist/src/recorder/highlevel/init.d.ts +125 -0
  46. package/dist/src/recorder/highlevel/init.js +279 -0
  47. package/dist/src/recorder/highlevel/init.test.d.ts +1 -0
  48. package/dist/src/recorder/highlevel/verify.d.ts +73 -0
  49. package/dist/src/recorder/highlevel/verify.js +74 -0
  50. package/dist/src/recorder/http/analyze-runner.d.ts +28 -0
  51. package/dist/src/recorder/http/analyze-runner.js +78 -0
  52. package/dist/src/recorder/http/analyze-runner.test.d.ts +1 -0
  53. package/dist/src/recorder/http/handlers.d.ts +51 -0
  54. package/dist/src/recorder/http/handlers.js +219 -0
  55. package/dist/src/recorder/http/wrapper-config.d.ts +20 -0
  56. package/dist/src/recorder/http/wrapper-config.js +82 -0
  57. package/dist/src/recorder/http/wrapper-envelope.d.ts +31 -0
  58. package/dist/src/recorder/http/wrapper-envelope.js +51 -0
  59. package/dist/src/recorder/http/wrapper-gates.d.ts +16 -0
  60. package/dist/src/recorder/http/wrapper-gates.js +28 -0
  61. package/dist/src/recorder/http/wrapper-metrics.d.ts +1 -0
  62. package/dist/src/recorder/http/wrapper-metrics.js +5 -0
  63. package/dist/src/recorder/http/wrapper-registry.d.ts +50 -0
  64. package/dist/src/recorder/http/wrapper-registry.js +98 -0
  65. package/dist/src/recorder/http/wrapper-server.d.ts +59 -0
  66. package/dist/src/recorder/http/wrapper-server.js +240 -0
  67. package/dist/src/recorder/http/wrapper.test.d.ts +1 -0
  68. package/dist/src/recorder/observability/logger.d.ts +5 -0
  69. package/dist/src/recorder/observability/logger.js +13 -0
  70. package/dist/src/recorder/runner/config.d.ts +34 -0
  71. package/dist/src/recorder/runner/config.js +84 -0
  72. package/dist/src/recorder/runner/config.test.d.ts +1 -0
  73. package/dist/src/recorder/runner/reap.d.ts +79 -0
  74. package/dist/src/recorder/runner/reap.js +230 -0
  75. package/dist/src/recorder/runner/reap.test.d.ts +1 -0
  76. package/dist/src/recorder/runner/runner-port.d.ts +95 -0
  77. package/dist/src/recorder/runner/runner-port.js +518 -0
  78. package/dist/src/recorder/runner/runner-port.test.d.ts +1 -0
  79. package/dist/src/recorder/runner/session-keys.d.ts +28 -0
  80. package/dist/src/recorder/runner/session-keys.js +72 -0
  81. package/dist/src/recorder/runner/session-keys.test.d.ts +1 -0
  82. package/dist/src/recorder/runner/verify-runner-main.d.ts +96 -0
  83. package/dist/src/recorder/runner/verify-runner-main.js +292 -0
  84. package/dist/src/recorder/runner/verify-runner-main.test.d.ts +1 -0
  85. package/dist/src/registry-api.d.ts +1 -1
  86. package/dist/src/registry-api.types.test.d.ts +1 -0
  87. package/dist/src/registry-transaction.d.ts +42 -0
  88. package/dist/src/registry-transaction.js +194 -0
  89. package/dist/src/registry-transaction.test.d.ts +1 -0
  90. package/dist/src/registry.d.ts +58 -16
  91. package/dist/src/registry.js +131 -15
  92. package/dist/src/serialization.d.ts +1 -1
  93. package/dist/src/serialization.js +3 -3
  94. package/dist/src/types.d.ts +2 -0
  95. package/package.json +8 -5
  96. package/scripts/m1-daemon-navigate-drive.mjs +138 -0
  97. package/scripts/m1-extension-spike.mjs +237 -0
  98. package/scripts/m1-live-spike.mjs +156 -0
  99. package/scripts/m6b-connect-back-spike.sh +31 -0
  100. package/clis/12306/utils.test.js +0 -331
  101. package/clis/1688/assets.test.js +0 -39
  102. package/clis/1688/download.test.js +0 -31
  103. package/clis/1688/item.test.js +0 -67
  104. package/clis/1688/search.test.js +0 -75
  105. package/clis/1688/shared.test.js +0 -57
  106. package/clis/1688/store.test.js +0 -62
  107. package/clis/36kr/article.test.js +0 -46
  108. package/clis/36kr/hot.test.js +0 -15
  109. package/clis/36kr/news.test.js +0 -85
  110. package/clis/51job/utils.test.js +0 -69
  111. package/clis/_atlassian/shared.test.js +0 -170
  112. package/clis/aibase/news.test.js +0 -59
  113. package/clis/amazon/bestsellers.test.js +0 -29
  114. package/clis/amazon/discussion.test.js +0 -124
  115. package/clis/amazon/offer.test.js +0 -29
  116. package/clis/amazon/product.test.js +0 -24
  117. package/clis/amazon/rankings.test.js +0 -41
  118. package/clis/amazon/search.test.js +0 -22
  119. package/clis/amazon/shared.test.js +0 -44
  120. package/clis/apple-podcasts/commands.test.js +0 -119
  121. package/clis/apple-podcasts/utils.test.js +0 -57
  122. package/clis/arxiv/arxiv.test.js +0 -112
  123. package/clis/baidu-scholar/search.test.js +0 -23
  124. package/clis/barchart/greeks.test.js +0 -138
  125. package/clis/bilibili/comment.test.js +0 -153
  126. package/clis/bilibili/comments.test.js +0 -142
  127. package/clis/bilibili/dynamic.test.js +0 -68
  128. package/clis/bilibili/hot.test.js +0 -17
  129. package/clis/bilibili/subtitle.test.js +0 -196
  130. package/clis/bilibili/summary.test.js +0 -210
  131. package/clis/bilibili/utils.test.js +0 -61
  132. package/clis/bilibili/video.test.js +0 -132
  133. package/clis/binance/commands.test.js +0 -70
  134. package/clis/bloomberg/utils.test.js +0 -129
  135. package/clis/booking/booking.test.js +0 -356
  136. package/clis/boss/chatlist.test.js +0 -211
  137. package/clis/boss/chatmsg.test.js +0 -230
  138. package/clis/boss/search.test.js +0 -78
  139. package/clis/boss/utils.test.js +0 -34
  140. package/clis/brave/search.test.js +0 -76
  141. package/clis/chaoxing/utils.test.js +0 -45
  142. package/clis/chatgpt/commands.test.js +0 -45
  143. package/clis/chatgpt/envelope.test.js +0 -108
  144. package/clis/chatgpt/image.test.js +0 -188
  145. package/clis/chatgpt/utils.test.js +0 -330
  146. package/clis/chatgpt-app/ax.test.js +0 -35
  147. package/clis/chatwise/composer.test.js +0 -186
  148. package/clis/chess/analyze.test.js +0 -79
  149. package/clis/chess/game.test.js +0 -178
  150. package/clis/chess/games.test.js +0 -164
  151. package/clis/chess/stats.test.js +0 -79
  152. package/clis/chess/utils.test.js +0 -230
  153. package/clis/claude/ask.test.js +0 -338
  154. package/clis/claude/commands.test.js +0 -118
  155. package/clis/claude/utils.test.js +0 -148
  156. package/clis/cnki/search.test.js +0 -18
  157. package/clis/codex/sidebar.test.js +0 -329
  158. package/clis/coingecko/coingecko.test.js +0 -109
  159. package/clis/confluence/commands.test.js +0 -195
  160. package/clis/coupang/coupang.test.js +0 -159
  161. package/clis/coupang/utils.test.js +0 -62
  162. package/clis/ctrip/ctrip.test.js +0 -719
  163. package/clis/dblp/dblp.test.js +0 -397
  164. package/clis/deepseek/ask.test.js +0 -312
  165. package/clis/deepseek/detail.test.js +0 -81
  166. package/clis/deepseek/send.test.js +0 -107
  167. package/clis/deepseek/utils.test.js +0 -370
  168. package/clis/defillama/defillama.test.js +0 -99
  169. package/clis/devto/devto.test.js +0 -236
  170. package/clis/dianping/dianping.test.js +0 -578
  171. package/clis/douban/download.test.js +0 -170
  172. package/clis/douban/movie-hot.test.js +0 -14
  173. package/clis/douban/search.test.js +0 -11
  174. package/clis/douban/subject.test.js +0 -11
  175. package/clis/douban/utils.test.js +0 -438
  176. package/clis/doubao/detail.test.js +0 -42
  177. package/clis/doubao/history.test.js +0 -37
  178. package/clis/doubao/utils.test.js +0 -344
  179. package/clis/douyin/_shared/browser-fetch.test.js +0 -64
  180. package/clis/douyin/_shared/creation-id.test.js +0 -22
  181. package/clis/douyin/_shared/imagex-upload.test.js +0 -87
  182. package/clis/douyin/_shared/sts2.test.js +0 -27
  183. package/clis/douyin/_shared/text-extra.test.js +0 -37
  184. package/clis/douyin/_shared/timing.test.js +0 -28
  185. package/clis/douyin/_shared/tos-upload-short-read.test.js +0 -83
  186. package/clis/douyin/_shared/tos-upload.test.js +0 -229
  187. package/clis/douyin/_shared/transcode.test.js +0 -91
  188. package/clis/douyin/_shared/vod-upload.test.js +0 -38
  189. package/clis/douyin/activities.test.js +0 -55
  190. package/clis/douyin/collections.test.js +0 -45
  191. package/clis/douyin/delete.test.js +0 -100
  192. package/clis/douyin/draft.test.js +0 -335
  193. package/clis/douyin/drafts.test.js +0 -11
  194. package/clis/douyin/hashtag.test.js +0 -171
  195. package/clis/douyin/location.test.js +0 -23
  196. package/clis/douyin/profile.test.js +0 -46
  197. package/clis/douyin/publish-upload-id.test.js +0 -170
  198. package/clis/douyin/publish.test.js +0 -38
  199. package/clis/douyin/stats.test.js +0 -22
  200. package/clis/douyin/update.test.js +0 -11
  201. package/clis/douyin/user-videos.test.js +0 -151
  202. package/clis/douyin/videos.test.js +0 -54
  203. package/clis/duckduckgo/search.test.js +0 -128
  204. package/clis/duckduckgo/suggest.test.js +0 -66
  205. package/clis/eastmoney/hot-rank.test.js +0 -59
  206. package/clis/endoflife/endoflife.test.js +0 -51
  207. package/clis/facebook/feed.test.js +0 -169
  208. package/clis/facebook/marketplace.test.js +0 -91
  209. package/clis/facebook/notifications.test.js +0 -458
  210. package/clis/facebook/search.test.js +0 -55
  211. package/clis/flathub/flathub.test.js +0 -90
  212. package/clis/flomo/memos.test.js +0 -144
  213. package/clis/gemini/ask.test.js +0 -100
  214. package/clis/gemini/deep-research-result.test.js +0 -155
  215. package/clis/gemini/deep-research.test.js +0 -181
  216. package/clis/gemini/reply-state.test.js +0 -671
  217. package/clis/gemini/utils.test.js +0 -198
  218. package/clis/geogebra/geogebra.test.js +0 -175
  219. package/clis/gitee/search.test.js +0 -65
  220. package/clis/gitee/user.test.js +0 -63
  221. package/clis/google/utils.test.js +0 -75
  222. package/clis/google-scholar/cite.test.js +0 -47
  223. package/clis/google-scholar/profile.test.js +0 -49
  224. package/clis/google-scholar/search.test.js +0 -71
  225. package/clis/goproxy/goproxy.test.js +0 -103
  226. package/clis/gov-law/commands.test.js +0 -39
  227. package/clis/gov-policy/gov-policy.test.js +0 -224
  228. package/clis/grok/ask.test.js +0 -29
  229. package/clis/grok/utils.test.js +0 -103
  230. package/clis/hackernews/hackernews.test.js +0 -132
  231. package/clis/hf/hf.test.js +0 -16
  232. package/clis/hupu/hot.test.js +0 -224
  233. package/clis/imdb/utils.test.js +0 -88
  234. package/clis/indeed/indeed.test.js +0 -375
  235. package/clis/instagram/_shared/private-publish.test.js +0 -705
  236. package/clis/instagram/_shared/protocol-capture.test.js +0 -114
  237. package/clis/instagram/download.test.js +0 -118
  238. package/clis/instagram/note.test.js +0 -53
  239. package/clis/instagram/post.test.js +0 -1647
  240. package/clis/instagram/reel.test.js +0 -167
  241. package/clis/instagram/story.test.js +0 -134
  242. package/clis/jd/commands.test.js +0 -41
  243. package/clis/jd/item.test.js +0 -354
  244. package/clis/jianyu/search.test.js +0 -153
  245. package/clis/jianyu/shared/procurement-contract.test.js +0 -72
  246. package/clis/jianyu/shared/procurement-detail.test.js +0 -84
  247. package/clis/jike/read.test.js +0 -86
  248. package/clis/jira/commands.test.js +0 -287
  249. package/clis/juejin/search.js +0 -123
  250. package/clis/juejin/search.test.js +0 -98
  251. package/clis/lesswrong/frontpage.test.js +0 -37
  252. package/clis/lichess/lichess.test.js +0 -85
  253. package/clis/linkedin/connect.test.js +0 -213
  254. package/clis/linkedin/inbox.test.js +0 -152
  255. package/clis/linkedin/job-detail.test.js +0 -38
  256. package/clis/linkedin/jobs-preferences.test.js +0 -43
  257. package/clis/linkedin/people-search.test.js +0 -216
  258. package/clis/linkedin/post-analytics.test.js +0 -40
  259. package/clis/linkedin/posts.test.js +0 -40
  260. package/clis/linkedin/profile-analytics.test.js +0 -67
  261. package/clis/linkedin/profile-experience.test.js +0 -152
  262. package/clis/linkedin/profile-projects.test.js +0 -111
  263. package/clis/linkedin/profile-read.test.js +0 -77
  264. package/clis/linkedin/safe-send.test.js +0 -204
  265. package/clis/linkedin/salesnav-inbox.test.js +0 -113
  266. package/clis/linkedin/salesnav-message.test.js +0 -172
  267. package/clis/linkedin/salesnav-search.test.js +0 -76
  268. package/clis/linkedin/salesnav-thread.test.js +0 -79
  269. package/clis/linkedin/search.test.js +0 -222
  270. package/clis/linkedin/sent-invitations.test.js +0 -62
  271. package/clis/linkedin/services-read.test.js +0 -105
  272. package/clis/linkedin/thread-snapshot.test.js +0 -89
  273. package/clis/linkedin/timeline.test.js +0 -81
  274. package/clis/linkedin-learning/course.test.js +0 -114
  275. package/clis/linkedin-learning/search.test.js +0 -144
  276. package/clis/linkedin-learning/trending.test.js +0 -123
  277. package/clis/linux-do/feed.test.js +0 -153
  278. package/clis/linux-do/topic-content.test.js +0 -59
  279. package/clis/lobsters/lobsters.test.js +0 -169
  280. package/clis/notebooklm/add-source.test.js +0 -97
  281. package/clis/notebooklm/compat.test.js +0 -16
  282. package/clis/notebooklm/create.test.js +0 -58
  283. package/clis/notebooklm/generate-audio.test.js +0 -63
  284. package/clis/notebooklm/generate-slides.test.js +0 -75
  285. package/clis/notebooklm/history.test.js +0 -58
  286. package/clis/notebooklm/note-list.test.js +0 -56
  287. package/clis/notebooklm/notes-get.test.js +0 -72
  288. package/clis/notebooklm/open.test.js +0 -63
  289. package/clis/notebooklm/rpc.test.js +0 -131
  290. package/clis/notebooklm/source-fulltext.test.js +0 -106
  291. package/clis/notebooklm/source-get.test.js +0 -84
  292. package/clis/notebooklm/source-guide.test.js +0 -104
  293. package/clis/notebooklm/summary.test.js +0 -78
  294. package/clis/notebooklm/utils.test.js +0 -449
  295. package/clis/notebooklm/write-note.test.js +0 -70
  296. package/clis/nuget/nuget.test.js +0 -111
  297. package/clis/oeis/oeis.test.js +0 -88
  298. package/clis/ones/task-helpers.test.js +0 -12
  299. package/clis/ones/worklog.test.js +0 -20
  300. package/clis/openfda/openfda.test.js +0 -114
  301. package/clis/openreview/openreview.test.js +0 -427
  302. package/clis/osv/osv.test.js +0 -97
  303. package/clis/paperreview/commands.test.js +0 -243
  304. package/clis/paperreview/utils.test.js +0 -49
  305. package/clis/pixiv/download.test.js +0 -87
  306. package/clis/pixiv/illusts.test.js +0 -99
  307. package/clis/pixiv/search.test.js +0 -83
  308. package/clis/powerchina/search.test.js +0 -93
  309. package/clis/producthunt/utils.test.js +0 -64
  310. package/clis/pubmed/pubmed.test.js +0 -276
  311. package/clis/quark/utils.test.js +0 -58
  312. package/clis/qwen/utils.test.js +0 -45
  313. package/clis/reddit/extract-media.test.js +0 -149
  314. package/clis/reddit/frontpage.test.js +0 -34
  315. package/clis/reddit/home.test.js +0 -170
  316. package/clis/reddit/hot.test.js +0 -32
  317. package/clis/reddit/popular.test.js +0 -26
  318. package/clis/reddit/read.test.js +0 -335
  319. package/clis/reddit/reply.test.js +0 -89
  320. package/clis/reddit/search.test.js +0 -26
  321. package/clis/reddit/subreddit-info.test.js +0 -163
  322. package/clis/reddit/subreddit.test.js +0 -31
  323. package/clis/reddit/subscribed.test.js +0 -168
  324. package/clis/reddit/whoami.test.js +0 -105
  325. package/clis/rednote/navigation.test.js +0 -26
  326. package/clis/rednote/rednote.test.js +0 -222
  327. package/clis/rest-countries/rest-countries.test.js +0 -83
  328. package/clis/reuters/reuters.test.js +0 -299
  329. package/clis/rfc/rfc.test.js +0 -74
  330. package/clis/sinafinance/stock.test.js +0 -59
  331. package/clis/spotify/utils.test.js +0 -67
  332. package/clis/stackoverflow/stackoverflow.test.js +0 -404
  333. package/clis/steam/steam.test.js +0 -46
  334. package/clis/substack/utils.test.js +0 -44
  335. package/clis/suno/commands.test.js +0 -188
  336. package/clis/suno/download.test.js +0 -151
  337. package/clis/suno/generate.test.js +0 -252
  338. package/clis/suno/utils.test.js +0 -329
  339. package/clis/taobao/commands.test.js +0 -41
  340. package/clis/tdx/hot-rank.test.js +0 -59
  341. package/clis/ths/hot-rank.test.js +0 -64
  342. package/clis/tieba/commands.test.js +0 -79
  343. package/clis/tieba/utils.test.js +0 -290
  344. package/clis/tiktok/creator-videos.test.js +0 -113
  345. package/clis/tiktok/refactor.test.js +0 -389
  346. package/clis/tiktok/write-refactor.test.js +0 -370
  347. package/clis/toutiao/toutiao.test.js +0 -378
  348. package/clis/tvmaze/tvmaze.test.js +0 -93
  349. package/clis/twitter/bookmark-folder.test.js +0 -395
  350. package/clis/twitter/bookmark-folders.test.js +0 -152
  351. package/clis/twitter/bookmark.test.js +0 -74
  352. package/clis/twitter/bookmarks.test.js +0 -205
  353. package/clis/twitter/delete.test.js +0 -85
  354. package/clis/twitter/device-follow.test.js +0 -287
  355. package/clis/twitter/download.test.js +0 -457
  356. package/clis/twitter/followers.test.js +0 -62
  357. package/clis/twitter/following.test.js +0 -338
  358. package/clis/twitter/hide-reply.test.js +0 -76
  359. package/clis/twitter/like.test.js +0 -73
  360. package/clis/twitter/likes.test.js +0 -220
  361. package/clis/twitter/list-add.test.js +0 -133
  362. package/clis/twitter/list-create.test.js +0 -169
  363. package/clis/twitter/list-remove.test.js +0 -110
  364. package/clis/twitter/list-tweets.test.js +0 -196
  365. package/clis/twitter/lists.test.js +0 -253
  366. package/clis/twitter/post.test.js +0 -258
  367. package/clis/twitter/profile.test.js +0 -39
  368. package/clis/twitter/quote.test.js +0 -194
  369. package/clis/twitter/reply.test.js +0 -237
  370. package/clis/twitter/retweet.test.js +0 -73
  371. package/clis/twitter/search.test.js +0 -349
  372. package/clis/twitter/shared.test.js +0 -794
  373. package/clis/twitter/thread.test.js +0 -58
  374. package/clis/twitter/timeline.test.js +0 -104
  375. package/clis/twitter/trending.test.js +0 -15
  376. package/clis/twitter/tweets.test.js +0 -362
  377. package/clis/twitter/unbookmark.test.js +0 -73
  378. package/clis/twitter/unlike.test.js +0 -75
  379. package/clis/twitter/unretweet.test.js +0 -73
  380. package/clis/twitter/utils.test.js +0 -169
  381. package/clis/uisdc/news.test.js +0 -66
  382. package/clis/uiverse/_shared.test.js +0 -62
  383. package/clis/uiverse/navigation.test.js +0 -12
  384. package/clis/upwork/upwork.test.js +0 -566
  385. package/clis/wanfang/search.test.js +0 -23
  386. package/clis/web/read.test.js +0 -392
  387. package/clis/weibo/delete.test.js +0 -94
  388. package/clis/weibo/envelope.test.js +0 -85
  389. package/clis/weibo/favorites.test.js +0 -114
  390. package/clis/weibo/publish.test.js +0 -192
  391. package/clis/weibo/user-posts.test.js +0 -92
  392. package/clis/weibo/utils.test.js +0 -36
  393. package/clis/weixin/drafts.test.js +0 -69
  394. package/clis/weixin/search.test.js +0 -227
  395. package/clis/weread/ai-outline.test.js +0 -83
  396. package/clis/weread/book-search.test.js +0 -242
  397. package/clis/weread/commands.test.js +0 -398
  398. package/clis/weread/private-api-regression.test.js +0 -369
  399. package/clis/weread/search-regression.test.js +0 -494
  400. package/clis/weread/utils.test.js +0 -128
  401. package/clis/weread-official/commands.test.js +0 -385
  402. package/clis/weread-official/utils.test.js +0 -242
  403. package/clis/wikidata/wikidata.test.js +0 -83
  404. package/clis/wikipedia/trending.test.js +0 -57
  405. package/clis/wttr/wttr.test.js +0 -84
  406. package/clis/xianyu/chat.test.js +0 -84
  407. package/clis/xianyu/im.test.js +0 -253
  408. package/clis/xianyu/item.test.js +0 -56
  409. package/clis/xianyu/publish.test.js +0 -220
  410. package/clis/xianyu/search.test.js +0 -17
  411. package/clis/xiaoe/xiaoe.test.js +0 -486
  412. package/clis/xiaohongshu/comments.test.js +0 -258
  413. package/clis/xiaohongshu/creator-note-detail.test.js +0 -451
  414. package/clis/xiaohongshu/creator-notes-summary.test.js +0 -94
  415. package/clis/xiaohongshu/creator-notes.test.js +0 -281
  416. package/clis/xiaohongshu/creator-stats.test.js +0 -24
  417. package/clis/xiaohongshu/delete-note.test.js +0 -172
  418. package/clis/xiaohongshu/download.test.js +0 -316
  419. package/clis/xiaohongshu/navigation.test.js +0 -34
  420. package/clis/xiaohongshu/note.test.js +0 -249
  421. package/clis/xiaohongshu/publish.test.js +0 -608
  422. package/clis/xiaohongshu/search.test.js +0 -325
  423. package/clis/xiaohongshu/user-helpers.test.js +0 -160
  424. package/clis/xiaoyuzhou/auth.test.js +0 -124
  425. package/clis/xiaoyuzhou/download.test.js +0 -135
  426. package/clis/xiaoyuzhou/podcast-episodes.test.js +0 -78
  427. package/clis/xiaoyuzhou/transcript.test.js +0 -195
  428. package/clis/xiaoyuzhou/utils.test.js +0 -39
  429. package/clis/xueqiu/comments.test.js +0 -696
  430. package/clis/xueqiu/danjuan-utils.test.js +0 -41
  431. package/clis/xueqiu/utils.test.js +0 -26
  432. package/clis/yahoo/search.test.js +0 -94
  433. package/clis/youdao/note.test.js +0 -99
  434. package/clis/youtube/channel.test.js +0 -59
  435. package/clis/youtube/feed.test.js +0 -131
  436. package/clis/youtube/transcript-group.test.js +0 -99
  437. package/clis/youtube/transcript.test.js +0 -296
  438. package/clis/youtube/utils.test.js +0 -84
  439. package/clis/yuanbao/ask.test.js +0 -124
  440. package/clis/yuanbao/new.test.js +0 -30
  441. package/clis/yuanbao/shared.test.js +0 -80
  442. package/clis/zhihu/answer-comments.test.js +0 -287
  443. package/clis/zhihu/answer-detail.test.js +0 -338
  444. package/clis/zhihu/answer.test.js +0 -54
  445. package/clis/zhihu/collection.test.js +0 -337
  446. package/clis/zhihu/collections.test.js +0 -182
  447. package/clis/zhihu/comment.test.js +0 -50
  448. package/clis/zhihu/download.test.js +0 -12
  449. package/clis/zhihu/favorite.test.js +0 -59
  450. package/clis/zhihu/follow.test.js +0 -42
  451. package/clis/zhihu/like.test.js +0 -41
  452. package/clis/zhihu/question.test.js +0 -251
  453. package/clis/zhihu/recommend.test.js +0 -143
  454. package/clis/zhihu/search.test.js +0 -198
  455. package/clis/zhihu/target.test.js +0 -77
  456. package/clis/zhihu/text.test.js +0 -24
  457. package/clis/zhihu/write-shared.test.js +0 -176
  458. package/clis/zlibrary/commands.test.js +0 -65
  459. package/clis/zsxq/search.test.js +0 -24
  460. package/clis/zsxq/topic.test.js +0 -30
  461. package/clis/zsxq/topics.test.js +0 -24
  462. package/clis/zsxq/utils.test.js +0 -31
  463. package/dist/src/adapter-shadow.test.js +0 -49
  464. package/dist/src/adapter-source.test.js +0 -29
  465. package/dist/src/browser/analyze.test.js +0 -166
  466. package/dist/src/browser/article-extract.e2e.test.js +0 -105
  467. package/dist/src/browser/article-extract.test.js +0 -94
  468. package/dist/src/browser/ax-snapshot.test.js +0 -91
  469. package/dist/src/browser/base-page.test.js +0 -758
  470. package/dist/src/browser/cdp-click-fixture.test.js +0 -87
  471. package/dist/src/browser/cdp.test.js +0 -79
  472. package/dist/src/browser/compound.test.js +0 -175
  473. package/dist/src/browser/daemon-client.test.js +0 -199
  474. package/dist/src/browser/dom-helpers.test.js +0 -106
  475. package/dist/src/browser/dom-snapshot.test.js +0 -313
  476. package/dist/src/browser/errors.test.js +0 -60
  477. package/dist/src/browser/extract.test.js +0 -129
  478. package/dist/src/browser/find.test.js +0 -180
  479. package/dist/src/browser/html-tree.test.js +0 -181
  480. package/dist/src/browser/network-cache.test.js +0 -58
  481. package/dist/src/browser/network-key.test.js +0 -49
  482. package/dist/src/browser/page.test.js +0 -378
  483. package/dist/src/browser/shape-filter.test.js +0 -101
  484. package/dist/src/browser/shape.test.js +0 -82
  485. package/dist/src/browser/stealth.test.js +0 -134
  486. package/dist/src/browser/target-errors.test.js +0 -94
  487. package/dist/src/browser/target-resolver.test.js +0 -118
  488. package/dist/src/browser/utils.test.js +0 -29
  489. package/dist/src/browser/verify-fixture.test.js +0 -219
  490. package/dist/src/browser.test.js +0 -232
  491. package/dist/src/build-manifest.test.js +0 -341
  492. package/dist/src/capabilityRouting.test.js +0 -63
  493. package/dist/src/cli-argv-preprocess.test.js +0 -209
  494. package/dist/src/cli.test.js +0 -2803
  495. package/dist/src/commanderAdapter.test.js +0 -352
  496. package/dist/src/commands/daemon.test.js +0 -219
  497. package/dist/src/completion.test.js +0 -24
  498. package/dist/src/convention-audit.test.js +0 -247
  499. package/dist/src/daemon.test.js +0 -60
  500. package/dist/src/doctor.test.js +0 -263
  501. package/dist/src/download/article-download.test.js +0 -235
  502. package/dist/src/download/index.test.js +0 -140
  503. package/dist/src/download/media-download.test.js +0 -112
  504. package/dist/src/download/progress.test.js +0 -36
  505. package/dist/src/electron-apps.test.js +0 -49
  506. package/dist/src/engine.test.js +0 -312
  507. package/dist/src/errors.test.js +0 -109
  508. package/dist/src/execution.test.js +0 -623
  509. package/dist/src/extension-manifest-regression.test.js +0 -12
  510. package/dist/src/external.test.js +0 -146
  511. package/dist/src/help.test.js +0 -58
  512. package/dist/src/hooks.test.js +0 -92
  513. package/dist/src/interceptor.test.js +0 -81
  514. package/dist/src/launcher.test.js +0 -138
  515. package/dist/src/node-network.test.js +0 -55
  516. package/dist/src/observation/artifact.test.js +0 -121
  517. package/dist/src/observation/manager.test.js +0 -13
  518. package/dist/src/observation/redaction.test.js +0 -32
  519. package/dist/src/observation/retention.test.js +0 -118
  520. package/dist/src/observation/ring-buffer.test.js +0 -22
  521. package/dist/src/output.test.js +0 -53
  522. package/dist/src/package-exports.test.js +0 -112
  523. package/dist/src/pipeline/executor.test.js +0 -182
  524. package/dist/src/pipeline/steps/download.test.js +0 -102
  525. package/dist/src/pipeline/steps/fetch.test.js +0 -117
  526. package/dist/src/pipeline/template.test.js +0 -175
  527. package/dist/src/pipeline/transform.test.js +0 -133
  528. package/dist/src/plugin-manifest.test.js +0 -179
  529. package/dist/src/plugin-scaffold.test.js +0 -83
  530. package/dist/src/plugin.test.js +0 -1324
  531. package/dist/src/registry.test.js +0 -209
  532. package/dist/src/runtime-detect.test.js +0 -39
  533. package/dist/src/serialization.test.js +0 -78
  534. package/dist/src/snapshotFormatter.test.js +0 -521
  535. package/dist/src/tui.test.js +0 -19
  536. package/dist/src/update-check.test.js +0 -116
  537. package/dist/src/utils.test.js +0 -155
  538. package/dist/src/validate.test.js +0 -90
  539. package/dist/src/weixin-download.test.js +0 -45
@@ -1,235 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import * as os from 'node:os';
3
- import * as path from 'node:path';
4
- import { afterEach, describe, expect, it } from 'vitest';
5
- import { downloadArticle } from './article-download.js';
6
- const tempDirs = [];
7
- afterEach(() => {
8
- for (const dir of tempDirs) {
9
- try {
10
- fs.rmSync(dir, { recursive: true, force: true });
11
- }
12
- catch {
13
- // Ignore cleanup errors in tests.
14
- }
15
- }
16
- tempDirs.length = 0;
17
- });
18
- async function runAndRead(contentHtml, opts = {}) {
19
- const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-article-'));
20
- tempDirs.push(tempDir);
21
- const result = await downloadArticle({
22
- title: 'Test Article',
23
- contentHtml,
24
- }, {
25
- output: tempDir,
26
- downloadImages: false,
27
- ...(opts.cleanSelectors && { cleanSelectors: opts.cleanSelectors }),
28
- });
29
- expect(result[0].status).toBe('success');
30
- return fs.readFileSync(result[0].saved, 'utf8');
31
- }
32
- describe('downloadArticle', () => {
33
- it('returns the saved markdown file path on success', async () => {
34
- const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-article-'));
35
- tempDirs.push(tempDir);
36
- const result = await downloadArticle({
37
- title: 'Test Article',
38
- author: 'Author',
39
- publishTime: '2026-04-20 12:00:00',
40
- sourceUrl: 'https://example.com/article',
41
- contentHtml: '<p>Hello world</p>',
42
- }, {
43
- output: tempDir,
44
- downloadImages: false,
45
- });
46
- expect(result).toHaveLength(1);
47
- expect(result[0].status).toBe('success');
48
- expect(result[0].saved).toMatch(new RegExp(`^${tempDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`));
49
- expect(path.extname(result[0].saved)).toBe('.md');
50
- expect(fs.existsSync(result[0].saved)).toBe(true);
51
- expect(fs.readFileSync(result[0].saved, 'utf8')).toContain('Hello world');
52
- });
53
- describe('markdown pipeline', () => {
54
- it('converts GFM tables', async () => {
55
- const md = await runAndRead('<table><thead><tr><th>a</th><th>b</th></tr></thead>' +
56
- '<tbody><tr><td>1</td><td>2</td></tr></tbody></table>');
57
- expect(md).toMatch(/\|\s*a\s*\|\s*b\s*\|/);
58
- expect(md).toMatch(/\|\s*---\s*\|\s*---\s*\|/);
59
- expect(md).toMatch(/\|\s*1\s*\|\s*2\s*\|/);
60
- });
61
- it('converts strikethrough and task lists', async () => {
62
- const md = await runAndRead('<p><del>gone</del></p>' +
63
- '<ul><li><input type="checkbox" checked>done</li><li><input type="checkbox">todo</li></ul>');
64
- expect(md).toContain('~~gone~~');
65
- expect(md).toContain('[x] done');
66
- expect(md).toContain('[ ] todo');
67
- });
68
- it('strips script / style / noscript / form but keeps iframe as a link', async () => {
69
- const md = await runAndRead('<p>keep</p>' +
70
- '<script>alert(1)</script>' +
71
- '<style>.x{color:red}</style>' +
72
- '<noscript>nojs</noscript>' +
73
- '<iframe src="https://www.youtube.com/embed/abc" title="Demo video"></iframe>' +
74
- '<form><button>click</button></form>');
75
- expect(md).toContain('keep');
76
- expect(md).not.toContain('alert');
77
- expect(md).not.toContain('color:red');
78
- expect(md).not.toContain('nojs');
79
- expect(md).not.toContain('click');
80
- // Iframe degrades to a link preserving the embedded URL.
81
- expect(md).toContain('[Demo video](https://www.youtube.com/embed/abc)');
82
- });
83
- it('strips SVG nodes entirely', async () => {
84
- const md = await runAndRead('<p>before</p><svg><circle cx="5" cy="5" r="4"/></svg><p>after</p>');
85
- expect(md).toContain('before');
86
- expect(md).toContain('after');
87
- expect(md).not.toContain('svg');
88
- expect(md).not.toContain('circle');
89
- });
90
- it('drops base64 data URI images but keeps regular images', async () => {
91
- const md = await runAndRead('<p><img alt="inline" src="data:image/png;base64,iVBORw0KGgo="></p>' +
92
- '<p><img alt="keep" src="https://example.com/a.jpg"></p>');
93
- expect(md).not.toContain('data:image');
94
- expect(md).toContain('![keep](https://example.com/a.jpg)');
95
- });
96
- it('collapses 3+ blank lines and strips lone bullet / middle-dot residue', async () => {
97
- const md = await runAndRead('<p>top</p>' +
98
- '<p>-</p>' +
99
- '<p>·</p>' +
100
- '<p>bottom</p>');
101
- expect(md).not.toMatch(/\n{3,}/);
102
- expect(md).not.toMatch(/^\s*-\s*$/m);
103
- expect(md).not.toMatch(/^\s*·\s*$/m);
104
- expect(md).toContain('top');
105
- expect(md).toContain('bottom');
106
- });
107
- it('strips page chrome (header / footer / nav / aside)', async () => {
108
- const md = await runAndRead('<header><p>page-header-text</p></header>' +
109
- '<nav><a href="/">home-link</a></nav>' +
110
- '<p>article-body</p>' +
111
- '<aside><p>sidebar-text</p></aside>' +
112
- '<footer><p>page-footer-text</p></footer>');
113
- expect(md).toContain('article-body');
114
- expect(md).not.toContain('page-header-text');
115
- expect(md).not.toContain('home-link');
116
- expect(md).not.toContain('sidebar-text');
117
- expect(md).not.toContain('page-footer-text');
118
- });
119
- it('cleanSelectors removes matching nodes before conversion', async () => {
120
- const md = await runAndRead('<p>keep-me</p>' +
121
- '<div class="vote-card">折叠卡</div>' +
122
- '<section class="reward-panel">赞赏栏</section>' +
123
- '<p>also-keep</p>', { cleanSelectors: ['.vote-card', '.reward-panel'] });
124
- expect(md).toContain('keep-me');
125
- expect(md).toContain('also-keep');
126
- expect(md).not.toContain('折叠卡');
127
- expect(md).not.toContain('赞赏栏');
128
- });
129
- it('cleanSelectors silently ignores invalid selectors', async () => {
130
- const md = await runAndRead('<p>survives</p><div class="x">and-this-too</div>', { cleanSelectors: ['!!!not-a-valid-selector', '.missing'] });
131
- expect(md).toContain('survives');
132
- expect(md).toContain('and-this-too');
133
- });
134
- it('cleanSelectors keeps valid selectors active when one selector is invalid', async () => {
135
- const md = await runAndRead('<p>keep</p><div class="vote-card">strip-me</div><p>also-keep</p>', { cleanSelectors: ['!!!not-a-valid-selector', '.vote-card'] });
136
- expect(md).toContain('keep');
137
- expect(md).toContain('also-keep');
138
- expect(md).not.toContain('strip-me');
139
- });
140
- it('preserves <video> as inline HTML with src + poster', async () => {
141
- const md = await runAndRead('<p>before</p>' +
142
- '<video src="https://cdn.example.com/clip.mp4" poster="https://cdn.example.com/poster.jpg"></video>' +
143
- '<p>after</p>');
144
- expect(md).toContain('<video src="https://cdn.example.com/clip.mp4" controls poster="https://cdn.example.com/poster.jpg"></video>');
145
- expect(md).toContain('before');
146
- expect(md).toContain('after');
147
- });
148
- it('falls back to <source> inside <video> when src attribute is absent', async () => {
149
- const md = await runAndRead('<video><source src="https://cdn.example.com/clip.mp4" type="video/mp4"></video>');
150
- expect(md).toContain('<video src="https://cdn.example.com/clip.mp4" controls></video>');
151
- });
152
- it('drops <video> with no src and no <source>', async () => {
153
- const md = await runAndRead('<p>before</p><video></video><p>after</p>');
154
- expect(md).not.toContain('<video');
155
- expect(md).toContain('before');
156
- expect(md).toContain('after');
157
- });
158
- it('preserves <audio> as inline HTML', async () => {
159
- const md = await runAndRead('<audio src="https://cdn.example.com/podcast.mp3"></audio>');
160
- expect(md).toContain('<audio src="https://cdn.example.com/podcast.mp3" controls></audio>');
161
- });
162
- it('degrades <iframe> to a markdown link with title', async () => {
163
- const md = await runAndRead('<iframe src="https://codepen.io/pen/abc" title="Live demo"></iframe>');
164
- expect(md).toContain('[Live demo](https://codepen.io/pen/abc)');
165
- });
166
- it('defaults iframe title to "Embedded content" when missing', async () => {
167
- const md = await runAndRead('<iframe src="https://example.com/embed"></iframe>');
168
- expect(md).toContain('[Embedded content](https://example.com/embed)');
169
- });
170
- it('drops <iframe> with no src', async () => {
171
- const md = await runAndRead('<p>before</p><iframe></iframe><p>after</p>');
172
- expect(md).not.toContain('iframe');
173
- expect(md).toContain('before');
174
- expect(md).toContain('after');
175
- });
176
- });
177
- describe('stdout mode', () => {
178
- it('writes markdown to process.stdout and skips file write', async () => {
179
- const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-article-'));
180
- tempDirs.push(tempDir);
181
- const chunks = [];
182
- const originalWrite = process.stdout.write.bind(process.stdout);
183
- process.stdout.write = ((chunk) => {
184
- chunks.push(typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8'));
185
- return true;
186
- });
187
- try {
188
- const result = await downloadArticle({
189
- title: 'Piped',
190
- contentHtml: '<p>Streaming body</p>',
191
- sourceUrl: 'https://example.com/a',
192
- }, {
193
- output: tempDir,
194
- stdout: true,
195
- });
196
- expect(result[0].status).toBe('success');
197
- expect(result[0].saved).toBe('-');
198
- expect(fs.readdirSync(tempDir)).toHaveLength(0);
199
- const emitted = chunks.join('');
200
- expect(emitted).toContain('# Piped');
201
- expect(emitted).toContain('Streaming body');
202
- expect(emitted.endsWith('\n')).toBe(true);
203
- }
204
- finally {
205
- process.stdout.write = originalWrite;
206
- }
207
- });
208
- it('keeps remote image URLs intact in stdout mode (no download)', async () => {
209
- const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-article-'));
210
- tempDirs.push(tempDir);
211
- const chunks = [];
212
- const originalWrite = process.stdout.write.bind(process.stdout);
213
- process.stdout.write = ((chunk) => {
214
- chunks.push(typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8'));
215
- return true;
216
- });
217
- try {
218
- await downloadArticle({
219
- title: 'WithImage',
220
- contentHtml: '<p><img src="https://example.com/a.jpg"></p>',
221
- imageUrls: ['https://example.com/a.jpg'],
222
- }, {
223
- output: tempDir,
224
- downloadImages: true,
225
- stdout: true,
226
- });
227
- expect(fs.readdirSync(tempDir)).toHaveLength(0);
228
- expect(chunks.join('')).toContain('https://example.com/a.jpg');
229
- }
230
- finally {
231
- process.stdout.write = originalWrite;
232
- }
233
- });
234
- });
235
- });
@@ -1,140 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import * as http from 'node:http';
3
- import * as os from 'node:os';
4
- import * as path from 'node:path';
5
- import { afterEach, describe, expect, it, vi } from 'vitest';
6
- import { exportCookiesToNetscape, formatCookieHeader, httpDownload, resolveRedirectUrl } from './index.js';
7
- const servers = [];
8
- const tempDirs = [];
9
- afterEach(async () => {
10
- vi.unstubAllEnvs();
11
- await Promise.all(servers.map((server) => new Promise((resolve, reject) => {
12
- server.close((err) => (err ? reject(err) : resolve()));
13
- })));
14
- servers.length = 0;
15
- for (const dir of tempDirs) {
16
- try {
17
- fs.rmSync(dir, { recursive: true, force: true });
18
- }
19
- catch { /* ignore */ }
20
- }
21
- tempDirs.length = 0;
22
- });
23
- async function startServer(handler, hostname = '127.0.0.1') {
24
- const server = http.createServer(handler);
25
- servers.push(server);
26
- await new Promise((resolve) => server.listen(0, hostname, resolve));
27
- const address = server.address();
28
- if (!address || typeof address === 'string') {
29
- throw new Error('Failed to start test server');
30
- }
31
- return `http://${hostname}:${address.port}`;
32
- }
33
- // Windows Defender can briefly lock newly-written .tmp files, causing EPERM.
34
- // Retry once to handle this flakiness.
35
- describe('download helpers', { retry: process.platform === 'win32' ? 2 : 0 }, () => {
36
- it('resolves relative redirects against the original URL', () => {
37
- expect(resolveRedirectUrl('https://example.com/a/file', '/cdn/file.bin')).toBe('https://example.com/cdn/file.bin');
38
- expect(resolveRedirectUrl('https://example.com/a/file', '../next')).toBe('https://example.com/next');
39
- });
40
- it('formats browser cookies into a Cookie header', () => {
41
- expect(formatCookieHeader([
42
- { name: 'sid', value: 'abc', domain: 'example.com' },
43
- { name: 'ct0', value: 'def', domain: 'example.com' },
44
- ])).toBe('sid=abc; ct0=def');
45
- });
46
- it('fails after exceeding the redirect limit', async () => {
47
- const baseUrl = await startServer((_req, res) => {
48
- res.statusCode = 302;
49
- res.setHeader('Location', '/loop');
50
- res.end();
51
- });
52
- const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-dl-'));
53
- tempDirs.push(tempDir);
54
- const destPath = path.join(tempDir, 'file.txt');
55
- const result = await httpDownload(`${baseUrl}/loop`, destPath, { maxRedirects: 2 });
56
- expect(result).toEqual({
57
- success: false,
58
- size: 0,
59
- error: 'Too many redirects (> 2)',
60
- });
61
- expect(fs.existsSync(destPath)).toBe(false);
62
- });
63
- it('does not forward cookies across cross-domain redirects', async () => {
64
- let forwardedCookie;
65
- const targetUrl = await startServer((req, res) => {
66
- forwardedCookie = req.headers.cookie;
67
- res.statusCode = 200;
68
- res.end('ok');
69
- }, 'localhost');
70
- const redirectUrl = await startServer((_req, res) => {
71
- res.statusCode = 302;
72
- res.setHeader('Location', targetUrl);
73
- res.end();
74
- });
75
- const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-dl-'));
76
- tempDirs.push(tempDir);
77
- const destPath = path.join(tempDir, 'redirect.txt');
78
- const result = await httpDownload(`${redirectUrl}/start`, destPath, { cookies: 'sid=abc' });
79
- expect(result).toEqual({ success: true, size: 2 });
80
- expect(forwardedCookie).toBeUndefined();
81
- expect(fs.readFileSync(destPath, 'utf8')).toBe('ok');
82
- });
83
- it('does not forward cookie headers across cross-domain redirects', async () => {
84
- let forwardedCookie;
85
- const targetUrl = await startServer((req, res) => {
86
- forwardedCookie = req.headers.cookie;
87
- res.statusCode = 200;
88
- res.end('ok');
89
- }, 'localhost');
90
- const redirectUrl = await startServer((_req, res) => {
91
- res.statusCode = 302;
92
- res.setHeader('Location', targetUrl);
93
- res.end();
94
- });
95
- const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-dl-'));
96
- tempDirs.push(tempDir);
97
- const destPath = path.join(tempDir, 'redirect-header.txt');
98
- const result = await httpDownload(`${redirectUrl}/start`, destPath, {
99
- headers: { Cookie: 'sid=header-cookie' },
100
- });
101
- expect(result).toEqual({ success: true, size: 2 });
102
- expect(forwardedCookie).toBeUndefined();
103
- expect(fs.readFileSync(destPath, 'utf8')).toBe('ok');
104
- });
105
- it('bypasses proxy settings for loopback downloads', async () => {
106
- vi.stubEnv('HTTP_PROXY', 'http://127.0.0.1:9');
107
- const baseUrl = await startServer((_req, res) => {
108
- res.statusCode = 200;
109
- res.end('ok');
110
- });
111
- const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-dl-'));
112
- tempDirs.push(tempDir);
113
- const destPath = path.join(tempDir, 'loopback.txt');
114
- const result = await httpDownload(`${baseUrl}/ok`, destPath);
115
- expect(result).toEqual({ success: true, size: 2 });
116
- expect(fs.readFileSync(destPath, 'utf8')).toBe('ok');
117
- });
118
- it.skipIf(process.platform === 'win32')('writes the Netscape cookie file with 0o600 owner-only permissions', async () => {
119
- const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-dl-'));
120
- tempDirs.push(tempDir);
121
- const cookiesPath = path.join(tempDir, 'cookies.txt');
122
- exportCookiesToNetscape([
123
- { name: 'sid', value: 'secret', domain: 'example.com' },
124
- ], cookiesPath);
125
- const mode = fs.statSync(cookiesPath).mode & 0o777;
126
- expect(mode).toBe(0o600);
127
- });
128
- it.skipIf(process.platform === 'win32')('tightens an existing Netscape cookie file before rewriting it', async () => {
129
- const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-dl-'));
130
- tempDirs.push(tempDir);
131
- const cookiesPath = path.join(tempDir, 'cookies.txt');
132
- fs.writeFileSync(cookiesPath, 'stale-cookie', { mode: 0o644 });
133
- exportCookiesToNetscape([
134
- { name: 'sid', value: 'secret', domain: 'example.com' },
135
- ], cookiesPath);
136
- const mode = fs.statSync(cookiesPath).mode & 0o777;
137
- expect(mode).toBe(0o600);
138
- expect(fs.readFileSync(cookiesPath, 'utf8')).toContain('sid\tsecret');
139
- });
140
- });
@@ -1,112 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import * as http from 'node:http';
3
- import * as os from 'node:os';
4
- import * as path from 'node:path';
5
- import { afterEach, describe, expect, it } from 'vitest';
6
- import { downloadMedia } from './media-download.js';
7
- const servers = [];
8
- const tempDirs = [];
9
- afterEach(async () => {
10
- await Promise.all(servers.map((server) => new Promise((resolve, reject) => {
11
- server.close((err) => (err ? reject(err) : resolve()));
12
- })));
13
- servers.length = 0;
14
- for (const dir of tempDirs) {
15
- try {
16
- fs.rmSync(dir, { recursive: true, force: true });
17
- }
18
- catch { /* ignore */ }
19
- }
20
- tempDirs.length = 0;
21
- });
22
- async function startServer(handler) {
23
- const server = http.createServer(handler);
24
- servers.push(server);
25
- await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
26
- const address = server.address();
27
- if (!address || typeof address === 'string') {
28
- throw new Error('Failed to start test server');
29
- }
30
- return `http://127.0.0.1:${address.port}`;
31
- }
32
- // Windows runners occasionally exceed the default 5s timeout on the first
33
- // http.createServer + downloadMedia roundtrip (cold-start cost on a loaded VM).
34
- describe('media downloads', { retry: process.platform === 'win32' ? 2 : 0 }, () => {
35
- it('keeps custom filenames inside the output directory', async () => {
36
- const baseUrl = await startServer((_req, res) => {
37
- res.statusCode = 200;
38
- res.end('image');
39
- });
40
- const parentDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-media-parent-'));
41
- tempDirs.push(parentDir);
42
- const outputDir = path.join(parentDir, 'downloads');
43
- const results = await downloadMedia([
44
- { type: 'image', url: `${baseUrl}/image.jpg`, filename: '../escape.jpg' },
45
- ], {
46
- output: outputDir,
47
- verbose: false,
48
- });
49
- expect(results).toEqual([
50
- { index: 1, type: 'image', status: 'success', size: '5.0 B' },
51
- ]);
52
- expect(fs.readFileSync(path.join(outputDir, 'escape.jpg'), 'utf8')).toBe('image');
53
- expect(fs.existsSync(path.join(parentDir, 'escape.jpg'))).toBe(false);
54
- });
55
- it('strips nested and absolute path components from custom filenames', async () => {
56
- const baseUrl = await startServer((_req, res) => {
57
- res.statusCode = 200;
58
- res.end('image');
59
- });
60
- const parentDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-media-parent-'));
61
- tempDirs.push(parentDir);
62
- const outputDir = path.join(parentDir, 'downloads');
63
- const results = await downloadMedia([
64
- { type: 'image', url: `${baseUrl}/one.jpg`, filename: '../../etc/passwd' },
65
- { type: 'image', url: `${baseUrl}/two.jpg`, filename: '/tmp/evil.jpg' },
66
- { type: 'image', url: `${baseUrl}/three.jpg`, filename: 'nested/path/photo.png' },
67
- { type: 'image', url: `${baseUrl}/four.jpg`, filename: '..\\windows\\escape.jpg' },
68
- ], {
69
- output: outputDir,
70
- verbose: false,
71
- });
72
- expect(results).toEqual([
73
- { index: 1, type: 'image', status: 'success', size: '5.0 B' },
74
- { index: 2, type: 'image', status: 'success', size: '5.0 B' },
75
- { index: 3, type: 'image', status: 'success', size: '5.0 B' },
76
- { index: 4, type: 'image', status: 'success', size: '5.0 B' },
77
- ]);
78
- expect(fs.readFileSync(path.join(outputDir, 'passwd'), 'utf8')).toBe('image');
79
- expect(fs.readFileSync(path.join(outputDir, 'evil.jpg'), 'utf8')).toBe('image');
80
- expect(fs.readFileSync(path.join(outputDir, 'photo.png'), 'utf8')).toBe('image');
81
- expect(fs.readFileSync(path.join(outputDir, 'escape.jpg'), 'utf8')).toBe('image');
82
- expect(fs.existsSync(path.join(parentDir, 'etc', 'passwd'))).toBe(false);
83
- expect(fs.existsSync(path.join(parentDir, 'tmp', 'evil.jpg'))).toBe(false);
84
- });
85
- it('falls back to generated names for empty dot-directory filenames', async () => {
86
- const baseUrl = await startServer((_req, res) => {
87
- res.statusCode = 200;
88
- res.end('image');
89
- });
90
- const parentDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'bycli-media-parent-'));
91
- tempDirs.push(parentDir);
92
- const outputDir = path.join(parentDir, 'downloads');
93
- const results = await downloadMedia([
94
- { type: 'image', url: `${baseUrl}/dot.jpg`, filename: '.' },
95
- { type: 'image', url: `${baseUrl}/dotdot.jpg`, filename: '..' },
96
- { type: 'image', url: `${baseUrl}/blank.jpg`, filename: ' ' },
97
- ], {
98
- output: outputDir,
99
- filenamePrefix: '../unsafe prefix',
100
- verbose: false,
101
- });
102
- expect(results).toEqual([
103
- { index: 1, type: 'image', status: 'success', size: '5.0 B' },
104
- { index: 2, type: 'image', status: 'success', size: '5.0 B' },
105
- { index: 3, type: 'image', status: 'success', size: '5.0 B' },
106
- ]);
107
- expect(fs.readFileSync(path.join(outputDir, 'unsafe_prefix_1.jpg'), 'utf8')).toBe('image');
108
- expect(fs.readFileSync(path.join(outputDir, 'unsafe_prefix_2.jpg'), 'utf8')).toBe('image');
109
- expect(fs.readFileSync(path.join(outputDir, 'unsafe_prefix_3.jpg'), 'utf8')).toBe('image');
110
- expect(fs.existsSync(path.join(parentDir, 'unsafe prefix_1.jpg'))).toBe(false);
111
- });
112
- });
@@ -1,36 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
- import { createProgressBar, formatBytes } from './progress.js';
3
- describe('formatBytes', () => {
4
- it('returns a stable zero-byte label for invalid or sub-byte values', () => {
5
- expect(formatBytes(-1)).toBe('0 B');
6
- expect(formatBytes(0.5)).toBe('0 B');
7
- expect(formatBytes(Number.NaN)).toBe('0 B');
8
- expect(formatBytes(Number.POSITIVE_INFINITY)).toBe('0 B');
9
- });
10
- it('caps very large values at the largest supported unit', () => {
11
- expect(formatBytes(1024 ** 5)).toBe('1024.0 TB');
12
- });
13
- });
14
- describe('download progress display', () => {
15
- afterEach(() => {
16
- vi.restoreAllMocks();
17
- });
18
- it('clamps percentages above 100 to keep the progress bar renderable', () => {
19
- const write = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
20
- const progress = createProgressBar('file.bin', 0, 1);
21
- expect(() => progress.update(150, 100)).not.toThrow();
22
- expect(write).toHaveBeenCalledWith(expect.stringContaining('100%'));
23
- });
24
- it('clamps negative percentages to zero', () => {
25
- const write = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
26
- const progress = createProgressBar('file.bin', 0, 1);
27
- expect(() => progress.update(-10, 100)).not.toThrow();
28
- expect(write).toHaveBeenCalledWith(expect.stringContaining('0%'));
29
- });
30
- it('renders zero percent when the total size is unknown', () => {
31
- const write = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
32
- const progress = createProgressBar('file.bin', 0, 1);
33
- expect(() => progress.update(50, 0)).not.toThrow();
34
- expect(write).toHaveBeenCalledWith(expect.stringContaining('0%'));
35
- });
36
- });
@@ -1,49 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { builtinApps, getElectronApp, isElectronApp, loadApps } from './electron-apps.js';
3
- describe('electron-apps registry', () => {
4
- it('returns builtin app entry for cursor', () => {
5
- const app = getElectronApp('cursor');
6
- expect(app).toBeDefined();
7
- expect(app.port).toBe(9226);
8
- expect(app.processName).toBe('Cursor');
9
- });
10
- it('returns builtin app entry for codex', () => {
11
- const app = getElectronApp('codex');
12
- expect(app).toBeDefined();
13
- expect(app.port).toBe(9238);
14
- });
15
- it('keeps builtin Electron app CDP ports unique and off the browser-bridge port', () => {
16
- const ports = Object.values(builtinApps).map((app) => app.port);
17
- expect(new Set(ports).size).toBe(ports.length);
18
- expect(ports).not.toContain(9222);
19
- });
20
- it('returns undefined for non-Electron sites', () => {
21
- expect(getElectronApp('bilibili')).toBeUndefined();
22
- expect(getElectronApp('hackernews')).toBeUndefined();
23
- });
24
- it('isElectronApp returns true for registered apps', () => {
25
- expect(isElectronApp('cursor')).toBe(true);
26
- expect(isElectronApp('codex')).toBe(true);
27
- expect(isElectronApp('chatwise')).toBe(true);
28
- });
29
- it('isElectronApp returns false for non-Electron sites', () => {
30
- expect(isElectronApp('bilibili')).toBe(false);
31
- expect(isElectronApp('notion')).toBe(false);
32
- expect(isElectronApp('unknown-app')).toBe(false);
33
- });
34
- it('loadApps merges user config additively', () => {
35
- const apps = loadApps({
36
- myapp: { port: 9234, processName: 'MyApp' },
37
- });
38
- expect(apps.myapp).toBeDefined();
39
- expect(apps.myapp.port).toBe(9234);
40
- // Builtins still present
41
- expect(apps.cursor).toBeDefined();
42
- });
43
- it('loadApps does not override builtin entries', () => {
44
- const apps = loadApps({
45
- cursor: { port: 9999, processName: 'FakeCursor' },
46
- });
47
- expect(apps.cursor.port).toBe(9226); // Builtin wins
48
- });
49
- });