@agentrhq/webcmd 0.1.1 → 0.1.2

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 (415) hide show
  1. package/README.md +317 -24
  2. package/dist/src/adapter-shadow.d.ts +11 -0
  3. package/dist/src/adapter-shadow.js +72 -0
  4. package/dist/src/adapter-shadow.test.d.ts +1 -0
  5. package/dist/src/adapter-shadow.test.js +49 -0
  6. package/dist/src/adapter-source.d.ts +11 -0
  7. package/dist/src/adapter-source.js +24 -0
  8. package/dist/src/adapter-source.test.d.ts +1 -0
  9. package/dist/src/adapter-source.test.js +29 -0
  10. package/dist/src/brand.d.ts +9 -0
  11. package/dist/src/brand.js +9 -0
  12. package/dist/src/brand.test.d.ts +1 -0
  13. package/dist/src/brand.test.js +15 -0
  14. package/dist/src/browser/analyze.d.ts +118 -0
  15. package/dist/src/browser/analyze.js +405 -0
  16. package/dist/src/browser/analyze.test.d.ts +1 -0
  17. package/dist/src/browser/analyze.test.js +209 -0
  18. package/dist/src/browser/article-extract.d.ts +57 -0
  19. package/dist/src/browser/article-extract.e2e.test.d.ts +1 -0
  20. package/dist/src/browser/article-extract.e2e.test.js +105 -0
  21. package/dist/src/browser/article-extract.js +169 -0
  22. package/dist/src/browser/article-extract.test.d.ts +1 -0
  23. package/dist/src/browser/article-extract.test.js +94 -0
  24. package/dist/src/browser/ax-snapshot.d.ts +37 -0
  25. package/dist/src/browser/ax-snapshot.js +217 -0
  26. package/dist/src/browser/ax-snapshot.test.d.ts +1 -0
  27. package/dist/src/browser/ax-snapshot.test.js +91 -0
  28. package/dist/src/browser/base-page.d.ts +151 -0
  29. package/dist/src/browser/base-page.js +1060 -0
  30. package/dist/src/browser/base-page.test.d.ts +1 -0
  31. package/dist/src/browser/base-page.test.js +803 -0
  32. package/dist/src/browser/bridge-readiness.d.ts +14 -0
  33. package/dist/src/browser/bridge-readiness.js +24 -0
  34. package/dist/src/browser/bridge-readiness.test.d.ts +1 -0
  35. package/dist/src/browser/bridge-readiness.test.js +58 -0
  36. package/dist/src/browser/bridge.d.ts +26 -0
  37. package/dist/src/browser/bridge.js +58 -0
  38. package/dist/src/browser/cdp-click-fixture.test.d.ts +1 -0
  39. package/dist/src/browser/cdp-click-fixture.test.js +87 -0
  40. package/dist/src/browser/cdp.d.ts +46 -0
  41. package/dist/src/browser/cdp.js +536 -0
  42. package/dist/src/browser/cdp.test.d.ts +1 -0
  43. package/dist/src/browser/cdp.test.js +79 -0
  44. package/dist/src/browser/compound.d.ts +59 -0
  45. package/dist/src/browser/compound.js +112 -0
  46. package/dist/src/browser/compound.test.d.ts +1 -0
  47. package/dist/src/browser/compound.test.js +175 -0
  48. package/dist/src/browser/config.d.ts +2 -0
  49. package/dist/src/browser/config.js +14 -0
  50. package/dist/src/browser/daemon-client.d.ts +32 -0
  51. package/dist/src/browser/daemon-client.js +135 -0
  52. package/dist/src/browser/daemon-client.test.d.ts +1 -0
  53. package/dist/src/browser/daemon-client.test.js +326 -0
  54. package/dist/src/browser/daemon-lifecycle.d.ts +37 -0
  55. package/dist/src/browser/daemon-lifecycle.js +147 -0
  56. package/dist/src/browser/daemon-transport.d.ts +54 -0
  57. package/dist/src/browser/daemon-transport.js +68 -0
  58. package/dist/src/browser/daemon-version.d.ts +4 -0
  59. package/dist/src/browser/daemon-version.js +12 -0
  60. package/dist/src/browser/dom-helpers.d.ts +41 -0
  61. package/dist/src/browser/dom-helpers.js +239 -0
  62. package/dist/src/browser/dom-helpers.test.d.ts +1 -0
  63. package/dist/src/browser/dom-helpers.test.js +106 -0
  64. package/dist/src/browser/dom-snapshot.d.ts +93 -0
  65. package/dist/src/browser/dom-snapshot.js +894 -0
  66. package/dist/src/browser/dom-snapshot.test.d.ts +11 -0
  67. package/dist/src/browser/dom-snapshot.test.js +313 -0
  68. package/dist/src/browser/errors.d.ts +38 -0
  69. package/dist/src/browser/errors.js +79 -0
  70. package/dist/src/browser/errors.test.d.ts +1 -0
  71. package/dist/src/browser/errors.test.js +60 -0
  72. package/dist/src/browser/extract.d.ts +69 -0
  73. package/dist/src/browser/extract.js +132 -0
  74. package/dist/src/browser/extract.test.d.ts +1 -0
  75. package/dist/src/browser/extract.test.js +129 -0
  76. package/dist/src/browser/find.d.ts +84 -0
  77. package/dist/src/browser/find.js +398 -0
  78. package/dist/src/browser/find.test.d.ts +1 -0
  79. package/dist/src/browser/find.test.js +180 -0
  80. package/dist/src/browser/html-tree.d.ts +75 -0
  81. package/dist/src/browser/html-tree.js +112 -0
  82. package/dist/src/browser/html-tree.test.d.ts +1 -0
  83. package/dist/src/browser/html-tree.test.js +181 -0
  84. package/dist/src/browser/index.d.ts +14 -0
  85. package/dist/src/browser/index.js +12 -0
  86. package/dist/src/browser/network-cache.d.ts +49 -0
  87. package/dist/src/browser/network-cache.js +78 -0
  88. package/dist/src/browser/network-cache.test.d.ts +1 -0
  89. package/dist/src/browser/network-cache.test.js +75 -0
  90. package/dist/src/browser/network-interceptor.d.ts +11 -0
  91. package/dist/src/browser/network-interceptor.js +11 -0
  92. package/dist/src/browser/network-key.d.ts +22 -0
  93. package/dist/src/browser/network-key.js +66 -0
  94. package/dist/src/browser/network-key.test.d.ts +1 -0
  95. package/dist/src/browser/network-key.test.js +49 -0
  96. package/dist/src/browser/page.d.ts +83 -0
  97. package/dist/src/browser/page.js +453 -0
  98. package/dist/src/browser/page.test.d.ts +1 -0
  99. package/dist/src/browser/page.test.js +406 -0
  100. package/dist/src/browser/profile.d.ts +14 -0
  101. package/dist/src/browser/profile.js +86 -0
  102. package/dist/src/browser/protocol.d.ts +63 -0
  103. package/dist/src/browser/protocol.js +1 -0
  104. package/dist/src/browser/runtime/local-cloak/actions.d.ts +3 -0
  105. package/dist/src/browser/runtime/local-cloak/actions.js +240 -0
  106. package/dist/src/browser/runtime/local-cloak/downloads.d.ts +3 -0
  107. package/dist/src/browser/runtime/local-cloak/downloads.js +28 -0
  108. package/dist/src/browser/runtime/local-cloak/network.d.ts +28 -0
  109. package/dist/src/browser/runtime/local-cloak/network.js +120 -0
  110. package/dist/src/browser/runtime/local-cloak/network.test.d.ts +1 -0
  111. package/dist/src/browser/runtime/local-cloak/network.test.js +124 -0
  112. package/dist/src/browser/runtime/local-cloak/profiles.d.ts +6 -0
  113. package/dist/src/browser/runtime/local-cloak/profiles.js +17 -0
  114. package/dist/src/browser/runtime/local-cloak/profiles.test.d.ts +1 -0
  115. package/dist/src/browser/runtime/local-cloak/profiles.test.js +19 -0
  116. package/dist/src/browser/runtime/local-cloak/provider.d.ts +15 -0
  117. package/dist/src/browser/runtime/local-cloak/provider.js +27 -0
  118. package/dist/src/browser/runtime/local-cloak/provider.test.d.ts +1 -0
  119. package/dist/src/browser/runtime/local-cloak/provider.test.js +244 -0
  120. package/dist/src/browser/runtime/local-cloak/session-manager.d.ts +77 -0
  121. package/dist/src/browser/runtime/local-cloak/session-manager.js +280 -0
  122. package/dist/src/browser/runtime/local-cloak/session-manager.test.d.ts +1 -0
  123. package/dist/src/browser/runtime/local-cloak/session-manager.test.js +92 -0
  124. package/dist/src/browser/runtime/provider.d.ts +9 -0
  125. package/dist/src/browser/runtime/provider.js +1 -0
  126. package/dist/src/browser/runtime/provider.test.d.ts +2 -0
  127. package/dist/src/browser/runtime/provider.test.js +49 -0
  128. package/dist/src/browser/shape-filter.d.ts +52 -0
  129. package/dist/src/browser/shape-filter.js +101 -0
  130. package/dist/src/browser/shape-filter.test.d.ts +1 -0
  131. package/dist/src/browser/shape-filter.test.js +101 -0
  132. package/dist/src/browser/shape.d.ts +23 -0
  133. package/dist/src/browser/shape.js +95 -0
  134. package/dist/src/browser/shape.test.d.ts +1 -0
  135. package/dist/src/browser/shape.test.js +82 -0
  136. package/dist/src/browser/stealth.d.ts +11 -0
  137. package/dist/src/browser/stealth.js +359 -0
  138. package/dist/src/browser/stealth.test.d.ts +1 -0
  139. package/dist/src/browser/stealth.test.js +134 -0
  140. package/dist/src/browser/tabs.d.ts +13 -0
  141. package/dist/src/browser/tabs.js +70 -0
  142. package/dist/src/browser/target-errors.d.ts +39 -0
  143. package/dist/src/browser/target-errors.js +45 -0
  144. package/dist/src/browser/target-errors.test.d.ts +1 -0
  145. package/dist/src/browser/target-errors.test.js +94 -0
  146. package/dist/src/browser/target-resolver.d.ts +158 -0
  147. package/dist/src/browser/target-resolver.js +570 -0
  148. package/dist/src/browser/target-resolver.test.d.ts +1 -0
  149. package/dist/src/browser/target-resolver.test.js +118 -0
  150. package/dist/src/browser/utils.d.ts +20 -0
  151. package/dist/src/browser/utils.js +64 -0
  152. package/dist/src/browser/utils.test.d.ts +1 -0
  153. package/dist/src/browser/utils.test.js +29 -0
  154. package/dist/src/browser/verify-fixture.d.ts +65 -0
  155. package/dist/src/browser/verify-fixture.js +318 -0
  156. package/dist/src/browser/verify-fixture.test.d.ts +1 -0
  157. package/dist/src/browser/verify-fixture.test.js +219 -0
  158. package/dist/src/browser/visual-refs.d.ts +11 -0
  159. package/dist/src/browser/visual-refs.js +108 -0
  160. package/dist/src/browser.test.d.ts +1 -0
  161. package/dist/src/browser.test.js +297 -0
  162. package/dist/src/build-manifest.d.ts +103 -0
  163. package/dist/src/build-manifest.js +363 -0
  164. package/dist/src/build-manifest.test.d.ts +1 -0
  165. package/dist/src/build-manifest.test.js +341 -0
  166. package/dist/src/capabilityRouting.d.ts +19 -0
  167. package/dist/src/capabilityRouting.js +56 -0
  168. package/dist/src/capabilityRouting.test.d.ts +1 -0
  169. package/dist/src/capabilityRouting.test.js +63 -0
  170. package/dist/src/cli-argv-preprocess.d.ts +63 -0
  171. package/dist/src/cli-argv-preprocess.js +303 -0
  172. package/dist/src/cli-argv-preprocess.test.d.ts +1 -0
  173. package/dist/src/cli-argv-preprocess.test.js +304 -0
  174. package/dist/src/cli.d.ts +79 -0
  175. package/dist/src/cli.js +3350 -0
  176. package/dist/src/cli.test.d.ts +1 -0
  177. package/dist/src/cli.test.js +3041 -0
  178. package/dist/src/commanderAdapter.d.ts +21 -0
  179. package/dist/src/commanderAdapter.js +208 -0
  180. package/dist/src/commanderAdapter.test.d.ts +1 -0
  181. package/dist/src/commanderAdapter.test.js +352 -0
  182. package/dist/src/commands/auth.d.ts +40 -0
  183. package/dist/src/commands/auth.js +434 -0
  184. package/dist/src/commands/auth.test.d.ts +1 -0
  185. package/dist/src/commands/auth.test.js +252 -0
  186. package/dist/src/commands/daemon.d.ts +9 -0
  187. package/dist/src/commands/daemon.js +91 -0
  188. package/dist/src/commands/daemon.test.d.ts +1 -0
  189. package/dist/src/commands/daemon.test.js +293 -0
  190. package/dist/src/completion-fast.d.ts +22 -0
  191. package/dist/src/completion-fast.js +88 -0
  192. package/dist/src/completion-shared.d.ts +13 -0
  193. package/dist/src/completion-shared.js +61 -0
  194. package/dist/src/completion.d.ts +21 -0
  195. package/dist/src/completion.js +68 -0
  196. package/dist/src/completion.test.d.ts +1 -0
  197. package/dist/src/completion.test.js +24 -0
  198. package/dist/src/constants.d.ts +18 -0
  199. package/dist/src/constants.js +41 -0
  200. package/dist/src/convention-audit.d.ts +50 -0
  201. package/dist/src/convention-audit.js +553 -0
  202. package/dist/src/convention-audit.test.d.ts +1 -0
  203. package/dist/src/convention-audit.test.js +255 -0
  204. package/dist/src/daemon/server.d.ts +13 -0
  205. package/dist/src/daemon/server.js +185 -0
  206. package/dist/src/daemon/server.test.d.ts +1 -0
  207. package/dist/src/daemon/server.test.js +132 -0
  208. package/dist/src/daemon-utils.d.ts +18 -0
  209. package/dist/src/daemon-utils.js +37 -0
  210. package/dist/src/daemon.d.ts +1 -0
  211. package/dist/src/daemon.js +27 -0
  212. package/dist/src/daemon.test.d.ts +1 -0
  213. package/dist/src/daemon.test.js +60 -0
  214. package/dist/src/discovery.d.ts +46 -0
  215. package/dist/src/discovery.js +341 -0
  216. package/dist/src/doctor.d.ts +39 -0
  217. package/dist/src/doctor.js +168 -0
  218. package/dist/src/doctor.test.d.ts +1 -0
  219. package/dist/src/doctor.test.js +302 -0
  220. package/dist/src/download/article-download.d.ts +72 -0
  221. package/dist/src/download/article-download.js +305 -0
  222. package/dist/src/download/article-download.test.d.ts +1 -0
  223. package/dist/src/download/article-download.test.js +235 -0
  224. package/dist/src/download/index.d.ts +71 -0
  225. package/dist/src/download/index.js +363 -0
  226. package/dist/src/download/index.test.d.ts +1 -0
  227. package/dist/src/download/index.test.js +174 -0
  228. package/dist/src/download/media-download.d.ts +49 -0
  229. package/dist/src/download/media-download.js +127 -0
  230. package/dist/src/download/media-download.test.d.ts +1 -0
  231. package/dist/src/download/media-download.test.js +112 -0
  232. package/dist/src/download/progress.d.ts +36 -0
  233. package/dist/src/download/progress.js +120 -0
  234. package/dist/src/download/progress.test.d.ts +1 -0
  235. package/dist/src/download/progress.test.js +36 -0
  236. package/dist/src/electron-apps.d.ts +31 -0
  237. package/dist/src/electron-apps.js +91 -0
  238. package/dist/src/electron-apps.test.d.ts +1 -0
  239. package/dist/src/electron-apps.test.js +76 -0
  240. package/dist/src/engine.test.d.ts +1 -0
  241. package/dist/src/engine.test.js +312 -0
  242. package/dist/src/errors.d.ts +114 -0
  243. package/dist/src/errors.js +174 -0
  244. package/dist/src/errors.test.d.ts +1 -0
  245. package/dist/src/errors.test.js +109 -0
  246. package/dist/src/execution.d.ts +24 -0
  247. package/dist/src/execution.js +509 -0
  248. package/dist/src/execution.test.d.ts +1 -0
  249. package/dist/src/execution.test.js +645 -0
  250. package/dist/src/external.d.ts +49 -0
  251. package/dist/src/external.js +211 -0
  252. package/dist/src/external.test.d.ts +1 -0
  253. package/dist/src/external.test.js +110 -0
  254. package/dist/src/help.d.ts +88 -0
  255. package/dist/src/help.js +556 -0
  256. package/dist/src/help.test.d.ts +1 -0
  257. package/dist/src/help.test.js +58 -0
  258. package/dist/src/hooks.d.ts +46 -0
  259. package/dist/src/hooks.js +58 -0
  260. package/dist/src/hooks.test.d.ts +4 -0
  261. package/dist/src/hooks.test.js +92 -0
  262. package/dist/src/interceptor.d.ts +44 -0
  263. package/dist/src/interceptor.js +183 -0
  264. package/dist/src/interceptor.test.d.ts +4 -0
  265. package/dist/src/interceptor.test.js +81 -0
  266. package/dist/src/launcher.d.ts +41 -0
  267. package/dist/src/launcher.js +226 -0
  268. package/dist/src/launcher.test.d.ts +1 -0
  269. package/dist/src/launcher.test.js +153 -0
  270. package/dist/src/logger.d.ts +26 -0
  271. package/dist/src/logger.js +49 -0
  272. package/dist/src/main.d.ts +5 -0
  273. package/dist/src/main.js +177 -0
  274. package/dist/src/manifest-types.d.ts +43 -0
  275. package/dist/src/manifest-types.js +9 -0
  276. package/dist/src/node-network.d.ts +10 -0
  277. package/dist/src/node-network.js +174 -0
  278. package/dist/src/node-network.test.d.ts +1 -0
  279. package/dist/src/node-network.test.js +55 -0
  280. package/dist/src/observation/artifact.d.ts +16 -0
  281. package/dist/src/observation/artifact.js +260 -0
  282. package/dist/src/observation/artifact.test.d.ts +1 -0
  283. package/dist/src/observation/artifact.test.js +121 -0
  284. package/dist/src/observation/events.d.ts +89 -0
  285. package/dist/src/observation/events.js +1 -0
  286. package/dist/src/observation/index.d.ts +7 -0
  287. package/dist/src/observation/index.js +7 -0
  288. package/dist/src/observation/manager.d.ts +9 -0
  289. package/dist/src/observation/manager.js +27 -0
  290. package/dist/src/observation/manager.test.d.ts +1 -0
  291. package/dist/src/observation/manager.test.js +13 -0
  292. package/dist/src/observation/redaction.d.ts +11 -0
  293. package/dist/src/observation/redaction.js +81 -0
  294. package/dist/src/observation/redaction.test.d.ts +1 -0
  295. package/dist/src/observation/redaction.test.js +32 -0
  296. package/dist/src/observation/retention.d.ts +32 -0
  297. package/dist/src/observation/retention.js +160 -0
  298. package/dist/src/observation/retention.test.d.ts +1 -0
  299. package/dist/src/observation/retention.test.js +118 -0
  300. package/dist/src/observation/ring-buffer.d.ts +22 -0
  301. package/dist/src/observation/ring-buffer.js +45 -0
  302. package/dist/src/observation/ring-buffer.test.d.ts +1 -0
  303. package/dist/src/observation/ring-buffer.test.js +22 -0
  304. package/dist/src/observation/session.d.ts +25 -0
  305. package/dist/src/observation/session.js +50 -0
  306. package/dist/src/output.d.ts +14 -0
  307. package/dist/src/output.js +152 -0
  308. package/dist/src/output.test.d.ts +1 -0
  309. package/dist/src/output.test.js +53 -0
  310. package/dist/src/package-exports.test.d.ts +1 -0
  311. package/dist/src/package-exports.test.js +112 -0
  312. package/dist/src/package-paths.d.ts +8 -0
  313. package/dist/src/package-paths.js +41 -0
  314. package/dist/src/pipeline/executor.d.ts +11 -0
  315. package/dist/src/pipeline/executor.js +93 -0
  316. package/dist/src/pipeline/executor.test.d.ts +4 -0
  317. package/dist/src/pipeline/executor.test.js +182 -0
  318. package/dist/src/pipeline/index.d.ts +5 -0
  319. package/dist/src/pipeline/index.js +5 -0
  320. package/dist/src/pipeline/registry.d.ts +28 -0
  321. package/dist/src/pipeline/registry.js +55 -0
  322. package/dist/src/pipeline/steps/browser.d.ts +13 -0
  323. package/dist/src/pipeline/steps/browser.js +86 -0
  324. package/dist/src/pipeline/steps/download.d.ts +18 -0
  325. package/dist/src/pipeline/steps/download.js +252 -0
  326. package/dist/src/pipeline/steps/download.test.d.ts +1 -0
  327. package/dist/src/pipeline/steps/download.test.js +102 -0
  328. package/dist/src/pipeline/steps/fetch.d.ts +5 -0
  329. package/dist/src/pipeline/steps/fetch.js +122 -0
  330. package/dist/src/pipeline/steps/fetch.test.d.ts +1 -0
  331. package/dist/src/pipeline/steps/fetch.test.js +117 -0
  332. package/dist/src/pipeline/steps/intercept.d.ts +5 -0
  333. package/dist/src/pipeline/steps/intercept.js +50 -0
  334. package/dist/src/pipeline/steps/tap.d.ts +12 -0
  335. package/dist/src/pipeline/steps/tap.js +93 -0
  336. package/dist/src/pipeline/steps/transform.d.ts +9 -0
  337. package/dist/src/pipeline/steps/transform.js +70 -0
  338. package/dist/src/pipeline/template.d.ts +17 -0
  339. package/dist/src/pipeline/template.js +344 -0
  340. package/dist/src/pipeline/template.test.d.ts +4 -0
  341. package/dist/src/pipeline/template.test.js +197 -0
  342. package/dist/src/pipeline/transform.test.d.ts +4 -0
  343. package/dist/src/pipeline/transform.test.js +133 -0
  344. package/dist/src/plugin-manifest.d.ts +70 -0
  345. package/dist/src/plugin-manifest.js +160 -0
  346. package/dist/src/plugin-manifest.test.d.ts +4 -0
  347. package/dist/src/plugin-manifest.test.js +179 -0
  348. package/dist/src/plugin-scaffold.d.ts +28 -0
  349. package/dist/src/plugin-scaffold.js +143 -0
  350. package/dist/src/plugin-scaffold.test.d.ts +4 -0
  351. package/dist/src/plugin-scaffold.test.js +83 -0
  352. package/dist/src/plugin.d.ts +146 -0
  353. package/dist/src/plugin.js +1303 -0
  354. package/dist/src/plugin.test.d.ts +4 -0
  355. package/dist/src/plugin.test.js +1363 -0
  356. package/dist/src/registry-api.d.ts +13 -0
  357. package/dist/src/registry-api.js +10 -0
  358. package/dist/src/registry.d.ts +123 -0
  359. package/dist/src/registry.js +125 -0
  360. package/dist/src/registry.test.d.ts +4 -0
  361. package/dist/src/registry.test.js +209 -0
  362. package/dist/src/runtime-copy.test.d.ts +1 -0
  363. package/dist/src/runtime-copy.test.js +29 -0
  364. package/dist/src/runtime-detect.d.ts +31 -0
  365. package/dist/src/runtime-detect.js +47 -0
  366. package/dist/src/runtime-detect.test.d.ts +1 -0
  367. package/dist/src/runtime-detect.test.js +39 -0
  368. package/dist/src/runtime-identity.test.d.ts +1 -0
  369. package/dist/src/runtime-identity.test.js +18 -0
  370. package/dist/src/runtime.d.ts +47 -0
  371. package/dist/src/runtime.js +54 -0
  372. package/dist/src/scripts/framework.d.ts +7 -0
  373. package/dist/src/scripts/framework.js +25 -0
  374. package/dist/src/scripts/interact.d.ts +4 -0
  375. package/dist/src/scripts/interact.js +20 -0
  376. package/dist/src/scripts/store.d.ts +13 -0
  377. package/dist/src/scripts/store.js +48 -0
  378. package/dist/src/serialization.d.ts +42 -0
  379. package/dist/src/serialization.js +103 -0
  380. package/dist/src/serialization.test.d.ts +1 -0
  381. package/dist/src/serialization.test.js +78 -0
  382. package/dist/src/skills.d.ts +14 -0
  383. package/dist/src/skills.js +127 -0
  384. package/dist/src/skills.test.d.ts +1 -0
  385. package/dist/src/skills.test.js +71 -0
  386. package/dist/src/snapshotFormatter.d.ts +11 -0
  387. package/dist/src/snapshotFormatter.js +338 -0
  388. package/dist/src/snapshotFormatter.test.d.ts +7 -0
  389. package/dist/src/snapshotFormatter.test.js +521 -0
  390. package/dist/src/tui.d.ts +28 -0
  391. package/dist/src/tui.js +179 -0
  392. package/dist/src/tui.test.d.ts +1 -0
  393. package/dist/src/tui.test.js +19 -0
  394. package/dist/src/types.d.ts +236 -0
  395. package/dist/src/types.js +7 -0
  396. package/dist/src/update-check.d.ts +66 -0
  397. package/dist/src/update-check.js +206 -0
  398. package/dist/src/update-check.test.d.ts +1 -0
  399. package/dist/src/update-check.test.js +117 -0
  400. package/dist/src/utils.d.ts +57 -0
  401. package/dist/src/utils.js +152 -0
  402. package/dist/src/utils.test.d.ts +1 -0
  403. package/dist/src/utils.test.js +155 -0
  404. package/dist/src/validate.d.ts +21 -0
  405. package/dist/src/validate.js +130 -0
  406. package/dist/src/validate.test.d.ts +9 -0
  407. package/dist/src/validate.test.js +90 -0
  408. package/dist/src/verify.d.ts +26 -0
  409. package/dist/src/verify.js +67 -0
  410. package/dist/src/version.d.ts +4 -0
  411. package/dist/src/version.js +22 -0
  412. package/dist/src/weixin-download.test.d.ts +1 -0
  413. package/dist/src/weixin-download.test.js +45 -0
  414. package/package.json +4 -3
  415. package/scripts/check-package-bin.mjs +95 -0
@@ -0,0 +1,235 @@
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(), 'webcmd-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(), 'webcmd-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(), 'webcmd-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(), 'webcmd-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
+ });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Download utilities: HTTP downloads, yt-dlp wrapper, format conversion.
3
+ */
4
+ import type { BrowserCookie } from '../types.js';
5
+ export type { BrowserCookie } from '../types.js';
6
+ export interface DownloadOptions {
7
+ cookies?: string;
8
+ headers?: Record<string, string>;
9
+ timeout?: number;
10
+ onProgress?: (received: number, total: number) => void;
11
+ maxRedirects?: number;
12
+ }
13
+ export interface YtdlpOptions {
14
+ cookies?: string;
15
+ cookiesFile?: string;
16
+ format?: string;
17
+ extraArgs?: string[];
18
+ onProgress?: (percent: number) => void;
19
+ }
20
+ /** Check if yt-dlp is available in PATH. */
21
+ export declare function checkYtdlp(): boolean;
22
+ /**
23
+ * Detect content type from URL and optional headers.
24
+ */
25
+ export declare function detectContentType(url: string, contentType?: string): 'image' | 'video' | 'document' | 'binary';
26
+ /**
27
+ * Check if URL requires yt-dlp for download.
28
+ */
29
+ export declare function requiresYtdlp(url: string): boolean;
30
+ /**
31
+ * HTTP download with progress callback.
32
+ */
33
+ export declare function httpDownload(url: string, destPath: string, options?: DownloadOptions, redirectCount?: number): Promise<{
34
+ success: boolean;
35
+ size: number;
36
+ error?: string;
37
+ }>;
38
+ export declare function resolveRedirectUrl(currentUrl: string, location: string): string;
39
+ /**
40
+ * Export cookies to Netscape format for yt-dlp.
41
+ */
42
+ export declare function exportCookiesToNetscape(cookies: BrowserCookie[], filePath: string): void;
43
+ export declare function formatCookieHeader(cookies: BrowserCookie[]): string;
44
+ /**
45
+ * Download video using yt-dlp.
46
+ */
47
+ export declare function ytdlpDownload(url: string, destPath: string, options?: YtdlpOptions): Promise<{
48
+ success: boolean;
49
+ size: number;
50
+ error?: string;
51
+ }>;
52
+ /**
53
+ * Save document content to file.
54
+ */
55
+ export declare function saveDocument(content: string, destPath: string, format?: 'json' | 'markdown' | 'html' | 'text', metadata?: Record<string, unknown>): Promise<{
56
+ success: boolean;
57
+ size: number;
58
+ error?: string;
59
+ }>;
60
+ /**
61
+ * Sanitize filename by removing invalid characters.
62
+ */
63
+ export declare function sanitizeFilename(name: string, maxLength?: number): string;
64
+ /**
65
+ * Generate filename from URL if not provided.
66
+ */
67
+ export declare function generateFilename(url: string, index: number, extension?: string): string;
68
+ /**
69
+ * Get temp directory for cookie files.
70
+ */
71
+ export declare function getTempDir(): string;
@@ -0,0 +1,363 @@
1
+ /**
2
+ * Download utilities: HTTP downloads, yt-dlp wrapper, format conversion.
3
+ */
4
+ import { spawn } from 'node:child_process';
5
+ import * as fs from 'node:fs';
6
+ import * as path from 'node:path';
7
+ import * as os from 'node:os';
8
+ import { Readable, Transform } from 'node:stream';
9
+ import { pipeline } from 'node:stream/promises';
10
+ import { URL } from 'node:url';
11
+ import { isBinaryInstalled } from '../external.js';
12
+ import { getErrorMessage } from '../errors.js';
13
+ import { fetchWithNodeNetwork } from '../node-network.js';
14
+ import { log } from '../logger.js';
15
+ /** Check if yt-dlp is available in PATH. */
16
+ export function checkYtdlp() {
17
+ return isBinaryInstalled('yt-dlp');
18
+ }
19
+ /** Domains that host video content and can be downloaded via yt-dlp. */
20
+ const VIDEO_PLATFORM_DOMAINS = [
21
+ 'youtube.com', 'youtu.be', 'bilibili.com', 'twitter.com',
22
+ 'x.com', 'tiktok.com', 'vimeo.com', 'twitch.tv',
23
+ ];
24
+ /**
25
+ * Whether a URL's host is a known video platform.
26
+ *
27
+ * Matches on the parsed hostname with a domain boundary (exact host or a
28
+ * subdomain) rather than a raw substring of the whole URL. A plain
29
+ * `url.includes('x.com')` wrongly classifies `netflix.com`, `max.com`, etc.
30
+ * (and any URL whose path merely contains a token like `youtu.be`) as a
31
+ * video platform, routing ordinary files to yt-dlp and mislabelling content
32
+ * types. Mirrors the host-boundary check used in execution.ts.
33
+ */
34
+ function isVideoPlatformUrl(url) {
35
+ let host;
36
+ try {
37
+ host = new URL(url).hostname.toLowerCase();
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ return VIDEO_PLATFORM_DOMAINS.some((d) => host === d || host.endsWith(`.${d}`));
43
+ }
44
+ const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.ico', '.bmp', '.avif']);
45
+ const VIDEO_EXTENSIONS = new Set(['.mp4', '.webm', '.avi', '.mov', '.mkv', '.flv', '.m3u8', '.ts']);
46
+ const DOC_EXTENSIONS = new Set(['.html', '.htm', '.json', '.xml', '.txt', '.md', '.markdown']);
47
+ /**
48
+ * Detect content type from URL and optional headers.
49
+ */
50
+ export function detectContentType(url, contentType) {
51
+ if (contentType) {
52
+ if (contentType.startsWith('image/'))
53
+ return 'image';
54
+ if (contentType.startsWith('video/'))
55
+ return 'video';
56
+ if (contentType.startsWith('text/') || contentType.includes('json') || contentType.includes('xml'))
57
+ return 'document';
58
+ }
59
+ const ext = path.extname(new URL(url).pathname).toLowerCase();
60
+ if (IMAGE_EXTENSIONS.has(ext))
61
+ return 'image';
62
+ if (VIDEO_EXTENSIONS.has(ext))
63
+ return 'video';
64
+ if (isVideoPlatformUrl(url))
65
+ return 'video';
66
+ if (DOC_EXTENSIONS.has(ext))
67
+ return 'document';
68
+ return 'binary';
69
+ }
70
+ /**
71
+ * Check if URL requires yt-dlp for download.
72
+ */
73
+ export function requiresYtdlp(url) {
74
+ return isVideoPlatformUrl(url);
75
+ }
76
+ /**
77
+ * HTTP download with progress callback.
78
+ */
79
+ export async function httpDownload(url, destPath, options = {}, redirectCount = 0) {
80
+ const { cookies, headers = {}, timeout = 30000, onProgress, maxRedirects = 10 } = options;
81
+ const requestHeaders = {
82
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36',
83
+ ...headers,
84
+ };
85
+ if (cookies) {
86
+ requestHeaders['Cookie'] = cookies;
87
+ }
88
+ const tempPath = `${destPath}.tmp`;
89
+ const cleanupTempFile = async () => {
90
+ try {
91
+ await fs.promises.rm(tempPath, { force: true });
92
+ }
93
+ catch {
94
+ // Ignore cleanup errors so the original failure is preserved.
95
+ }
96
+ };
97
+ const controller = new AbortController();
98
+ const timer = setTimeout(() => controller.abort(), timeout);
99
+ try {
100
+ const response = await fetchWithNodeNetwork(url, {
101
+ headers: requestHeaders,
102
+ signal: controller.signal,
103
+ redirect: 'manual',
104
+ });
105
+ clearTimeout(timer);
106
+ // Handle redirects before creating any file handles.
107
+ if (response.status >= 300 && response.status < 400) {
108
+ const location = response.headers.get('location');
109
+ if (location) {
110
+ if (redirectCount >= maxRedirects) {
111
+ return { success: false, size: 0, error: `Too many redirects (> ${maxRedirects})` };
112
+ }
113
+ const redirectUrl = resolveRedirectUrl(url, location);
114
+ const originalHost = new URL(url).hostname;
115
+ const redirectHost = new URL(redirectUrl).hostname;
116
+ const redirectOptions = originalHost === redirectHost
117
+ ? options
118
+ : { ...options, cookies: undefined, headers: stripCookieHeaders(options.headers) };
119
+ return httpDownload(redirectUrl, destPath, redirectOptions, redirectCount + 1);
120
+ }
121
+ }
122
+ if (response.status !== 200) {
123
+ return { success: false, size: 0, error: `HTTP ${response.status}` };
124
+ }
125
+ if (!response.body) {
126
+ return { success: false, size: 0, error: 'Empty response body' };
127
+ }
128
+ const totalSize = parseInt(response.headers.get('content-length') || '0', 10);
129
+ let received = 0;
130
+ const progressStream = new Transform({
131
+ transform(chunk, _encoding, callback) {
132
+ received += chunk.length;
133
+ if (onProgress)
134
+ onProgress(received, totalSize);
135
+ callback(null, chunk);
136
+ },
137
+ });
138
+ try {
139
+ await fs.promises.mkdir(path.dirname(destPath), { recursive: true });
140
+ await pipeline(Readable.fromWeb(response.body), progressStream, fs.createWriteStream(tempPath));
141
+ await fs.promises.rename(tempPath, destPath);
142
+ return { success: true, size: received };
143
+ }
144
+ catch (err) {
145
+ await cleanupTempFile();
146
+ return { success: false, size: 0, error: getErrorMessage(err) };
147
+ }
148
+ }
149
+ catch (err) {
150
+ clearTimeout(timer);
151
+ await cleanupTempFile();
152
+ return { success: false, size: 0, error: err instanceof Error ? err.message : String(err) };
153
+ }
154
+ }
155
+ export function resolveRedirectUrl(currentUrl, location) {
156
+ return new URL(location, currentUrl).toString();
157
+ }
158
+ function stripCookieHeaders(headers) {
159
+ if (!headers)
160
+ return headers;
161
+ return Object.fromEntries(Object.entries(headers).filter(([key]) => key.toLowerCase() !== 'cookie'));
162
+ }
163
+ /**
164
+ * Export cookies to Netscape format for yt-dlp.
165
+ */
166
+ export function exportCookiesToNetscape(cookies, filePath) {
167
+ const lines = [
168
+ '# Netscape HTTP Cookie File',
169
+ '# https://curl.se/docs/http-cookies.html',
170
+ '# This is a generated file! Do not edit.',
171
+ '',
172
+ ];
173
+ for (const cookie of cookies) {
174
+ const domain = cookie.domain.startsWith('.') ? cookie.domain : `.${cookie.domain}`;
175
+ const includeSubdomains = 'TRUE';
176
+ const cookiePath = cookie.path || '/';
177
+ const secure = cookie.secure ? 'TRUE' : 'FALSE';
178
+ const expiry = typeof cookie.expirationDate === 'number' && cookie.expirationDate > 0
179
+ ? Math.floor(cookie.expirationDate)
180
+ : Math.floor(Date.now() / 1000) + 86400 * 365; // fallback: 1 year from now
181
+ const safeName = cookie.name.replace(/[\t\n\r]/g, '');
182
+ const safeValue = cookie.value.replace(/[\t\n\r]/g, '');
183
+ lines.push(`${domain}\t${includeSubdomains}\t${cookiePath}\t${secure}\t${expiry}\t${safeName}\t${safeValue}`);
184
+ }
185
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
186
+ // 0o600: file holds live session cookies, must be owner-only. fchmod before
187
+ // writing also tightens a pre-existing broad file before new secrets land.
188
+ let fd;
189
+ try {
190
+ fd = fs.openSync(filePath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_TRUNC, 0o600);
191
+ fs.fchmodSync(fd, 0o600);
192
+ fs.writeFileSync(fd, lines.join('\n'), 'utf8');
193
+ }
194
+ finally {
195
+ if (fd !== undefined) {
196
+ fs.closeSync(fd);
197
+ }
198
+ }
199
+ }
200
+ export function formatCookieHeader(cookies) {
201
+ return cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join('; ');
202
+ }
203
+ /**
204
+ * Download video using yt-dlp.
205
+ */
206
+ export async function ytdlpDownload(url, destPath, options = {}) {
207
+ const { cookiesFile, format = 'best', extraArgs = [], onProgress } = options;
208
+ if (!checkYtdlp()) {
209
+ return { success: false, size: 0, error: 'yt-dlp not installed. Install with: pip install yt-dlp' };
210
+ }
211
+ return new Promise((resolve) => {
212
+ const dir = path.dirname(destPath);
213
+ fs.mkdirSync(dir, { recursive: true });
214
+ // Build yt-dlp arguments
215
+ const args = [
216
+ url,
217
+ '-o', destPath,
218
+ '-f', format,
219
+ '--no-playlist',
220
+ '--progress',
221
+ ];
222
+ if (cookiesFile) {
223
+ if (fs.existsSync(cookiesFile)) {
224
+ args.push('--cookies', cookiesFile);
225
+ }
226
+ else {
227
+ log.warn(`[download] Cookies file not found: ${cookiesFile}, falling back to browser cookies`);
228
+ args.push('--cookies-from-browser', 'chrome');
229
+ }
230
+ }
231
+ else {
232
+ // Try to use browser cookies
233
+ args.push('--cookies-from-browser', 'chrome');
234
+ }
235
+ args.push(...extraArgs);
236
+ const proc = spawn('yt-dlp', args, {
237
+ stdio: ['ignore', 'pipe', 'pipe'],
238
+ });
239
+ let lastPercent = 0;
240
+ let errorOutput = '';
241
+ proc.stderr.on('data', (data) => {
242
+ const line = data.toString();
243
+ errorOutput += line;
244
+ // Parse progress from yt-dlp output
245
+ const match = line.match(/(\d+\.?\d*)%/);
246
+ if (match && onProgress) {
247
+ const percent = parseFloat(match[1]);
248
+ if (percent > lastPercent) {
249
+ lastPercent = percent;
250
+ onProgress(percent);
251
+ }
252
+ }
253
+ });
254
+ proc.stdout.on('data', (data) => {
255
+ const line = data.toString();
256
+ const match = line.match(/(\d+\.?\d*)%/);
257
+ if (match && onProgress) {
258
+ const percent = parseFloat(match[1]);
259
+ if (percent > lastPercent) {
260
+ lastPercent = percent;
261
+ onProgress(percent);
262
+ }
263
+ }
264
+ });
265
+ proc.on('close', (code) => {
266
+ if (code === 0 && fs.existsSync(destPath)) {
267
+ const stats = fs.statSync(destPath);
268
+ resolve({ success: true, size: stats.size });
269
+ }
270
+ else {
271
+ // Check for common yt-dlp output patterns
272
+ const patterns = fs.readdirSync(dir).filter(f => f.startsWith(path.basename(destPath, path.extname(destPath))));
273
+ if (patterns.length > 0) {
274
+ const actualFile = path.join(dir, patterns[0]);
275
+ const stats = fs.statSync(actualFile);
276
+ resolve({ success: true, size: stats.size });
277
+ }
278
+ else {
279
+ resolve({ success: false, size: 0, error: errorOutput.slice(0, 200) || `Exit code ${code}` });
280
+ }
281
+ }
282
+ });
283
+ proc.on('error', (err) => {
284
+ resolve({ success: false, size: 0, error: err.message });
285
+ });
286
+ });
287
+ }
288
+ /**
289
+ * Save document content to file.
290
+ */
291
+ export async function saveDocument(content, destPath, format = 'markdown', metadata) {
292
+ try {
293
+ const dir = path.dirname(destPath);
294
+ fs.mkdirSync(dir, { recursive: true });
295
+ let output;
296
+ if (format === 'json') {
297
+ output = JSON.stringify({ ...metadata, content }, null, 2);
298
+ }
299
+ else if (format === 'markdown') {
300
+ // Add frontmatter if metadata exists
301
+ const frontmatter = metadata ? `---\n${Object.entries(metadata).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join('\n')}\n---\n\n` : '';
302
+ output = frontmatter + content;
303
+ }
304
+ else {
305
+ output = content;
306
+ }
307
+ fs.writeFileSync(destPath, output, 'utf-8');
308
+ return { success: true, size: Buffer.byteLength(output, 'utf-8') };
309
+ }
310
+ catch (err) {
311
+ return { success: false, size: 0, error: getErrorMessage(err) };
312
+ }
313
+ }
314
+ /**
315
+ * Sanitize filename by removing invalid characters.
316
+ */
317
+ export function sanitizeFilename(name, maxLength = 200) {
318
+ return name
319
+ .replace(/[<>:"/\\|?*\x00-\x1f]/g, '_') // Remove invalid chars
320
+ .replace(/\s+/g, '_') // Replace spaces with underscores
321
+ .replace(/_+/g, '_') // Collapse multiple underscores
322
+ .replace(/^_|_$/g, '') // Trim underscores
323
+ .slice(0, maxLength);
324
+ }
325
+ /**
326
+ * Generate filename from URL if not provided.
327
+ */
328
+ export function generateFilename(url, index, extension) {
329
+ try {
330
+ const parsedUrl = new URL(url);
331
+ const pathname = parsedUrl.pathname;
332
+ const basename = path.basename(pathname);
333
+ if (basename && basename !== '/' && basename.includes('.')) {
334
+ return sanitizeFilename(basename);
335
+ }
336
+ // Generate from hostname and index
337
+ const ext = extension || detectExtension(url);
338
+ const hostname = parsedUrl.hostname.replace(/^www\./, '');
339
+ return sanitizeFilename(`${hostname}_${index + 1}${ext}`);
340
+ }
341
+ catch {
342
+ const ext = extension || '.bin';
343
+ return `download_${index + 1}${ext}`;
344
+ }
345
+ }
346
+ /**
347
+ * Detect file extension from URL.
348
+ */
349
+ function detectExtension(url) {
350
+ const type = detectContentType(url);
351
+ switch (type) {
352
+ case 'image': return '.jpg';
353
+ case 'video': return '.mp4';
354
+ case 'document': return '.md';
355
+ default: return '.bin';
356
+ }
357
+ }
358
+ /**
359
+ * Get temp directory for cookie files.
360
+ */
361
+ export function getTempDir() {
362
+ return path.join(os.tmpdir(), 'webcmd-download');
363
+ }
@@ -0,0 +1 @@
1
+ export {};