@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,539 @@
1
+ import { join } from "node:path";
2
+
3
+ import { canonicalJson, sha256 } from "./canonical-json";
4
+ import {
5
+ parseMessageLikeMeSourceConversationCoordinateBindingV1,
6
+ type MessageLikeMeSourceConversationCoordinateBindingV1,
7
+ } from "./message-like-me-agentic-messaging";
8
+ import type { OperationInput } from "./model";
9
+ import type { ProviderPluginMessagingTargetV1 } from "./provider-plugin";
10
+ import {
11
+ openAuthenticatedPrivatePayload,
12
+ sealAuthenticatedPrivatePayload,
13
+ } from "./read-projections";
14
+ import {
15
+ createPrivateJsonIfAbsent,
16
+ ensurePrivateStateDirectory,
17
+ listPrivateStateDirectory,
18
+ readPrivateStateFileIfPresent,
19
+ removePrivateStateFile,
20
+ ghostgetStateHome,
21
+ } from "./storage";
22
+
23
+ const MAX_RECORD_BYTES = 1024 * 1024;
24
+ const MAX_ENCRYPTED_RECORD_BYTES = 2 * 1024 * 1024;
25
+
26
+ export type MessagingRouteRecordV1 = {
27
+ readonly schemaVersion: 1;
28
+ readonly format: "wrench.messaging-route-record";
29
+ readonly routeRef: string;
30
+ readonly createdAt: string;
31
+ readonly expiresAt: string;
32
+ readonly adapter: {
33
+ readonly id: string;
34
+ readonly version: string;
35
+ readonly hash: string;
36
+ };
37
+ readonly plugin: {
38
+ readonly id: string;
39
+ readonly version: string;
40
+ readonly closureHash: string;
41
+ };
42
+ readonly binding: {
43
+ readonly surfaceId: string;
44
+ readonly transport: "provider-api" | "web-session-api" | "linked-device" | "local-cli";
45
+ readonly implementationIdentity: string;
46
+ readonly messagingContractId: string;
47
+ };
48
+ readonly auth: {
49
+ readonly id: string;
50
+ readonly hash: string;
51
+ readonly subject: string;
52
+ };
53
+ readonly list: {
54
+ readonly operation: "messaging.list";
55
+ readonly input: OperationInput;
56
+ readonly inputHash: string;
57
+ readonly exactDataRevision: string;
58
+ readonly validatedAt: string;
59
+ };
60
+ readonly target: ProviderPluginMessagingTargetV1;
61
+ readonly resolution: "list-candidate" | "exact-coordinate";
62
+ readonly network: string;
63
+ readonly sourceConversationCoordinate:
64
+ | MessageLikeMeSourceConversationCoordinateBindingV1
65
+ | null;
66
+ readonly conversationProviderId: string;
67
+ readonly conversation: {
68
+ readonly kind: "single" | "group" | "unknown";
69
+ readonly title: string | null;
70
+ readonly participantCount: number;
71
+ readonly participantFingerprint: string;
72
+ readonly providerRevision: string | null;
73
+ };
74
+ };
75
+
76
+ export type MessagingContextRecordV1 = {
77
+ readonly schemaVersion: 1;
78
+ readonly format: "wrench.messaging-context-record";
79
+ readonly contextRef: string;
80
+ readonly routeRef: string;
81
+ readonly routeRecordHash: string;
82
+ readonly sourceConversationCoordinate:
83
+ | MessageLikeMeSourceConversationCoordinateBindingV1
84
+ | null;
85
+ readonly exactDataRevision: string;
86
+ readonly latestMessageRevision: string;
87
+ readonly validatedAt: string;
88
+ readonly expiresAt: string;
89
+ readonly limit: number;
90
+ readonly liveness: "fresh-as-of-live-preflight" | "freshness-unproven";
91
+ readonly replyTargets: Readonly<Record<string, string>>;
92
+ };
93
+
94
+ function messagingRoot(
95
+ environment: Readonly<Record<string, string | undefined>>,
96
+ ): string {
97
+ return join(ghostgetStateHome(environment), "messaging");
98
+ }
99
+
100
+ function ensureMessagingRecordDirectory(
101
+ kind: "routes" | "contexts",
102
+ environment: Readonly<Record<string, string | undefined>>,
103
+ ) {
104
+ ensurePrivateStateDirectory(messagingRoot(environment), environment);
105
+ return ensurePrivateStateDirectory(
106
+ join(messagingRoot(environment), kind),
107
+ environment,
108
+ );
109
+ }
110
+
111
+ function routePath(
112
+ routeRef: string,
113
+ environment: Readonly<Record<string, string | undefined>>,
114
+ ): string {
115
+ if (!/^wmroute_[A-Za-z0-9_-]{22}$/u.test(routeRef)) {
116
+ throw new Error("messaging route reference is malformed");
117
+ }
118
+ return join(messagingRoot(environment), "routes", `${routeRef}.json`);
119
+ }
120
+
121
+ function contextPath(
122
+ contextRef: string,
123
+ environment: Readonly<Record<string, string | undefined>>,
124
+ ): string {
125
+ if (!/^wmcontext_[A-Za-z0-9_-]{22}$/u.test(contextRef)) {
126
+ throw new Error("messaging context reference is malformed");
127
+ }
128
+ return join(messagingRoot(environment), "contexts", `${contextRef}.json`);
129
+ }
130
+
131
+ function routeDomain(routeRef: string): string {
132
+ return `wrench-messaging-route-record-v1:${routeRef}`;
133
+ }
134
+
135
+ function contextDomain(contextRef: string): string {
136
+ return `wrench-messaging-context-record-v1:${contextRef}`;
137
+ }
138
+
139
+ function observationTime(value: Date): number {
140
+ const result = value.getTime();
141
+ if (!Number.isFinite(result)) throw new Error("messaging record observation time is invalid");
142
+ return result;
143
+ }
144
+
145
+ function record(value: unknown, label: string): Record<string, unknown> {
146
+ if (
147
+ typeof value !== "object"
148
+ || value === null
149
+ || Array.isArray(value)
150
+ || Object.getPrototypeOf(value) !== Object.prototype
151
+ ) throw new Error(`${label} is malformed`);
152
+ return value as Record<string, unknown>;
153
+ }
154
+
155
+ function exactKeys(
156
+ value: Record<string, unknown>,
157
+ keys: readonly string[],
158
+ label: string,
159
+ ): void {
160
+ const actual = Object.keys(value).sort();
161
+ const expected = [...keys].sort();
162
+ if (
163
+ actual.length !== expected.length
164
+ || actual.some((key, index) => key !== expected[index])
165
+ ) throw new Error(`${label} has unsupported fields`);
166
+ }
167
+
168
+ function string(value: unknown, label: string, maximum = 4_096): string {
169
+ if (
170
+ typeof value !== "string"
171
+ || value.length < 1
172
+ || Buffer.byteLength(value, "utf8") > maximum
173
+ || /[\0\r\n]/u.test(value)
174
+ ) throw new Error(`${label} is malformed`);
175
+ return value;
176
+ }
177
+
178
+ function digest(value: unknown, label: string): string {
179
+ const result = string(value, label, 64);
180
+ if (!/^[a-f0-9]{64}$/u.test(result)) throw new Error(`${label} is malformed`);
181
+ return result;
182
+ }
183
+
184
+ function timestamp(value: unknown, label: string): string {
185
+ const result = string(value, label, 64);
186
+ if (!Number.isFinite(Date.parse(result)) || !result.endsWith("Z")) {
187
+ throw new Error(`${label} is malformed`);
188
+ }
189
+ return result;
190
+ }
191
+
192
+ function parseStringRecord(
193
+ value: unknown,
194
+ label: string,
195
+ ): Readonly<Record<string, string>> {
196
+ const source = record(value, label);
197
+ const entries = Object.entries(source);
198
+ if (entries.length < 1 || entries.length > 16) {
199
+ throw new Error(`${label} has an invalid field count`);
200
+ }
201
+ return Object.freeze(Object.fromEntries(entries.map(([key, candidate]) => [
202
+ string(key, `${label} key`, 128),
203
+ string(candidate, `${label}.${key}`, 4_096),
204
+ ])));
205
+ }
206
+
207
+ function parseOperationInput(value: unknown): OperationInput {
208
+ const source = record(value, "messaging route list input");
209
+ if (Buffer.byteLength(canonicalJson(source), "utf8") > 256 * 1024) {
210
+ throw new Error("messaging route list input is too large");
211
+ }
212
+ return Object.freeze(source) as OperationInput;
213
+ }
214
+
215
+ export function parseMessagingRouteRecordV1(
216
+ value: unknown,
217
+ ): MessagingRouteRecordV1 {
218
+ const source = record(value, "messaging route record");
219
+ exactKeys(source, [
220
+ "schemaVersion",
221
+ "format",
222
+ "routeRef",
223
+ "createdAt",
224
+ "expiresAt",
225
+ "adapter",
226
+ "plugin",
227
+ "binding",
228
+ "auth",
229
+ "list",
230
+ "target",
231
+ "resolution",
232
+ "network",
233
+ "sourceConversationCoordinate",
234
+ "conversationProviderId",
235
+ "conversation",
236
+ ], "messaging route record");
237
+ if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-route-record") {
238
+ throw new Error("messaging route record has an unsupported contract");
239
+ }
240
+ const adapter = record(source.adapter, "messaging route adapter");
241
+ exactKeys(adapter, ["id", "version", "hash"], "messaging route adapter");
242
+ const plugin = record(source.plugin, "messaging route plugin");
243
+ exactKeys(plugin, ["id", "version", "closureHash"], "messaging route plugin");
244
+ const binding = record(source.binding, "messaging route binding");
245
+ exactKeys(binding, ["surfaceId", "transport", "implementationIdentity", "messagingContractId"], "messaging route binding");
246
+ if (![
247
+ "provider-api",
248
+ "web-session-api",
249
+ "linked-device",
250
+ "local-cli",
251
+ ].includes(String(binding.transport))) {
252
+ throw new Error("messaging route binding transport is malformed");
253
+ }
254
+ const auth = record(source.auth, "messaging route auth");
255
+ exactKeys(auth, ["id", "hash", "subject"], "messaging route auth");
256
+ const list = record(source.list, "messaging route list");
257
+ exactKeys(list, ["operation", "input", "inputHash", "exactDataRevision", "validatedAt"], "messaging route list");
258
+ if (list.operation !== "messaging.list") {
259
+ throw new Error("messaging route list operation is malformed");
260
+ }
261
+ const conversation = record(source.conversation, "messaging route conversation");
262
+ exactKeys(conversation, ["kind", "title", "participantCount", "participantFingerprint", "providerRevision"], "messaging route conversation");
263
+ if (
264
+ conversation.kind !== "single"
265
+ && conversation.kind !== "group"
266
+ && conversation.kind !== "unknown"
267
+ ) throw new Error("messaging route conversation kind is malformed");
268
+ if (
269
+ (conversation.title !== null && typeof conversation.title !== "string")
270
+ || (conversation.providerRevision !== null && typeof conversation.providerRevision !== "string")
271
+ || !Number.isSafeInteger(conversation.participantCount)
272
+ || (conversation.participantCount as number) < 0
273
+ || (conversation.participantCount as number) > 10_000
274
+ ) throw new Error("messaging route conversation metadata is malformed");
275
+ const routeRef = string(source.routeRef, "messaging route reference", 128);
276
+ if (!/^wmroute_[A-Za-z0-9_-]{22}$/u.test(routeRef)) {
277
+ throw new Error("messaging route reference is malformed");
278
+ }
279
+ if (
280
+ source.resolution !== "list-candidate"
281
+ && source.resolution !== "exact-coordinate"
282
+ ) throw new Error("messaging route resolution state is malformed");
283
+ const resolution = source.resolution;
284
+ const network = string(source.network, "messaging route network", 64);
285
+ if (!/^[a-z][a-z0-9-]{0,63}$/u.test(network)) {
286
+ throw new Error("messaging route network is malformed");
287
+ }
288
+ const sourceConversationCoordinate = source.sourceConversationCoordinate === null
289
+ ? null
290
+ : parseMessageLikeMeSourceConversationCoordinateBindingV1(
291
+ source.sourceConversationCoordinate,
292
+ );
293
+ if (resolution === "list-candidate" && sourceConversationCoordinate !== null) {
294
+ throw new Error("messaging list candidate cannot claim an exact source coordinate");
295
+ }
296
+ return Object.freeze({
297
+ schemaVersion: 1,
298
+ format: "wrench.messaging-route-record",
299
+ routeRef,
300
+ createdAt: timestamp(source.createdAt, "messaging route createdAt"),
301
+ expiresAt: timestamp(source.expiresAt, "messaging route expiresAt"),
302
+ adapter: Object.freeze({
303
+ id: string(adapter.id, "messaging route adapter ID", 64),
304
+ version: string(adapter.version, "messaging route adapter version", 64),
305
+ hash: digest(adapter.hash, "messaging route adapter hash"),
306
+ }),
307
+ plugin: Object.freeze({
308
+ id: string(plugin.id, "messaging route plugin ID", 128),
309
+ version: string(plugin.version, "messaging route plugin version", 64),
310
+ closureHash: digest(plugin.closureHash, "messaging route plugin closure"),
311
+ }),
312
+ binding: Object.freeze({
313
+ surfaceId: string(binding.surfaceId, "messaging route surface", 64),
314
+ transport: binding.transport as MessagingRouteRecordV1["binding"]["transport"],
315
+ implementationIdentity: digest(binding.implementationIdentity, "messaging route implementation identity"),
316
+ messagingContractId: string(binding.messagingContractId, "messaging route contract ID", 128),
317
+ }),
318
+ auth: Object.freeze({
319
+ id: string(auth.id, "messaging route auth ID", 48),
320
+ hash: digest(auth.hash, "messaging route auth hash"),
321
+ subject: string(auth.subject, "messaging route auth subject", 512),
322
+ }),
323
+ list: Object.freeze({
324
+ operation: "messaging.list",
325
+ input: parseOperationInput(list.input),
326
+ inputHash: digest(list.inputHash, "messaging route list input hash"),
327
+ exactDataRevision: digest(list.exactDataRevision, "messaging route list data revision"),
328
+ validatedAt: timestamp(list.validatedAt, "messaging route list validatedAt"),
329
+ }),
330
+ target: parseStringRecord(source.target, "messaging route target"),
331
+ resolution,
332
+ network,
333
+ sourceConversationCoordinate,
334
+ conversationProviderId: string(source.conversationProviderId, "messaging route conversation identity"),
335
+ conversation: Object.freeze({
336
+ kind: conversation.kind,
337
+ title: conversation.title as string | null,
338
+ participantCount: conversation.participantCount as number,
339
+ participantFingerprint: digest(conversation.participantFingerprint, "messaging route participant fingerprint"),
340
+ providerRevision: conversation.providerRevision as string | null,
341
+ }),
342
+ });
343
+ }
344
+
345
+ export function parseMessagingContextRecordV1(
346
+ value: unknown,
347
+ ): MessagingContextRecordV1 {
348
+ const source = record(value, "messaging context record");
349
+ exactKeys(source, [
350
+ "schemaVersion",
351
+ "format",
352
+ "contextRef",
353
+ "routeRef",
354
+ "routeRecordHash",
355
+ "sourceConversationCoordinate",
356
+ "exactDataRevision",
357
+ "latestMessageRevision",
358
+ "validatedAt",
359
+ "expiresAt",
360
+ "limit",
361
+ "liveness",
362
+ "replyTargets",
363
+ ], "messaging context record");
364
+ if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-context-record") {
365
+ throw new Error("messaging context record has an unsupported contract");
366
+ }
367
+ const contextRef = string(source.contextRef, "messaging context reference", 128);
368
+ const routeRef = string(source.routeRef, "messaging context route reference", 128);
369
+ if (!/^wmcontext_[A-Za-z0-9_-]{22}$/u.test(contextRef) || !/^wmroute_[A-Za-z0-9_-]{22}$/u.test(routeRef)) {
370
+ throw new Error("messaging context record has malformed references");
371
+ }
372
+ const targetsSource = record(source.replyTargets, "messaging context reply targets");
373
+ if (Object.keys(targetsSource).length > 200) {
374
+ throw new Error("messaging context has too many reply targets");
375
+ }
376
+ const replyTargets = Object.freeze(Object.fromEntries(
377
+ Object.entries(targetsSource).map(([key, value]) => {
378
+ if (!/^wmreply_[A-Za-z0-9_-]{22}$/u.test(key)) {
379
+ throw new Error("messaging context reply reference is malformed");
380
+ }
381
+ return [key, string(value, "messaging context provider message identity")];
382
+ }),
383
+ ));
384
+ if (
385
+ !Number.isSafeInteger(source.limit)
386
+ || (source.limit as number) < 1
387
+ || (source.limit as number) > 200
388
+ ) throw new Error("messaging context limit is malformed");
389
+ if (
390
+ source.liveness !== "fresh-as-of-live-preflight"
391
+ && source.liveness !== "freshness-unproven"
392
+ ) throw new Error("messaging context liveness is malformed");
393
+ return Object.freeze({
394
+ schemaVersion: 1,
395
+ format: "wrench.messaging-context-record",
396
+ contextRef,
397
+ routeRef,
398
+ routeRecordHash: digest(source.routeRecordHash, "messaging context route record hash"),
399
+ sourceConversationCoordinate: source.sourceConversationCoordinate === null
400
+ ? null
401
+ : parseMessageLikeMeSourceConversationCoordinateBindingV1(
402
+ source.sourceConversationCoordinate,
403
+ ),
404
+ exactDataRevision: digest(source.exactDataRevision, "messaging context data revision"),
405
+ latestMessageRevision: digest(source.latestMessageRevision, "messaging context latest revision"),
406
+ validatedAt: timestamp(source.validatedAt, "messaging context validatedAt"),
407
+ expiresAt: timestamp(source.expiresAt, "messaging context expiresAt"),
408
+ limit: source.limit as number,
409
+ liveness: source.liveness,
410
+ replyTargets,
411
+ });
412
+ }
413
+
414
+ export function messagingRouteRecordHash(record: MessagingRouteRecordV1): string {
415
+ return sha256(canonicalJson(record));
416
+ }
417
+
418
+ export function saveMessagingRouteRecord(
419
+ record: MessagingRouteRecordV1,
420
+ environment: Readonly<Record<string, string | undefined>> = process.env,
421
+ ): void {
422
+ const parent = ensureMessagingRecordDirectory("routes", environment);
423
+ const created = createPrivateJsonIfAbsent(
424
+ routePath(record.routeRef, environment),
425
+ sealAuthenticatedPrivatePayload(
426
+ record,
427
+ routeDomain(record.routeRef),
428
+ environment,
429
+ MAX_RECORD_BYTES,
430
+ ),
431
+ { environment, expectedStateParent: parent },
432
+ );
433
+ if (!created.created) throw new Error("messaging route reference already exists");
434
+ }
435
+
436
+ export function loadMessagingRouteRecord(
437
+ routeRef: string,
438
+ environment: Readonly<Record<string, string | undefined>> = process.env,
439
+ observation: Date = new Date(),
440
+ ): MessagingRouteRecordV1 {
441
+ const text = readPrivateStateFileIfPresent(
442
+ routePath(routeRef, environment),
443
+ MAX_ENCRYPTED_RECORD_BYTES,
444
+ "messaging route record",
445
+ environment,
446
+ );
447
+ if (text === null) throw new Error("messaging route is unavailable or expired");
448
+ const parsed = parseMessagingRouteRecordV1(openAuthenticatedPrivatePayload(
449
+ JSON.parse(text) as unknown,
450
+ routeDomain(routeRef),
451
+ environment,
452
+ MAX_RECORD_BYTES,
453
+ ));
454
+ if (parsed.routeRef !== routeRef) throw new Error("messaging route record reference changed");
455
+ if (Date.parse(parsed.expiresAt) <= observationTime(observation)) {
456
+ throw new Error("messaging route is unavailable or expired");
457
+ }
458
+ return parsed;
459
+ }
460
+
461
+ export function saveMessagingContextRecord(
462
+ record: MessagingContextRecordV1,
463
+ environment: Readonly<Record<string, string | undefined>> = process.env,
464
+ ): void {
465
+ const parent = ensureMessagingRecordDirectory("contexts", environment);
466
+ const created = createPrivateJsonIfAbsent(
467
+ contextPath(record.contextRef, environment),
468
+ sealAuthenticatedPrivatePayload(
469
+ record,
470
+ contextDomain(record.contextRef),
471
+ environment,
472
+ MAX_RECORD_BYTES,
473
+ ),
474
+ { environment, expectedStateParent: parent },
475
+ );
476
+ if (!created.created) throw new Error("messaging context reference already exists");
477
+ }
478
+
479
+ export function loadMessagingContextRecord(
480
+ contextRef: string,
481
+ environment: Readonly<Record<string, string | undefined>> = process.env,
482
+ observation: Date = new Date(),
483
+ ): MessagingContextRecordV1 {
484
+ const text = readPrivateStateFileIfPresent(
485
+ contextPath(contextRef, environment),
486
+ MAX_ENCRYPTED_RECORD_BYTES,
487
+ "messaging context record",
488
+ environment,
489
+ );
490
+ if (text === null) throw new Error("messaging context is unavailable or expired");
491
+ const parsed = parseMessagingContextRecordV1(openAuthenticatedPrivatePayload(
492
+ JSON.parse(text) as unknown,
493
+ contextDomain(contextRef),
494
+ environment,
495
+ MAX_RECORD_BYTES,
496
+ ));
497
+ if (parsed.contextRef !== contextRef) throw new Error("messaging context record reference changed");
498
+ if (Date.parse(parsed.expiresAt) <= observationTime(observation)) {
499
+ throw new Error("messaging context is unavailable or expired");
500
+ }
501
+ return parsed;
502
+ }
503
+
504
+ export function purgeExpiredMessagingRecords(
505
+ environment: Readonly<Record<string, string | undefined>> = process.env,
506
+ observation: Date = new Date(),
507
+ ): Readonly<{ routes: number; contexts: number }> {
508
+ observationTime(observation);
509
+ let routes = 0;
510
+ let contexts = 0;
511
+ const purge = (
512
+ kind: "routes" | "contexts",
513
+ ): void => {
514
+ const directory = join(messagingRoot(environment), kind);
515
+ const identity = ensureMessagingRecordDirectory(kind, environment);
516
+ const entries = listPrivateStateDirectory(directory, environment, identity);
517
+ for (const entry of entries) {
518
+ if (entry.kind !== "file" || !entry.name.endsWith(".json")) continue;
519
+ const reference = entry.name.slice(0, -".json".length);
520
+ try {
521
+ if (kind === "routes") {
522
+ loadMessagingRouteRecord(reference, environment, observation);
523
+ } else {
524
+ loadMessagingContextRecord(reference, environment, observation);
525
+ }
526
+ } catch (error) {
527
+ if (!(error instanceof Error) || !error.message.includes("expired")) {
528
+ continue;
529
+ }
530
+ removePrivateStateFile(join(directory, entry.name), environment);
531
+ if (kind === "routes") routes += 1;
532
+ else contexts += 1;
533
+ }
534
+ }
535
+ };
536
+ purge("routes");
537
+ purge("contexts");
538
+ return Object.freeze({ routes, contexts });
539
+ }