@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,373 @@
1
+ import {
2
+ acquireCookieRecords,
3
+ type CookieRecordReader,
4
+ type CookieSelection,
5
+ } from "@hraness/kb/clip/acquire";
6
+ import { renderCookieHeader, type StrictCookie } from "@hraness/kb/clip/cookies";
7
+ import type { GhostgetAuth } from "./auth";
8
+ import type { OperationInput } from "./model";
9
+ import { pinnedHttpsFetch } from "./pinned-https";
10
+ import {
11
+ type WebSessionAuthorizationSource,
12
+ type WebSessionBrowserStorageSource,
13
+ type WebSessionHeaderValue,
14
+ type WebSessionJsonPathSegment,
15
+ type WebSessionTemplate,
16
+ type WebSessionValueTemplate,
17
+ } from "./web-session-template";
18
+
19
+ type JsonRecord = Record<string, unknown>;
20
+
21
+ function isInputArray(
22
+ value: OperationInput[string],
23
+ ): value is Extract<OperationInput[string], readonly unknown[]> {
24
+ return Array.isArray(value);
25
+ }
26
+
27
+ export type WebSessionSecretSource =
28
+ | WebSessionBrowserStorageSource
29
+ | WebSessionAuthorizationSource;
30
+
31
+ export type WebSessionHttpDependencies = {
32
+ readonly acquireCookies: CookieRecordReader;
33
+ readonly fetch: (
34
+ input: string | URL | Request,
35
+ init?: RequestInit,
36
+ ) => Promise<Response>;
37
+ /**
38
+ * Optional, trusted bootstrap for values that cannot be recovered from the
39
+ * cookie jar. The implementation may inspect browser network/storage state,
40
+ * but the returned value is used only in its reviewed header sink.
41
+ */
42
+ readonly resolveSecret?: (
43
+ source: WebSessionSecretSource,
44
+ context: { readonly origin: string; readonly header: string },
45
+ ) => Promise<string>;
46
+ };
47
+
48
+ export type WebSessionHttpResult = {
49
+ readonly status: number;
50
+ readonly output: Readonly<Record<string, unknown>> | null;
51
+ readonly responseBytes: number;
52
+ };
53
+
54
+ function requestInputUrl(input: string | URL | Request): URL {
55
+ if (input instanceof Request) {
56
+ if (input.body !== null || input.bodyUsed) {
57
+ throw new Error("authenticated pinned transport does not accept a Request body wrapper");
58
+ }
59
+ return new URL(input.url);
60
+ }
61
+ return new URL(input);
62
+ }
63
+
64
+ function cookieSelection(auth: GhostgetAuth, timeoutMs: number): CookieSelection {
65
+ if (auth.kind === "cookie-source") {
66
+ return {
67
+ cookieSources: [auth.source],
68
+ cookiesFile: undefined,
69
+ cookieProfile: auth.profile,
70
+ timeoutMs,
71
+ requireExplicitCookieScope: true,
72
+ };
73
+ }
74
+ if (auth.kind === "cookies-file") {
75
+ return {
76
+ cookieSources: [],
77
+ cookiesFile: auth.path,
78
+ cookieProfile: undefined,
79
+ timeoutMs,
80
+ requireExplicitCookieScope: true,
81
+ };
82
+ }
83
+ if (auth.kind === "browser-profile" && auth.cookieSource !== undefined) {
84
+ return {
85
+ cookieSources: [auth.cookieSource],
86
+ cookiesFile: undefined,
87
+ cookieProfile: auth.cookieProfile,
88
+ timeoutMs,
89
+ requireExplicitCookieScope: true,
90
+ };
91
+ }
92
+ if (auth.kind === "browser-profile") {
93
+ throw new Error("authenticated web API execution requires the browser auth locator to name a cookie source");
94
+ }
95
+ throw new Error("authenticated web API execution requires browser-session or cookie auth");
96
+ }
97
+
98
+ function scalar(value: unknown, name: string): string | number | boolean {
99
+ if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
100
+ throw new Error(`${name} must resolve to a scalar input`);
101
+ }
102
+ return value;
103
+ }
104
+
105
+ function renderValue(template: WebSessionValueTemplate, input: OperationInput): unknown {
106
+ if (template.kind === "literal") return template.value;
107
+ if (template.kind === "input") {
108
+ const value = input[template.name];
109
+ if (value === undefined) throw new Error(`reviewed request references missing input.${template.name}`);
110
+ if (template.valueType.endsWith("[]")) {
111
+ if (!isInputArray(value) || value.some((item) => typeof item === "object")) {
112
+ throw new Error(`input.${template.name} does not match its reviewed request type`);
113
+ }
114
+ return [...value];
115
+ }
116
+ return scalar(value, `input.${template.name}`);
117
+ }
118
+ if (template.kind === "array") return template.items.map((item) => renderValue(item, input));
119
+ const output: JsonRecord = {};
120
+ for (const entry of template.entries) output[entry.name] = renderValue(entry.value, input);
121
+ return output;
122
+ }
123
+
124
+ function cookieValue(cookies: readonly StrictCookie[], name: string): string {
125
+ const matches = cookies.filter((cookie) => cookie.name === name);
126
+ if (matches.length !== 1) {
127
+ throw new Error(`authenticated session must contain exactly one reviewed ${name} cookie`);
128
+ }
129
+ return matches[0]?.value ?? "";
130
+ }
131
+
132
+ function applyTransform(value: string, transform: "identity" | "strip-surrounding-quotes" | "url-decode"): string {
133
+ if (transform === "identity") return value;
134
+ if (transform === "strip-surrounding-quotes") {
135
+ return value.length >= 2 && value.startsWith('"') && value.endsWith('"')
136
+ ? value.slice(1, -1)
137
+ : value;
138
+ }
139
+ try {
140
+ return decodeURIComponent(value);
141
+ } catch {
142
+ throw new Error("reviewed browser token could not be URL-decoded");
143
+ }
144
+ }
145
+
146
+ async function resolveHeader(
147
+ header: string,
148
+ value: WebSessionHeaderValue,
149
+ cookies: readonly StrictCookie[],
150
+ template: WebSessionTemplate,
151
+ dependencies: WebSessionHttpDependencies,
152
+ ): Promise<string> {
153
+ if (value.kind === "literal") return value.value;
154
+ if (value.kind === "browser-csrf" && value.source.kind === "cookie") {
155
+ return applyTransform(cookieValue(cookies, value.source.name), value.transform);
156
+ }
157
+ if (dependencies.resolveSecret === undefined) {
158
+ throw new Error(`reviewed ${header} header requires a browser bootstrap that is not configured`);
159
+ }
160
+ const source = value.source;
161
+ if (source.kind === "cookie") throw new Error(`reviewed ${header} cookie source could not be resolved`);
162
+ if (source.kind === "meta") throw new Error(`reviewed ${header} meta source requires a browser-context executor`);
163
+ const resolved = await dependencies.resolveSecret(source, { origin: template.origin, header });
164
+ if (resolved.length < 1 || resolved.length > 64 * 1024 || /[\r\n\0]/u.test(resolved)) {
165
+ throw new Error(`browser bootstrap returned an invalid ${header} value`);
166
+ }
167
+ if (value.kind === "browser-authorization" && value.transform === "bearer") {
168
+ return resolved.startsWith("Bearer ") ? resolved : `Bearer ${resolved}`;
169
+ }
170
+ if (value.kind === "browser-csrf") return applyTransform(resolved, value.transform);
171
+ return resolved;
172
+ }
173
+
174
+ function requestUrl(template: WebSessionTemplate, input: OperationInput): URL {
175
+ const url = new URL(template.origin);
176
+ url.pathname = `/${template.request.path.map((segment) => {
177
+ const value = segment.kind === "literal" ? segment.value : scalar(input[segment.name], `input.${segment.name}`);
178
+ if (typeof value !== "string") throw new Error("reviewed path input must be a string");
179
+ return encodeURIComponent(value);
180
+ }).join("/")}`;
181
+ for (const parameter of template.request.query) {
182
+ const value = renderValue(parameter.value, input);
183
+ if (parameter.encoding === "json") url.searchParams.append(parameter.name, JSON.stringify(value));
184
+ else url.searchParams.append(parameter.name, String(scalar(value, `query.${parameter.name}`)));
185
+ }
186
+ if (url.origin !== template.origin) throw new Error("reviewed request escaped its exact origin");
187
+ return url;
188
+ }
189
+
190
+ function requestBody(template: WebSessionTemplate, input: OperationInput): string | undefined {
191
+ const body = template.request.body;
192
+ if (body.kind === "none") return undefined;
193
+ if (body.kind === "json") return JSON.stringify(renderValue(body.value, input));
194
+ const form = new URLSearchParams();
195
+ for (const field of body.fields) {
196
+ form.append(field.name, String(scalar(renderValue(field.value, input), `form.${field.name}`)));
197
+ }
198
+ return form.toString();
199
+ }
200
+
201
+ async function boundedResponse(response: Response, maximum: number): Promise<Uint8Array> {
202
+ if (response.body === null) return new Uint8Array();
203
+ const reader = response.body.getReader();
204
+ const chunks: Uint8Array[] = [];
205
+ let bytes = 0;
206
+ try {
207
+ for (;;) {
208
+ const next = await reader.read();
209
+ if (next.done) break;
210
+ const value: unknown = next.value;
211
+ if (!(value instanceof Uint8Array)) {
212
+ await reader.cancel();
213
+ throw new Error("authenticated web API response yielded a non-byte chunk");
214
+ }
215
+ bytes += value.byteLength;
216
+ if (bytes > maximum) {
217
+ await reader.cancel();
218
+ throw new Error(`authenticated web API response exceeded ${maximum} bytes`);
219
+ }
220
+ chunks.push(value);
221
+ }
222
+ } finally {
223
+ reader.releaseLock();
224
+ }
225
+ const output = new Uint8Array(bytes);
226
+ let offset = 0;
227
+ for (const chunk of chunks) {
228
+ output.set(chunk, offset);
229
+ offset += chunk.byteLength;
230
+ }
231
+ return output;
232
+ }
233
+
234
+ function contentTypeEssence(response: Response): string | null {
235
+ const raw = response.headers.get("content-type");
236
+ return raw === null ? null : (raw.split(";", 1)[0]?.trim().toLowerCase() || null);
237
+ }
238
+
239
+ function jsonPath(value: unknown, path: readonly WebSessionJsonPathSegment[]): { readonly found: boolean; readonly value: unknown } {
240
+ let current = value;
241
+ for (const segment of path) {
242
+ if (segment.kind === "key") {
243
+ if (typeof current !== "object" || current === null || Array.isArray(current) || !Object.hasOwn(current, segment.key)) {
244
+ return { found: false, value: undefined };
245
+ }
246
+ current = (current as JsonRecord)[segment.key];
247
+ } else {
248
+ if (!Array.isArray(current) || segment.index >= current.length) return { found: false, value: undefined };
249
+ current = current[segment.index];
250
+ }
251
+ }
252
+ return { found: true, value: current };
253
+ }
254
+
255
+ function projectionType(value: unknown): "string" | "number" | "boolean" | "null" | "object" | "array" {
256
+ if (value === null) return "null";
257
+ if (Array.isArray(value)) return "array";
258
+ if (typeof value === "object") return "object";
259
+ if (typeof value === "string") return "string";
260
+ if (typeof value === "number") return "number";
261
+ if (typeof value === "boolean") return "boolean";
262
+ throw new Error("reviewed response projection has an unsupported value type");
263
+ }
264
+
265
+ function parseProjectedResponse(
266
+ bytes: Uint8Array,
267
+ body: Extract<WebSessionTemplate["response"]["variants"][number]["body"], { readonly kind: "json" }>,
268
+ input: OperationInput,
269
+ ): Readonly<Record<string, unknown>> {
270
+ let parsed: unknown;
271
+ try {
272
+ parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)) as unknown;
273
+ } catch {
274
+ throw new Error("authenticated web API returned malformed JSON");
275
+ }
276
+ for (const binding of body.bindings) {
277
+ const actual = jsonPath(parsed, binding.path);
278
+ const expected = renderValue(binding.expected, input);
279
+ if (!actual.found || actual.value !== expected) throw new Error("authenticated web API response failed its reviewed target binding");
280
+ }
281
+ const output: JsonRecord = {};
282
+ for (const projection of body.projections) {
283
+ const selected = jsonPath(parsed, projection.path);
284
+ if (!selected.found) {
285
+ if (projection.required) throw new Error(`authenticated web API response omitted required projection ${projection.name}`);
286
+ continue;
287
+ }
288
+ if (projectionType(selected.value) !== projection.valueType) {
289
+ throw new Error(`authenticated web API projection ${projection.name} changed type`);
290
+ }
291
+ output[projection.name] = selected.value;
292
+ }
293
+ return output;
294
+ }
295
+
296
+ /** Execute one reviewed first-party exchange with session credentials kept in memory. */
297
+ export async function executeWebSessionTemplate(
298
+ template: WebSessionTemplate,
299
+ input: OperationInput,
300
+ auth: GhostgetAuth,
301
+ options: {
302
+ readonly timeoutMs: number;
303
+ readonly dependencies?: Partial<WebSessionHttpDependencies>;
304
+ /** Called after request preparation and immediately before the only network dispatch. */
305
+ readonly beforeRequest?: () => Promise<void>;
306
+ /** Called only after the exact response variant, bindings, and projections verify. */
307
+ readonly afterResponseVerified?: () => Promise<void>;
308
+ },
309
+ ): Promise<WebSessionHttpResult> {
310
+ const dependencies: WebSessionHttpDependencies = {
311
+ acquireCookies: options.dependencies?.acquireCookies ?? acquireCookieRecords,
312
+ fetch: options.dependencies?.fetch ?? ((input, init = {}) =>
313
+ pinnedHttpsFetch(requestInputUrl(input), init, options.timeoutMs)),
314
+ ...(options.dependencies?.resolveSecret === undefined
315
+ ? {}
316
+ : { resolveSecret: options.dependencies.resolveSecret }),
317
+ };
318
+ const url = requestUrl(template, input);
319
+ const cookieResult = await dependencies.acquireCookies(cookieSelection(auth, options.timeoutMs), url);
320
+ const headers = new Headers();
321
+ headers.set("cookie", renderCookieHeader(cookieResult.cookies));
322
+ for (const header of template.request.headers) {
323
+ headers.set(header.name, await resolveHeader(header.name, header.value, cookieResult.cookies, template, dependencies));
324
+ }
325
+ if (template.request.body.kind === "json") headers.set("content-type", "application/json");
326
+ if (template.request.body.kind === "form") headers.set("content-type", "application/x-www-form-urlencoded;charset=UTF-8");
327
+ if (template.request.method !== "GET" && template.request.method !== "HEAD") headers.set("origin", template.origin);
328
+ const body = requestBody(template, input);
329
+ const controller = new AbortController();
330
+ const timeout = setTimeout(() => controller.abort(), options.timeoutMs);
331
+ let response: Response | undefined;
332
+ try {
333
+ try {
334
+ await options.beforeRequest?.();
335
+ response = await dependencies.fetch(url, {
336
+ method: template.request.method,
337
+ headers,
338
+ ...(body === undefined ? {} : { body }),
339
+ redirect: "error",
340
+ signal: controller.signal,
341
+ });
342
+ } catch (error) {
343
+ throw new Error("authenticated web API request failed before a reviewed response was received", { cause: error });
344
+ }
345
+ const received = response;
346
+ const contentType = contentTypeEssence(received);
347
+ const variant = template.response.variants.find((candidate) =>
348
+ candidate.status === received.status && candidate.contentType === contentType);
349
+ if (variant === undefined) {
350
+ received.body?.cancel().catch(() => undefined);
351
+ throw new Error(`authenticated web API returned unreviewed status/content type ${received.status}/${contentType ?? "missing"}`);
352
+ }
353
+ const bytes = await boundedResponse(received, template.response.maxBytes);
354
+ let result: WebSessionHttpResult;
355
+ if (variant.body.kind === "empty") {
356
+ if (bytes.byteLength !== 0) throw new Error("authenticated web API returned an unexpected response body");
357
+ result = { status: received.status, output: null, responseBytes: 0 };
358
+ } else if (variant.body.kind === "discard") {
359
+ result = { status: received.status, output: null, responseBytes: bytes.byteLength };
360
+ } else {
361
+ result = {
362
+ status: received.status,
363
+ output: parseProjectedResponse(bytes, variant.body, input),
364
+ responseBytes: bytes.byteLength,
365
+ };
366
+ }
367
+ await options.afterResponseVerified?.();
368
+ return result;
369
+ } finally {
370
+ clearTimeout(timeout);
371
+ if (controller.signal.aborted) await response?.body?.cancel().catch(() => undefined);
372
+ }
373
+ }
@@ -0,0 +1,19 @@
1
+ /** Typed metadata at the native HTTP boundary; legacy message bytes stay stable. */
2
+ export class WebSessionResponseRejectedError extends Error {
3
+ constructor(message: string, readonly status: number, readonly contentType: string | null) { super(message); }
4
+ }
5
+
6
+ export class WebSessionReadTransportError extends Error {
7
+ constructor(message: string, cause: unknown) { super(message, { cause }); }
8
+ }
9
+
10
+ export class WebSessionAuthStateError extends Error { }
11
+
12
+ /** Classify a locally owned auth-state validator without interpreting its text. */
13
+ export function validateWebSessionAuthState<A>(validate: () => A): A {
14
+ try { return validate(); }
15
+ catch (cause) {
16
+ if (cause instanceof Error) throw new WebSessionAuthStateError(cause.message, { cause });
17
+ throw cause;
18
+ }
19
+ }
@@ -0,0 +1,124 @@
1
+ import * as Effect from "effect/Effect";
2
+ import type { BrowserFileResolver } from "./browser";
3
+ import type { WebSessionRecipe } from "./model";
4
+ import { OperationDeadline } from "./operation-deadline";
5
+ import { readAttempt, resumeReadOutcome, type ReadEffectFailure } from "./read-effect";
6
+ import { readNative } from "./read-effect-platform";
7
+ import { runReadEffect } from "./read-effect-runtime";
8
+ import {
9
+ trackWebSessionCleanupBarrier,
10
+ awaitWebSessionCleanupBarriers,
11
+ type TrackedWebSessionCleanupBarrier
12
+ } from "./web-session-cleanup-join";
13
+ import type {
14
+ WebSessionExecutionOptions,
15
+ WebSessionDispatchEvent,
16
+ WebSessionProviderAcceptedMutationTargetEvent,
17
+ WebSessionProviderBoundMutationTargetEvent,
18
+ WebSessionCleanupBarrierRegistrar
19
+ } from "./web-session-execution";
20
+
21
+ const WEB_SESSION_OPERATION_LABEL = "authenticated web operation deadline";
22
+
23
+ /** R1 owns native deadline acquisition, execution Exit and the separate
24
+ * content-bound cleanup join. Closing a fiber does not verify native cleanup. */
25
+ export async function runWebSessionReadWithDeadline<T>(
26
+ recipe: Pick<WebSessionRecipe, "timeoutMs">,
27
+ options: WebSessionExecutionOptions,
28
+ execute: (bounded: WebSessionExecutionOptions) => Promise<T>
29
+
30
+ ): Promise<T> {
31
+ const program: Effect.Effect<T, ReadEffectFailure> = Effect.scoped(Effect.gen(function*() {
32
+ const resource = yield* Effect.acquireRelease(
33
+ readAttempt(() => {
34
+ const ownedDeadline = options.operationDeadline === undefined
35
+ ? new OperationDeadline(recipe.timeoutMs, {
36
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
37
+ ...(options.deadlineClock === undefined
38
+ ? {}
39
+ : { clock: options.deadlineClock }),
40
+ })
41
+ : null;
42
+ const deadline = options.operationDeadline ?? ownedDeadline;
43
+ if (deadline === null) {
44
+ throw new Error("authenticated web operation deadline is unavailable");
45
+ }
46
+ return { ownedDeadline, deadline };
47
+ }),
48
+ resource => Effect.sync(() => resource.ownedDeadline?.dispose())
49
+ );
50
+ const { deadline } = resource;
51
+ const guardDispatch = (
52
+ callback: ((event: WebSessionDispatchEvent) => Promise<void>) | undefined,
53
+ ): ((event: WebSessionDispatchEvent) => Promise<void>) | undefined =>
54
+ callback === undefined
55
+ ? undefined
56
+ : (event) => deadline.run(
57
+ () => callback(event),
58
+ WEB_SESSION_OPERATION_LABEL,
59
+ );
60
+ const beforeDispatch = guardDispatch(options.beforeDispatch);
61
+ const afterProviderAcceptedMutationTarget =
62
+ options.afterProviderAcceptedMutationTarget === undefined
63
+ ? undefined
64
+ : (event: WebSessionProviderAcceptedMutationTargetEvent) => deadline.run(
65
+ () => options.afterProviderAcceptedMutationTarget!(event),
66
+ WEB_SESSION_OPERATION_LABEL,
67
+ );
68
+ const afterProviderBoundMutationTarget =
69
+ options.afterProviderBoundMutationTarget === undefined
70
+ ? undefined
71
+ : (event: WebSessionProviderBoundMutationTargetEvent) => deadline.run(
72
+ () => options.afterProviderBoundMutationTarget!(event),
73
+ WEB_SESSION_OPERATION_LABEL,
74
+ );
75
+ const afterDispatchVerified = guardDispatch(options.afterDispatchVerified);
76
+ const cleanupBarriers: TrackedWebSessionCleanupBarrier[] = [];
77
+ let acceptingCleanupBarriers = true;
78
+ const registerCleanupBarrier: WebSessionCleanupBarrierRegistrar = (barrier) => {
79
+ if (!acceptingCleanupBarriers) {
80
+ throw new Error("authenticated web cleanup registration is already closed");
81
+ }
82
+ const tracked = trackWebSessionCleanupBarrier(barrier);
83
+ try {
84
+ const registered =
85
+ options.registerCleanupBarrier?.(tracked.promise);
86
+ cleanupBarriers.push(tracked);
87
+ return typeof registered === "function" ? registered : undefined;
88
+ } catch (error) {
89
+ // Registration happens before its resource starts. If an outer registrar
90
+ // retained the promise before throwing, settle it safely so that scope
91
+ // does not wait forever for an operation that was never begun.
92
+ tracked.verified();
93
+ throw error;
94
+ }
95
+ };
96
+ const fileResolver = options.fileResolver === undefined
97
+ ? undefined
98
+ : (files: Parameters<BrowserFileResolver>[0]) => deadline.run(
99
+ () => options.fileResolver!(files),
100
+ WEB_SESSION_OPERATION_LABEL,
101
+ );
102
+ const boundedOptions: WebSessionExecutionOptions = {
103
+ ...(fileResolver === undefined ? {} : { fileResolver }),
104
+ ...(options.environment === undefined ? {} : { environment: options.environment }),
105
+ signal: deadline.signal,
106
+ operationDeadline: deadline,
107
+ registerCleanupBarrier,
108
+ ...(beforeDispatch === undefined ? {} : { beforeDispatch }),
109
+ ...(afterProviderAcceptedMutationTarget === undefined
110
+ ? {}
111
+ : { afterProviderAcceptedMutationTarget }),
112
+ ...(afterProviderBoundMutationTarget === undefined
113
+ ? {}
114
+ : { afterProviderBoundMutationTarget }),
115
+ ...(afterDispatchVerified === undefined ? {} : { afterDispatchVerified }),
116
+ };
117
+ const outcome = yield* Effect.exit(readNative(() => deadline.run(() => execute(boundedOptions), WEB_SESSION_OPERATION_LABEL)));
118
+ acceptingCleanupBarriers = false;
119
+ // Native barrier verification must settle even when the operation failed.
120
+ const cleanup = yield* Effect.exit(Effect.uninterruptible(readNative(() => awaitWebSessionCleanupBarriers(cleanupBarriers, options.deadlineClock))));
121
+ return yield* resumeReadOutcome(outcome, cleanup);
122
+ }));
123
+ return runReadEffect(program);
124
+ }