@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,532 @@
1
+ import { types as nodeTypes } from "node:util";
2
+
3
+ import {
4
+ canonicalWhatsAppAccountSubjectJid,
5
+ canonicalWhatsAppParticipantJid,
6
+ isCanonicalWhatsAppAccountSubject,
7
+ } from "./whatsapp-account-identity";
8
+
9
+ export const WHATSAPP_MESSAGE_EXPORT_PROJECTION_PROTOCOL_VERSION = 1 as const;
10
+ export const WHATSAPP_MESSAGE_EXPORT_PROJECTION_MAX_LIMIT = 500;
11
+ export const WHATSAPP_MESSAGE_EXPORT_PROJECTION_MAX_STDIN_BYTES = 16 * 1024;
12
+ export const WHATSAPP_MESSAGE_EXPORT_PROJECTION_MAX_STDOUT_BYTES = 8 * 1024 * 1024;
13
+ export const WHATSAPP_MESSAGE_EXPORT_PROJECTION_SCHEMA_FINGERPRINT =
14
+ "sha256:994b5024bc2479a269866060ea14a06230532b5aba8365d31b1f94113df3bc57" as const;
15
+
16
+ const MAX_ROWID = 9_223_372_036_854_775_807n;
17
+ const ROWID_PATTERN = /^(?:0|[1-9][0-9]{0,18})$/u;
18
+ const SHA256_PATTERN = /^[a-f0-9]{64}$/u;
19
+ const MESSAGE_ID_PATTERN = /^[A-Za-z0-9._~:-]{1,256}$/u;
20
+ const USER_JID_PATTERN = /^[1-9][0-9]{4,14}@s\.whatsapp\.net$/u;
21
+ const LID_JID_PATTERN = /^[1-9][0-9]{4,19}@lid$/u;
22
+ const GROUP_JID_PATTERN = /^[1-9][0-9]{4,19}(?:-[1-9][0-9]{0,19})?@g\.us$/u;
23
+ const PARTICIPANT_JID_PATTERN = /^(?:[1-9][0-9]{4,14}(?::[0-9]{1,5})?@s\.whatsapp\.net|[1-9][0-9]{4,19}(?::[0-9]{1,5})?@lid)$/u;
24
+
25
+ export type WhatsAppMessageExportFileIdentity = Readonly<{
26
+ dev: string;
27
+ ino: string;
28
+ }>;
29
+
30
+ export type WhatsAppMessageExportProjectionGeneration = Readonly<{
31
+ messageStoreIdentity: WhatsAppMessageExportFileIdentity;
32
+ size: string;
33
+ mtimeNs: string;
34
+ ctimeNs: string;
35
+ schemaFingerprint: typeof WHATSAPP_MESSAGE_EXPORT_PROJECTION_SCHEMA_FINGERPRINT;
36
+ }>;
37
+
38
+ export type WhatsAppMessageExportProjectionRequest = Readonly<{
39
+ schemaVersion: typeof WHATSAPP_MESSAGE_EXPORT_PROJECTION_PROTOCOL_VERSION;
40
+ operation: "message-like-me.export";
41
+ accountSubject: string;
42
+ cursor: string;
43
+ cursorAnchor: string | null;
44
+ limit: number;
45
+ expectedGeneration: WhatsAppMessageExportProjectionGeneration | null;
46
+ storeIdentity: WhatsAppMessageExportFileIdentity;
47
+ sessionIdentity: WhatsAppMessageExportFileIdentity;
48
+ messageStoreIdentity: WhatsAppMessageExportFileIdentity;
49
+ }>;
50
+
51
+ export type WhatsAppMessageExportProjectionItem = Readonly<{
52
+ rowid: string;
53
+ chatJid: string;
54
+ chatKind: "dm" | "group";
55
+ chatName: string | null;
56
+ messageId: string;
57
+ senderJid: string | null;
58
+ senderName: string | null;
59
+ timestamp: string;
60
+ fromMe: boolean;
61
+ text: string | null;
62
+ displayText: string | null;
63
+ quotedMessageId: string | null;
64
+ quotedSenderJid: string | null;
65
+ reactionToMessageId: string | null;
66
+ reactionEmoji: string | null;
67
+ mediaType: string | null;
68
+ mediaCaption: string | null;
69
+ fileName: string | null;
70
+ mimeType: string | null;
71
+ fileLength: number | null;
72
+ revoked: boolean;
73
+ deletedForMe: boolean;
74
+ deletedAt: string | null;
75
+ payloadPurgedAt: string | null;
76
+ edited: boolean;
77
+ editedAt: string | null;
78
+ }>;
79
+
80
+ export type WhatsAppMessageExportAccountJidAliases = Readonly<{
81
+ pnJid: string;
82
+ lidJid: string | null;
83
+ }>;
84
+
85
+ export const WHATSAPP_MESSAGE_EXPORT_PROJECTION_ERROR_CODES = Object.freeze([
86
+ "request-invalid",
87
+ "store-binding-invalid",
88
+ "session-binding-invalid",
89
+ "message-store-file-invalid",
90
+ "message-store-file-too-large",
91
+ "message-store-sidecar-present",
92
+ "message-store-sidecar-state-unverified",
93
+ "database-invalid",
94
+ "database-integrity-failed",
95
+ "schema-mismatch",
96
+ "owner-mismatch",
97
+ "generation-mismatch",
98
+ "projection-invalid",
99
+ "output-too-large",
100
+ ] as const);
101
+
102
+ export type WhatsAppMessageExportProjectionErrorCode =
103
+ (typeof WHATSAPP_MESSAGE_EXPORT_PROJECTION_ERROR_CODES)[number];
104
+
105
+ export type WhatsAppMessageExportProjectionSuccess = Readonly<{
106
+ schemaVersion: typeof WHATSAPP_MESSAGE_EXPORT_PROJECTION_PROTOCOL_VERSION;
107
+ status: "succeeded";
108
+ projectionGeneration: WhatsAppMessageExportProjectionGeneration;
109
+ accountJidAliases: WhatsAppMessageExportAccountJidAliases;
110
+ nonConversationChatsExcluded: boolean;
111
+ messages: readonly WhatsAppMessageExportProjectionItem[];
112
+ nextCursor: string | null;
113
+ localInsertPageComplete: boolean;
114
+ checkpoint: Readonly<{ cursor: string; anchor: string | null }>;
115
+ }>;
116
+
117
+ export type WhatsAppMessageExportProjectionFailure = Readonly<{
118
+ schemaVersion: typeof WHATSAPP_MESSAGE_EXPORT_PROJECTION_PROTOCOL_VERSION;
119
+ status: "failed";
120
+ errorCode: WhatsAppMessageExportProjectionErrorCode;
121
+ }>;
122
+
123
+ export type WhatsAppMessageExportProjectionResponse =
124
+ | WhatsAppMessageExportProjectionSuccess
125
+ | WhatsAppMessageExportProjectionFailure;
126
+
127
+ type JsonRecord = Readonly<Record<string, unknown>>;
128
+
129
+ function fail(label: string): never {
130
+ throw new Error(`${label} did not match the WhatsApp message export projection protocol`);
131
+ }
132
+
133
+ function record(value: unknown, label: string): JsonRecord {
134
+ if (
135
+ typeof value !== "object"
136
+ || value === null
137
+ || Array.isArray(value)
138
+ || nodeTypes.isProxy(value)
139
+ || (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)
140
+ ) return fail(label);
141
+ const descriptors = Object.getOwnPropertyDescriptors(value);
142
+ for (const key of Reflect.ownKeys(descriptors)) {
143
+ if (typeof key !== "string") return fail(label);
144
+ const descriptor = descriptors[key];
145
+ if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor)) {
146
+ return fail(label);
147
+ }
148
+ }
149
+ return value as JsonRecord;
150
+ }
151
+
152
+ function exactKeys(value: JsonRecord, expected: readonly string[], label: string): void {
153
+ const actual = Object.keys(value).sort();
154
+ const wanted = [...expected].sort();
155
+ if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) fail(label);
156
+ }
157
+
158
+ function denseArray(value: unknown, label: string, maximum: number): readonly unknown[] {
159
+ if (
160
+ !Array.isArray(value)
161
+ || nodeTypes.isProxy(value)
162
+ || Object.getPrototypeOf(value) !== Array.prototype
163
+ || value.length > maximum
164
+ ) return fail(label);
165
+ const keys = Reflect.ownKeys(value);
166
+ if (keys.length !== value.length + 1 || keys.some((key) => typeof key !== "string")) {
167
+ return fail(label);
168
+ }
169
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
170
+ if (
171
+ lengthDescriptor === undefined
172
+ || lengthDescriptor.enumerable
173
+ || !("value" in lengthDescriptor)
174
+ || lengthDescriptor.value !== value.length
175
+ ) return fail(label);
176
+ for (let index = 0; index < value.length; index += 1) {
177
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
178
+ if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor)) {
179
+ return fail(label);
180
+ }
181
+ }
182
+ return value;
183
+ }
184
+
185
+ function unsigned(value: unknown, label: string, allowZero = true): string {
186
+ if (typeof value !== "string" || !ROWID_PATTERN.test(value) || BigInt(value) > MAX_ROWID) {
187
+ return fail(label);
188
+ }
189
+ if (!allowZero && value === "0") return fail(label);
190
+ return value;
191
+ }
192
+
193
+ function fileIdentity(value: unknown, label: string): WhatsAppMessageExportFileIdentity {
194
+ const parsed = record(value, label);
195
+ exactKeys(parsed, ["dev", "ino"], label);
196
+ return Object.freeze({
197
+ dev: unsigned(parsed.dev, `${label}.dev`),
198
+ ino: unsigned(parsed.ino, `${label}.ino`, false),
199
+ });
200
+ }
201
+
202
+ function generation(value: unknown, label: string): WhatsAppMessageExportProjectionGeneration {
203
+ const parsed = record(value, label);
204
+ exactKeys(parsed, ["messageStoreIdentity", "size", "mtimeNs", "ctimeNs", "schemaFingerprint"], label);
205
+ if (parsed.schemaFingerprint !== WHATSAPP_MESSAGE_EXPORT_PROJECTION_SCHEMA_FINGERPRINT) fail(label);
206
+ return Object.freeze({
207
+ messageStoreIdentity: fileIdentity(parsed.messageStoreIdentity, `${label}.messageStoreIdentity`),
208
+ size: unsigned(parsed.size, `${label}.size`, false),
209
+ mtimeNs: unsigned(parsed.mtimeNs, `${label}.mtimeNs`),
210
+ ctimeNs: unsigned(parsed.ctimeNs, `${label}.ctimeNs`),
211
+ schemaFingerprint: WHATSAPP_MESSAGE_EXPORT_PROJECTION_SCHEMA_FINGERPRINT,
212
+ });
213
+ }
214
+
215
+ function sameGeneration(
216
+ left: WhatsAppMessageExportProjectionGeneration,
217
+ right: WhatsAppMessageExportProjectionGeneration,
218
+ ): boolean {
219
+ return left.messageStoreIdentity.dev === right.messageStoreIdentity.dev
220
+ && left.messageStoreIdentity.ino === right.messageStoreIdentity.ino
221
+ && left.size === right.size
222
+ && left.mtimeNs === right.mtimeNs
223
+ && left.ctimeNs === right.ctimeNs
224
+ && left.schemaFingerprint === right.schemaFingerprint;
225
+ }
226
+
227
+ function text(value: unknown, label: string, maximum: number, nullable = true): string | null {
228
+ if (nullable && value === null) return null;
229
+ if (typeof value !== "string" || Buffer.byteLength(value, "utf8") > maximum) return fail(label);
230
+ return value;
231
+ }
232
+
233
+ function participantJid(value: unknown, label: string, nullable = false): string | null {
234
+ if (nullable && (value === null || value === "")) return null;
235
+ if (
236
+ typeof value !== "string"
237
+ || value.length > 96
238
+ || !PARTICIPANT_JID_PATTERN.test(value)
239
+ ) return fail(label);
240
+ return value;
241
+ }
242
+
243
+ function conversationJid(
244
+ value: unknown,
245
+ kind: "dm" | "group",
246
+ label: string,
247
+ ): string {
248
+ if (
249
+ typeof value !== "string"
250
+ || value.length > 96
251
+ || (kind === "dm" && !USER_JID_PATTERN.test(value) && !LID_JID_PATTERN.test(value))
252
+ || (kind === "group" && !GROUP_JID_PATTERN.test(value))
253
+ ) return fail(label);
254
+ return value;
255
+ }
256
+
257
+ function messageId(value: unknown, label: string, nullable = false): string | null {
258
+ if (nullable && (value === null || value === "")) return null;
259
+ if (typeof value !== "string" || !MESSAGE_ID_PATTERN.test(value)) return fail(label);
260
+ return value;
261
+ }
262
+
263
+ function timestamp(value: unknown, label: string, nullable = false): string | null {
264
+ if (nullable && value === null) return null;
265
+ if (typeof value !== "string") return fail(label);
266
+ const parsed = new Date(value);
267
+ if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== value) return fail(label);
268
+ return value;
269
+ }
270
+
271
+ function item(
272
+ value: unknown,
273
+ label: string,
274
+ accountJids: ReadonlySet<string> | undefined,
275
+ ): WhatsAppMessageExportProjectionItem {
276
+ const parsed = record(value, label);
277
+ exactKeys(parsed, [
278
+ "rowid", "chatJid", "chatKind", "chatName", "messageId", "senderJid", "senderName",
279
+ "timestamp", "fromMe", "text", "displayText", "quotedMessageId", "quotedSenderJid",
280
+ "reactionToMessageId", "reactionEmoji", "mediaType", "mediaCaption", "fileName",
281
+ "mimeType", "fileLength", "revoked", "deletedForMe", "deletedAt", "payloadPurgedAt",
282
+ "edited", "editedAt",
283
+ ], label);
284
+ if (
285
+ (parsed.chatKind !== "dm" && parsed.chatKind !== "group")
286
+ || typeof parsed.fromMe !== "boolean"
287
+ || typeof parsed.revoked !== "boolean"
288
+ || typeof parsed.deletedForMe !== "boolean"
289
+ || typeof parsed.edited !== "boolean"
290
+ || (parsed.fileLength !== null && (
291
+ typeof parsed.fileLength !== "number"
292
+ || !Number.isSafeInteger(parsed.fileLength)
293
+ || parsed.fileLength < 0
294
+ ))
295
+ ) return fail(label);
296
+ const fileName = text(parsed.fileName, `${label}.fileName`, 8 * 1024);
297
+ if (fileName !== null && (fileName === "." || fileName === ".." || fileName.includes("/") || fileName.includes("\\"))) {
298
+ return fail(`${label}.fileName`);
299
+ }
300
+ const sentAt = timestamp(parsed.timestamp, `${label}.timestamp`)! as string;
301
+ const editedAt = timestamp(parsed.editedAt, `${label}.editedAt`, true);
302
+ const deletedAt = timestamp(parsed.deletedAt, `${label}.deletedAt`, true);
303
+ const payloadPurgedAt = timestamp(parsed.payloadPurgedAt, `${label}.payloadPurgedAt`, true);
304
+ const reactionToMessageId = messageId(
305
+ parsed.reactionToMessageId,
306
+ `${label}.reactionToMessageId`,
307
+ true,
308
+ );
309
+ const reactionEmoji = text(parsed.reactionEmoji, `${label}.reactionEmoji`, 8 * 1024);
310
+ if ((parsed.edited && editedAt === null) || (!parsed.edited && editedAt !== null) || (editedAt !== null && editedAt < sentAt)) {
311
+ return fail(`${label}.editedAt`);
312
+ }
313
+ if (
314
+ (reactionEmoji !== null && reactionEmoji.length > 0 && reactionToMessageId === null)
315
+ || (deletedAt !== null && !parsed.revoked && !parsed.deletedForMe)
316
+ || (deletedAt !== null && deletedAt < sentAt)
317
+ || (payloadPurgedAt !== null && payloadPurgedAt < sentAt)
318
+ ) return fail(label);
319
+ const projected = Object.freeze({
320
+ rowid: unsigned(parsed.rowid, `${label}.rowid`, false),
321
+ chatJid: conversationJid(parsed.chatJid, parsed.chatKind, `${label}.chatJid`),
322
+ chatKind: parsed.chatKind,
323
+ chatName: text(parsed.chatName, `${label}.chatName`, 8 * 1024),
324
+ messageId: messageId(parsed.messageId, `${label}.messageId`)! as string,
325
+ senderJid: participantJid(parsed.senderJid, `${label}.senderJid`, true),
326
+ senderName: text(parsed.senderName, `${label}.senderName`, 8 * 1024),
327
+ timestamp: sentAt,
328
+ fromMe: parsed.fromMe,
329
+ text: text(parsed.text, `${label}.text`, 1024 * 1024),
330
+ displayText: text(parsed.displayText, `${label}.displayText`, 1024 * 1024),
331
+ quotedMessageId: messageId(parsed.quotedMessageId, `${label}.quotedMessageId`, true),
332
+ quotedSenderJid: participantJid(parsed.quotedSenderJid, `${label}.quotedSenderJid`, true),
333
+ reactionToMessageId,
334
+ reactionEmoji,
335
+ mediaType: text(parsed.mediaType, `${label}.mediaType`, 256),
336
+ mediaCaption: text(parsed.mediaCaption, `${label}.mediaCaption`, 1024 * 1024),
337
+ fileName,
338
+ mimeType: text(parsed.mimeType, `${label}.mimeType`, 256),
339
+ fileLength: parsed.fileLength as number | null,
340
+ revoked: parsed.revoked,
341
+ deletedForMe: parsed.deletedForMe,
342
+ deletedAt,
343
+ payloadPurgedAt,
344
+ edited: parsed.edited,
345
+ editedAt,
346
+ });
347
+ if (accountJids !== undefined) {
348
+ const senderJid = projected.senderJid === null
349
+ ? null
350
+ : canonicalWhatsAppParticipantJid(projected.senderJid);
351
+ const senderMustBeSelf = projected.fromMe;
352
+ const valid = projected.chatKind === "dm"
353
+ ? senderJid === null
354
+ || (senderMustBeSelf ? accountJids.has(senderJid) : senderJid === projected.chatJid)
355
+ : senderJid === null
356
+ || (senderMustBeSelf ? accountJids.has(senderJid) : !accountJids.has(senderJid));
357
+ if (!valid) return fail(`${label}.senderJid`);
358
+ }
359
+ return projected;
360
+ }
361
+
362
+ function accountJidAliases(
363
+ value: unknown,
364
+ accountSubject: string | undefined,
365
+ ): WhatsAppMessageExportAccountJidAliases {
366
+ const parsed = record(value, "response.accountJidAliases");
367
+ exactKeys(parsed, ["pnJid", "lidJid"], "response.accountJidAliases");
368
+ const pnJid = typeof parsed.pnJid === "string" && USER_JID_PATTERN.test(parsed.pnJid)
369
+ ? parsed.pnJid
370
+ : fail("response.accountJidAliases.pnJid");
371
+ const lidJid = parsed.lidJid === null
372
+ ? null
373
+ : typeof parsed.lidJid === "string" && LID_JID_PATTERN.test(parsed.lidJid)
374
+ ? parsed.lidJid
375
+ : fail("response.accountJidAliases.lidJid");
376
+ if (
377
+ accountSubject !== undefined
378
+ && canonicalWhatsAppAccountSubjectJid(accountSubject) !== pnJid
379
+ && canonicalWhatsAppAccountSubjectJid(accountSubject) !== lidJid
380
+ ) fail("response.accountJidAliases binding");
381
+ return Object.freeze({ pnJid, lidJid });
382
+ }
383
+
384
+ export function parseWhatsAppMessageExportProjectionRequest(
385
+ value: unknown,
386
+ ): WhatsAppMessageExportProjectionRequest {
387
+ const parsed = record(value, "request");
388
+ exactKeys(parsed, [
389
+ "schemaVersion", "operation", "accountSubject", "cursor", "cursorAnchor", "limit",
390
+ "expectedGeneration", "storeIdentity", "sessionIdentity", "messageStoreIdentity",
391
+ ], "request");
392
+ if (
393
+ parsed.schemaVersion !== WHATSAPP_MESSAGE_EXPORT_PROJECTION_PROTOCOL_VERSION
394
+ || parsed.operation !== "message-like-me.export"
395
+ || !isCanonicalWhatsAppAccountSubject(parsed.accountSubject)
396
+ || typeof parsed.limit !== "number"
397
+ || !Number.isSafeInteger(parsed.limit)
398
+ || parsed.limit < 1
399
+ || parsed.limit > WHATSAPP_MESSAGE_EXPORT_PROJECTION_MAX_LIMIT
400
+ ) return fail("request");
401
+ const cursor = unsigned(parsed.cursor, "request.cursor");
402
+ const cursorAnchor = parsed.cursorAnchor === null
403
+ ? null
404
+ : typeof parsed.cursorAnchor === "string" && SHA256_PATTERN.test(parsed.cursorAnchor)
405
+ ? parsed.cursorAnchor
406
+ : fail("request.cursorAnchor");
407
+ const expectedGeneration = parsed.expectedGeneration === null
408
+ ? null
409
+ : generation(parsed.expectedGeneration, "request.expectedGeneration");
410
+ if (
411
+ (cursor === "0") !== (cursorAnchor === null)
412
+ || (cursor === "0") !== (expectedGeneration === null)
413
+ ) return fail("request checkpoint");
414
+ const messageStoreIdentity = fileIdentity(parsed.messageStoreIdentity, "request.messageStoreIdentity");
415
+ if (expectedGeneration !== null && (
416
+ expectedGeneration.messageStoreIdentity.dev !== messageStoreIdentity.dev
417
+ || expectedGeneration.messageStoreIdentity.ino !== messageStoreIdentity.ino
418
+ )) return fail("request.expectedGeneration");
419
+ return Object.freeze({
420
+ schemaVersion: WHATSAPP_MESSAGE_EXPORT_PROJECTION_PROTOCOL_VERSION,
421
+ operation: "message-like-me.export",
422
+ accountSubject: parsed.accountSubject,
423
+ cursor,
424
+ cursorAnchor,
425
+ limit: parsed.limit,
426
+ expectedGeneration,
427
+ storeIdentity: fileIdentity(parsed.storeIdentity, "request.storeIdentity"),
428
+ sessionIdentity: fileIdentity(parsed.sessionIdentity, "request.sessionIdentity"),
429
+ messageStoreIdentity,
430
+ });
431
+ }
432
+
433
+ export function parseWhatsAppMessageExportProjectionResponse(
434
+ value: unknown,
435
+ request?: WhatsAppMessageExportProjectionRequest,
436
+ ): WhatsAppMessageExportProjectionResponse {
437
+ const parsed = record(value, "response");
438
+ if (parsed.status === "failed") {
439
+ exactKeys(parsed, ["schemaVersion", "status", "errorCode"], "response");
440
+ if (
441
+ parsed.schemaVersion !== WHATSAPP_MESSAGE_EXPORT_PROJECTION_PROTOCOL_VERSION
442
+ || typeof parsed.errorCode !== "string"
443
+ || !(WHATSAPP_MESSAGE_EXPORT_PROJECTION_ERROR_CODES as readonly string[]).includes(parsed.errorCode)
444
+ ) return fail("response");
445
+ return Object.freeze({
446
+ schemaVersion: WHATSAPP_MESSAGE_EXPORT_PROJECTION_PROTOCOL_VERSION,
447
+ status: "failed",
448
+ errorCode: parsed.errorCode as WhatsAppMessageExportProjectionErrorCode,
449
+ });
450
+ }
451
+ exactKeys(parsed, [
452
+ "schemaVersion", "status", "projectionGeneration", "accountJidAliases", "nonConversationChatsExcluded", "messages",
453
+ "nextCursor", "localInsertPageComplete", "checkpoint",
454
+ ], "response");
455
+ if (
456
+ parsed.schemaVersion !== WHATSAPP_MESSAGE_EXPORT_PROJECTION_PROTOCOL_VERSION
457
+ || parsed.status !== "succeeded"
458
+ || typeof parsed.nonConversationChatsExcluded !== "boolean"
459
+ || typeof parsed.localInsertPageComplete !== "boolean"
460
+ ) return fail("response");
461
+ const rawMessages = denseArray(
462
+ parsed.messages,
463
+ "response.messages",
464
+ WHATSAPP_MESSAGE_EXPORT_PROJECTION_MAX_LIMIT,
465
+ );
466
+ const projectionGeneration = generation(parsed.projectionGeneration, "response.projectionGeneration");
467
+ if (request !== undefined && (
468
+ projectionGeneration.messageStoreIdentity.dev !== request.messageStoreIdentity.dev
469
+ || projectionGeneration.messageStoreIdentity.ino !== request.messageStoreIdentity.ino
470
+ || (request.expectedGeneration !== null && !sameGeneration(projectionGeneration, request.expectedGeneration))
471
+ || rawMessages.length > request.limit
472
+ || (!parsed.localInsertPageComplete && rawMessages.length !== request.limit)
473
+ )) return fail("response projection binding");
474
+ const aliases = accountJidAliases(parsed.accountJidAliases, request?.accountSubject);
475
+ const accountJids = new Set([aliases.pnJid, aliases.lidJid].filter(
476
+ (jid): jid is string => jid !== null,
477
+ ));
478
+ const messages = rawMessages.map((value, index) =>
479
+ item(value, `response.messages[${index}]`, accountJids));
480
+ for (let index = 0; index < messages.length; index += 1) {
481
+ const previous = index === 0 ? request?.cursor : messages[index - 1]?.rowid;
482
+ if (previous !== undefined && BigInt(messages[index]!.rowid) <= BigInt(previous)) {
483
+ return fail("response.messages ordering");
484
+ }
485
+ }
486
+ const nextCursor = parsed.nextCursor === null
487
+ ? null
488
+ : unsigned(parsed.nextCursor, "response.nextCursor", false);
489
+ if (
490
+ (parsed.localInsertPageComplete && nextCursor !== null)
491
+ || (!parsed.localInsertPageComplete && (messages.length === 0 || nextCursor !== messages.at(-1)?.rowid))
492
+ ) return fail("response.nextCursor");
493
+ const checkpoint = record(parsed.checkpoint, "response.checkpoint");
494
+ exactKeys(checkpoint, ["cursor", "anchor"], "response.checkpoint");
495
+ const checkpointCursor = unsigned(checkpoint.cursor, "response.checkpoint.cursor");
496
+ const checkpointAnchor = checkpoint.anchor === null
497
+ ? null
498
+ : typeof checkpoint.anchor === "string" && SHA256_PATTERN.test(checkpoint.anchor)
499
+ ? checkpoint.anchor
500
+ : fail("response.checkpoint.anchor");
501
+ if (
502
+ (checkpointCursor === "0") !== (checkpointAnchor === null)
503
+ || (messages.length > 0 && checkpointCursor !== messages.at(-1)?.rowid)
504
+ || (messages.length === 0 && request !== undefined
505
+ && (checkpointCursor !== request.cursor || checkpointAnchor !== request.cursorAnchor))
506
+ || (!parsed.localInsertPageComplete && checkpointCursor !== nextCursor)
507
+ ) return fail("response.checkpoint");
508
+ return Object.freeze({
509
+ schemaVersion: WHATSAPP_MESSAGE_EXPORT_PROJECTION_PROTOCOL_VERSION,
510
+ status: "succeeded",
511
+ projectionGeneration,
512
+ accountJidAliases: aliases,
513
+ nonConversationChatsExcluded: parsed.nonConversationChatsExcluded,
514
+ messages: Object.freeze(messages),
515
+ nextCursor,
516
+ localInsertPageComplete: parsed.localInsertPageComplete,
517
+ checkpoint: Object.freeze({ cursor: checkpointCursor, anchor: checkpointAnchor }),
518
+ });
519
+ }
520
+
521
+ export function createWhatsAppMessageExportProjectionFailure(
522
+ errorCode: WhatsAppMessageExportProjectionErrorCode,
523
+ ): WhatsAppMessageExportProjectionFailure {
524
+ if (!(WHATSAPP_MESSAGE_EXPORT_PROJECTION_ERROR_CODES as readonly string[]).includes(errorCode)) {
525
+ return fail("errorCode");
526
+ }
527
+ return Object.freeze({
528
+ schemaVersion: WHATSAPP_MESSAGE_EXPORT_PROJECTION_PROTOCOL_VERSION,
529
+ status: "failed",
530
+ errorCode,
531
+ });
532
+ }
@@ -0,0 +1,132 @@
1
+ import type { OperationInput } from "../model";
2
+ import type {
3
+ ProviderPluginMessagingDefinitionV1,
4
+ ProviderPluginMessagingTargetV1,
5
+ } from "../provider-plugin";
6
+ import { parseWhatsAppJid, whatsappTargetJid } from "./whatsapp-web";
7
+ import { materializeWhatsAppMessagingRead } from "./whatsapp-omni";
8
+
9
+ type JsonRecord = Readonly<Record<string, unknown>>;
10
+
11
+ function parseTarget(value: unknown): ProviderPluginMessagingTargetV1 {
12
+ if (
13
+ typeof value !== "object"
14
+ || value === null
15
+ || Array.isArray(value)
16
+ || Object.getPrototypeOf(value) !== Object.prototype
17
+ ) throw new Error("WhatsApp messaging target must be a plain object");
18
+ const descriptors = Object.getOwnPropertyDescriptors(value);
19
+ if (
20
+ Reflect.ownKeys(descriptors).length !== 1
21
+ || descriptors.conversationJid === undefined
22
+ || !descriptors.conversationJid.enumerable
23
+ || !("value" in descriptors.conversationJid)
24
+ ) throw new Error("WhatsApp messaging target has unsupported fields");
25
+ const source = value as JsonRecord;
26
+ return Object.freeze({
27
+ conversationJid: whatsappTargetJid(
28
+ source.conversationJid,
29
+ "WhatsApp messaging target conversation",
30
+ ),
31
+ });
32
+ }
33
+
34
+ function conversationJid(target: ProviderPluginMessagingTargetV1): string {
35
+ const value = target.conversationJid;
36
+ if (value === undefined) {
37
+ throw new Error("WhatsApp messaging target changed after parsing");
38
+ }
39
+ return value;
40
+ }
41
+
42
+ export const whatsappMessagingDefinition = Object.freeze({
43
+ schemaVersion: 1,
44
+ contractId: "wrench.provider-messaging.whatsapp.v1",
45
+ network: "whatsapp",
46
+ contextLiveness: "freshness-unproven",
47
+ listOperation: "messaging.list",
48
+ contextOperation: "messaging.read",
49
+ enumerateRoutes: (_input: OperationInput, page) => Object.freeze(
50
+ page.entities.map((entity) => {
51
+ if (entity.kind !== "conversation") {
52
+ throw new Error("WhatsApp messaging route projection contained a non-conversation");
53
+ }
54
+ const jid = whatsappTargetJid(
55
+ entity.providerId,
56
+ "WhatsApp messaging route conversation",
57
+ );
58
+ const kind = parseWhatsAppJid(jid).kind;
59
+ return Object.freeze({
60
+ target: Object.freeze({ conversationJid: jid }),
61
+ conversationProviderId: jid,
62
+ conversationKind: kind === "group" ? "group" as const : "single" as const,
63
+ title: entity.title,
64
+ participants: entity.participants,
65
+ providerRevision: entity.providerRevision,
66
+ });
67
+ }),
68
+ ),
69
+ resolveRoute: Object.freeze({
70
+ operation: "messaging.read",
71
+ input: (target: ProviderPluginMessagingTargetV1) => {
72
+ const parsed = parseTarget(target);
73
+ return Object.freeze({
74
+ conversation_jid: whatsappTargetJid(
75
+ conversationJid(parsed),
76
+ "WhatsApp exact conversation candidate",
77
+ ),
78
+ limit: 1,
79
+ });
80
+ },
81
+ candidates: (target, output) => {
82
+ const parsed = parseTarget(target);
83
+ const exactJid = whatsappTargetJid(
84
+ conversationJid(parsed),
85
+ "WhatsApp exact conversation candidate",
86
+ );
87
+ const exactInput = Object.freeze({
88
+ conversation_jid: exactJid,
89
+ limit: 1,
90
+ });
91
+ const page = materializeWhatsAppMessagingRead(exactInput, output);
92
+ for (const entity of page.entities) {
93
+ if (
94
+ entity.kind !== "message"
95
+ || entity.conversationProviderId !== exactJid
96
+ ) throw new Error("WhatsApp exact route read returned another conversation");
97
+ }
98
+ return Object.freeze([Object.freeze({
99
+ target: Object.freeze({ conversationJid: exactJid }),
100
+ conversationProviderId: exactJid,
101
+ conversationKind: parseWhatsAppJid(exactJid).kind === "group"
102
+ ? "group" as const
103
+ : "single" as const,
104
+ title: null,
105
+ participants: Object.freeze([]),
106
+ providerRevision: null,
107
+ })]);
108
+ },
109
+ sourceConversationCoordinate: (
110
+ target,
111
+ _output,
112
+ _expectedAccountSubject,
113
+ ) => {
114
+ parseTarget(target);
115
+ return null;
116
+ },
117
+ }),
118
+ parseTarget,
119
+ contextInput: (target, limit) => {
120
+ const parsed = parseTarget(target);
121
+ return Object.freeze({
122
+ conversation_jid: conversationJid(parsed),
123
+ limit,
124
+ });
125
+ },
126
+ action: Object.freeze({
127
+ state: "unavailable",
128
+ reply: "unsupported",
129
+ reason:
130
+ "capture-required: the official read runtime has no Ghostget-qualified mutation transport",
131
+ }),
132
+ } satisfies ProviderPluginMessagingDefinitionV1);