@hraness/ghostget 0.17.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 (500) hide show
  1. package/CHANGELOG.md +458 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1359 -0
  4. package/bunfig.toml +2 -0
  5. package/dist/apple-photos-client.js +704 -0
  6. package/dist/beeper-client.js +4602 -0
  7. package/dist/client.js +1605 -0
  8. package/dist/index-26yq8q16.js +31 -0
  9. package/dist/index-9t13b4k7.js +508 -0
  10. package/dist/index-axwm9b3v.js +5 -0
  11. package/dist/index-gwk7rbyj.js +30 -0
  12. package/dist/index.js +701 -0
  13. package/dist/messaging.js +1130 -0
  14. package/dist/omni-client.js +1406 -0
  15. package/dist/whatsapp-client.js +355 -0
  16. package/docs/imessage-direct-provider.md +106 -0
  17. package/docs/rental-listings.md +94 -0
  18. package/package.json +600 -0
  19. package/skills/ghostget/SKILL.md +251 -0
  20. package/skills/ghostget/agents/openai.yaml +4 -0
  21. package/skills/ghostget/references/adapter-contract.md +181 -0
  22. package/skills/ghostget/references/apple-photos.md +115 -0
  23. package/skills/ghostget/references/article-drafts.md +235 -0
  24. package/skills/ghostget/references/code-owned-provider-scaffold.md +134 -0
  25. package/skills/ghostget/references/cross-posting-video.md +139 -0
  26. package/skills/ghostget/references/cross-posting.md +82 -0
  27. package/skills/ghostget/references/derivation.md +390 -0
  28. package/skills/ghostget/references/hraness-social-profile-stats.json +269 -0
  29. package/skills/ghostget/references/install.md +66 -0
  30. package/skills/ghostget/references/linkedin-adapter.md +442 -0
  31. package/skills/ghostget/references/messaging.md +168 -0
  32. package/skills/ghostget/references/meta-comet-contract.md +153 -0
  33. package/skills/ghostget/references/platform-patterns.md +122 -0
  34. package/skills/ghostget/references/provider-plugins.md +496 -0
  35. package/skills/ghostget/references/reddit-flair.md +78 -0
  36. package/skills/ghostget/references/safety-and-state.md +214 -0
  37. package/skills/ghostget/references/settlement-and-duplicate-cleanup.md +79 -0
  38. package/skills/ghostget/references/social-platform-routing.md +66 -0
  39. package/skills/ghostget/references/social-profile-stats.md +195 -0
  40. package/skills/ghostget/references/social-video-platform-routing.md +89 -0
  41. package/skills/ghostget/references/whatsapp-adapter.md +101 -0
  42. package/skills/ghostget/references/x-adapter.md +332 -0
  43. package/skills/ghostget/references/x-ai-disclosure.md +71 -0
  44. package/src/apple-photos-cli.ts +639 -0
  45. package/src/apple-photos-client-types.ts +168 -0
  46. package/src/apple-photos-client.ts +201 -0
  47. package/src/apple-photos-contact-evidence.ts +838 -0
  48. package/src/apple-photos-local-source.ts +1131 -0
  49. package/src/args.ts +2053 -0
  50. package/src/article-draft-document.ts +417 -0
  51. package/src/article-draft-embeds.ts +123 -0
  52. package/src/article-draft-images.ts +214 -0
  53. package/src/assets/adapter-template/wrench-adapter.json +40 -0
  54. package/src/assets/adapters/beeper/wrench-web-adapter.json +1500 -0
  55. package/src/assets/adapters/beeper/wrench-web-adapter.v1.0.0.json +129 -0
  56. package/src/assets/adapters/beeper/wrench-web-adapter.v1.1.0.json +207 -0
  57. package/src/assets/adapters/beeper/wrench-web-adapter.v2.0.0.json +1463 -0
  58. package/src/assets/adapters/beeper/wrench-web-adapter.v2.1.0.json +1463 -0
  59. package/src/assets/adapters/beeper/wrench-web-adapter.v2.2.0.json +1475 -0
  60. package/src/assets/adapters/beeper/wrench-web-adapter.v2.3.0.json +1488 -0
  61. package/src/assets/adapters/bluesky/wrench-web-adapter.json +684 -0
  62. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.0.0.json +581 -0
  63. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.1.0.json +581 -0
  64. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.2.0.json +581 -0
  65. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.3.0.json +615 -0
  66. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.4.0.json +684 -0
  67. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.5.0.json +684 -0
  68. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.6.0.json +684 -0
  69. package/src/assets/adapters/clasificados/wrench-web-adapter.json +54 -0
  70. package/src/assets/adapters/facebook/wrench-web-adapter.json +750 -0
  71. package/src/assets/adapters/facebook/wrench-web-adapter.v1.0.0.json +706 -0
  72. package/src/assets/adapters/facebook/wrench-web-adapter.v1.1.0.json +703 -0
  73. package/src/assets/adapters/facebook/wrench-web-adapter.v1.2.0.json +750 -0
  74. package/src/assets/adapters/facebook-group/wrench-web-adapter.json +682 -0
  75. package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.0.0.json +583 -0
  76. package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.1.0.json +682 -0
  77. package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.json +437 -0
  78. package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.0.0.json +383 -0
  79. package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.0.json +437 -0
  80. package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.1.json +437 -0
  81. package/src/assets/adapters/facebook-page/wrench-web-adapter.json +880 -0
  82. package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.0.0.json +740 -0
  83. package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.1.0.json +880 -0
  84. package/src/assets/adapters/github/wrench-web-adapter.json +69 -0
  85. package/src/assets/adapters/github/wrench-web-adapter.v1.0.0.json +42 -0
  86. package/src/assets/adapters/github/wrench-web-adapter.v1.1.0.json +69 -0
  87. package/src/assets/adapters/gmail/wrench-adapter.json +161 -0
  88. package/src/assets/adapters/gmail/wrench-adapter.v1.2.0.json +157 -0
  89. package/src/assets/adapters/hacker-news/wrench-web-adapter.json +315 -0
  90. package/src/assets/adapters/hacker-news/wrench-web-adapter.v1.0.0.json +315 -0
  91. package/src/assets/adapters/imessage/wrench-web-adapter.json +236 -0
  92. package/src/assets/adapters/instagram/wrench-web-adapter.json +719 -0
  93. package/src/assets/adapters/instagram/wrench-web-adapter.v1.0.0.json +627 -0
  94. package/src/assets/adapters/instagram/wrench-web-adapter.v1.1.0.json +609 -0
  95. package/src/assets/adapters/instagram/wrench-web-adapter.v1.2.0.json +640 -0
  96. package/src/assets/adapters/instagram/wrench-web-adapter.v1.3.0.json +671 -0
  97. package/src/assets/adapters/instagram/wrench-web-adapter.v1.4.0.json +710 -0
  98. package/src/assets/adapters/instagram/wrench-web-adapter.v1.5.0.json +710 -0
  99. package/src/assets/adapters/instagram/wrench-web-adapter.v1.6.0.json +719 -0
  100. package/src/assets/adapters/instagram/wrench-web-adapter.v1.7.0.json +719 -0
  101. package/src/assets/adapters/linkedin/wrench-adapter.json +442 -0
  102. package/src/assets/adapters/linkedin/wrench-adapter.v0.4.0.json +124 -0
  103. package/src/assets/adapters/linkedin/wrench-adapter.v1.0.0.json +411 -0
  104. package/src/assets/adapters/linkedin/wrench-web-adapter.json +914 -0
  105. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.0.0.json +604 -0
  106. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.1.0.json +604 -0
  107. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.10.0.json +809 -0
  108. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.11.0.json +809 -0
  109. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.12.0.json +820 -0
  110. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.13.0.json +819 -0
  111. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.14.0.json +819 -0
  112. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.15.0.json +819 -0
  113. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.16.0.json +874 -0
  114. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.17.0.json +874 -0
  115. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.18.0.json +874 -0
  116. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.19.0.json +874 -0
  117. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.2.0.json +731 -0
  118. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.3.0.json +768 -0
  119. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.4.0.json +808 -0
  120. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.5.0.json +808 -0
  121. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.6.0.json +808 -0
  122. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.7.0.json +825 -0
  123. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.8.0.json +792 -0
  124. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.9.0.json +809 -0
  125. package/src/assets/adapters/reddit/wrench-web-adapter.json +855 -0
  126. package/src/assets/adapters/reddit/wrench-web-adapter.v1.0.0.json +577 -0
  127. package/src/assets/adapters/reddit/wrench-web-adapter.v1.1.0.json +604 -0
  128. package/src/assets/adapters/reddit/wrench-web-adapter.v1.10.0.json +712 -0
  129. package/src/assets/adapters/reddit/wrench-web-adapter.v1.11.0.json +712 -0
  130. package/src/assets/adapters/reddit/wrench-web-adapter.v1.12.0.json +712 -0
  131. package/src/assets/adapters/reddit/wrench-web-adapter.v1.2.0.json +653 -0
  132. package/src/assets/adapters/reddit/wrench-web-adapter.v1.3.0.json +712 -0
  133. package/src/assets/adapters/reddit/wrench-web-adapter.v1.4.0.json +713 -0
  134. package/src/assets/adapters/reddit/wrench-web-adapter.v1.5.0.json +712 -0
  135. package/src/assets/adapters/reddit/wrench-web-adapter.v1.6.0.json +713 -0
  136. package/src/assets/adapters/reddit/wrench-web-adapter.v1.7.0.json +713 -0
  137. package/src/assets/adapters/reddit/wrench-web-adapter.v1.8.0.json +713 -0
  138. package/src/assets/adapters/reddit/wrench-web-adapter.v1.9.0.json +713 -0
  139. package/src/assets/adapters/substack/wrench-web-adapter.json +936 -0
  140. package/src/assets/adapters/substack/wrench-web-adapter.v1.0.0.json +817 -0
  141. package/src/assets/adapters/substack/wrench-web-adapter.v1.1.0.json +812 -0
  142. package/src/assets/adapters/substack/wrench-web-adapter.v1.2.0.json +812 -0
  143. package/src/assets/adapters/substack/wrench-web-adapter.v1.3.0.json +902 -0
  144. package/src/assets/adapters/substack/wrench-web-adapter.v1.4.0.json +936 -0
  145. package/src/assets/adapters/substack/wrench-web-adapter.v1.5.0.json +936 -0
  146. package/src/assets/adapters/substack/wrench-web-adapter.v1.6.0.json +936 -0
  147. package/src/assets/adapters/threads/wrench-web-adapter.json +692 -0
  148. package/src/assets/adapters/threads/wrench-web-adapter.v1.0.0.json +629 -0
  149. package/src/assets/adapters/threads/wrench-web-adapter.v1.1.0.json +623 -0
  150. package/src/assets/adapters/threads/wrench-web-adapter.v1.2.0.json +622 -0
  151. package/src/assets/adapters/threads/wrench-web-adapter.v1.3.0.json +622 -0
  152. package/src/assets/adapters/threads/wrench-web-adapter.v1.4.0.json +622 -0
  153. package/src/assets/adapters/threads/wrench-web-adapter.v1.5.0.json +699 -0
  154. package/src/assets/adapters/threads/wrench-web-adapter.v1.6.0.json +693 -0
  155. package/src/assets/adapters/threads/wrench-web-adapter.v1.7.0.json +692 -0
  156. package/src/assets/adapters/tiktok/wrench-web-adapter.json +762 -0
  157. package/src/assets/adapters/tiktok/wrench-web-adapter.v1.0.0.json +681 -0
  158. package/src/assets/adapters/tiktok/wrench-web-adapter.v1.1.0.json +708 -0
  159. package/src/assets/adapters/tiktok/wrench-web-adapter.v1.2.0.json +762 -0
  160. package/src/assets/adapters/tiktok/wrench-web-adapter.v1.3.0.json +762 -0
  161. package/src/assets/adapters/twitch/wrench-web-adapter.json +42 -0
  162. package/src/assets/adapters/twitch/wrench-web-adapter.v1.0.0.json +42 -0
  163. package/src/assets/adapters/whatsapp/wrench-web-adapter.json +400 -0
  164. package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.0.0.json +355 -0
  165. package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.1.0.json +386 -0
  166. package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.2.0.json +386 -0
  167. package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.3.0.json +400 -0
  168. package/src/assets/adapters/x/wrench-adapter.json +749 -0
  169. package/src/assets/adapters/x/wrench-adapter.v1.0.0.json +700 -0
  170. package/src/assets/adapters/x/wrench-adapter.v1.1.0.json +707 -0
  171. package/src/assets/adapters/x/wrench-web-adapter.json +712 -0
  172. package/src/assets/adapters/x/wrench-web-adapter.v1.1.0.json +579 -0
  173. package/src/assets/adapters/x/wrench-web-adapter.v1.10.0.json +685 -0
  174. package/src/assets/adapters/x/wrench-web-adapter.v1.11.0.json +712 -0
  175. package/src/assets/adapters/x/wrench-web-adapter.v1.12.0.json +712 -0
  176. package/src/assets/adapters/x/wrench-web-adapter.v1.13.0.json +712 -0
  177. package/src/assets/adapters/x/wrench-web-adapter.v1.2.0.json +578 -0
  178. package/src/assets/adapters/x/wrench-web-adapter.v1.3.0.json +610 -0
  179. package/src/assets/adapters/x/wrench-web-adapter.v1.4.0.json +617 -0
  180. package/src/assets/adapters/x/wrench-web-adapter.v1.5.0.json +634 -0
  181. package/src/assets/adapters/x/wrench-web-adapter.v1.6.0.json +632 -0
  182. package/src/assets/adapters/x/wrench-web-adapter.v1.7.0.json +649 -0
  183. package/src/assets/adapters/x/wrench-web-adapter.v1.8.0.json +649 -0
  184. package/src/assets/adapters/x/wrench-web-adapter.v1.9.0.json +683 -0
  185. package/src/assets/adapters/youtube/wrench-web-adapter.json +561 -0
  186. package/src/assets/adapters/youtube/wrench-web-adapter.v1.0.0.json +465 -0
  187. package/src/assets/adapters/youtube/wrench-web-adapter.v1.1.0.json +519 -0
  188. package/src/assets/adapters/youtube/wrench-web-adapter.v1.2.0.json +561 -0
  189. package/src/assets/adapters/youtube/wrench-web-adapter.v1.3.0.json +561 -0
  190. package/src/assets/code-owned-provider-template/plugin.test.ts.template +54 -0
  191. package/src/assets/code-owned-provider-template/plugin.ts.template +102 -0
  192. package/src/assets/code-owned-provider-template/runtime.internal.test.ts.template +44 -0
  193. package/src/assets/code-owned-provider-template/runtime.ts.template +59 -0
  194. package/src/assets/code-owned-provider-template/wrench-adapter.json.template +33 -0
  195. package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
  196. package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
  197. package/src/assets/fonts/nebula-sans/PROVENANCE.md +14 -0
  198. package/src/assets.d.ts +4 -0
  199. package/src/auth.ts +1157 -0
  200. package/src/beeper-client-types.ts +169 -0
  201. package/src/beeper-client.ts +250 -0
  202. package/src/beeper-contact-interactions-cli.ts +180 -0
  203. package/src/beeper-contact-interactions.ts +1612 -0
  204. package/src/beeper-message-bundle-v1.ts +257 -0
  205. package/src/beeper-message-like-me-cli.ts +84 -0
  206. package/src/beeper-message-like-me-export.ts +2274 -0
  207. package/src/beeper-message-like-me-recovery.ts +931 -0
  208. package/src/beeper-message-like-me-source.ts +4034 -0
  209. package/src/browser-admission.ts +928 -0
  210. package/src/browser-snapshots.ts +260 -0
  211. package/src/browser.ts +3873 -0
  212. package/src/canonical-json.ts +60 -0
  213. package/src/catalog-cli.ts +638 -0
  214. package/src/cli.ts +208 -0
  215. package/src/client-types.ts +301 -0
  216. package/src/client.ts +2726 -0
  217. package/src/confirmed-write-failure.ts +44 -0
  218. package/src/confirmed-write-model.ts +72 -0
  219. package/src/confirmed-write-platform.ts +1055 -0
  220. package/src/confirmed-write-program.ts +224 -0
  221. package/src/confirmed-write-runtime.ts +16 -0
  222. package/src/cursor-token.ts +228 -0
  223. package/src/derivation-dnr-readiness.ts +367 -0
  224. package/src/derivation-file-chooser.ts +384 -0
  225. package/src/derivation-network-boundary.ts +835 -0
  226. package/src/derivation-network-guard.ts +782 -0
  227. package/src/derivation-network-proxy-helper.ts +440 -0
  228. package/src/derivation-network-proxy.ts +385 -0
  229. package/src/derive-command-helper.ts +693 -0
  230. package/src/derive-fixtures.ts +343 -0
  231. package/src/derive-review.ts +980 -0
  232. package/src/derive.ts +4754 -0
  233. package/src/fixtures/beeper-message-like-me-v1/accounts.ndjson +2 -0
  234. package/src/fixtures/beeper-message-like-me-v1/conversations.ndjson +1 -0
  235. package/src/fixtures/beeper-message-like-me-v1/manifest.json +1 -0
  236. package/src/fixtures/beeper-message-like-me-v1/messages.ndjson +2 -0
  237. package/src/fixtures/beeper-message-like-me-v1/participants.ndjson +3 -0
  238. package/src/fixtures/beeper-message-like-me-v1/reactions.ndjson +1 -0
  239. package/src/fixtures/beeper-message-like-me-v1/tombstones.ndjson +1 -0
  240. package/src/ghostget.ts +3672 -0
  241. package/src/gmail-capture.ts +1244 -0
  242. package/src/har-internal.ts +1242 -0
  243. package/src/har.ts +743 -0
  244. package/src/index.ts +218 -0
  245. package/src/invocation-read-platform.ts +55 -0
  246. package/src/invocation-read-program.ts +93 -0
  247. package/src/invocation-read-runtime.ts +11 -0
  248. package/src/linked-device-lifecycle-admission.ts +450 -0
  249. package/src/linked-device-lifecycle-journal.ts +1560 -0
  250. package/src/linked-device-lifecycle-runtime.ts +1602 -0
  251. package/src/local-cli-admission.ts +135 -0
  252. package/src/local-cli-contracts.ts +271 -0
  253. package/src/local-cli-execution.ts +98 -0
  254. package/src/local-cli-surface-contract.ts +1510 -0
  255. package/src/local-cli-tool-identity.ts +336 -0
  256. package/src/media/archive.ts +2325 -0
  257. package/src/media/args.ts +303 -0
  258. package/src/media/cli.ts +287 -0
  259. package/src/media/doctor.ts +475 -0
  260. package/src/media/ffmpeg.ts +1330 -0
  261. package/src/media/http-capture.ts +711 -0
  262. package/src/media/http-probe.ts +593 -0
  263. package/src/media/http.ts +387 -0
  264. package/src/media/index.ts +10 -0
  265. package/src/media/local-transcription.ts +613 -0
  266. package/src/media/lock.ts +321 -0
  267. package/src/media/manifest.ts +1712 -0
  268. package/src/media/metadata.ts +906 -0
  269. package/src/media/process.ts +723 -0
  270. package/src/media/revision.ts +166 -0
  271. package/src/media/runtime-closure.ts +1063 -0
  272. package/src/media/source-router.ts +187 -0
  273. package/src/media/transcriber-config.ts +1122 -0
  274. package/src/media/transcript-persistence-model.ts +15 -0
  275. package/src/media/transcript-persistence-platform.ts +89 -0
  276. package/src/media/transcript-persistence-program.ts +39 -0
  277. package/src/media/transcript-persistence-runtime.ts +24 -0
  278. package/src/media/transcript.ts +616 -0
  279. package/src/media/utf8-order.ts +30 -0
  280. package/src/media/whisper-cpp.ts +464 -0
  281. package/src/media/whisper-language.ts +58 -0
  282. package/src/media/yt-dlp.ts +439 -0
  283. package/src/message-like-me-agentic-messaging.ts +882 -0
  284. package/src/messaging-action-store.ts +793 -0
  285. package/src/messaging-runtime.ts +2136 -0
  286. package/src/messaging-store.ts +539 -0
  287. package/src/messaging-types.ts +1663 -0
  288. package/src/messaging.ts +334 -0
  289. package/src/model.ts +2671 -0
  290. package/src/oauth-google.ts +888 -0
  291. package/src/omni-client-types.ts +306 -0
  292. package/src/omni-client.ts +2225 -0
  293. package/src/omni-limits.ts +34 -0
  294. package/src/omni-model.ts +1614 -0
  295. package/src/omni-request.ts +379 -0
  296. package/src/omni-runtime.ts +1632 -0
  297. package/src/operation-deadline.ts +189 -0
  298. package/src/path-helper.ts +1656 -0
  299. package/src/pinned-https.ts +194 -0
  300. package/src/plan-assets.ts +832 -0
  301. package/src/platform-catalog.ts +1832 -0
  302. package/src/plugins/beeper-linked-device/plugin.ts +310 -0
  303. package/src/plugins/bluesky-web/plugin.ts +178 -0
  304. package/src/plugins/clasificados-web/plugin.ts +66 -0
  305. package/src/plugins/github-web/plugin.ts +80 -0
  306. package/src/plugins/gmail-official/plugin.ts +110 -0
  307. package/src/plugins/hacker-news-web/plugin.ts +52 -0
  308. package/src/plugins/imessage-direct/plugin.ts +195 -0
  309. package/src/plugins/imessage-direct/vendor/0001-fix-keep-AppleScript-send-payloads-out-of-child-argv.patch +1430 -0
  310. package/src/plugins/imessage-direct/vendor/0002-feat-rpc-add-exact-chat-lookup.patch +225 -0
  311. package/src/plugins/imessage-direct/vendor/provenance.json +80 -0
  312. package/src/plugins/linkedin-official/plugin.ts +84 -0
  313. package/src/plugins/linkedin-web/plugin.ts +412 -0
  314. package/src/plugins/meta-web/plugin.ts +317 -0
  315. package/src/plugins/reddit-web/flair.ts +492 -0
  316. package/src/plugins/reddit-web/plugin.ts +477 -0
  317. package/src/plugins/substack-web/plugin.ts +124 -0
  318. package/src/plugins/tiktok-web/plugin.ts +67 -0
  319. package/src/plugins/twitch-web/plugin.ts +54 -0
  320. package/src/plugins/whatsapp-linked-device/plugin.ts +105 -0
  321. package/src/plugins/x-official/plugin.ts +64 -0
  322. package/src/plugins/x-web/plugin.ts +488 -0
  323. package/src/plugins/youtube-web/plugin.ts +101 -0
  324. package/src/portable-run-recovery.ts +466 -0
  325. package/src/process-identity.ts +501 -0
  326. package/src/profile-clone-helper.ts +104 -0
  327. package/src/provider-catalog-views.ts +51 -0
  328. package/src/provider-context.ts +69 -0
  329. package/src/provider-contract-definitions-x.ts +45 -0
  330. package/src/provider-contract-definitions.ts +948 -0
  331. package/src/provider-contract-input-gmail.ts +176 -0
  332. package/src/provider-contract-input-linkedin.ts +77 -0
  333. package/src/provider-contract-input-x.ts +304 -0
  334. package/src/provider-contract-planning.ts +23 -0
  335. package/src/provider-contract-semantic-identity.ts +54 -0
  336. package/src/provider-contracts.ts +251 -0
  337. package/src/provider-http.ts +723 -0
  338. package/src/provider-plugin-auth.ts +36 -0
  339. package/src/provider-plugin-builtins.ts +229 -0
  340. package/src/provider-plugin-cleanup-barrier.ts +229 -0
  341. package/src/provider-plugin-cleanup-execution.ts +79 -0
  342. package/src/provider-plugin-cleanup-resource.ts +470 -0
  343. package/src/provider-plugin-contract-identity.ts +707 -0
  344. package/src/provider-plugin-host.ts +1972 -0
  345. package/src/provider-plugin-identifiers.ts +60 -0
  346. package/src/provider-plugin-invocation-lease.ts +889 -0
  347. package/src/provider-plugin-lifecycle-kernel.ts +566 -0
  348. package/src/provider-plugin-lifecycle.ts +1455 -0
  349. package/src/provider-plugin-module-analysis.ts +336 -0
  350. package/src/provider-plugin-package.ts +2530 -0
  351. package/src/provider-plugin-portable-authority.ts +106 -0
  352. package/src/provider-plugin-portable-catalog.ts +94 -0
  353. package/src/provider-plugin-portable-identity.ts +315 -0
  354. package/src/provider-plugin-portable-runtime.ts +2057 -0
  355. package/src/provider-plugin-protocol.ts +1625 -0
  356. package/src/provider-plugin-registry.ts +3342 -0
  357. package/src/provider-plugin-store.ts +2155 -0
  358. package/src/provider-plugin-test-fixture.ts +5 -0
  359. package/src/provider-plugin.ts +4907 -0
  360. package/src/provider-plugins.generated.ts +43 -0
  361. package/src/provider-plugins.ts +8 -0
  362. package/src/provider-subject.ts +41 -0
  363. package/src/provider.ts +315 -0
  364. package/src/providers/beeper-local-runtime.ts +5426 -0
  365. package/src/providers/beeper-local.ts +2739 -0
  366. package/src/providers/beeper-messaging.ts +462 -0
  367. package/src/providers/beeper-omni.ts +1159 -0
  368. package/src/providers/bluesky-web-runtime.ts +2895 -0
  369. package/src/providers/bluesky-web.ts +1614 -0
  370. package/src/providers/clasificados-web-runtime.ts +255 -0
  371. package/src/providers/clasificados-web.ts +503 -0
  372. package/src/providers/contact-projection.ts +301 -0
  373. package/src/providers/github-read-model.ts +94 -0
  374. package/src/providers/github-read-platform.ts +165 -0
  375. package/src/providers/github-read-program.ts +97 -0
  376. package/src/providers/github-web-runtime.ts +320 -0
  377. package/src/providers/github-web.ts +403 -0
  378. package/src/providers/gmail-api.ts +2834 -0
  379. package/src/providers/gmail-omni.ts +830 -0
  380. package/src/providers/gmail.ts +1130 -0
  381. package/src/providers/hacker-news-web-runtime.ts +220 -0
  382. package/src/providers/hacker-news-web.ts +809 -0
  383. package/src/providers/imessage-direct-install.ts +354 -0
  384. package/src/providers/imessage-direct-messaging.ts +532 -0
  385. package/src/providers/imessage-direct-omni.ts +400 -0
  386. package/src/providers/imessage-direct-runtime.ts +1498 -0
  387. package/src/providers/imessage-direct.ts +351 -0
  388. package/src/providers/instagram-video-foundations.ts +913 -0
  389. package/src/providers/instagram-web-profile-browser.ts +600 -0
  390. package/src/providers/iso-bmff.ts +338 -0
  391. package/src/providers/linkedin-company-platform.ts +42 -0
  392. package/src/providers/linkedin-company-program.ts +88 -0
  393. package/src/providers/linkedin-contact-failure.ts +84 -0
  394. package/src/providers/linkedin-contact-platform.ts +45 -0
  395. package/src/providers/linkedin-contact-program.ts +91 -0
  396. package/src/providers/linkedin-profile-activity-failure.ts +76 -0
  397. package/src/providers/linkedin-profile-activity-platform.ts +41 -0
  398. package/src/providers/linkedin-profile-activity-program.ts +67 -0
  399. package/src/providers/linkedin-read-failure.ts +52 -0
  400. package/src/providers/linkedin-self-platform.ts +53 -0
  401. package/src/providers/linkedin-self-program.ts +122 -0
  402. package/src/providers/linkedin-web-article-browser.ts +794 -0
  403. package/src/providers/linkedin-web-bootstrap.ts +146 -0
  404. package/src/providers/linkedin-web-contact.ts +830 -0
  405. package/src/providers/linkedin-web-feed-browser.ts +832 -0
  406. package/src/providers/linkedin-web-feed.ts +864 -0
  407. package/src/providers/linkedin-web-post-browser.ts +653 -0
  408. package/src/providers/linkedin-web-profile-browser.ts +683 -0
  409. package/src/providers/linkedin-web-runtime.ts +2388 -0
  410. package/src/providers/linkedin-web.ts +3305 -0
  411. package/src/providers/linkedin.ts +1885 -0
  412. package/src/providers/meta-bootstrap.ts +1043 -0
  413. package/src/providers/meta-facebook-group.ts +690 -0
  414. package/src/providers/meta-marketplace-relay.ts +747 -0
  415. package/src/providers/meta-omni.ts +373 -0
  416. package/src/providers/meta-relay-bundle.ts +1049 -0
  417. package/src/providers/meta-web-descriptors.ts +1740 -0
  418. package/src/providers/meta-web-runtime.ts +3388 -0
  419. package/src/providers/meta-web.ts +2931 -0
  420. package/src/providers/read-failure.ts +139 -0
  421. package/src/providers/reddit-omni.ts +462 -0
  422. package/src/providers/reddit-web-runtime.ts +1929 -0
  423. package/src/providers/reddit-web.ts +1611 -0
  424. package/src/providers/rental-listings.ts +460 -0
  425. package/src/providers/substack-omni.ts +406 -0
  426. package/src/providers/substack-video-mp4.ts +33 -0
  427. package/src/providers/substack-web-runtime.ts +2812 -0
  428. package/src/providers/substack-web.ts +1502 -0
  429. package/src/providers/tiktok-video-mp4.ts +33 -0
  430. package/src/providers/tiktok-web-runtime.ts +770 -0
  431. package/src/providers/tiktok-web.ts +2786 -0
  432. package/src/providers/twitch-web-runtime.ts +226 -0
  433. package/src/providers/twitch-web.ts +274 -0
  434. package/src/providers/whatsapp-account-identity.ts +55 -0
  435. package/src/providers/whatsapp-contact-projection-helper.ts +874 -0
  436. package/src/providers/whatsapp-contact-projection-protocol.ts +403 -0
  437. package/src/providers/whatsapp-interaction-projection-helper.ts +1452 -0
  438. package/src/providers/whatsapp-interaction-projection-protocol.ts +462 -0
  439. package/src/providers/whatsapp-message-export-projection-protocol.ts +532 -0
  440. package/src/providers/whatsapp-messaging.ts +132 -0
  441. package/src/providers/whatsapp-omni.ts +586 -0
  442. package/src/providers/whatsapp-web-runtime.ts +3108 -0
  443. package/src/providers/whatsapp-web.ts +1037 -0
  444. package/src/providers/x-image-provenance.ts +523 -0
  445. package/src/providers/x-made-with-ai.ts +106 -0
  446. package/src/providers/x-transaction-id.ts +413 -0
  447. package/src/providers/x-web-runtime.ts +3485 -0
  448. package/src/providers/x-web.ts +2332 -0
  449. package/src/providers/x.ts +1889 -0
  450. package/src/providers/youtube-web-runtime.ts +1400 -0
  451. package/src/providers/youtube-web.ts +1221 -0
  452. package/src/read-admission-runtime.ts +91 -0
  453. package/src/read-client.ts +521 -0
  454. package/src/read-effect-platform.ts +7 -0
  455. package/src/read-effect-runtime.ts +14 -0
  456. package/src/read-effect.ts +52 -0
  457. package/src/read-projection-admission.ts +866 -0
  458. package/src/read-projections.ts +3151 -0
  459. package/src/recovery.ts +1492 -0
  460. package/src/reviewed-template.ts +146 -0
  461. package/src/run-journal.ts +1272 -0
  462. package/src/runtime.ts +5116 -0
  463. package/src/scripts/generate-provider-plugin-catalog.ts +121 -0
  464. package/src/scripts/install-whatsapp-protocol.sh +232 -0
  465. package/src/scripts/recover-install-stages.ts +1491 -0
  466. package/src/scripts/resolve-state-home.ts +30 -0
  467. package/src/scripts/scaffold-web-provider.ts +973 -0
  468. package/src/scripts/sync-bundled-adapters.ts +695 -0
  469. package/src/session-secrets.ts +1183 -0
  470. package/src/state-helper.bunfig.toml +2 -0
  471. package/src/state-helper.ts +2914 -0
  472. package/src/storage.ts +3842 -0
  473. package/src/transport-policy.ts +2 -0
  474. package/src/usage.ts +182 -0
  475. package/src/version.ts +2 -0
  476. package/src/web-session-authentication-policy.ts +136 -0
  477. package/src/web-session-cleanup-admission.ts +2344 -0
  478. package/src/web-session-cleanup-join.ts +99 -0
  479. package/src/web-session-client.ts +996 -0
  480. package/src/web-session-contract-definitions.ts +820 -0
  481. package/src/web-session-contract-planning.ts +42 -0
  482. package/src/web-session-contracts.ts +228 -0
  483. package/src/web-session-cookies.ts +500 -0
  484. package/src/web-session-execution.ts +385 -0
  485. package/src/web-session-http.ts +373 -0
  486. package/src/web-session-read-errors.ts +19 -0
  487. package/src/web-session-read-runtime.ts +124 -0
  488. package/src/web-session-recovery.ts +818 -0
  489. package/src/web-session-sites.ts +58 -0
  490. package/src/web-session-template.ts +994 -0
  491. package/src/web-session.ts +93 -0
  492. package/src/whatsapp-client-binding.ts +21 -0
  493. package/src/whatsapp-client-types.ts +75 -0
  494. package/src/whatsapp-client.ts +395 -0
  495. package/src/whatsapp-export-coordinate.ts +17 -0
  496. package/src/whatsapp-message-bundle-v2.ts +195 -0
  497. package/src/whatsapp-message-like-me-cli.ts +195 -0
  498. package/src/whatsapp-message-like-me-export.ts +1072 -0
  499. package/src/whatsapp-message-like-me-source.ts +1012 -0
  500. package/tsconfig.json +67 -0
@@ -0,0 +1,439 @@
1
+ import { isLiteralLanguageTag, isValidBrowserSpec, type CaptureMode } from "./args";
2
+ import {
3
+ isWellFormedIdentity,
4
+ parseProbeMetadata,
5
+ type YtDlpAuthorizationIdentityInput,
6
+ type CaptionSelection,
7
+ type ParseProbeResult,
8
+ type ProbeMetadata,
9
+ } from "./metadata";
10
+ import {
11
+ runProcess,
12
+ urlDerivedRedactions,
13
+ type CommandArgv,
14
+ type ProcessFailureReason,
15
+ type ProcessResult,
16
+ type RunProcessOptions,
17
+ } from "./process";
18
+
19
+ const PROBE_TIMEOUT_MS = 2 * 60 * 1_000;
20
+ const CAPTURE_TIMEOUT_MS = 24 * 60 * 60 * 1_000;
21
+ const PROBE_STDOUT_LIMIT_BYTES = 32 * 1024 * 1024;
22
+ const CAPTURE_STDOUT_LIMIT_BYTES = 16 * 1024;
23
+ const TOOL_STDERR_LIMIT_BYTES = 2 * 1024 * 1024;
24
+ const CAPTURE_IDENTITY_PREFIX = "WRENCH_MEDIA_CAPTURE_IDENTITY_V1\t";
25
+ const CAPTURE_IDENTITY_MAX_CODE_UNITS = 512;
26
+ const CAPTURE_EXTENSION_MAX_CODE_UNITS = 16;
27
+ const RESERVED_CAPTURE_EXTENSIONS: ReadonlySet<string> = new Set([
28
+ "ass",
29
+ "avif",
30
+ "bmp",
31
+ "description",
32
+ "dfxp",
33
+ "gif",
34
+ "ico",
35
+ "jpeg",
36
+ "jfif",
37
+ "jpg",
38
+ "json",
39
+ "json3",
40
+ "jxl",
41
+ "lrc",
42
+ "part",
43
+ "png",
44
+ "sami",
45
+ "scc",
46
+ "srv1",
47
+ "srv2",
48
+ "srv3",
49
+ "srt",
50
+ "svg",
51
+ "tif",
52
+ "tiff",
53
+ "txt",
54
+ "ttml",
55
+ "vtt",
56
+ "webp",
57
+ "xml",
58
+ "ytdl",
59
+ ]);
60
+
61
+ // yt-dlp loads ambient configuration before command-line arguments. These
62
+ // Ghostget media-owned negatives are therefore always emitted on the CLI, including
63
+ // when the user explicitly inherits config, so config cannot weaken them.
64
+ const OWNED_PROBE_BOUNDARY_ARGS = [
65
+ "--no-remote-components",
66
+ "--ignore-dynamic-mpd",
67
+ "--no-wait-for-video",
68
+ "--no-live-from-start",
69
+ "--no-allow-unplayable-formats",
70
+ ] as const;
71
+
72
+ const OWNED_CAPTURE_BOUNDARY_ARGS = [
73
+ ...OWNED_PROBE_BOUNDARY_ARGS,
74
+ "--no-keep-fragments",
75
+ "--no-hls-split-discontinuity",
76
+ ] as const;
77
+
78
+ // `after_move` is the first point where a media capture's final file exists.
79
+ // Transcript-only runs do not execute that stage, so they report at
80
+ // `after_video`, which is the corresponding once-per-item completion boundary.
81
+ const CAPTURE_IDENTITY_TEMPLATE = `${CAPTURE_IDENTITY_PREFIX}{"extractor":%(extractor_key,extractor|)j,"id":%(id|)j,"ext":%(ext|)j}`;
82
+
83
+ export interface YtDlpAuthOptions {
84
+ readonly browser?: string;
85
+ readonly inheritConfig: boolean;
86
+ readonly authContextSha256?: string;
87
+ }
88
+
89
+ export interface ProbeYtDlpOptions extends YtDlpAuthOptions {
90
+ readonly executable: string;
91
+ readonly url: string;
92
+ readonly signal?: AbortSignal;
93
+ }
94
+
95
+ export interface CaptureYtDlpOptions extends YtDlpAuthOptions {
96
+ readonly executable: string;
97
+ readonly url: string;
98
+ readonly mode: CaptureMode;
99
+ readonly captureDirectory: string;
100
+ readonly temporaryDirectory: string;
101
+ readonly caption: CaptionSelection | null;
102
+ readonly persistDescriptiveMetadata: boolean;
103
+ readonly privateRedactions?: readonly string[];
104
+ readonly signal?: AbortSignal;
105
+ }
106
+
107
+ export interface YtDlpProcessFailure {
108
+ readonly ok: false;
109
+ readonly diagnostic: string;
110
+ /** Exact process-layer reason; archive maps `aborted` to cancellation. */
111
+ readonly processReason: ProcessFailureReason;
112
+ }
113
+
114
+ export interface YtDlpOutputFailure {
115
+ readonly ok: false;
116
+ readonly diagnostic: string;
117
+ readonly processReason?: never;
118
+ }
119
+
120
+ export type YtDlpProbeResult =
121
+ | { readonly ok: true; readonly metadata: ProbeMetadata }
122
+ | Extract<ParseProbeResult, { readonly kind: "unsupported" }>
123
+ | YtDlpProcessFailure
124
+ | YtDlpOutputFailure;
125
+
126
+ export interface YtDlpCaptureIdentity {
127
+ readonly extractor: string;
128
+ readonly id: string;
129
+ readonly ext: string;
130
+ }
131
+
132
+ export type YtDlpCaptureResult =
133
+ | { readonly ok: true; readonly identity: YtDlpCaptureIdentity }
134
+ | YtDlpProcessFailure
135
+ | YtDlpOutputFailure;
136
+
137
+ export type ParseYtDlpCaptureIdentityResult = YtDlpCaptureResult;
138
+
139
+ export interface YtDlpDependencies {
140
+ readonly runProcess: (argv: CommandArgv, options: RunProcessOptions) => Promise<ProcessResult>;
141
+ }
142
+
143
+ const defaultDependencies: YtDlpDependencies = { runProcess: (argv, options) => runProcess(argv, options) };
144
+ const SHA256_PATTERN = /^[0-9a-f]{64}$/u;
145
+
146
+ function authorizationIdentity(
147
+ options: YtDlpAuthOptions,
148
+ ): YtDlpAuthorizationIdentityInput | undefined {
149
+ if (options.browser !== undefined && !isValidBrowserSpec(options.browser)) {
150
+ throw new TypeError("yt-dlp browser selector is malformed");
151
+ }
152
+ if (options.browser !== undefined && options.inheritConfig) {
153
+ throw new TypeError("yt-dlp browser and ambient configuration are mutually exclusive");
154
+ }
155
+ const hasPrivateAccess = options.browser !== undefined || options.inheritConfig;
156
+ if (hasPrivateAccess !== (options.authContextSha256 !== undefined)) {
157
+ throw new TypeError("yt-dlp private access requires exactly one authorization context");
158
+ }
159
+ if (options.authContextSha256 === undefined) return undefined;
160
+ if (!SHA256_PATTERN.test(options.authContextSha256)) {
161
+ throw new TypeError("yt-dlp authorization context digest is malformed");
162
+ }
163
+ return {
164
+ mode: options.browser === undefined ? "ambient_config" : "browser",
165
+ contextSha256: options.authContextSha256,
166
+ };
167
+ }
168
+
169
+ function configurationArgs(options: YtDlpAuthOptions): string[] {
170
+ return options.inheritConfig ? [] : ["--ignore-config", "--no-plugin-dirs"];
171
+ }
172
+
173
+ function authenticationArgs(options: YtDlpAuthOptions): string[] {
174
+ return options.browser === undefined ? [] : ["--cookies-from-browser", options.browser];
175
+ }
176
+
177
+ export function buildYtDlpProbeArgv(options: ProbeYtDlpOptions): CommandArgv {
178
+ authorizationIdentity(options);
179
+ return [
180
+ options.executable,
181
+ ...configurationArgs(options),
182
+ ...OWNED_PROBE_BOUNDARY_ARGS,
183
+ "--no-playlist",
184
+ "--skip-download",
185
+ "--dump-single-json",
186
+ "--no-warnings",
187
+ ...authenticationArgs(options),
188
+ "--",
189
+ options.url,
190
+ ];
191
+ }
192
+
193
+ function modeArgs(mode: CaptureMode): string[] {
194
+ switch (mode) {
195
+ case "archive":
196
+ return ["--format", "bv*+ba/b", "--merge-output-format", "mkv"];
197
+ case "audio":
198
+ return ["--format", "bestaudio/best"];
199
+ case "video":
200
+ return ["--format", "bestvideo/best"];
201
+ case "transcript":
202
+ return ["--skip-download"];
203
+ }
204
+ }
205
+
206
+ function captionArgs(caption: CaptionSelection | null): string[] {
207
+ if (caption === null) return [];
208
+ if (!isLiteralLanguageTag(caption.language)) {
209
+ throw new TypeError("caption language must be a literal BCP-47-style tag");
210
+ }
211
+ return [
212
+ caption.source === "manual" ? "--write-subs" : "--write-auto-subs",
213
+ "--sub-langs",
214
+ caption.language,
215
+ "--sub-format",
216
+ "vtt/best",
217
+ "--convert-subs",
218
+ "vtt",
219
+ ];
220
+ }
221
+
222
+ function captureIdentityPrintArg(mode: CaptureMode): string {
223
+ const when = mode === "transcript" ? "after_video" : "after_move";
224
+ return `${when}:${CAPTURE_IDENTITY_TEMPLATE}`;
225
+ }
226
+
227
+ export function buildYtDlpCaptureArgv(options: CaptureYtDlpOptions): CommandArgv {
228
+ authorizationIdentity(options);
229
+ return [
230
+ options.executable,
231
+ ...configurationArgs(options),
232
+ ...OWNED_CAPTURE_BOUNDARY_ARGS,
233
+ "--no-playlist",
234
+ "--abort-on-error",
235
+ "--abort-on-unavailable-fragments",
236
+ "--check-formats",
237
+ "--quiet",
238
+ "--no-progress",
239
+ "--no-simulate",
240
+ "--continue",
241
+ "--part",
242
+ "--no-overwrites",
243
+ "--no-mtime",
244
+ "--retries",
245
+ "10",
246
+ "--fragment-retries",
247
+ "10",
248
+ "--retry-sleep",
249
+ "http:exp=1:20",
250
+ "--retry-sleep",
251
+ "fragment:exp=1:20",
252
+ // Probe JSON is parsed into Ghostget media's owned metadata model. Explicitly disable
253
+ // raw info sidecars even when an inherited yt-dlp config enables them.
254
+ "--no-write-info-json",
255
+ ...(options.persistDescriptiveMetadata
256
+ ? ["--write-description", "--write-thumbnail"]
257
+ : ["--no-write-description", "--no-write-thumbnail"]),
258
+ ...captionArgs(options.caption),
259
+ ...modeArgs(options.mode),
260
+ "--paths",
261
+ `home:${options.captureDirectory}`,
262
+ "--paths",
263
+ `temp:${options.temporaryDirectory}`,
264
+ "--output",
265
+ "media.%(ext)s",
266
+ // `--print` is a machine-readable provenance channel, not user-facing
267
+ // progress. JSON conversion safely quotes arbitrary extractor identities.
268
+ "--print",
269
+ captureIdentityPrintArg(options.mode),
270
+ ...authenticationArgs(options),
271
+ "--",
272
+ options.url,
273
+ ];
274
+ }
275
+
276
+ function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
277
+ return typeof value === "object" && value !== null && !Array.isArray(value);
278
+ }
279
+
280
+ function hasIdentityControlCharacter(value: string): boolean {
281
+ for (const character of value) {
282
+ const codePoint = character.codePointAt(0);
283
+ if (
284
+ codePoint !== undefined
285
+ && (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f))
286
+ ) {
287
+ return true;
288
+ }
289
+ }
290
+ return false;
291
+ }
292
+
293
+ function boundedIdentityString(value: unknown): string | null {
294
+ if (typeof value !== "string") return null;
295
+ // Provider identity is opaque. Normalizing or trimming here could make two
296
+ // distinct post-capture identities compare equal across the probe boundary.
297
+ return value.length > 0
298
+ && value.length <= CAPTURE_IDENTITY_MAX_CODE_UNITS
299
+ && !hasIdentityControlCharacter(value)
300
+ && isWellFormedIdentity(value)
301
+ ? value
302
+ : null;
303
+ }
304
+
305
+ /** Revalidates completion extensions at every injected adapter boundary. */
306
+ export function isSafeYtDlpCaptureExtension(value: unknown): value is string {
307
+ return typeof value === "string"
308
+ && value.length > 0
309
+ && value.length <= CAPTURE_EXTENSION_MAX_CODE_UNITS
310
+ && /^[a-z0-9]+$/u.test(value)
311
+ && !RESERVED_CAPTURE_EXTENSIONS.has(value);
312
+ }
313
+
314
+ /**
315
+ * Parses only Ghostget media's prefixed, allowlisted completion record. Other stdout can
316
+ * be produced by an explicitly inherited yt-dlp config, but never crosses this
317
+ * boundary. Exactly one Ghostget media record is required.
318
+ */
319
+ export function parseYtDlpCaptureIdentity(
320
+ stdout: unknown,
321
+ ): ParseYtDlpCaptureIdentityResult {
322
+ if (typeof stdout !== "string") {
323
+ return { ok: false, diagnostic: "yt-dlp capture did not return an identity record" };
324
+ }
325
+
326
+ const records = stdout
327
+ .split("\n")
328
+ .filter((line) => line.startsWith(CAPTURE_IDENTITY_PREFIX));
329
+ if (records.length === 0) {
330
+ return { ok: false, diagnostic: "yt-dlp capture did not return an identity record" };
331
+ }
332
+ if (records.length !== 1) {
333
+ return { ok: false, diagnostic: "yt-dlp capture returned multiple identity records" };
334
+ }
335
+
336
+ const record = records[0];
337
+ if (record === undefined) {
338
+ return { ok: false, diagnostic: "yt-dlp capture did not return an identity record" };
339
+ }
340
+
341
+ let value: unknown;
342
+ try {
343
+ value = JSON.parse(record.slice(CAPTURE_IDENTITY_PREFIX.length));
344
+ } catch {
345
+ return { ok: false, diagnostic: "yt-dlp capture returned a malformed identity record" };
346
+ }
347
+ if (!isRecord(value)) {
348
+ return { ok: false, diagnostic: "yt-dlp capture returned a malformed identity record" };
349
+ }
350
+ const keys = Object.keys(value).toSorted();
351
+ if (
352
+ keys.length !== 3
353
+ || keys[0] !== "ext"
354
+ || keys[1] !== "extractor"
355
+ || keys[2] !== "id"
356
+ ) {
357
+ return { ok: false, diagnostic: "yt-dlp capture returned a malformed identity record" };
358
+ }
359
+ const extractor = boundedIdentityString(value["extractor"]);
360
+ const id = boundedIdentityString(value["id"]);
361
+ const ext = value["ext"];
362
+ if (extractor === null || id === null || !isSafeYtDlpCaptureExtension(ext)) {
363
+ return { ok: false, diagnostic: "yt-dlp capture returned a malformed identity record" };
364
+ }
365
+ return { ok: true, identity: { extractor, id, ext } };
366
+ }
367
+
368
+ export async function probeWithYtDlp(
369
+ options: ProbeYtDlpOptions,
370
+ dependencies: YtDlpDependencies = defaultDependencies,
371
+ ): Promise<YtDlpProbeResult> {
372
+ const result = await dependencies.runProcess(buildYtDlpProbeArgv(options), {
373
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
374
+ timeoutMs: PROBE_TIMEOUT_MS,
375
+ maxStdoutBytes: PROBE_STDOUT_LIMIT_BYTES,
376
+ maxStderrBytes: TOOL_STDERR_LIMIT_BYTES,
377
+ redactions: [
378
+ options.url,
379
+ ...urlDerivedRedactions(options.url),
380
+ ...(options.browser === undefined ? [] : [options.browser]),
381
+ ],
382
+ });
383
+ if (!result.ok) {
384
+ return {
385
+ ok: false,
386
+ diagnostic: result.diagnostic,
387
+ processReason: result.reason,
388
+ };
389
+ }
390
+ if (result.stdoutTruncated) return { ok: false, diagnostic: "yt-dlp probe exceeded Ghostget media's metadata limit" };
391
+ let value: unknown;
392
+ try { value = JSON.parse(result.stdout); } catch { return { ok: false, diagnostic: "yt-dlp probe did not return valid JSON" }; }
393
+ const parsed = parseProbeMetadata(value, options.url, authorizationIdentity(options));
394
+ if (parsed.ok || parsed.kind === "unsupported") return parsed;
395
+ return { ok: false, diagnostic: parsed.message };
396
+ }
397
+
398
+ export async function captureWithYtDlp(
399
+ options: CaptureYtDlpOptions,
400
+ dependencies: YtDlpDependencies = defaultDependencies,
401
+ ): Promise<YtDlpCaptureResult> {
402
+ const result = await dependencies.runProcess(buildYtDlpCaptureArgv(options), {
403
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
404
+ timeoutMs: CAPTURE_TIMEOUT_MS,
405
+ maxStdoutBytes: CAPTURE_STDOUT_LIMIT_BYTES,
406
+ maxStderrBytes: TOOL_STDERR_LIMIT_BYTES,
407
+ redactions: [
408
+ options.url,
409
+ ...urlDerivedRedactions(options.url),
410
+ ...(options.privateRedactions ?? []),
411
+ ...(options.browser === undefined ? [] : [options.browser]),
412
+ ],
413
+ });
414
+ if (!result.ok) {
415
+ return {
416
+ ok: false,
417
+ diagnostic: result.diagnostic,
418
+ processReason: result.reason,
419
+ };
420
+ }
421
+ if (result.stdoutTruncated) {
422
+ return { ok: false, diagnostic: "yt-dlp capture exceeded Ghostget media's identity output limit" };
423
+ }
424
+ return parseYtDlpCaptureIdentity(result.stdout);
425
+ }
426
+
427
+ export async function ytDlpVersion(
428
+ executable: string,
429
+ dependencies: YtDlpDependencies = defaultDependencies,
430
+ ): Promise<string> {
431
+ const result = await dependencies.runProcess([executable, "--version"], {
432
+ timeoutMs: 15_000,
433
+ maxStdoutBytes: 32 * 1024,
434
+ maxStderrBytes: 32 * 1024,
435
+ });
436
+ if (!result.ok) return "unknown";
437
+ const firstLine = result.stdout.trim().split(/\s+/u)[0];
438
+ return firstLine === undefined || firstLine.length === 0 || firstLine.length > 128 ? "unknown" : firstLine;
439
+ }