@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,464 @@
1
+ import { constants as fsConstants } from "node:fs";
2
+ import { lstat, open, realpath } from "node:fs/promises";
3
+ import { join, relative } from "node:path";
4
+
5
+ import {
6
+ type CommandArgv,
7
+ } from "./process";
8
+ import {
9
+ parseRuntimeClosureRecord,
10
+ runAttestedRuntimeProcess,
11
+ sameRuntimeClosureRecord,
12
+ type AttestedRuntimeProcessResult,
13
+ type AttestRuntimeClosureOptions,
14
+ type RuntimeClosureAttestation,
15
+ type RuntimeClosureRecord,
16
+ } from "./runtime-closure";
17
+ import {
18
+ parseStrictLocalWebVtt,
19
+ stripWebVttMarkup,
20
+ validateTranscriptCues,
21
+ type TranscriptCue,
22
+ } from "./transcript";
23
+ import {
24
+ normalizeWhisperCppLanguage,
25
+ whisperCppLanguageArgument,
26
+ } from "./whisper-language";
27
+
28
+ export {
29
+ normalizeWhisperCppLanguage,
30
+ whisperCppLanguageArgument,
31
+ } from "./whisper-language";
32
+
33
+ /** Pinned Ghostget media invocation contract; changing it changes transcript provenance. */
34
+ export const WHISPER_CPP_PROFILE = "wrench-media-whisper-cpp-v1";
35
+
36
+ /** Fixed PCM contract produced by Ghostget media before invoking whisper.cpp. */
37
+ export const WHISPER_CPP_NORMALIZATION_PROFILE = "pcm-s16le-16000hz-mono-v1";
38
+
39
+ const DEFAULT_TIMEOUT_MS = 2 * 60 * 60 * 1_000;
40
+ const PROCESS_STDOUT_LIMIT_BYTES = 64 * 1024;
41
+ const PROCESS_STDERR_LIMIT_BYTES = 1024 * 1024;
42
+ const MAX_VTT_BYTES = 64 * 1024 * 1024;
43
+ const MAX_JSON_BYTES = 16 * 1024 * 1024;
44
+ const OUTPUT_STEM = "transcript";
45
+
46
+ type WhisperCppOutputKind = "vtt" | "json";
47
+
48
+ export type WhisperCppTranscription = Readonly<{
49
+ language: string | null;
50
+ cues: readonly TranscriptCue[];
51
+ vtt: string;
52
+ text: string;
53
+ json: string;
54
+ }>;
55
+
56
+ export type WhisperCppFailureCode =
57
+ | "invalid-request"
58
+ | "runtime-closure-changed"
59
+ | "work-directory"
60
+ | "output-exists"
61
+ | "process"
62
+ | "output-missing"
63
+ | "output-unsafe"
64
+ | "output-too-large"
65
+ | "output-changed"
66
+ | "invalid-json"
67
+ | "invalid-language"
68
+ | "language-mismatch"
69
+ | "invalid-vtt"
70
+ | "output-mismatch";
71
+
72
+ export type ParsedWhisperCppResult =
73
+ | Readonly<{ ok: true; status: "transcribed"; transcript: WhisperCppTranscription }>
74
+ | Readonly<{ ok: true; status: "no-speech"; language: string | null }>
75
+ | Readonly<{
76
+ ok: false;
77
+ status: "error";
78
+ error: Readonly<{ code: WhisperCppFailureCode; message: string }>;
79
+ }>;
80
+
81
+ export type WhisperCppResult =
82
+ | Readonly<
83
+ Extract<ParsedWhisperCppResult, { readonly ok: true }>
84
+ & { runtimeClosure: RuntimeClosureAttestation }
85
+ >
86
+ | Extract<ParsedWhisperCppResult, { readonly ok: false }>;
87
+
88
+ export type RunWhisperCppOptions = Readonly<{
89
+ executable: string;
90
+ modelPath: string;
91
+ pcmPath: string;
92
+ requestedLanguage: string;
93
+ signal?: AbortSignal;
94
+ /** A fresh private directory owned by Ghostget media's current transcription attempt. */
95
+ workDirectory: string;
96
+ /** Exact private runtime closure pinned by `ghostget transcriber setup`. */
97
+ runtimeClosure: RuntimeClosureRecord;
98
+ timeoutMs?: number;
99
+ }>;
100
+
101
+ export type WhisperCppDependencies = Readonly<{
102
+ runAttestedRuntimeProcess: (
103
+ options: AttestRuntimeClosureOptions,
104
+ ) => Promise<AttestedRuntimeProcessResult>;
105
+ /** Test seam after pathname inspection and before the no-follow output open. */
106
+ beforeOutputOpen?: (path: string, kind: WhisperCppOutputKind) => Promise<void>;
107
+ }>;
108
+
109
+ const defaultDependencies: WhisperCppDependencies = {
110
+ runAttestedRuntimeProcess: (options) => runAttestedRuntimeProcess(options),
111
+ };
112
+
113
+ /**
114
+ * Accepts `auto` or a deliberately small BCP-47-style language token. This is
115
+ * an argv value rather than a path, so controls, separators, and tool grammar
116
+ * are never admitted.
117
+ */
118
+ /** Builds Ghostget media's complete whisper.cpp argv; `Bun.spawn` receives this directly. */
119
+ export function buildWhisperCppArgv(
120
+ options: Readonly<{
121
+ executable: string;
122
+ modelPath: string;
123
+ pcmPath: string;
124
+ requestedLanguage: string;
125
+ outputPrefix: string;
126
+ }>,
127
+ ): CommandArgv {
128
+ const language = normalizeWhisperCppLanguage(options.requestedLanguage);
129
+ if (language === null) throw new TypeError("whisper.cpp language must be auto or a literal BCP-47-style tag");
130
+ const toolLanguage = whisperCppLanguageArgument(language);
131
+ if (toolLanguage === null) throw new TypeError("whisper.cpp language must have a supported primary subtag");
132
+ for (const value of [options.executable, options.modelPath, options.pcmPath, options.outputPrefix]) {
133
+ if (!isNonemptyNulFreeString(value)) {
134
+ throw new TypeError("whisper.cpp executable and filesystem paths must be nonempty and NUL-free");
135
+ }
136
+ }
137
+ return [
138
+ options.executable,
139
+ "--model",
140
+ options.modelPath,
141
+ "--file",
142
+ options.pcmPath,
143
+ "--language",
144
+ toolLanguage,
145
+ "--threads",
146
+ "4",
147
+ "--processors",
148
+ "1",
149
+ "--no-gpu",
150
+ "--output-vtt",
151
+ "--output-json-full",
152
+ "--output-file",
153
+ options.outputPrefix,
154
+ "--no-prints",
155
+ ];
156
+ }
157
+
158
+ /**
159
+ * Runs whisper.cpp in a caller-created fresh directory. The adapter consumes
160
+ * only its fixed output names, validates them, and never exposes tool stderr.
161
+ */
162
+ export async function runWhisperCpp(
163
+ options: RunWhisperCppOptions,
164
+ dependencies: WhisperCppDependencies = defaultDependencies,
165
+ ): Promise<WhisperCppResult> {
166
+ const language = normalizeWhisperCppLanguage(options.requestedLanguage);
167
+ if (language === null) return failure("invalid-request", "local transcription language is invalid");
168
+ if (![options.executable, options.modelPath, options.pcmPath, options.workDirectory].every(isNonemptyNulFreeString)) {
169
+ return failure("invalid-request", "local transcription executable and filesystem paths are invalid");
170
+ }
171
+ const expectedRuntime = parseRuntimeClosureRecord(options.runtimeClosure);
172
+ if (!expectedRuntime.ok) {
173
+ return failure("invalid-request", "local transcription runtime identity is invalid");
174
+ }
175
+
176
+ const prepared = await prepareOutputDirectory(options.workDirectory);
177
+ if (!prepared.ok) return prepared.result;
178
+ const { outputPrefix } = prepared;
179
+
180
+ let process: AttestedRuntimeProcessResult;
181
+ try {
182
+ const argv = buildWhisperCppArgv({
183
+ executable: options.executable,
184
+ modelPath: options.modelPath,
185
+ pcmPath: options.pcmPath,
186
+ requestedLanguage: language,
187
+ outputPrefix,
188
+ });
189
+ process = await dependencies.runAttestedRuntimeProcess({
190
+ executablePath: options.executable,
191
+ executableSha256: expectedRuntime.record.executableSha256,
192
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
193
+ probeArguments: argv.slice(1),
194
+ timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
195
+ maxStdoutBytes: PROCESS_STDOUT_LIMIT_BYTES,
196
+ maxStderrBytes: PROCESS_STDERR_LIMIT_BYTES,
197
+ });
198
+ } catch {
199
+ return failure("process", "local transcription process did not complete safely");
200
+ }
201
+ if (!sameRuntimeClosureRecord(process.attestation, expectedRuntime.record)) {
202
+ return failure(
203
+ "runtime-closure-changed",
204
+ "local transcription runtime closure changed during inference",
205
+ );
206
+ }
207
+
208
+ const vtt = await readOwnedOutput(outputPrefix, "vtt", MAX_VTT_BYTES, dependencies);
209
+ if (!vtt.ok) return vtt.result;
210
+ const json = await readOwnedOutput(outputPrefix, "json", MAX_JSON_BYTES, dependencies);
211
+ if (!json.ok) return json.result;
212
+
213
+ const parsed = parseWhisperCppOutputs(vtt.value, json.value, language);
214
+ return parsed.ok
215
+ ? { ...parsed, runtimeClosure: process.attestation }
216
+ : parsed;
217
+ }
218
+
219
+ /**
220
+ * Parses already bounded whisper.cpp output. The public result contains only
221
+ * Ghostget media's canonical transcript derivatives, not the tool's JSON envelope.
222
+ */
223
+ export function parseWhisperCppOutputs(
224
+ vttInput: unknown,
225
+ jsonInput: unknown,
226
+ requestedLanguage: unknown,
227
+ ): ParsedWhisperCppResult {
228
+ const language = normalizeWhisperCppLanguage(requestedLanguage);
229
+ if (language === null) return failure("invalid-request", "local transcription language is invalid");
230
+ if (typeof vttInput !== "string") return failure("invalid-vtt", "local transcription did not produce WebVTT");
231
+ if (typeof jsonInput !== "string") return failure("invalid-json", "local transcription did not produce JSON");
232
+ if (vttInput.length > MAX_VTT_BYTES) return failure("output-too-large", "local transcription WebVTT exceeds Ghostget media's limit");
233
+ if (jsonInput.length > MAX_JSON_BYTES) return failure("output-too-large", "local transcription JSON exceeds Ghostget media's limit");
234
+
235
+ const json = parseWhisperCppJson(jsonInput);
236
+ if (!json.ok) return json.result;
237
+ if (
238
+ language !== "auto"
239
+ && json.language !== null
240
+ && !languagesMatch(language, json.language)
241
+ ) return failure("language-mismatch", "local transcription returned an unexpected language");
242
+
243
+ const parsedVtt = parseStrictLocalWebVtt(vttInput);
244
+ if (!parsedVtt.ok) {
245
+ return failure("invalid-vtt", "local transcription returned invalid WebVTT");
246
+ }
247
+ if (parsedVtt.cues.length === 0 || json.cues.length === 0) {
248
+ if (parsedVtt.cues.length !== json.cues.length) {
249
+ return failure("output-mismatch", "local transcription WebVTT and JSON disagree");
250
+ }
251
+ return { ok: true, status: "no-speech", language: json.language };
252
+ }
253
+ const validated = validateTranscriptCues(parsedVtt.cues);
254
+ const validatedJson = validateTranscriptCues(json.cues);
255
+ if (!validated.ok) {
256
+ return failure("invalid-vtt", "local transcription returned invalid timed cues");
257
+ }
258
+ if (!validatedJson.ok) {
259
+ return failure("invalid-json", "local transcription JSON contains invalid timed cues");
260
+ }
261
+ if (validated.json !== validatedJson.json) {
262
+ return failure("output-mismatch", "local transcription WebVTT and JSON disagree");
263
+ }
264
+ return {
265
+ ok: true,
266
+ status: "transcribed",
267
+ transcript: {
268
+ language: json.language,
269
+ cues: validated.cues,
270
+ vtt: validated.vtt,
271
+ text: validated.text,
272
+ json: validated.json,
273
+ },
274
+ };
275
+ }
276
+
277
+ function failure(code: WhisperCppFailureCode, message: string): Extract<
278
+ ParsedWhisperCppResult,
279
+ { readonly ok: false }
280
+ > {
281
+ return { ok: false, status: "error", error: { code, message } };
282
+ }
283
+
284
+ async function prepareOutputDirectory(
285
+ candidate: string,
286
+ ): Promise<Readonly<{ ok: true; directory: string; outputPrefix: string }> | Readonly<{ ok: false; result: WhisperCppResult }>> {
287
+ try {
288
+ const metadata = await lstat(candidate);
289
+ if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
290
+ return { ok: false, result: failure("work-directory", "local transcription work directory is unsafe") };
291
+ }
292
+ const directory = await realpath(candidate);
293
+ const outputPrefix = join(directory, OUTPUT_STEM);
294
+ if (!isContainedPath(directory, outputPrefix)) {
295
+ return { ok: false, result: failure("work-directory", "local transcription work directory is unsafe") };
296
+ }
297
+ for (const suffix of ["vtt", "json"] as const) {
298
+ try {
299
+ await lstat(`${outputPrefix}.${suffix}`);
300
+ return { ok: false, result: failure("output-exists", "local transcription output already exists") };
301
+ } catch (error) {
302
+ if (isMissing(error)) continue;
303
+ return { ok: false, result: failure("work-directory", "local transcription work directory cannot be inspected") };
304
+ }
305
+ }
306
+ return { ok: true, directory, outputPrefix };
307
+ } catch {
308
+ return { ok: false, result: failure("work-directory", "local transcription work directory is unsafe") };
309
+ }
310
+ }
311
+
312
+ async function readOwnedOutput(
313
+ outputPrefix: string,
314
+ kind: WhisperCppOutputKind,
315
+ maximumBytes: number,
316
+ dependencies: WhisperCppDependencies,
317
+ ): Promise<Readonly<{ ok: true; value: string }> | Readonly<{ ok: false; result: WhisperCppResult }>> {
318
+ const path = `${outputPrefix}.${kind}`;
319
+ try {
320
+ const before = await lstat(path);
321
+ if (!before.isFile() || before.isSymbolicLink()) return unsafeOutput();
322
+ if (before.size === 0) return missingOutput();
323
+ if (before.size > maximumBytes) return tooLargeOutput();
324
+
325
+ await dependencies.beforeOutputOpen?.(path, kind);
326
+ const handle = await open(
327
+ path,
328
+ fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK,
329
+ );
330
+ try {
331
+ const opened = await handle.stat();
332
+ if (!opened.isFile() || opened.size === 0) return unsafeOutput();
333
+ if (opened.size > maximumBytes) return tooLargeOutput();
334
+ const bytes = await handle.readFile();
335
+ const [after, finalPath] = await Promise.all([handle.stat(), lstat(path)]);
336
+ if (
337
+ !finalPath.isFile()
338
+ || finalPath.isSymbolicLink()
339
+ || !sameFileState(before, opened)
340
+ || !sameFileState(opened, after)
341
+ || !sameFileState(opened, finalPath)
342
+ || bytes.byteLength !== opened.size
343
+ ) {
344
+ return { ok: false, result: failure("output-changed", "local transcription output changed while being read") };
345
+ }
346
+ return { ok: true, value: new TextDecoder("utf-8", { fatal: true }).decode(bytes) };
347
+ } finally {
348
+ await handle.close();
349
+ }
350
+ } catch (error) {
351
+ if (isMissing(error)) return missingOutput();
352
+ return unsafeOutput();
353
+ }
354
+ }
355
+
356
+ function parseWhisperCppJson(
357
+ input: string,
358
+ ): Readonly<{ ok: true; language: string | null; cues: readonly TranscriptCue[] }> | Readonly<{ ok: false; result: WhisperCppResult }> {
359
+ let value: unknown;
360
+ try {
361
+ value = JSON.parse(input) as unknown;
362
+ } catch {
363
+ return { ok: false, result: failure("invalid-json", "local transcription returned invalid JSON") };
364
+ }
365
+ if (!isRecord(value)) {
366
+ return { ok: false, result: failure("invalid-json", "local transcription JSON must be an object") };
367
+ }
368
+
369
+ const candidates = [
370
+ value.language,
371
+ recordProperty(value.result, "language"),
372
+ recordProperty(value.transcription, "language"),
373
+ recordProperty(value.transcription_info, "language"),
374
+ ].filter((candidate): candidate is unknown => candidate !== undefined);
375
+ const languages: string[] = [];
376
+ for (const candidate of candidates) {
377
+ const language = normalizeWhisperCppLanguage(candidate);
378
+ if (language === null || language === "auto") {
379
+ return { ok: false, result: failure("invalid-language", "local transcription JSON language is invalid") };
380
+ }
381
+ languages.push(language);
382
+ }
383
+ const unique = [...new Set(languages)];
384
+ if (unique.length > 1) {
385
+ return { ok: false, result: failure("invalid-language", "local transcription JSON languages disagree") };
386
+ }
387
+ const transcription = value.transcription;
388
+ if (!Array.isArray(transcription)) {
389
+ return { ok: false, result: failure("invalid-json", "local transcription JSON has no segment array") };
390
+ }
391
+ const cues: TranscriptCue[] = [];
392
+ for (let index = 0; index < transcription.length; index += 1) {
393
+ const segment: unknown = transcription[index];
394
+ if (!isRecord(segment) || !isRecord(segment.offsets) || typeof segment.text !== "string") {
395
+ return { ok: false, result: failure("invalid-json", "local transcription JSON contains an invalid segment") };
396
+ }
397
+ const startMs = segment.offsets.from;
398
+ const endMs = segment.offsets.to;
399
+ const text = stripWebVttMarkup(segment.text);
400
+ if (
401
+ !Number.isSafeInteger(startMs)
402
+ || !Number.isSafeInteger(endMs)
403
+ || (startMs as number) < 0
404
+ || (endMs as number) <= (startMs as number)
405
+ || text.length === 0
406
+ ) {
407
+ return { ok: false, result: failure("invalid-json", "local transcription JSON contains an invalid segment") };
408
+ }
409
+ cues.push({ startMs: startMs as number, endMs: endMs as number, text });
410
+ }
411
+ return { ok: true, language: unique[0] ?? null, cues };
412
+ }
413
+
414
+ function recordProperty(value: unknown, key: string): unknown {
415
+ return isRecord(value) ? value[key] : undefined;
416
+ }
417
+
418
+ function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
419
+ return typeof value === "object" && value !== null && !Array.isArray(value);
420
+ }
421
+
422
+ function isNonemptyNulFreeString(value: unknown): value is string {
423
+ return typeof value === "string" && value.length > 0 && !value.includes("\0");
424
+ }
425
+
426
+ function languagesMatch(requested: string, detected: string): boolean {
427
+ const requestedToolLanguage = whisperCppLanguageArgument(requested);
428
+ const detectedToolLanguage = whisperCppLanguageArgument(detected);
429
+ return requestedToolLanguage !== null
430
+ && requestedToolLanguage !== "auto"
431
+ && requestedToolLanguage === detectedToolLanguage;
432
+ }
433
+
434
+ function isContainedPath(directory: string, candidate: string): boolean {
435
+ const relativePath = relative(directory, candidate);
436
+ return relativePath.length > 0 && !relativePath.startsWith("..") && !relativePath.includes("\\0");
437
+ }
438
+
439
+ function sameFileState(
440
+ left: Readonly<{ dev: number; ino: number; size: number; mtimeMs: number; ctimeMs: number }>,
441
+ right: Readonly<{ dev: number; ino: number; size: number; mtimeMs: number; ctimeMs: number }>,
442
+ ): boolean {
443
+ return left.dev === right.dev
444
+ && left.ino === right.ino
445
+ && left.size === right.size
446
+ && left.mtimeMs === right.mtimeMs
447
+ && left.ctimeMs === right.ctimeMs;
448
+ }
449
+
450
+ function isMissing(error: unknown): boolean {
451
+ return isRecord(error) && error.code === "ENOENT";
452
+ }
453
+
454
+ function missingOutput(): Readonly<{ ok: false; result: WhisperCppResult }> {
455
+ return { ok: false, result: failure("output-missing", "local transcription did not produce its expected output") };
456
+ }
457
+
458
+ function unsafeOutput(): Readonly<{ ok: false; result: WhisperCppResult }> {
459
+ return { ok: false, result: failure("output-unsafe", "local transcription output is unsafe") };
460
+ }
461
+
462
+ function tooLargeOutput(): Readonly<{ ok: false; result: WhisperCppResult }> {
463
+ return { ok: false, result: failure("output-too-large", "local transcription output exceeds Ghostget media's limit") };
464
+ }
@@ -0,0 +1,58 @@
1
+ // Frozen to whisper.cpp's 100-language map for the v1 adapter profile. A map
2
+ // change is a provenance-affecting adapter change, not an ambient upgrade.
3
+ const WHISPER_CPP_LANGUAGE_CODES: ReadonlySet<string> = new Set([
4
+ "af", "am", "ar", "as", "az", "ba", "be", "bg", "bn", "bo",
5
+ "br", "bs", "ca", "cs", "cy", "da", "de", "el", "en", "es",
6
+ "et", "eu", "fa", "fi", "fo", "fr", "gl", "gu", "ha", "haw",
7
+ "he", "hi", "hr", "ht", "hu", "hy", "id", "is", "it", "ja",
8
+ "jw", "ka", "kk", "km", "kn", "ko", "la", "lb", "ln", "lo",
9
+ "lt", "lv", "mg", "mi", "mk", "ml", "mn", "mr", "ms", "mt",
10
+ "my", "ne", "nl", "nn", "no", "oc", "pa", "pl", "ps", "pt",
11
+ "ro", "ru", "sa", "sd", "si", "sk", "sl", "sn", "so", "sq",
12
+ "sr", "su", "sv", "sw", "ta", "te", "tg", "th", "tk", "tl",
13
+ "tr", "tt", "uk", "ur", "uz", "vi", "yi", "yo", "yue", "zh",
14
+ ]);
15
+
16
+ const WHISPER_CPP_LANGUAGE_ALIASES: ReadonlyMap<string, string> = new Map([
17
+ ["fil", "tl"],
18
+ ["in", "id"],
19
+ ["iw", "he"],
20
+ ["jv", "jw"],
21
+ ["nb", "no"],
22
+ ]);
23
+
24
+ const RESERVED_LANGUAGE_TOKENS = new Set(["all", "live_chat"]);
25
+
26
+ /** Normalizes one supported requested language while preserving regional identity. */
27
+ export function normalizeWhisperCppLanguage(value: unknown): string | null {
28
+ if (typeof value !== "string") return null;
29
+ const normalized = value.normalize("NFC");
30
+ if (normalized === "auto") return "auto";
31
+ if (!/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,3}$/u.test(normalized)) return null;
32
+ const canonical = normalized.toLowerCase();
33
+ if (RESERVED_LANGUAGE_TOKENS.has(canonical)) return null;
34
+ const primary = canonical.split("-", 1)[0];
35
+ if (primary === undefined) return null;
36
+ const toolLanguage = WHISPER_CPP_LANGUAGE_ALIASES.get(primary) ?? primary;
37
+ return WHISPER_CPP_LANGUAGE_CODES.has(toolLanguage) ? canonical : null;
38
+ }
39
+
40
+ /**
41
+ * whisper.cpp accepts its own short language identifiers, not regional BCP-47
42
+ * tags. Ghostget media keeps the complete requested tag in provenance and cache identity,
43
+ * while the native CLI receives only its normalized primary language subtag.
44
+ */
45
+ export function whisperCppLanguageArgument(value: unknown): string | null {
46
+ const normalized = normalizeWhisperCppLanguage(value);
47
+ if (normalized === null || normalized === "auto") return normalized;
48
+ const primary = normalized.split("-", 1)[0];
49
+ return primary === undefined
50
+ ? null
51
+ : WHISPER_CPP_LANGUAGE_ALIASES.get(primary) ?? primary;
52
+ }
53
+
54
+ /** Exact concrete language accepted in current-schema local transcript output. */
55
+ export function isConcreteWhisperCppLanguage(value: unknown): value is string {
56
+ if (typeof value !== "string" || value === "auto") return false;
57
+ return normalizeWhisperCppLanguage(value) === value;
58
+ }