@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,818 @@
1
+ import type { GhostgetAuth } from "./auth";
2
+ import { loadAuth } from "./auth";
3
+ import {
4
+ canonicalJson,
5
+ sha256,
6
+ type LocalCliRecipe,
7
+ type OperationInput,
8
+ type WebSessionRecipe,
9
+ } from "./model";
10
+ import { cleanupPlanAssets } from "./plan-assets";
11
+ import { requireProviderPluginAuth } from "./provider-plugin-auth";
12
+ import {
13
+ runProviderPluginPlanConformance,
14
+ type ProviderPluginReconciliationContextV1,
15
+ type ProviderPluginReconciliationOptionsV1,
16
+ } from "./provider-plugin";
17
+ import { withLocalCliProviderCleanupAdmission } from "./local-cli-admission";
18
+ import type {
19
+ ProviderPluginOperationResolutionV1,
20
+ ProviderPluginRegistry,
21
+ } from "./provider-plugin-registry";
22
+ import { providerPluginRegistry } from "./provider-plugins";
23
+ import {
24
+ appendReconciliationObservation,
25
+ readProviderAcceptedMutationTargetEvidence,
26
+ readRecoveryCapsule,
27
+ removeProviderAcceptedMutationTargetEvidence,
28
+ removeRecoveryCapsule,
29
+ type ReconciliationObservation,
30
+ type RecoveryCapsule,
31
+ } from "./recovery";
32
+ import {
33
+ readRunReceipt,
34
+ releaseReconciledRunRecovery,
35
+ type RunReceipt,
36
+ } from "./runtime";
37
+ import {
38
+ getWebSessionContract,
39
+ isCompatibleWebSessionContractHash,
40
+ } from "./web-session-contracts";
41
+ import {
42
+ getLocalCliContract,
43
+ isCompatibleLocalCliContractHash,
44
+ } from "./local-cli-contracts";
45
+
46
+ type Environment = Readonly<Record<string, string | undefined>>;
47
+
48
+ export const LEGACY_X_CONTENT_SAVE_CONTRACT_HASH =
49
+ "7685de6bc2283991b5694b5f7ffda22f5251618ea76fe3e865d45ebe78f5d691";
50
+ export const LEGACY_X_CONTENT_SAVE_ADAPTER_HASH =
51
+ "731d3dee1bbf60c2d1ca33d4089039d8a3fb1ca2c9f3c5fe735c0531ef4539b4";
52
+ export const PRE_PROVIDER_PLUGIN_X_CONTENT_SAVE_CONTRACT_HASH =
53
+ "bcc3ae7768b24c0faa7654f9511570f2a85b4aced33e5f7f9bbfbbe559df99c9";
54
+ export const PRE_PROVIDER_PLUGIN_X_LIKES_SET_CONTRACT_HASH =
55
+ "994ebbb3206063a4a12d89c4ad73c1e73d6f8868e1135ca4278b3bc08c6dec4b";
56
+ export const PRE_PROVIDER_PLUGIN_X_ADAPTER_VERSION = "1.1.0";
57
+ export const PRE_PROVIDER_PLUGIN_X_ADAPTER_HASH =
58
+ "c9cec73197907ff846c4ef8142f3e8dd9d0cff3c9d4621102a35154d77f6265a";
59
+
60
+ export type ReconcileRunResult = {
61
+ readonly ok: boolean;
62
+ readonly status: "reconciliation-observed" | "reconciliation-inconclusive";
63
+ readonly runId: string;
64
+ readonly originalReceiptStatus: RunReceipt["status"];
65
+ readonly receiptUnchanged: true;
66
+ readonly providerWriteDispatched: false;
67
+ readonly recoveryArtifactsReleased: boolean;
68
+ readonly observation: ReconciliationObservation;
69
+ };
70
+
71
+ /**
72
+ * A transport-neutral deterministic observation seam for recovery tests.
73
+ * Production routing uses the resolved plugin binding's lazy reconciliation
74
+ * hook. Values cross an untrusted boundary and are validated below.
75
+ */
76
+ export type WebSessionRecoveryDependencies = {
77
+ readonly observeActualState?: (
78
+ recipe: WebSessionRecipe,
79
+ input: OperationInput,
80
+ auth: GhostgetAuth,
81
+ context?: ProviderPluginReconciliationContextV1,
82
+ ) => Promise<unknown>;
83
+ readonly observeLocalCliActualState?: (
84
+ recipe: LocalCliRecipe,
85
+ input: OperationInput,
86
+ auth: GhostgetAuth,
87
+ context?: ProviderPluginReconciliationContextV1,
88
+ options?: ProviderPluginReconciliationOptionsV1,
89
+ ) => Promise<unknown>;
90
+ readonly releaseRecoveryArtifacts: (
91
+ runId: string,
92
+ planDigest: string,
93
+ environment: Environment,
94
+ ) => void;
95
+ };
96
+
97
+ const defaultReleaseRecoveryArtifacts:
98
+ WebSessionRecoveryDependencies["releaseRecoveryArtifacts"] = (
99
+ runId,
100
+ planDigest,
101
+ environment,
102
+ ) => {
103
+ // Remove the digest-bound media first so a cleanup failure leaves the
104
+ // encrypted capsule available for another inspection.
105
+ cleanupPlanAssets(planDigest, environment);
106
+ removeProviderAcceptedMutationTargetEvidence(runId, environment);
107
+ removeRecoveryCapsule(runId, environment);
108
+ };
109
+
110
+ function isUnknownRecord(value: unknown): value is Record<string, unknown> {
111
+ return typeof value === "object"
112
+ && value !== null
113
+ && !Array.isArray(value);
114
+ }
115
+
116
+ type SelectedReconciliation = {
117
+ readonly receipt: ReconciliationReceipt;
118
+ readonly resolution: ProviderPluginOperationResolutionV1;
119
+ readonly input: OperationInput;
120
+ readonly inputSource: "capsule" | "provided";
121
+ readonly planDigest: string;
122
+ readonly contractIdentity:
123
+ | "current"
124
+ | "pre-provider-plugin-x"
125
+ | "ancient-x";
126
+ readonly reconciliationContext?: ProviderPluginReconciliationContextV1;
127
+ };
128
+
129
+ type ReconciliationReceipt = Extract<
130
+ RunReceipt,
131
+ { readonly schemaVersion: 4 | 7 }
132
+ >;
133
+
134
+ type PreProviderPluginXContract = {
135
+ readonly action: "content.save" | "likes.set";
136
+ readonly version: 1 | 2;
137
+ readonly hash: string;
138
+ };
139
+
140
+ const PRE_PROVIDER_PLUGIN_X_CONTRACTS = Object.freeze([
141
+ Object.freeze({
142
+ action: "content.save",
143
+ version: 1,
144
+ hash: PRE_PROVIDER_PLUGIN_X_CONTENT_SAVE_CONTRACT_HASH,
145
+ }),
146
+ Object.freeze({
147
+ action: "likes.set",
148
+ version: 2,
149
+ hash: PRE_PROVIDER_PLUGIN_X_LIKES_SET_CONTRACT_HASH,
150
+ }),
151
+ ] as const satisfies readonly PreProviderPluginXContract[]);
152
+
153
+ function isUnsettled(status: RunReceipt["status"]): boolean {
154
+ return status === "pending"
155
+ || status === "partial"
156
+ || status === "indeterminate";
157
+ }
158
+
159
+ function assertReconciliationReceipt(
160
+ receipt: RunReceipt,
161
+ ): asserts receipt is ReconciliationReceipt {
162
+ if (!isUnsettled(receipt.status)) {
163
+ throw new Error("only an unsettled run can be reconciled");
164
+ }
165
+ if (receipt.planDigest === null) {
166
+ throw new Error("the unsettled run has no exact confirmation-plan digest");
167
+ }
168
+ if (receipt.risk !== "R2" && receipt.risk !== "R3") {
169
+ throw new Error(
170
+ "this reconciliation contract supports only exact R2/R3 desired-state writes",
171
+ );
172
+ }
173
+ if (
174
+ receipt.risk === "R3"
175
+ && receipt.adapter.id === "x-web"
176
+ && receipt.operation === "content.save"
177
+ ) {
178
+ throw new Error(
179
+ "this reconciliation contract supports only exact R2 X desired-state writes",
180
+ );
181
+ }
182
+ if (
183
+ receipt.dispatch.planned < 1
184
+ || receipt.dispatch.started < 1
185
+ || receipt.dispatch.started > receipt.dispatch.planned
186
+ || receipt.dispatch.verified < 0
187
+ || receipt.dispatch.verified > receipt.dispatch.started
188
+ ) {
189
+ throw new Error(
190
+ "the unsettled run does not have the exact one-dispatch or plugin-declared dispatched write schedule",
191
+ );
192
+ }
193
+ if (
194
+ (receipt.schemaVersion !== 4 || receipt.transport !== "web-session-api")
195
+ && (receipt.schemaVersion !== 7 || receipt.transport !== "local-cli")
196
+ ) {
197
+ throw new Error(
198
+ "this reconciliation command supports authenticated session plugin runs",
199
+ );
200
+ }
201
+ }
202
+
203
+ function isLegacyXContentSave(
204
+ receipt: Extract<RunReceipt, { readonly schemaVersion: 4 }>,
205
+ ): boolean {
206
+ return receipt.operation === "content.save"
207
+ && receipt.webSessionContractHash === LEGACY_X_CONTENT_SAVE_CONTRACT_HASH
208
+ && receipt.adapter.id === "x-web"
209
+ && receipt.adapter.version === "1.0.0"
210
+ && receipt.adapter.hash === LEGACY_X_CONTENT_SAVE_ADAPTER_HASH;
211
+ }
212
+
213
+ function isPreProviderPluginXContract(
214
+ receipt: Extract<RunReceipt, { readonly schemaVersion: 4 }>,
215
+ capsule: RecoveryCapsule,
216
+ ): boolean {
217
+ const contractIdentity = capsule.contract;
218
+ if (
219
+ receipt.adapter.id !== "x-web"
220
+ || receipt.adapter.version !== PRE_PROVIDER_PLUGIN_X_ADAPTER_VERSION
221
+ || receipt.adapter.hash !== PRE_PROVIDER_PLUGIN_X_ADAPTER_HASH
222
+ || contractIdentity.transport !== "web-session-api"
223
+ || contractIdentity.site !== "x"
224
+ ) {
225
+ return false;
226
+ }
227
+ return PRE_PROVIDER_PLUGIN_X_CONTRACTS.some((contract) =>
228
+ receipt.operation === contract.action
229
+ && receipt.webSessionContractHash === contract.hash
230
+ && contractIdentity.action === contract.action
231
+ && contractIdentity.version === contract.version
232
+ && contractIdentity.hash === contract.hash
233
+ );
234
+ }
235
+
236
+ function legacyXRecipe(): WebSessionRecipe {
237
+ return {
238
+ site: "x",
239
+ action: "content.save",
240
+ contractVersion: 1,
241
+ timeoutMs: 60_000,
242
+ maxOutputBytes: 2 * 1024 * 1024,
243
+ };
244
+ }
245
+
246
+ function assertCapsuleMatchesReceipt(
247
+ capsule: RecoveryCapsule,
248
+ receipt: ReconciliationReceipt,
249
+ ): void {
250
+ const contractMatches = receipt.transport === "local-cli"
251
+ ? capsule.contract.transport === "local-cli"
252
+ && canonicalJson(capsule.contract.identity)
253
+ === canonicalJson(receipt.localCliContract)
254
+ : capsule.contract.transport === "web-session-api"
255
+ && capsule.contract.action === receipt.operation
256
+ && capsule.contract.hash === receipt.webSessionContractHash;
257
+ if (
258
+ capsule.runId !== receipt.runId
259
+ || capsule.createdAt !== receipt.startedAt
260
+ || capsule.planDigest !== receipt.planDigest
261
+ || canonicalJson(capsule.adapter) !== canonicalJson(receipt.adapter)
262
+ || capsule.operation !== receipt.operation
263
+ || capsule.risk !== receipt.risk
264
+ || capsule.inputHash !== receipt.inputHash
265
+ || canonicalJson(capsule.auth) !== canonicalJson(receipt.auth)
266
+ || !contractMatches
267
+ ) {
268
+ throw new Error(
269
+ "encrypted recovery capsule does not match the immutable run receipt",
270
+ );
271
+ }
272
+ }
273
+
274
+ function validateRecoveryInput(
275
+ resolution: ProviderPluginOperationResolutionV1,
276
+ value: OperationInput,
277
+ ): OperationInput {
278
+ const issues = resolution.operation.validateInput(value);
279
+ if (issues.length > 0) {
280
+ throw new Error(`reconciliation input is invalid: ${issues.join("; ")}`);
281
+ }
282
+ // The encrypted capsule parser already checks the bounded OperationInput
283
+ // grammar. Canonicalizing here also rejects unsupported runtime values on
284
+ // the legacy caller-supplied path.
285
+ canonicalJson(value);
286
+ return value;
287
+ }
288
+
289
+ function resolveOperation(
290
+ transport: "web-session-api" | "local-cli",
291
+ site: string,
292
+ action: string,
293
+ version: number,
294
+ registry: ProviderPluginRegistry,
295
+ ): ProviderPluginOperationResolutionV1 {
296
+ const binding = transport === "local-cli"
297
+ ? registry.requireRoute("local-cli", site)
298
+ : registry.requireSessionRoute(site);
299
+ const resolution = registry.requireOperationDefinition(
300
+ binding.transport,
301
+ site,
302
+ action,
303
+ version,
304
+ );
305
+ if (resolution.binding.transport !== transport) {
306
+ throw new Error("reconciliation resolved to the wrong plugin transport");
307
+ }
308
+ if (
309
+ resolution.operation.reconciliation === undefined
310
+ || resolution.binding.reconcile === undefined
311
+ ) {
312
+ throw new Error(
313
+ `provider plugin operation ${site}/${action}@${version} has no registered reconciler`,
314
+ );
315
+ }
316
+ return resolution;
317
+ }
318
+
319
+ function providerBoundTargetReconciliationContext(
320
+ receipt: ReconciliationReceipt,
321
+ capsule: RecoveryCapsule,
322
+ resolution: ProviderPluginOperationResolutionV1,
323
+ input: OperationInput,
324
+ environment: Environment,
325
+ ): ProviderPluginReconciliationContextV1 | undefined {
326
+ const reconciliation = resolution.operation.reconciliation;
327
+ if (
328
+ reconciliation?.kind !== "provider-accepted-target-presence"
329
+ && reconciliation?.kind !== "provider-bound-target-desired-state"
330
+ ) {
331
+ return undefined;
332
+ }
333
+ const targetKind = reconciliation.kind === "provider-accepted-target-presence"
334
+ ? "provider-accepted"
335
+ : "provider-bound";
336
+ if (
337
+ receipt.dispatchStarted !== true
338
+ || receipt.dispatch.planned !== 1
339
+ || receipt.dispatch.started !== 1
340
+ || receipt.dispatch.verified < 0
341
+ || receipt.dispatch.verified > 1
342
+ ) {
343
+ throw new Error(
344
+ `${targetKind} target reconciliation requires one exact started dispatch`,
345
+ );
346
+ }
347
+ const plannedDispatches = runProviderPluginPlanConformance(
348
+ resolution.operation,
349
+ input,
350
+ );
351
+ const plannedDispatch = plannedDispatches[0];
352
+ if (plannedDispatches.length !== 1 || plannedDispatch === undefined) {
353
+ throw new Error(
354
+ `${targetKind} target reconciliation requires one exact confirmed dispatch`,
355
+ );
356
+ }
357
+ const dispatch = Object.freeze({
358
+ id: plannedDispatch.id,
359
+ index: 1,
360
+ planned: 1,
361
+ });
362
+ const evidence = readProviderAcceptedMutationTargetEvidence(
363
+ capsule,
364
+ dispatch,
365
+ environment,
366
+ );
367
+ if (evidence === null) {
368
+ throw new Error(
369
+ reconciliation.kind === "provider-accepted-target-presence"
370
+ ? "this provider-accepted target run has no encrypted response-derived target and is not safely reconcilable"
371
+ : "this provider-bound target run has no encrypted exact pre-dispatch target and is not safely reconcilable",
372
+ );
373
+ }
374
+ return Object.freeze({
375
+ schemaVersion: 1,
376
+ kind: reconciliation.kind,
377
+ dispatch,
378
+ target: evidence.target,
379
+ });
380
+ }
381
+
382
+ function selectReconciliation(
383
+ receipt: ReconciliationReceipt,
384
+ providedInput: unknown,
385
+ environment: Environment,
386
+ registry: ProviderPluginRegistry,
387
+ ): SelectedReconciliation {
388
+ const capsule = readRecoveryCapsule(
389
+ receipt.runId,
390
+ receipt.auth.id,
391
+ receipt.auth.hash,
392
+ environment,
393
+ );
394
+ if (capsule !== null) {
395
+ assertCapsuleMatchesReceipt(capsule, receipt);
396
+ const recoveryRoute = capsule.contract.transport === "web-session-api"
397
+ ? {
398
+ transport: "web-session-api" as const,
399
+ surface: capsule.contract.site,
400
+ action: capsule.contract.action,
401
+ version: capsule.contract.version,
402
+ }
403
+ : capsule.contract.transport === "local-cli"
404
+ ? {
405
+ transport: "local-cli" as const,
406
+ surface: capsule.contract.identity.surface,
407
+ action: capsule.contract.identity.action,
408
+ version: capsule.contract.identity.version,
409
+ }
410
+ : null;
411
+ if (recoveryRoute === null) {
412
+ throw new Error("recovery capsule transport changed unexpectedly");
413
+ }
414
+ const resolution = resolveOperation(
415
+ recoveryRoute.transport,
416
+ recoveryRoute.surface,
417
+ recoveryRoute.action,
418
+ recoveryRoute.version,
419
+ registry,
420
+ );
421
+ const input = validateRecoveryInput(resolution, capsule.input);
422
+ if (
423
+ providedInput !== undefined
424
+ && canonicalJson(providedInput) !== canonicalJson(input)
425
+ ) {
426
+ throw new Error(
427
+ "provided reconciliation input does not match the encrypted recovery capsule",
428
+ );
429
+ }
430
+ const reconciliationContext =
431
+ providerBoundTargetReconciliationContext(
432
+ receipt,
433
+ capsule,
434
+ resolution,
435
+ input,
436
+ environment,
437
+ );
438
+ return {
439
+ receipt,
440
+ resolution,
441
+ input,
442
+ inputSource: "capsule",
443
+ planDigest: receipt.planDigest as string,
444
+ contractIdentity: receipt.schemaVersion === 4
445
+ && isPreProviderPluginXContract(receipt, capsule)
446
+ ? "pre-provider-plugin-x"
447
+ : "current",
448
+ ...(reconciliationContext === undefined
449
+ ? {}
450
+ : { reconciliationContext }),
451
+ };
452
+ }
453
+
454
+ if (receipt.schemaVersion !== 4 || !isLegacyXContentSave(receipt)) {
455
+ throw new Error(
456
+ "this run should have an encrypted recovery capsule; refusing caller-supplied replacement input",
457
+ );
458
+ }
459
+ if (
460
+ typeof providedInput !== "object"
461
+ || providedInput === null
462
+ || Array.isArray(providedInput)
463
+ ) {
464
+ throw new Error(
465
+ "this legacy run has no encrypted recovery capsule; provide its exact original input with --input",
466
+ );
467
+ }
468
+ const recipe = legacyXRecipe();
469
+ const resolution = resolveOperation(
470
+ "web-session-api",
471
+ recipe.site,
472
+ recipe.action,
473
+ recipe.contractVersion,
474
+ registry,
475
+ );
476
+ const input = validateRecoveryInput(
477
+ resolution,
478
+ providedInput as OperationInput,
479
+ );
480
+ if (sha256(canonicalJson(input)) !== receipt.inputHash) {
481
+ throw new Error(
482
+ "provided reconciliation input does not match the run's canonical input hash",
483
+ );
484
+ }
485
+ if (receipt.planDigest === null) {
486
+ throw new Error("the unsettled run has no exact confirmation-plan digest");
487
+ }
488
+ return {
489
+ receipt,
490
+ resolution,
491
+ input,
492
+ inputSource: "provided",
493
+ planDigest: receipt.planDigest,
494
+ contractIdentity: "ancient-x",
495
+ };
496
+ }
497
+
498
+ function assertExactHistoricalXSchedule(
499
+ receipt: Extract<RunReceipt, { readonly schemaVersion: 4 }>,
500
+ ): void {
501
+ if (receipt.risk !== "R2") {
502
+ throw new Error(
503
+ "this reconciliation contract supports only exact R2 X desired-state writes",
504
+ );
505
+ }
506
+ if (
507
+ receipt.dispatchStarted !== true
508
+ || receipt.dispatch.planned !== 1
509
+ || receipt.dispatch.started !== 1
510
+ || receipt.dispatch.verified < 0
511
+ || receipt.dispatch.verified > 1
512
+ ) {
513
+ throw new Error(
514
+ "the unsettled X run does not have the exact one-dispatch desired-state schedule",
515
+ );
516
+ }
517
+ }
518
+
519
+ function assertSupportedContract(
520
+ selected: SelectedReconciliation,
521
+ registry: ProviderPluginRegistry,
522
+ ): void {
523
+ const { binding, operation, contractVersion } = selected.resolution;
524
+ if (
525
+ binding.transport === "provider-api"
526
+ || (
527
+ selected.receipt.transport === "local-cli"
528
+ ? binding.transport !== "local-cli"
529
+ : binding.transport === "local-cli"
530
+ )
531
+ ) {
532
+ throw new Error("reconciliation resolved to the wrong plugin transport");
533
+ }
534
+ if (operation.risk !== selected.receipt.risk) {
535
+ throw new Error(
536
+ "the unsettled run risk does not match its resolved plugin operation",
537
+ );
538
+ }
539
+ if (selected.contractIdentity !== "current") {
540
+ if (selected.receipt.schemaVersion !== 4) {
541
+ throw new Error("historical reconciliation identity is not a web-session receipt");
542
+ }
543
+ assertExactHistoricalXSchedule(selected.receipt);
544
+ return;
545
+ }
546
+ const compatible = selected.receipt.transport === "local-cli"
547
+ ? isCompatibleLocalCliContractHash(
548
+ getLocalCliContract({
549
+ surface: binding.surfaceId,
550
+ action: operation.name,
551
+ contractVersion,
552
+ timeoutMs: 60_000,
553
+ maxOutputBytes: 2 * 1024 * 1024,
554
+ }, registry),
555
+ selected.receipt.localCliContract.hash,
556
+ registry,
557
+ )
558
+ && canonicalJson(selected.receipt.localCliContract.tool)
559
+ === canonicalJson(
560
+ binding.transport === "local-cli" ? binding.tool : null,
561
+ )
562
+ : isCompatibleWebSessionContractHash(
563
+ getWebSessionContract({
564
+ site: binding.surfaceId,
565
+ action: operation.name,
566
+ contractVersion,
567
+ timeoutMs: 60_000,
568
+ maxOutputBytes: 2 * 1024 * 1024,
569
+ }, registry),
570
+ selected.receipt.webSessionContractHash,
571
+ registry,
572
+ );
573
+ if (!compatible) {
574
+ throw new Error(
575
+ "the unsettled run is bound to an unsupported authenticated session contract hash; its receipt, encrypted recovery capsule, and plan assets were retained. Run `ghostget doctor`, then use the exact predecessor build or complete a manual evidence review before reconciliation",
576
+ );
577
+ }
578
+ }
579
+
580
+ async function observeActualState(
581
+ selected: SelectedReconciliation,
582
+ auth: GhostgetAuth,
583
+ dependency:
584
+ | WebSessionRecoveryDependencies["observeActualState"]
585
+ | undefined,
586
+ localDependency:
587
+ | WebSessionRecoveryDependencies["observeLocalCliActualState"]
588
+ | undefined,
589
+ registry: ProviderPluginRegistry,
590
+ environment: Environment,
591
+ ): Promise<boolean> {
592
+ const { binding, operation, contractVersion } = selected.resolution;
593
+ if (binding.transport === "provider-api" || binding.reconcile === undefined) {
594
+ throw new Error(
595
+ `provider plugin operation ${binding.surfaceId}/${operation.name}@${contractVersion} has no registered reconciler`,
596
+ );
597
+ }
598
+ const value = binding.transport === "local-cli"
599
+ ? await withLocalCliProviderCleanupAdmission(
600
+ {
601
+ registry,
602
+ binding,
603
+ auth,
604
+ purpose: {
605
+ kind: "reconcile",
606
+ action: operation.name,
607
+ contractVersion,
608
+ recoveryRunId: selected.receipt.runId,
609
+ },
610
+ environment,
611
+ },
612
+ (registerCleanupBarrier) => localDependency === undefined
613
+ ? binding.reconcile!(
614
+ operation.name,
615
+ selected.input,
616
+ auth,
617
+ selected.reconciliationContext,
618
+ { environment, registerCleanupBarrier },
619
+ )
620
+ : localDependency(
621
+ {
622
+ surface: binding.surfaceId,
623
+ action: operation.name,
624
+ contractVersion,
625
+ timeoutMs: 60_000,
626
+ maxOutputBytes: 2 * 1024 * 1024,
627
+ },
628
+ selected.input,
629
+ auth,
630
+ selected.reconciliationContext,
631
+ { environment, registerCleanupBarrier },
632
+ ),
633
+ )
634
+ : dependency === undefined
635
+ ? await binding.reconcile(
636
+ operation.name,
637
+ selected.input,
638
+ auth,
639
+ selected.reconciliationContext,
640
+ )
641
+ : await dependency(
642
+ {
643
+ site: binding.surfaceId,
644
+ action: operation.name,
645
+ contractVersion,
646
+ timeoutMs: 60_000,
647
+ maxOutputBytes: 2 * 1024 * 1024,
648
+ },
649
+ selected.input,
650
+ auth,
651
+ selected.reconciliationContext,
652
+ );
653
+ if (
654
+ !isUnknownRecord(value)
655
+ || Object.keys(value).length !== 2
656
+ || !Object.hasOwn(value, "actualState")
657
+ || !Object.hasOwn(value, "reason")
658
+ || typeof value.actualState !== "boolean"
659
+ || typeof value.reason !== "string"
660
+ || value.reason.length < 1
661
+ || value.reason.length > 200
662
+ ) {
663
+ throw new Error("provider plugin reconciliation returned an invalid readback");
664
+ }
665
+ return value.actualState;
666
+ }
667
+
668
+ export async function reconcileWebSessionRun(
669
+ runId: string,
670
+ providedInput: unknown,
671
+ options: {
672
+ readonly environment?: Environment;
673
+ readonly now?: Date;
674
+ readonly dependencies?: Partial<WebSessionRecoveryDependencies>;
675
+ readonly registry?: ProviderPluginRegistry;
676
+ } = {},
677
+ ): Promise<ReconcileRunResult> {
678
+ const environment = options.environment ?? process.env;
679
+ const registry = options.registry ?? providerPluginRegistry;
680
+ const receipt = readRunReceipt(runId, environment);
681
+ assertReconciliationReceipt(receipt);
682
+ const selected = selectReconciliation(
683
+ receipt,
684
+ providedInput,
685
+ environment,
686
+ registry,
687
+ );
688
+ assertSupportedContract(selected, registry);
689
+
690
+ const auth = loadAuth(receipt.auth.id, environment);
691
+ if (
692
+ sha256(canonicalJson(auth)) !== receipt.auth.hash
693
+ || auth.kind !== receipt.auth.kind
694
+ ) {
695
+ throw new Error(
696
+ "the current auth locator no longer matches the unsettled run's exact realm",
697
+ );
698
+ }
699
+ requireProviderPluginAuth(selected.resolution.binding, auth);
700
+ if (
701
+ auth.subject === undefined
702
+ || !selected.resolution.binding.subject.matches(auth.subject)
703
+ ) {
704
+ throw new Error(
705
+ "the current auth locator no longer has the plugin's exact bound subject",
706
+ );
707
+ }
708
+ if (sha256(canonicalJson(selected.input)) !== receipt.inputHash) {
709
+ throw new Error(
710
+ "reconciliation input does not match the run's canonical input hash",
711
+ );
712
+ }
713
+
714
+ const reconciliation = selected.resolution.operation.reconciliation;
715
+ if (reconciliation === undefined) {
716
+ throw new Error("provider plugin operation has no registered reconciler");
717
+ }
718
+ const desired = reconciliation.kind === "boolean-desired-state"
719
+ ? reconciliation.desiredState(selected.input)
720
+ : reconciliation.kind === "provider-accepted-target-presence"
721
+ ? true
722
+ : reconciliation.desiredState;
723
+ if (typeof desired !== "boolean") {
724
+ throw new Error(
725
+ "provider plugin reconciliation desired state is invalid",
726
+ );
727
+ }
728
+ const common = {
729
+ schemaVersion: 1 as const,
730
+ observationId: crypto.randomUUID(),
731
+ runId: receipt.runId,
732
+ observedAt: (options.now ?? new Date()).toISOString(),
733
+ receiptHash: sha256(canonicalJson(receipt)),
734
+ adapterHash: receipt.adapter.hash,
735
+ operation: receipt.operation,
736
+ inputHash: receipt.inputHash,
737
+ authHash: receipt.auth.hash,
738
+ contractHash: receipt.transport === "local-cli"
739
+ ? receipt.localCliContract.hash
740
+ : receipt.webSessionContractHash,
741
+ inputSource: selected.inputSource,
742
+ };
743
+ let observation: ReconciliationObservation;
744
+ try {
745
+ const actual = await observeActualState(
746
+ selected,
747
+ auth,
748
+ options.dependencies?.observeActualState,
749
+ options.dependencies?.observeLocalCliActualState,
750
+ registry,
751
+ environment,
752
+ );
753
+ if (
754
+ reconciliation.kind === "provider-accepted-target-presence"
755
+ && actual !== true
756
+ ) {
757
+ throw new Error(
758
+ "provider-accepted target absence is not proof that the earlier write was not applied",
759
+ );
760
+ }
761
+ const matched = actual === desired;
762
+ observation = {
763
+ ...common,
764
+ outcome: matched
765
+ ? "desired-state-observed"
766
+ : "desired-state-not-observed",
767
+ desiredStateMatched: matched,
768
+ actualState: actual,
769
+ reason: "exact-readback",
770
+ };
771
+ } catch {
772
+ observation = {
773
+ ...common,
774
+ outcome: "inconclusive",
775
+ desiredStateMatched: null,
776
+ actualState: null,
777
+ reason: "readback-failed",
778
+ };
779
+ }
780
+ appendReconciliationObservation(observation, environment);
781
+
782
+ let recoveryArtifactsReleased = false;
783
+ if (observation.outcome === "desired-state-observed") {
784
+ try {
785
+ const releaseKind = releaseReconciledRunRecovery(
786
+ receipt.runId,
787
+ common.receiptHash,
788
+ environment,
789
+ options.now ?? new Date(),
790
+ );
791
+ if (releaseKind === "legacy-no-journal") {
792
+ (
793
+ options.dependencies?.releaseRecoveryArtifacts
794
+ ?? defaultReleaseRecoveryArtifacts
795
+ )(receipt.runId, selected.planDigest, environment);
796
+ }
797
+ recoveryArtifactsReleased =
798
+ releaseKind !== "journal-retained-for-duplicate-successor";
799
+ } catch (error) {
800
+ throw new Error(
801
+ "the desired-state observation was stored, but its recovery artifacts could not be fully released; rerun reconciliation",
802
+ { cause: error },
803
+ );
804
+ }
805
+ }
806
+ return {
807
+ ok: observation.outcome === "desired-state-observed",
808
+ status: observation.outcome === "inconclusive"
809
+ ? "reconciliation-inconclusive"
810
+ : "reconciliation-observed",
811
+ runId: receipt.runId,
812
+ originalReceiptStatus: receipt.status,
813
+ receiptUnchanged: true,
814
+ providerWriteDispatched: false,
815
+ recoveryArtifactsReleased,
816
+ observation,
817
+ };
818
+ }