youtube-knowledge-mcp 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (478) hide show
  1. package/README.md +153 -38
  2. package/dist/brain-schemas.d.ts +10 -10
  3. package/dist/brain-schemas.d.ts.map +1 -1
  4. package/dist/brain-schemas.js +0 -4
  5. package/dist/brain-schemas.js.map +1 -1
  6. package/dist/harvest-schemas.d.ts +75 -0
  7. package/dist/harvest-schemas.d.ts.map +1 -0
  8. package/dist/harvest-schemas.js +106 -0
  9. package/dist/harvest-schemas.js.map +1 -0
  10. package/dist/http/app.d.ts +16 -0
  11. package/dist/http/app.d.ts.map +1 -0
  12. package/dist/http/app.js +176 -0
  13. package/dist/http/app.js.map +1 -0
  14. package/dist/http/auth.d.ts +38 -0
  15. package/dist/http/auth.d.ts.map +1 -0
  16. package/dist/http/auth.js +79 -0
  17. package/dist/http/auth.js.map +1 -0
  18. package/dist/http/config.d.ts +41 -0
  19. package/dist/http/config.d.ts.map +1 -0
  20. package/dist/http/config.js +65 -0
  21. package/dist/http/config.js.map +1 -0
  22. package/dist/http/rate-limiter.d.ts +24 -0
  23. package/dist/http/rate-limiter.d.ts.map +1 -0
  24. package/dist/http/rate-limiter.js +55 -0
  25. package/dist/http/rate-limiter.js.map +1 -0
  26. package/dist/http/sessions.d.ts +34 -0
  27. package/dist/http/sessions.d.ts.map +1 -0
  28. package/dist/http/sessions.js +62 -0
  29. package/dist/http/sessions.js.map +1 -0
  30. package/dist/http.d.ts +6 -48
  31. package/dist/http.d.ts.map +1 -1
  32. package/dist/http.js +35 -252
  33. package/dist/http.js.map +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +11 -517
  36. package/dist/index.js.map +1 -1
  37. package/dist/prompts-brain.d.ts +4 -0
  38. package/dist/prompts-brain.d.ts.map +1 -0
  39. package/dist/prompts-brain.js +48 -0
  40. package/dist/prompts-brain.js.map +1 -0
  41. package/dist/prompts-research.d.ts +4 -0
  42. package/dist/prompts-research.d.ts.map +1 -0
  43. package/dist/prompts-research.js +74 -0
  44. package/dist/prompts-research.js.map +1 -0
  45. package/dist/prompts-shared.d.ts +13 -0
  46. package/dist/prompts-shared.d.ts.map +1 -0
  47. package/dist/prompts-shared.js +7 -0
  48. package/dist/prompts-shared.js.map +1 -0
  49. package/dist/prompts.d.ts.map +1 -1
  50. package/dist/prompts.js +5 -100
  51. package/dist/prompts.js.map +1 -1
  52. package/dist/registry/brain-tools.d.ts +4 -0
  53. package/dist/registry/brain-tools.d.ts.map +1 -0
  54. package/dist/registry/brain-tools.js +104 -0
  55. package/dist/registry/brain-tools.js.map +1 -0
  56. package/dist/registry/channel-tools.d.ts +4 -0
  57. package/dist/registry/channel-tools.d.ts.map +1 -0
  58. package/dist/registry/channel-tools.js +53 -0
  59. package/dist/registry/channel-tools.js.map +1 -0
  60. package/dist/registry/harvest-tools.d.ts +10 -0
  61. package/dist/registry/harvest-tools.d.ts.map +1 -0
  62. package/dist/registry/harvest-tools.js +109 -0
  63. package/dist/registry/harvest-tools.js.map +1 -0
  64. package/dist/registry/health-tools.d.ts +4 -0
  65. package/dist/registry/health-tools.d.ts.map +1 -0
  66. package/dist/registry/health-tools.js +39 -0
  67. package/dist/registry/health-tools.js.map +1 -0
  68. package/dist/registry/index.d.ts +8 -0
  69. package/dist/registry/index.d.ts.map +1 -0
  70. package/dist/registry/index.js +29 -0
  71. package/dist/registry/index.js.map +1 -0
  72. package/dist/registry/library-tools.d.ts +4 -0
  73. package/dist/registry/library-tools.d.ts.map +1 -0
  74. package/dist/registry/library-tools.js +116 -0
  75. package/dist/registry/library-tools.js.map +1 -0
  76. package/dist/registry/media-tools.d.ts +4 -0
  77. package/dist/registry/media-tools.d.ts.map +1 -0
  78. package/dist/registry/media-tools.js +100 -0
  79. package/dist/registry/media-tools.js.map +1 -0
  80. package/dist/registry/thumbnail-tools.d.ts +7 -0
  81. package/dist/registry/thumbnail-tools.d.ts.map +1 -0
  82. package/dist/registry/thumbnail-tools.js +72 -0
  83. package/dist/registry/thumbnail-tools.js.map +1 -0
  84. package/dist/registry/transcript-tools.d.ts +4 -0
  85. package/dist/registry/transcript-tools.d.ts.map +1 -0
  86. package/dist/registry/transcript-tools.js +68 -0
  87. package/dist/registry/transcript-tools.js.map +1 -0
  88. package/dist/registry/types.d.ts +41 -0
  89. package/dist/registry/types.d.ts.map +1 -0
  90. package/dist/registry/types.js +17 -0
  91. package/dist/registry/types.js.map +1 -0
  92. package/dist/registry/video-tools.d.ts +4 -0
  93. package/dist/registry/video-tools.d.ts.map +1 -0
  94. package/dist/registry/video-tools.js +101 -0
  95. package/dist/registry/video-tools.js.map +1 -0
  96. package/dist/resources.d.ts.map +1 -1
  97. package/dist/resources.js +13 -10
  98. package/dist/resources.js.map +1 -1
  99. package/dist/schemas.d.ts +250 -2
  100. package/dist/schemas.d.ts.map +1 -1
  101. package/dist/schemas.js +61 -0
  102. package/dist/schemas.js.map +1 -1
  103. package/dist/thumbnail-resource.d.ts +8 -0
  104. package/dist/thumbnail-resource.d.ts.map +1 -0
  105. package/dist/thumbnail-resource.js +38 -0
  106. package/dist/thumbnail-resource.js.map +1 -0
  107. package/dist/thumbnail-schemas.d.ts +512 -0
  108. package/dist/thumbnail-schemas.d.ts.map +1 -0
  109. package/dist/thumbnail-schemas.js +95 -0
  110. package/dist/thumbnail-schemas.js.map +1 -0
  111. package/dist/tools/ask-brain.js +1 -1
  112. package/dist/tools/ask-brain.js.map +1 -1
  113. package/dist/tools/batch.js +1 -1
  114. package/dist/tools/batch.js.map +1 -1
  115. package/dist/tools/brain-info.js +1 -1
  116. package/dist/tools/brain-info.js.map +1 -1
  117. package/dist/tools/build-brain.js +1 -1
  118. package/dist/tools/build-brain.js.map +1 -1
  119. package/dist/tools/check-health.d.ts +61 -0
  120. package/dist/tools/check-health.d.ts.map +1 -1
  121. package/dist/tools/check-health.js +94 -6
  122. package/dist/tools/check-health.js.map +1 -1
  123. package/dist/tools/clips.d.ts +9 -41
  124. package/dist/tools/clips.d.ts.map +1 -1
  125. package/dist/tools/clips.js +52 -128
  126. package/dist/tools/clips.js.map +1 -1
  127. package/dist/tools/fetch-channel-thumbnails.d.ts +179 -0
  128. package/dist/tools/fetch-channel-thumbnails.d.ts.map +1 -0
  129. package/dist/tools/fetch-channel-thumbnails.js +123 -0
  130. package/dist/tools/fetch-channel-thumbnails.js.map +1 -0
  131. package/dist/tools/fetch-videos.d.ts +18 -4
  132. package/dist/tools/fetch-videos.d.ts.map +1 -1
  133. package/dist/tools/fetch-videos.js +64 -20
  134. package/dist/tools/fetch-videos.js.map +1 -1
  135. package/dist/tools/frames-subtitles.d.ts +44 -0
  136. package/dist/tools/frames-subtitles.d.ts.map +1 -0
  137. package/dist/tools/frames-subtitles.js +104 -0
  138. package/dist/tools/frames-subtitles.js.map +1 -0
  139. package/dist/tools/get-channel-info.d.ts +2 -0
  140. package/dist/tools/get-channel-info.d.ts.map +1 -1
  141. package/dist/tools/get-channel-info.js +8 -1
  142. package/dist/tools/get-channel-info.js.map +1 -1
  143. package/dist/tools/get-comments.d.ts +238 -6
  144. package/dist/tools/get-comments.d.ts.map +1 -1
  145. package/dist/tools/get-comments.js +85 -21
  146. package/dist/tools/get-comments.js.map +1 -1
  147. package/dist/tools/get-coverage.d.ts +122 -0
  148. package/dist/tools/get-coverage.d.ts.map +1 -0
  149. package/dist/tools/get-coverage.js +105 -0
  150. package/dist/tools/get-coverage.js.map +1 -0
  151. package/dist/tools/get-playlist-info.d.ts.map +1 -1
  152. package/dist/tools/get-playlist-info.js +1 -0
  153. package/dist/tools/get-playlist-info.js.map +1 -1
  154. package/dist/tools/get-thumbnail.d.ts +38 -0
  155. package/dist/tools/get-thumbnail.d.ts.map +1 -0
  156. package/dist/tools/get-thumbnail.js +152 -0
  157. package/dist/tools/get-thumbnail.js.map +1 -0
  158. package/dist/tools/get-transcript.d.ts.map +1 -1
  159. package/dist/tools/get-transcript.js +2 -14
  160. package/dist/tools/get-transcript.js.map +1 -1
  161. package/dist/tools/harvest-channel.d.ts +92 -0
  162. package/dist/tools/harvest-channel.d.ts.map +1 -0
  163. package/dist/tools/harvest-channel.js +62 -0
  164. package/dist/tools/harvest-channel.js.map +1 -0
  165. package/dist/tools/harvest-comments.d.ts +81 -0
  166. package/dist/tools/harvest-comments.d.ts.map +1 -0
  167. package/dist/tools/harvest-comments.js +115 -0
  168. package/dist/tools/harvest-comments.js.map +1 -0
  169. package/dist/tools/library.d.ts.map +1 -1
  170. package/dist/tools/library.js +14 -4
  171. package/dist/tools/library.js.map +1 -1
  172. package/dist/tools/list-channel-thumbnails.d.ts +235 -0
  173. package/dist/tools/list-channel-thumbnails.d.ts.map +1 -0
  174. package/dist/tools/list-channel-thumbnails.js +79 -0
  175. package/dist/tools/list-channel-thumbnails.js.map +1 -0
  176. package/dist/tools/list-library.js +1 -1
  177. package/dist/tools/list-library.js.map +1 -1
  178. package/dist/tools/prune-harvest.d.ts +59 -0
  179. package/dist/tools/prune-harvest.d.ts.map +1 -0
  180. package/dist/tools/prune-harvest.js +108 -0
  181. package/dist/tools/prune-harvest.js.map +1 -0
  182. package/dist/tools/query-comments.d.ts +125 -0
  183. package/dist/tools/query-comments.d.ts.map +1 -0
  184. package/dist/tools/query-comments.js +106 -0
  185. package/dist/tools/query-comments.js.map +1 -0
  186. package/dist/tools/query-videos.d.ts +118 -0
  187. package/dist/tools/query-videos.d.ts.map +1 -0
  188. package/dist/tools/query-videos.js +97 -0
  189. package/dist/tools/query-videos.js.map +1 -0
  190. package/dist/tools/repair-store.d.ts +34 -0
  191. package/dist/tools/repair-store.d.ts.map +1 -0
  192. package/dist/tools/repair-store.js +58 -0
  193. package/dist/tools/repair-store.js.map +1 -0
  194. package/dist/tools/save-to-library.d.ts.map +1 -1
  195. package/dist/tools/save-to-library.js +5 -1
  196. package/dist/tools/save-to-library.js.map +1 -1
  197. package/dist/tools/search-channels.d.ts +6 -0
  198. package/dist/tools/search-channels.d.ts.map +1 -1
  199. package/dist/tools/search-channels.js +5 -1
  200. package/dist/tools/search-channels.js.map +1 -1
  201. package/dist/tools/search-transcript.d.ts.map +1 -1
  202. package/dist/tools/search-transcript.js +6 -13
  203. package/dist/tools/search-transcript.js.map +1 -1
  204. package/dist/tools/search-videos.d.ts +3 -0
  205. package/dist/tools/search-videos.d.ts.map +1 -1
  206. package/dist/tools/search-videos.js +1 -1
  207. package/dist/tools/search-videos.js.map +1 -1
  208. package/dist/utils/batches.d.ts +15 -0
  209. package/dist/utils/batches.d.ts.map +1 -0
  210. package/dist/utils/batches.js +31 -0
  211. package/dist/utils/batches.js.map +1 -0
  212. package/dist/utils/brain-build.d.ts.map +1 -1
  213. package/dist/utils/brain-build.js +1 -12
  214. package/dist/utils/brain-build.js.map +1 -1
  215. package/dist/utils/brain-index.d.ts.map +1 -1
  216. package/dist/utils/brain-index.js +3 -2
  217. package/dist/utils/brain-index.js.map +1 -1
  218. package/dist/utils/brain-lock.d.ts +2 -18
  219. package/dist/utils/brain-lock.d.ts.map +1 -1
  220. package/dist/utils/brain-lock.js +4 -80
  221. package/dist/utils/brain-lock.js.map +1 -1
  222. package/dist/utils/brain-lookup.d.ts +8 -0
  223. package/dist/utils/brain-lookup.d.ts.map +1 -1
  224. package/dist/utils/brain-lookup.js +5 -31
  225. package/dist/utils/brain-lookup.js.map +1 -1
  226. package/dist/utils/brain-storage.d.ts.map +1 -1
  227. package/dist/utils/brain-storage.js +5 -5
  228. package/dist/utils/brain-storage.js.map +1 -1
  229. package/dist/utils/caption-probe.d.ts +7 -0
  230. package/dist/utils/caption-probe.d.ts.map +1 -0
  231. package/dist/utils/caption-probe.js +36 -0
  232. package/dist/utils/caption-probe.js.map +1 -0
  233. package/dist/utils/channel-images.d.ts +16 -0
  234. package/dist/utils/channel-images.d.ts.map +1 -0
  235. package/dist/utils/channel-images.js +35 -0
  236. package/dist/utils/channel-images.js.map +1 -0
  237. package/dist/utils/channel-lookup.d.ts +5 -0
  238. package/dist/utils/channel-lookup.d.ts.map +1 -0
  239. package/dist/utils/channel-lookup.js +37 -0
  240. package/dist/utils/channel-lookup.js.map +1 -0
  241. package/dist/utils/chapters.d.ts +11 -0
  242. package/dist/utils/chapters.d.ts.map +1 -0
  243. package/dist/utils/chapters.js +24 -0
  244. package/dist/utils/chapters.js.map +1 -0
  245. package/dist/utils/clips.d.ts.map +1 -1
  246. package/dist/utils/clips.js +18 -26
  247. package/dist/utils/clips.js.map +1 -1
  248. package/dist/utils/comment-store.d.ts +71 -0
  249. package/dist/utils/comment-store.d.ts.map +1 -0
  250. package/dist/utils/comment-store.js +129 -0
  251. package/dist/utils/comment-store.js.map +1 -0
  252. package/dist/utils/comment-threads.d.ts +51 -0
  253. package/dist/utils/comment-threads.d.ts.map +1 -0
  254. package/dist/utils/comment-threads.js +76 -0
  255. package/dist/utils/comment-threads.js.map +1 -0
  256. package/dist/utils/coverage-text.d.ts +3 -0
  257. package/dist/utils/coverage-text.d.ts.map +1 -0
  258. package/dist/utils/coverage-text.js +62 -0
  259. package/dist/utils/coverage-text.js.map +1 -0
  260. package/dist/utils/coverage.d.ts +57 -0
  261. package/dist/utils/coverage.d.ts.map +1 -0
  262. package/dist/utils/coverage.js +142 -0
  263. package/dist/utils/coverage.js.map +1 -0
  264. package/dist/utils/env.d.ts +26 -0
  265. package/dist/utils/env.d.ts.map +1 -0
  266. package/dist/utils/env.js +72 -0
  267. package/dist/utils/env.js.map +1 -0
  268. package/dist/utils/errors.d.ts +1 -1
  269. package/dist/utils/errors.d.ts.map +1 -1
  270. package/dist/utils/errors.js +28 -2
  271. package/dist/utils/errors.js.map +1 -1
  272. package/dist/utils/file-lock.d.ts +34 -0
  273. package/dist/utils/file-lock.d.ts.map +1 -0
  274. package/dist/utils/file-lock.js +91 -0
  275. package/dist/utils/file-lock.js.map +1 -0
  276. package/dist/utils/flat-listing.d.ts +89 -0
  277. package/dist/utils/flat-listing.d.ts.map +1 -0
  278. package/dist/utils/flat-listing.js +68 -0
  279. package/dist/utils/flat-listing.js.map +1 -0
  280. package/dist/utils/format.d.ts +18 -4
  281. package/dist/utils/format.d.ts.map +1 -1
  282. package/dist/utils/format.js +2 -6
  283. package/dist/utils/format.js.map +1 -1
  284. package/dist/utils/guard.d.ts +21 -0
  285. package/dist/utils/guard.d.ts.map +1 -0
  286. package/dist/utils/guard.js +105 -0
  287. package/dist/utils/guard.js.map +1 -0
  288. package/dist/utils/harvest-catalog.d.ts +44 -0
  289. package/dist/utils/harvest-catalog.d.ts.map +1 -0
  290. package/dist/utils/harvest-catalog.js +164 -0
  291. package/dist/utils/harvest-catalog.js.map +1 -0
  292. package/dist/utils/harvest-lock.d.ts +3 -0
  293. package/dist/utils/harvest-lock.d.ts.map +1 -0
  294. package/dist/utils/harvest-lock.js +26 -0
  295. package/dist/utils/harvest-lock.js.map +1 -0
  296. package/dist/utils/harvest-receipts.d.ts +14 -0
  297. package/dist/utils/harvest-receipts.d.ts.map +1 -0
  298. package/dist/utils/harvest-receipts.js +110 -0
  299. package/dist/utils/harvest-receipts.js.map +1 -0
  300. package/dist/utils/image-dimensions.d.ts +19 -0
  301. package/dist/utils/image-dimensions.d.ts.map +1 -0
  302. package/dist/utils/image-dimensions.js +93 -0
  303. package/dist/utils/image-dimensions.js.map +1 -0
  304. package/dist/utils/image-fetch.d.ts +31 -0
  305. package/dist/utils/image-fetch.d.ts.map +1 -0
  306. package/dist/utils/image-fetch.js +141 -0
  307. package/dist/utils/image-fetch.js.map +1 -0
  308. package/dist/utils/json-file.d.ts +14 -5
  309. package/dist/utils/json-file.d.ts.map +1 -1
  310. package/dist/utils/json-file.js +24 -9
  311. package/dist/utils/json-file.js.map +1 -1
  312. package/dist/utils/listing-cursor.d.ts +31 -0
  313. package/dist/utils/listing-cursor.d.ts.map +1 -0
  314. package/dist/utils/listing-cursor.js +39 -0
  315. package/dist/utils/listing-cursor.js.map +1 -0
  316. package/dist/utils/paths.d.ts +10 -0
  317. package/dist/utils/paths.d.ts.map +1 -1
  318. package/dist/utils/paths.js +14 -0
  319. package/dist/utils/paths.js.map +1 -1
  320. package/dist/utils/pattern.d.ts +30 -0
  321. package/dist/utils/pattern.d.ts.map +1 -0
  322. package/dist/utils/pattern.js +130 -0
  323. package/dist/utils/pattern.js.map +1 -0
  324. package/dist/utils/pot-preflight.d.ts +53 -0
  325. package/dist/utils/pot-preflight.d.ts.map +1 -0
  326. package/dist/utils/pot-preflight.js +146 -0
  327. package/dist/utils/pot-preflight.js.map +1 -0
  328. package/dist/utils/search-index.d.ts.map +1 -1
  329. package/dist/utils/search-index.js +9 -4
  330. package/dist/utils/search-index.js.map +1 -1
  331. package/dist/utils/sleep.d.ts +9 -0
  332. package/dist/utils/sleep.d.ts.map +1 -0
  333. package/dist/utils/sleep.js +25 -0
  334. package/dist/utils/sleep.js.map +1 -0
  335. package/dist/utils/storage.d.ts.map +1 -1
  336. package/dist/utils/storage.js +14 -8
  337. package/dist/utils/storage.js.map +1 -1
  338. package/dist/utils/store-health.d.ts +37 -0
  339. package/dist/utils/store-health.d.ts.map +1 -0
  340. package/dist/utils/store-health.js +92 -0
  341. package/dist/utils/store-health.js.map +1 -0
  342. package/dist/utils/store-migrations.d.ts +33 -0
  343. package/dist/utils/store-migrations.d.ts.map +1 -0
  344. package/dist/utils/store-migrations.js +41 -0
  345. package/dist/utils/store-migrations.js.map +1 -0
  346. package/dist/utils/store-paths.d.ts +33 -0
  347. package/dist/utils/store-paths.d.ts.map +1 -0
  348. package/dist/utils/store-paths.js +48 -0
  349. package/dist/utils/store-paths.js.map +1 -0
  350. package/dist/utils/store-rows.d.ts +22 -0
  351. package/dist/utils/store-rows.d.ts.map +1 -0
  352. package/dist/utils/store-rows.js +62 -0
  353. package/dist/utils/store-rows.js.map +1 -0
  354. package/dist/utils/store-schema.d.ts +16 -0
  355. package/dist/utils/store-schema.d.ts.map +1 -0
  356. package/dist/utils/store-schema.js +168 -0
  357. package/dist/utils/store-schema.js.map +1 -0
  358. package/dist/utils/store.d.ts +34 -0
  359. package/dist/utils/store.d.ts.map +1 -0
  360. package/dist/utils/store.js +157 -0
  361. package/dist/utils/store.js.map +1 -0
  362. package/dist/utils/subtitles.d.ts +7 -0
  363. package/dist/utils/subtitles.d.ts.map +1 -0
  364. package/dist/utils/subtitles.js +30 -0
  365. package/dist/utils/subtitles.js.map +1 -0
  366. package/dist/utils/thumbnail-entry.d.ts +19 -0
  367. package/dist/utils/thumbnail-entry.d.ts.map +1 -0
  368. package/dist/utils/thumbnail-entry.js +119 -0
  369. package/dist/utils/thumbnail-entry.js.map +1 -0
  370. package/dist/utils/thumbnail-fetch.d.ts +37 -0
  371. package/dist/utils/thumbnail-fetch.d.ts.map +1 -0
  372. package/dist/utils/thumbnail-fetch.js +89 -0
  373. package/dist/utils/thumbnail-fetch.js.map +1 -0
  374. package/dist/utils/thumbnail-ladder.d.ts +44 -0
  375. package/dist/utils/thumbnail-ladder.d.ts.map +1 -0
  376. package/dist/utils/thumbnail-ladder.js +68 -0
  377. package/dist/utils/thumbnail-ladder.js.map +1 -0
  378. package/dist/utils/thumbnail-listing.d.ts +16 -0
  379. package/dist/utils/thumbnail-listing.d.ts.map +1 -0
  380. package/dist/utils/thumbnail-listing.js +26 -0
  381. package/dist/utils/thumbnail-listing.js.map +1 -0
  382. package/dist/utils/thumbnail-paths.d.ts +20 -0
  383. package/dist/utils/thumbnail-paths.d.ts.map +1 -0
  384. package/dist/utils/thumbnail-paths.js +35 -0
  385. package/dist/utils/thumbnail-paths.js.map +1 -0
  386. package/dist/utils/thumbnail-store.d.ts +19 -0
  387. package/dist/utils/thumbnail-store.d.ts.map +1 -0
  388. package/dist/utils/thumbnail-store.js +125 -0
  389. package/dist/utils/thumbnail-store.js.map +1 -0
  390. package/dist/utils/transcript-cache.d.ts +20 -0
  391. package/dist/utils/transcript-cache.d.ts.map +1 -0
  392. package/dist/utils/transcript-cache.js +127 -0
  393. package/dist/utils/transcript-cache.js.map +1 -0
  394. package/dist/utils/transcript-search.d.ts +20 -0
  395. package/dist/utils/transcript-search.d.ts.map +1 -0
  396. package/dist/utils/transcript-search.js +35 -0
  397. package/dist/utils/transcript-search.js.map +1 -0
  398. package/dist/utils/transcript.d.ts +10 -31
  399. package/dist/utils/transcript.d.ts.map +1 -1
  400. package/dist/utils/transcript.js +2 -64
  401. package/dist/utils/transcript.js.map +1 -1
  402. package/dist/utils/validate-youtube.d.ts +35 -0
  403. package/dist/utils/validate-youtube.d.ts.map +1 -0
  404. package/dist/utils/validate-youtube.js +87 -0
  405. package/dist/utils/validate-youtube.js.map +1 -0
  406. package/dist/utils/validate.d.ts +11 -4
  407. package/dist/utils/validate.d.ts.map +1 -1
  408. package/dist/utils/validate.js +43 -36
  409. package/dist/utils/validate.js.map +1 -1
  410. package/dist/utils/version.d.ts +2 -0
  411. package/dist/utils/version.d.ts.map +1 -0
  412. package/dist/utils/version.js +19 -0
  413. package/dist/utils/version.js.map +1 -0
  414. package/dist/utils/video-stats-cache.d.ts +16 -0
  415. package/dist/utils/video-stats-cache.d.ts.map +1 -0
  416. package/dist/utils/video-stats-cache.js +81 -0
  417. package/dist/utils/video-stats-cache.js.map +1 -0
  418. package/dist/utils/video-stats.d.ts +186 -0
  419. package/dist/utils/video-stats.d.ts.map +1 -0
  420. package/dist/utils/video-stats.js +168 -0
  421. package/dist/utils/video-stats.js.map +1 -0
  422. package/dist/utils/video-store.d.ts +69 -0
  423. package/dist/utils/video-store.d.ts.map +1 -0
  424. package/dist/utils/video-store.js +77 -0
  425. package/dist/utils/video-store.js.map +1 -0
  426. package/dist/utils/youtube-channel.d.ts +51 -0
  427. package/dist/utils/youtube-channel.d.ts.map +1 -0
  428. package/dist/utils/youtube-channel.js +91 -0
  429. package/dist/utils/youtube-channel.js.map +1 -0
  430. package/dist/utils/youtube-download.d.ts +27 -0
  431. package/dist/utils/youtube-download.d.ts.map +1 -0
  432. package/dist/utils/youtube-download.js +118 -0
  433. package/dist/utils/youtube-download.js.map +1 -0
  434. package/dist/utils/youtube-schemas.d.ts +492 -0
  435. package/dist/utils/youtube-schemas.d.ts.map +1 -0
  436. package/dist/utils/youtube-schemas.js +148 -0
  437. package/dist/utils/youtube-schemas.js.map +1 -0
  438. package/dist/utils/youtube-search.d.ts +14 -0
  439. package/dist/utils/youtube-search.d.ts.map +1 -0
  440. package/dist/utils/youtube-search.js +39 -0
  441. package/dist/utils/youtube-search.js.map +1 -0
  442. package/dist/utils/youtube-url.d.ts +25 -0
  443. package/dist/utils/youtube-url.d.ts.map +1 -0
  444. package/dist/utils/youtube-url.js +75 -0
  445. package/dist/utils/youtube-url.js.map +1 -0
  446. package/dist/utils/youtube-video.d.ts +68 -0
  447. package/dist/utils/youtube-video.d.ts.map +1 -0
  448. package/dist/utils/youtube-video.js +110 -0
  449. package/dist/utils/youtube-video.js.map +1 -0
  450. package/dist/utils/youtube.d.ts +10 -122
  451. package/dist/utils/youtube.d.ts.map +1 -1
  452. package/dist/utils/youtube.js +11 -486
  453. package/dist/utils/youtube.js.map +1 -1
  454. package/dist/utils/ytdlp-env.d.ts +33 -0
  455. package/dist/utils/ytdlp-env.d.ts.map +1 -0
  456. package/dist/utils/ytdlp-env.js +121 -0
  457. package/dist/utils/ytdlp-env.js.map +1 -0
  458. package/dist/utils/ytdlp-limiter.d.ts +27 -0
  459. package/dist/utils/ytdlp-limiter.d.ts.map +1 -0
  460. package/dist/utils/ytdlp-limiter.js +85 -0
  461. package/dist/utils/ytdlp-limiter.js.map +1 -0
  462. package/dist/utils/ytdlp-pacer.d.ts +64 -0
  463. package/dist/utils/ytdlp-pacer.d.ts.map +1 -0
  464. package/dist/utils/ytdlp-pacer.js +165 -0
  465. package/dist/utils/ytdlp-pacer.js.map +1 -0
  466. package/dist/utils/ytdlp-parse.d.ts +13 -0
  467. package/dist/utils/ytdlp-parse.d.ts.map +1 -0
  468. package/dist/utils/ytdlp-parse.js +48 -0
  469. package/dist/utils/ytdlp-parse.js.map +1 -0
  470. package/dist/utils/ytdlp-stream.d.ts +32 -0
  471. package/dist/utils/ytdlp-stream.d.ts.map +1 -0
  472. package/dist/utils/ytdlp-stream.js +61 -0
  473. package/dist/utils/ytdlp-stream.js.map +1 -0
  474. package/dist/utils/ytdlp.d.ts +23 -18
  475. package/dist/utils/ytdlp.d.ts.map +1 -1
  476. package/dist/utils/ytdlp.js +56 -134
  477. package/dist/utils/ytdlp.js.map +1 -1
  478. package/package.json +3 -2
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The human- and model-readable form of a receipt.
3
+ *
4
+ * `structuredContent` carries the machine-readable truth, but a model reads
5
+ * the text block, and that is where an overclaim actually happens. So every
6
+ * extraction tool prepends this, from one function, rather than each writing
7
+ * its own wording and drifting.
8
+ *
9
+ * The incomplete form says three things on purpose: the numbers, why it
10
+ * stopped, and an explicit instruction not to describe it as everything. The
11
+ * last one reads as belt-and-braces next to the `complete: false` field, and
12
+ * it is: the field is for programs, the sentence is for the reader.
13
+ */
14
+ const UNITS = {
15
+ 'channel-catalog': 'videos',
16
+ 'video-details': 'videos',
17
+ 'video-comments': 'comments',
18
+ };
19
+ const WHY = {
20
+ COMPLETE: 'complete',
21
+ CAP_REACHED: 'stopped by the requested cap',
22
+ BUDGET_SPENT: 'stopped when the time budget ran out',
23
+ RATE_LIMITED: 'stopped because YouTube throttled this client',
24
+ CANCELLED: 'cancelled by the client',
25
+ TIMEOUT: 'timed out',
26
+ SOURCE_REFUSED: 'YouTube refused this content to the current session',
27
+ COMMENTS_DISABLED: 'comments are turned off for this video',
28
+ SOURCE_SILENT: 'YouTube states no total, so exhaustion cannot be proven',
29
+ PARTIAL_ERROR: 'some items failed',
30
+ NOT_ATTEMPTED: 'not attempted yet',
31
+ };
32
+ function count(value) {
33
+ return value.toLocaleString('en-US');
34
+ }
35
+ function ratio(coverage) {
36
+ if (coverage.expected === undefined || coverage.expected === 0)
37
+ return '';
38
+ const percent = (coverage.have / coverage.expected) * 100;
39
+ return ` (${percent < 0.1 && percent > 0 ? '<0.1' : percent.toFixed(1)}%)`;
40
+ }
41
+ export function renderCoverage(coverage) {
42
+ const unit = UNITS[coverage.scope];
43
+ const of = coverage.expected === undefined
44
+ ? `${count(coverage.have)} ${unit}`
45
+ : `${count(coverage.have)} of ${coverage.expectedSource === 'youtube:comment_count' ? 'about ' : ''}${count(coverage.expected)} ${unit}${ratio(coverage)}`;
46
+ const lines = [`Coverage: ${of} for ${coverage.targetId}.`];
47
+ if (coverage.complete) {
48
+ lines.push(`COMPLETE — ${WHY[coverage.reason]}, harvested ${coverage.harvestedAt}.`);
49
+ if (coverage.staleAfter !== undefined) {
50
+ lines.push(`Re-check after ${coverage.staleAfter} (this can change upstream).`);
51
+ }
52
+ return lines.join('\n');
53
+ }
54
+ const bias = coverage.sortApplied === 'top' && coverage.limitApplied !== undefined
55
+ ? ', sorted by "top" (a biased prefix, not a random sample)'
56
+ : '';
57
+ lines.push(`INCOMPLETE — ${WHY[coverage.reason]}${bias}.`, `Do not describe this as the full ${unit === 'comments' ? 'comment history' : 'video list'}.`);
58
+ if (coverage.note !== undefined)
59
+ lines.push(coverage.note);
60
+ return lines.join('\n');
61
+ }
62
+ //# sourceMappingURL=coverage-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage-text.js","sourceRoot":"","sources":["../../src/utils/coverage-text.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AAEH,MAAM,KAAK,GAAsC;IAC/C,iBAAiB,EAAE,QAAQ;IAC3B,eAAe,EAAE,QAAQ;IACzB,gBAAgB,EAAE,UAAU;CAC7B,CAAC;AAEF,MAAM,GAAG,GAAuC;IAC9C,QAAQ,EAAE,UAAU;IACpB,WAAW,EAAE,8BAA8B;IAC3C,YAAY,EAAE,sCAAsC;IACpD,YAAY,EAAE,+CAA+C;IAC7D,SAAS,EAAE,yBAAyB;IACpC,OAAO,EAAE,WAAW;IACpB,cAAc,EAAE,qDAAqD;IACrE,iBAAiB,EAAE,wCAAwC;IAC3D,aAAa,EAAE,yDAAyD;IACxE,aAAa,EAAE,mBAAmB;IAClC,aAAa,EAAE,mBAAmB;CACnC,CAAC;AAEF,SAAS,KAAK,CAAC,KAAa;IAC1B,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,KAAK,CAAC,QAAkB;IAC/B,IAAI,QAAQ,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,QAAQ,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1E,MAAM,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC;IAC1D,OAAO,KAAK,OAAO,GAAG,GAAG,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAkB;IAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,EAAE,GACN,QAAQ,CAAC,QAAQ,KAAK,SAAS;QAC7B,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE;QACnC,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,QAAQ,CAAC,cAAc,KAAK,uBAAuB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;IAE/J,MAAM,KAAK,GAAG,CAAC,aAAa,EAAE,QAAQ,QAAQ,CAAC,QAAQ,GAAG,CAAC,CAAC;IAE5D,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,cAAc,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,eAAe,QAAQ,CAAC,WAAW,GAAG,CAAC,CAAC;QACrF,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,kBAAkB,QAAQ,CAAC,UAAU,8BAA8B,CAAC,CAAC;QAClF,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,IAAI,GACR,QAAQ,CAAC,WAAW,KAAK,KAAK,IAAI,QAAQ,CAAC,YAAY,KAAK,SAAS;QACnE,CAAC,CAAC,0DAA0D;QAC5D,CAAC,CAAC,EAAE,CAAC;IAET,KAAK,CAAC,IAAI,CACR,gBAAgB,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,EAC9C,oCAAoC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,YAAY,GAAG,CAC9F,CAAC;IACF,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE3D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { type Coverage, type CompletenessReason, type ExpectedSource, type HarvestScope } from '../harvest-schemas.js';
2
+ /**
3
+ * The one constructor for a completeness receipt.
4
+ *
5
+ * A wrong `complete: true` is the worst thing this server can emit: it is the
6
+ * difference between a consumer saying "here is a sample of the comments" and
7
+ * "here is the video's full comment history". So `complete` is not a field a
8
+ * caller sets — it is derived here, and the derivation is guarded by
9
+ * invariants that are asserted in code rather than merely documented.
10
+ *
11
+ * The rule that does the most work: `expected` is what the SOURCE said, and is
12
+ * never derived from `have`. Copying the number we collected into the field
13
+ * that means "how many there are" would turn "we stopped" into "there was
14
+ * nothing more", silently, and every check downstream would agree with it.
15
+ */
16
+ /** A complete receipt is still only as fresh as its harvest; comments accrue. */
17
+ export declare const RECEIPT_STALE_MS: number;
18
+ /** Beyond this, receipts are summarised rather than listed in a tool result. */
19
+ export declare const COVERAGE_IN_RESULT_LIMIT = 100;
20
+ export interface ExpectedTotal {
21
+ value: number;
22
+ source: ExpectedSource;
23
+ }
24
+ export interface CoverageInput {
25
+ scope: HarvestScope;
26
+ targetId: string;
27
+ have: number;
28
+ source: string;
29
+ /**
30
+ * Only ever an explicit pair or nothing. There is deliberately no code path
31
+ * that accepts a bare number, because the number would have to come from
32
+ * somewhere and `have` is the nearest thing to hand.
33
+ */
34
+ expected?: ExpectedTotal;
35
+ ranToExhaustion?: boolean;
36
+ reason?: CompletenessReason;
37
+ limitApplied?: number;
38
+ sortApplied?: 'top' | 'new';
39
+ resumeToken?: string;
40
+ harvestedAt?: Date;
41
+ note?: string;
42
+ }
43
+ /** Thrown for a receipt that contradicts itself. Not a YouTubeError: no caller can fix it. */
44
+ export declare class CoverageInvariantError extends Error {
45
+ constructor(message: string);
46
+ }
47
+ /**
48
+ * Checks a receipt against every invariant.
49
+ *
50
+ * Called inside `coverageOf` and again by handlers before a result goes out,
51
+ * because the cost of the second call is nothing and the cost of a wrong
52
+ * `complete: true` reaching a consumer is the whole point of this module.
53
+ */
54
+ export declare function assertCoverageConsistent(coverage: Coverage): void;
55
+ /** Builds a validated receipt. The only way to make one. */
56
+ export declare function coverageOf(input: CoverageInput): Coverage;
57
+ //# sourceMappingURL=coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../src/utils/coverage.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;GAaG;AAEH,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,QAAiB,CAAC;AAE/C,gFAAgF;AAChF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,8FAA8F;AAC9F,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAwCD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CA0CjE;AAED,4DAA4D;AAC5D,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,QAAQ,CA8BzD"}
@@ -0,0 +1,142 @@
1
+ import { coverageSchema, } from '../harvest-schemas.js';
2
+ /**
3
+ * The one constructor for a completeness receipt.
4
+ *
5
+ * A wrong `complete: true` is the worst thing this server can emit: it is the
6
+ * difference between a consumer saying "here is a sample of the comments" and
7
+ * "here is the video's full comment history". So `complete` is not a field a
8
+ * caller sets — it is derived here, and the derivation is guarded by
9
+ * invariants that are asserted in code rather than merely documented.
10
+ *
11
+ * The rule that does the most work: `expected` is what the SOURCE said, and is
12
+ * never derived from `have`. Copying the number we collected into the field
13
+ * that means "how many there are" would turn "we stopped" into "there was
14
+ * nothing more", silently, and every check downstream would agree with it.
15
+ */
16
+ /** A complete receipt is still only as fresh as its harvest; comments accrue. */
17
+ export const RECEIPT_STALE_MS = 7 * 86_400_000;
18
+ /** Beyond this, receipts are summarised rather than listed in a tool result. */
19
+ export const COVERAGE_IN_RESULT_LIMIT = 100;
20
+ /** Thrown for a receipt that contradicts itself. Not a YouTubeError: no caller can fix it. */
21
+ export class CoverageInvariantError extends Error {
22
+ constructor(message) {
23
+ super(`Coverage invariant violated: ${message}`);
24
+ this.name = 'CoverageInvariantError';
25
+ }
26
+ }
27
+ function capWasBinding(input) {
28
+ return input.limitApplied !== undefined && input.have >= input.limitApplied;
29
+ }
30
+ /**
31
+ * Whether this server can prove it holds everything in scope.
32
+ *
33
+ * Two independent ways to prove it, and a cap overrides both:
34
+ *
35
+ * - the source stated a total and we hold at least that many, or
36
+ * - the source's own iteration ran out (`source-exhausted`).
37
+ *
38
+ * Exhaustion alone is not proof. Measured against yt-dlp 2026.08.19: a run
39
+ * capped at 40 parent comments returned a non-null `comment_count` of 58,
40
+ * because `itertools.islice` ends iteration at the cap exactly as a natural
41
+ * end would. So a binding cap disqualifies completeness even when the source
42
+ * says it finished.
43
+ */
44
+ function isProvablyComplete(input) {
45
+ if (capWasBinding(input))
46
+ return false;
47
+ if (input.expected !== undefined) {
48
+ if (input.expected.source === 'source-exhausted')
49
+ return input.ranToExhaustion !== false;
50
+ return input.have >= input.expected.value;
51
+ }
52
+ return false;
53
+ }
54
+ function derivedReason(input, complete) {
55
+ if (complete)
56
+ return 'COMPLETE';
57
+ if (input.reason !== undefined && input.reason !== 'COMPLETE')
58
+ return input.reason;
59
+ if (capWasBinding(input))
60
+ return 'CAP_REACHED';
61
+ // Nothing said there was more, and nothing proved there was not. That is not
62
+ // completeness; it is the absence of evidence, and it has its own name.
63
+ return 'SOURCE_SILENT';
64
+ }
65
+ /**
66
+ * Checks a receipt against every invariant.
67
+ *
68
+ * Called inside `coverageOf` and again by handlers before a result goes out,
69
+ * because the cost of the second call is nothing and the cost of a wrong
70
+ * `complete: true` reaching a consumer is the whole point of this module.
71
+ */
72
+ export function assertCoverageConsistent(coverage) {
73
+ const fail = (message) => {
74
+ throw new CoverageInvariantError(message);
75
+ };
76
+ if (coverage.complete && coverage.reason !== 'COMPLETE') {
77
+ fail(`complete is true but reason is ${coverage.reason}`);
78
+ }
79
+ if (!coverage.complete && coverage.reason === 'COMPLETE') {
80
+ fail('reason is COMPLETE but complete is false');
81
+ }
82
+ if ((coverage.expected === undefined) !== (coverage.expectedSource === undefined)) {
83
+ fail('expected and expectedSource must be present or absent together');
84
+ }
85
+ if (coverage.complete) {
86
+ const exhausted = coverage.expectedSource === 'source-exhausted';
87
+ const enough = coverage.expected !== undefined && coverage.have >= coverage.expected;
88
+ if (!exhausted && !enough) {
89
+ fail('complete is true without either a met expected total or an exhausted source');
90
+ }
91
+ }
92
+ // There is deliberately no separate "COMPLETE without an expected total"
93
+ // guard. It reads like a necessary invariant and is unreachable: reaching it
94
+ // needs reason COMPLETE, which the checks above already force to pair with
95
+ // complete:true, which in turn already demands either a met total or an
96
+ // exhausted source. An unreachable guard is not defence in depth, it is code
97
+ // no test can justify.
98
+ // One-directional on purpose. "A token exactly when incomplete" is the
99
+ // tempting symmetry, and it is wrong: plenty of incomplete receipts have
100
+ // nothing to resume — SOURCE_REFUSED, COMMENTS_DISABLED, a caller that
101
+ // simply did not supply one. What must never happen is a token on a
102
+ // complete receipt, which would invite a caller to page past the end.
103
+ if (coverage.resumeToken !== undefined && coverage.complete) {
104
+ fail('a complete receipt cannot carry a resumeToken');
105
+ }
106
+ if (coverage.limitApplied !== undefined &&
107
+ coverage.have >= coverage.limitApplied &&
108
+ coverage.reason === 'COMPLETE') {
109
+ fail('a binding cap cannot produce COMPLETE');
110
+ }
111
+ }
112
+ /** Builds a validated receipt. The only way to make one. */
113
+ export function coverageOf(input) {
114
+ const complete = isProvablyComplete(input);
115
+ const reason = derivedReason(input, complete);
116
+ const harvestedAt = input.harvestedAt ?? new Date();
117
+ const coverage = coverageSchema.parse({
118
+ scope: input.scope,
119
+ targetId: input.targetId,
120
+ complete,
121
+ reason,
122
+ have: input.have,
123
+ ...(input.expected === undefined
124
+ ? {}
125
+ : { expected: input.expected.value, expectedSource: input.expected.source }),
126
+ ...(input.ranToExhaustion === undefined ? {} : { ranToExhaustion: input.ranToExhaustion }),
127
+ source: input.source,
128
+ harvestedAt: harvestedAt.toISOString(),
129
+ ...(complete
130
+ ? { staleAfter: new Date(harvestedAt.getTime() + RECEIPT_STALE_MS).toISOString() }
131
+ : {}),
132
+ ...(input.limitApplied === undefined ? {} : { limitApplied: input.limitApplied }),
133
+ ...(input.sortApplied === undefined ? {} : { sortApplied: input.sortApplied }),
134
+ // A resume token is meaningless on a complete receipt, and its absence is
135
+ // how a caller knows there is nothing left to ask for.
136
+ ...(complete || input.resumeToken === undefined ? {} : { resumeToken: input.resumeToken }),
137
+ ...(input.note === undefined ? {} : { note: input.note }),
138
+ });
139
+ assertCoverageConsistent(coverage);
140
+ return coverage;
141
+ }
142
+ //# sourceMappingURL=coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../src/utils/coverage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Reading configuration from the environment without trusting it.
3
+ *
4
+ * A bare `Number()` of an environment value turns a typo into `NaN`, and `NaN` compares
5
+ * false with everything: a concurrency limit of `NaN` queued every yt-dlp call
6
+ * forever, and a cache TTL of `NaN` never expired anything. Every numeric or
7
+ * enumerated setting therefore comes through here, where an unusable value
8
+ * falls back to the default and says so once on stderr.
9
+ */
10
+ export interface IntBounds {
11
+ min?: number;
12
+ max?: number;
13
+ }
14
+ export declare function envString(name: string, env?: NodeJS.ProcessEnv): string | undefined;
15
+ export declare function envInt(name: string, fallback: number, bounds?: IntBounds, env?: NodeJS.ProcessEnv): number;
16
+ export declare function envBool(name: string, fallback: boolean, env?: NodeJS.ProcessEnv): boolean;
17
+ export declare function envList(name: string, env?: NodeJS.ProcessEnv): string[];
18
+ /**
19
+ * One of a closed set of values, or `undefined` when unset.
20
+ *
21
+ * Unlike the helpers above this throws on an unknown value: a browser name or
22
+ * proxy scheme that is misspelt is a configuration mistake the operator wants
23
+ * to hear about at boot, not a setting to quietly ignore.
24
+ */
25
+ export declare function envEnum<T extends string>(name: string, values: readonly T[], env?: NodeJS.ProcessEnv): T | undefined;
26
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/utils/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAMD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAGhG;AAED,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,SAAc,EACtB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAkBR;AAKD,wBAAgB,OAAO,CACrB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,EACjB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAOT;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,EAAE,CAKpF;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EACtC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,CAAC,EAAE,EACpB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,CAAC,GAAG,SAAS,CASf"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Reading configuration from the environment without trusting it.
3
+ *
4
+ * A bare `Number()` of an environment value turns a typo into `NaN`, and `NaN` compares
5
+ * false with everything: a concurrency limit of `NaN` queued every yt-dlp call
6
+ * forever, and a cache TTL of `NaN` never expired anything. Every numeric or
7
+ * enumerated setting therefore comes through here, where an unusable value
8
+ * falls back to the default and says so once on stderr.
9
+ */
10
+ function warn(name, reason) {
11
+ console.error(`Ignoring ${name}: ${reason}.`);
12
+ }
13
+ export function envString(name, env = process.env) {
14
+ const value = env[name]?.trim();
15
+ return value === undefined || value === '' ? undefined : value;
16
+ }
17
+ export function envInt(name, fallback, bounds = {}, env = process.env) {
18
+ const raw = envString(name, env);
19
+ if (raw === undefined)
20
+ return fallback;
21
+ const parsed = Number(raw);
22
+ if (!Number.isInteger(parsed)) {
23
+ warn(name, `"${raw}" is not a whole number, using ${fallback}`);
24
+ return fallback;
25
+ }
26
+ if (bounds.min !== undefined && parsed < bounds.min) {
27
+ warn(name, `${parsed} is below the minimum of ${bounds.min}, using ${fallback}`);
28
+ return fallback;
29
+ }
30
+ if (bounds.max !== undefined && parsed > bounds.max) {
31
+ warn(name, `${parsed} is above the maximum of ${bounds.max}, using ${fallback}`);
32
+ return fallback;
33
+ }
34
+ return parsed;
35
+ }
36
+ const TRUE_WORDS = new Set(['1', 'true', 'yes', 'on']);
37
+ const FALSE_WORDS = new Set(['0', 'false', 'no', 'off']);
38
+ export function envBool(name, fallback, env = process.env) {
39
+ const raw = envString(name, env)?.toLowerCase();
40
+ if (raw === undefined)
41
+ return fallback;
42
+ if (TRUE_WORDS.has(raw))
43
+ return true;
44
+ if (FALSE_WORDS.has(raw))
45
+ return false;
46
+ warn(name, `"${raw}" is not a boolean, using ${String(fallback)}`);
47
+ return fallback;
48
+ }
49
+ export function envList(name, env = process.env) {
50
+ return (env[name] ?? '')
51
+ .split(',')
52
+ .map((value) => value.trim())
53
+ .filter(Boolean);
54
+ }
55
+ /**
56
+ * One of a closed set of values, or `undefined` when unset.
57
+ *
58
+ * Unlike the helpers above this throws on an unknown value: a browser name or
59
+ * proxy scheme that is misspelt is a configuration mistake the operator wants
60
+ * to hear about at boot, not a setting to quietly ignore.
61
+ */
62
+ export function envEnum(name, values, env = process.env) {
63
+ const raw = envString(name, env);
64
+ if (raw === undefined)
65
+ return undefined;
66
+ const match = values.find((value) => value === raw.toLowerCase());
67
+ if (match === undefined) {
68
+ throw new Error(`${name} must be one of ${values.join(', ')}; got "${raw}".`);
69
+ }
70
+ return match;
71
+ }
72
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/utils/env.ts"],"names":[],"mappings":"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"}
@@ -7,7 +7,7 @@ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
7
7
  * than a wall of yt-dlp stderr it has to guess at — and never a leaked command
8
8
  * line or stack trace.
9
9
  */
10
- export type YouTubeErrorCode = 'PRIVATE' | 'AGE_GATED' | 'MEMBERS_ONLY' | 'PREMIUM_ONLY' | 'LOGIN_REQUIRED' | 'NOT_FOUND' | 'NO_CAPTIONS' | 'LIVE_NOT_ENDED' | 'RATE_LIMITED' | 'TIMEOUT' | 'CANCELLED' | 'YTDLP_MISSING' | 'FFMPEG_MISSING' | 'YTDLP_FAILED' | 'MALFORMED_RESPONSE' | 'INVALID_INPUT';
10
+ export type YouTubeErrorCode = 'PRIVATE' | 'AGE_GATED' | 'MEMBERS_ONLY' | 'PREMIUM_ONLY' | 'LOGIN_REQUIRED' | 'BOT_CHECK' | 'NOT_FOUND' | 'NO_CAPTIONS' | 'LIVE_NOT_ENDED' | 'RATE_LIMITED' | 'TIMEOUT' | 'CANCELLED' | 'YTDLP_MISSING' | 'FFMPEG_MISSING' | 'YTDLP_FAILED' | 'FETCH_FAILED' | 'MALFORMED_RESPONSE' | 'INVALID_INPUT' | 'STORE_UNAVAILABLE' | 'STORE_CORRUPT';
11
11
  export interface YouTubeErrorOptions {
12
12
  /** What the caller should try next. Shown to the model verbatim. */
13
13
  nextStep?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,WAAW,GACX,cAAc,GACd,cAAc,GACd,gBAAgB,GAChB,WAAW,GACX,aAAa,GACb,gBAAgB,GAChB,cAAc,GACd,SAAS,GACT,WAAW,GACX,eAAe,GACf,gBAAgB,GAChB,cAAc,GACd,oBAAoB,GACpB,eAAe,CAAC;AAEpB,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAEhB,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB;IAQtF,iFAAiF;IACjF,aAAa,IAAI,MAAM;CAGxB;AA6ED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,YAAY,CAkBlF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,YAAY,GAAG,SAAS,CA4C3B;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAQ3D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAM1D"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,WAAW,GACX,cAAc,GACd,cAAc,GACd,gBAAgB,GAChB,WAAW,GACX,WAAW,GACX,aAAa,GACb,gBAAgB,GAChB,cAAc,GACd,SAAS,GACT,WAAW,GACX,eAAe,GACf,gBAAgB,GAChB,cAAc,GACd,cAAc,GACd,oBAAoB,GACpB,eAAe,GACf,mBAAmB,GACnB,eAAe,CAAC;AAEpB,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAEhB,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB;IAQtF,iFAAiF;IACjF,aAAa,IAAI,MAAM;CAGxB;AAwGD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,YAAY,CAkBlF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,YAAY,GAAG,SAAS,CA2C3B;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAQ3D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAM1D"}
@@ -14,7 +14,33 @@ export class YouTubeError extends Error {
14
14
  return this.nextStep ? `${this.message}\n\n${this.nextStep}` : this.message;
15
15
  }
16
16
  }
17
+ /** The two settings that let yt-dlp read what a signed-out client cannot. */
18
+ const COOKIE_HINT = 'Set YOUTUBE_MCP_COOKIES_FROM_BROWSER (for example chrome) or YOUTUBE_MCP_COOKIES_FILE and restart the server; see the README section on signed-in content.';
17
19
  const RULES = [
20
+ {
21
+ // The one YouTube-authored sentence matched here, and the reason is that
22
+ // it was observed rather than remembered: on 2026-09-05 every per-video
23
+ // read from this project's own address printed it, verbatim, while flat
24
+ // channel listings kept working — so it is about the client, not the
25
+ // video, and the remedy is different. Matched on the words before the
26
+ // apostrophe, which YouTube writes as U+2019. Should YouTube reword it,
27
+ // the login hint yt-dlp appends still lands on LOGIN_REQUIRED below, whose
28
+ // next step names the same cookie settings. Listed first because both
29
+ // strings appear in the same output.
30
+ code: 'BOT_CHECK',
31
+ emits: 'Sign in to confirm you',
32
+ source: 'playabilityStatus.reason, re-raised by yt_dlp/extractor/youtube/_video.py',
33
+ message: 'YouTube is asking this client to prove it is not a bot.',
34
+ // Ordered by what was measured on 2026-09-06, not by what sounds most
35
+ // technical. From a datacenter address every account-free lever was
36
+ // installed and tested — a PO token provider (yt-dlp never even reaches
37
+ // the point of requesting one; it fails at the player response first),
38
+ // eight player clients, a 15-minute cooldown, and curl_cffi TLS
39
+ // impersonation — and none of them changed the answer. So the address
40
+ // comes first and the PO token provider comes last: leading with a remedy
41
+ // that was measured not to work sends the reader down a dead end.
42
+ nextStep: `Per-video reads are refused from this address. Datacenter and VPN/exit-node addresses are gated regardless of client settings, so check whether this host egresses through one. Otherwise: ${COOKIE_HINT} On a residential address a missing PO token provider can be the cause instead — check \`check_health\`.`,
43
+ },
18
44
  {
19
45
  code: 'RATE_LIMITED',
20
46
  // exceptions.py formats `HTTP Error {status}: {reason}`; the reason for 429
@@ -55,7 +81,7 @@ const RULES = [
55
81
  emits: 'for tips on effectively exporting YouTube cookies',
56
82
  source: 'yt_dlp/extractor/youtube/_base.py:664',
57
83
  message: 'This video requires a signed-in YouTube account.',
58
- nextStep: 'It may be private, age-restricted or members-only. Try a different video, or configure yt-dlp with browser cookies.',
84
+ nextStep: `It may be private, age-restricted or members-only. ${COOKIE_HINT}`,
59
85
  },
60
86
  ];
61
87
  /**
@@ -108,7 +134,7 @@ export function classifyPlayability(fields) {
108
134
  });
109
135
  case 'needs_auth':
110
136
  return new YouTubeError('AGE_GATED', 'This video is age-restricted and requires a signed-in account.', {
111
- nextStep: 'Age-gated videos cannot be read without authentication. Try a different video, or configure yt-dlp with browser cookies.',
137
+ nextStep: `Age-gated videos cannot be read without authentication. ${COOKIE_HINT}`,
112
138
  });
113
139
  }
114
140
  // 'is_live' is deliberately absent: a stream in progress has real metadata,
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { z } from 'zod';
2
+ import type { YouTubeError } from './errors.js';
3
+ /**
4
+ * Exclusive access to a directory's contents while a long job writes them.
5
+ *
6
+ * Two jobs on one channel would interleave their checkpoints, and the later
7
+ * write would silently drop whatever the other had recorded since it last
8
+ * read. A lock file rather than an in-process mutex because a second server
9
+ * process — a second editor window, a stray `npm start` — is the case that
10
+ * actually happens.
11
+ */
12
+ /** Long enough that a slow job is never mistaken for a dead one. */
13
+ export declare const LOCK_STALE_MS: number;
14
+ export declare const lockRecordSchema: z.ZodObject<{
15
+ pid: z.ZodNumber;
16
+ startedAt: z.ZodString;
17
+ }, "strip", z.ZodTypeAny, {
18
+ startedAt: string;
19
+ pid: number;
20
+ }, {
21
+ startedAt: string;
22
+ pid: number;
23
+ }>;
24
+ export type LockRecord = z.infer<typeof lockRecordSchema>;
25
+ /**
26
+ * Run `work` holding the lock at `path`.
27
+ *
28
+ * It is released on any exit, including cancellation. A process killed outright
29
+ * leaves one behind, which is why a lock is also broken when its owner is gone
30
+ * or when it is simply too old to be real. `held` builds the error to throw
31
+ * when someone else genuinely has it, so each caller can name its own tool.
32
+ */
33
+ export declare function withFileLock<T>(path: string, work: () => Promise<T>, held: (existing: LockRecord | undefined) => YouTubeError): Promise<T>;
34
+ //# sourceMappingURL=file-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-lock.d.ts","sourceRoot":"","sources":["../../src/utils/file-lock.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD;;;;;;;;GAQG;AAEH,oEAAoE;AACpE,eAAO,MAAM,aAAa,QAAiB,CAAC;AAE5C,eAAO,MAAM,gBAAgB;;;;;;;;;EAG3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACtB,IAAI,EAAE,CAAC,QAAQ,EAAE,UAAU,GAAG,SAAS,KAAK,YAAY,GACvD,OAAO,CAAC,CAAC,CAAC,CAoBZ"}
@@ -0,0 +1,91 @@
1
+ import { rm, writeFile } from 'node:fs/promises';
2
+ import { z } from 'zod';
3
+ import { readJsonFile } from './json-file.js';
4
+ /**
5
+ * Exclusive access to a directory's contents while a long job writes them.
6
+ *
7
+ * Two jobs on one channel would interleave their checkpoints, and the later
8
+ * write would silently drop whatever the other had recorded since it last
9
+ * read. A lock file rather than an in-process mutex because a second server
10
+ * process — a second editor window, a stray `npm start` — is the case that
11
+ * actually happens.
12
+ */
13
+ /** Long enough that a slow job is never mistaken for a dead one. */
14
+ export const LOCK_STALE_MS = 30 * 60 * 1000;
15
+ export const lockRecordSchema = z.object({
16
+ pid: z.number().int(),
17
+ startedAt: z.string(),
18
+ });
19
+ /**
20
+ * Run `work` holding the lock at `path`.
21
+ *
22
+ * It is released on any exit, including cancellation. A process killed outright
23
+ * leaves one behind, which is why a lock is also broken when its owner is gone
24
+ * or when it is simply too old to be real. `held` builds the error to throw
25
+ * when someone else genuinely has it, so each caller can name its own tool.
26
+ */
27
+ export async function withFileLock(path, work, held) {
28
+ if (!(await acquire(path))) {
29
+ const existing = await readJsonFile(path, lockRecordSchema);
30
+ // An unreadable lock is one being written this instant, which is as held as
31
+ // it gets. Only a lock whose owner is demonstrably gone gets broken.
32
+ if (existing === undefined || !isStale(existing))
33
+ throw held(existing);
34
+ await rm(path, { force: true });
35
+ // Losing this second race means another caller broke the same stale lock
36
+ // first, and is now the legitimate owner.
37
+ if (!(await acquire(path)))
38
+ throw held(undefined);
39
+ }
40
+ try {
41
+ return await work();
42
+ }
43
+ finally {
44
+ await rm(path, { force: true });
45
+ }
46
+ }
47
+ /**
48
+ * Create the lock file, or report that someone else already has.
49
+ *
50
+ * `wx` fails when the file exists, and that check and the creation are one
51
+ * operation in the kernel. Reading first and writing after is not a lock: two
52
+ * jobs starting together both see nothing and both proceed.
53
+ */
54
+ async function acquire(path) {
55
+ const lock = { pid: process.pid, startedAt: new Date().toISOString() };
56
+ try {
57
+ await writeFile(path, JSON.stringify(lock), { encoding: 'utf-8', flag: 'wx' });
58
+ return true;
59
+ }
60
+ catch (error) {
61
+ if (errorCode(error) === 'EEXIST')
62
+ return false;
63
+ throw error;
64
+ }
65
+ }
66
+ function isStale({ pid, startedAt }) {
67
+ const started = Date.parse(startedAt);
68
+ if (Number.isNaN(started) || Date.now() - started > LOCK_STALE_MS)
69
+ return true;
70
+ return !isProcessAlive(pid);
71
+ }
72
+ function isProcessAlive(pid) {
73
+ try {
74
+ // Signal 0 performs the permission and existence checks without delivering
75
+ // anything.
76
+ process.kill(pid, 0);
77
+ return true;
78
+ }
79
+ catch (error) {
80
+ // EPERM means the process exists but belongs to another user, which for
81
+ // this purpose is very much alive.
82
+ return errorCode(error) === 'EPERM';
83
+ }
84
+ }
85
+ /** The `code` a Node system error carries, when it is one. */
86
+ function errorCode(error) {
87
+ return typeof error === 'object' && error !== null && 'code' in error
88
+ ? String(error.code)
89
+ : undefined;
90
+ }
91
+ //# sourceMappingURL=file-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../../src/utils/file-lock.ts"],"names":[],"mappings":"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"}