@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,600 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import type { GhostgetAuth } from "../auth";
4
+ import {
5
+ PreservedBrowserArtifactsError,
6
+ browserResultData,
7
+ createBrowserSession,
8
+ runCommand,
9
+ type BrowserSession,
10
+ type CreateBrowserSessionOptions,
11
+ } from "../browser";
12
+ import type { GhostgetManifest } from "../model";
13
+ import type {
14
+ WebSessionCleanupResourcePublisher,
15
+ WebSessionOperationDeadline,
16
+ } from "../web-session-execution";
17
+ import { jsonScriptLiteral } from "../canonical-json";
18
+
19
+ const INSTAGRAM_ORIGIN = "https://www.instagram.com";
20
+ const INSTAGRAM_ROOT_URL = `${INSTAGRAM_ORIGIN}/`;
21
+ const INSTAGRAM_PRE_COOKIE_REALM_URL = `${INSTAGRAM_ORIGIN}/robots.txt`;
22
+ const INSTAGRAM_INITIAL_ROOT_BATCH = JSON.stringify([
23
+ ["open", INSTAGRAM_ORIGIN],
24
+ ]);
25
+ const INSTAGRAM_INITIAL_BLANK_BATCH = JSON.stringify([
26
+ ["open", "about:blank"],
27
+ ]);
28
+ const INSTAGRAM_INITIAL_REALM_BATCH = JSON.stringify([
29
+ ["open", INSTAGRAM_PRE_COOKIE_REALM_URL],
30
+ ]);
31
+ const INSTAGRAM_PROFILE_ROUTE = "/api/v1/users/web_profile_info/";
32
+ const INSTAGRAM_WEB_APP_ID = "936619743392459";
33
+ const MAX_VIEWER_HTML_BYTES = 12 * 1024 * 1024;
34
+ const MAX_PROFILE_JSON_BYTES = 8 * 1024 * 1024;
35
+ const BROWSER_ENVELOPE_BYTES = 64 * 1024;
36
+
37
+ const profileBrowserManifest: GhostgetManifest = Object.freeze({
38
+ schemaVersion: 4,
39
+ id: "instagram-profile-runtime",
40
+ version: "1.0.0",
41
+ displayName: "Instagram profile stats runtime",
42
+ surfaceId: "instagram",
43
+ origins: Object.freeze([INSTAGRAM_ORIGIN]),
44
+ browserDomains: Object.freeze(["www.instagram.com"]),
45
+ operations: Object.freeze({}),
46
+ });
47
+
48
+ export type InstagramProfileBrowserTransport = {
49
+ readonly readCurrentViewerHtml: () => Promise<string>;
50
+ readonly readProfileJson: (profile: string) => Promise<unknown>;
51
+ readonly close: () => Promise<void>;
52
+ };
53
+
54
+ export type InstagramProfileBrowserDependencies = {
55
+ readonly createBrowserSession: typeof createBrowserSession;
56
+ readonly runCommand: typeof runCommand;
57
+ };
58
+
59
+ type BrowserReadBinding = {
60
+ readonly kind: "html" | "json";
61
+ readonly maxBytes: number;
62
+ readonly path: string;
63
+ readonly referrer: string;
64
+ };
65
+
66
+ type BrowserReadState =
67
+ | "ready"
68
+ | "viewer-pending"
69
+ | "viewer"
70
+ | "profile-pending"
71
+ | "complete";
72
+
73
+ const INSTAGRAM_RESPONSE_MEDIA_TYPE =
74
+ /^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/u;
75
+
76
+ export type InstagramProfileBrowserFailureCategory =
77
+ | "authwall"
78
+ | "body-envelope"
79
+ | "bootstrap"
80
+ | "browser-envelope"
81
+ | "browser-command"
82
+ | "execution-context"
83
+ | "output-bound"
84
+ | "profile-json"
85
+ | "provider-fetch"
86
+ | "response-envelope"
87
+ | "response-rejected"
88
+ | "startup";
89
+
90
+ export class InstagramProfileBrowserFailure extends Error {
91
+ readonly category: InstagramProfileBrowserFailureCategory;
92
+
93
+ constructor(category: InstagramProfileBrowserFailureCategory, message: string) {
94
+ super(message);
95
+ this.name = "InstagramProfileBrowserFailure";
96
+ this.category = category;
97
+ }
98
+ }
99
+
100
+ export class InstagramProfileBrowserResponseRejectedError
101
+ extends InstagramProfileBrowserFailure {
102
+ readonly status: number;
103
+ readonly contentType: string;
104
+
105
+ constructor(status: number, contentType: string) {
106
+ super(
107
+ "response-rejected",
108
+ "Instagram profile browser request returned a reviewed rejection",
109
+ );
110
+ this.name = "InstagramProfileBrowserResponseRejectedError";
111
+ if (
112
+ !Number.isSafeInteger(status)
113
+ || status < 100
114
+ || status > 599
115
+ || contentType.length > 128
116
+ || (contentType !== "" && !INSTAGRAM_RESPONSE_MEDIA_TYPE.test(contentType))
117
+ ) throw new Error(
118
+ "Instagram profile browser returned a malformed response category",
119
+ );
120
+ this.status = status;
121
+ this.contentType = contentType === "" ? "missing" : contentType;
122
+ }
123
+ }
124
+
125
+ function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
126
+ return typeof value === "object" && value !== null && !Array.isArray(value);
127
+ }
128
+
129
+ function exactKeys(
130
+ value: Readonly<Record<string, unknown>>,
131
+ expected: readonly string[],
132
+ label: string,
133
+ ): void {
134
+ if (Object.keys(value).sort().join(",") !== [...expected].sort().join(",")) {
135
+ throw new Error(`${label} returned an unexpected result shape`);
136
+ }
137
+ }
138
+
139
+ function exactInstagramProfile(value: string): string {
140
+ if (!/^[a-z0-9._]{1,30}$/u.test(value)) {
141
+ throw new Error(
142
+ "Instagram profile browser target must be one canonical lowercase handle",
143
+ );
144
+ }
145
+ return value;
146
+ }
147
+
148
+ function profilePath(profile: string): string {
149
+ const url = new URL(INSTAGRAM_PROFILE_ROUTE, INSTAGRAM_ORIGIN);
150
+ url.searchParams.set("username", profile);
151
+ return `${url.pathname}${url.search}`;
152
+ }
153
+
154
+ function browserReadEvaluationSource(binding: BrowserReadBinding): string {
155
+ const bound = jsonScriptLiteral(binding);
156
+ return `(async()=>{const input=${bound};if(location.origin!=="${INSTAGRAM_ORIGIN}")throw new Error("unexpected Instagram origin");if((input.kind!=="json"&&input.kind!=="html")||!Number.isSafeInteger(input.maxBytes)||input.maxBytes<1||input.maxBytes>${MAX_VIEWER_HTML_BYTES})throw new Error("invalid Instagram profile browser request binding");const expected=new URL(input.path,"${INSTAGRAM_ORIGIN}");if(expected.origin!=="${INSTAGRAM_ORIGIN}"||expected.username!==""||expected.password!==""||expected.hash!==""||expected.href!=="${INSTAGRAM_ORIGIN}"+input.path)throw new Error("invalid Instagram profile browser path binding");if(input.kind==="html"){if(expected.pathname!=="/"||expected.search!==""||input.referrer!=="${INSTAGRAM_ROOT_URL}")throw new Error("Instagram viewer browser request escaped its exact route")}else{const names=[...expected.searchParams.keys()];const profile=expected.searchParams.get("username");if(expected.pathname!=="${INSTAGRAM_PROFILE_ROUTE}"||names.length!==1||names[0]!=="username"||typeof profile!=="string"||!/^[a-z0-9._]{1,30}$/.test(profile)||expected.search!=="?username="+encodeURIComponent(profile)||input.referrer!=="${INSTAGRAM_ORIGIN}/"+profile+"/")throw new Error("Instagram profile browser request escaped its exact route")}const headers=input.kind==="json"?{accept:"application/json, text/plain, */*","x-ig-app-id":"${INSTAGRAM_WEB_APP_ID}","x-requested-with":"XMLHttpRequest"}:{accept:"text/html,application/xhtml+xml"};const response=await fetch(input.path,{cache:"no-store",credentials:"include",headers,method:"GET",redirect:"error",referrer:input.referrer,referrerPolicy:"same-origin"});const responseUrl=new URL(response.url);if(responseUrl.origin!=="${INSTAGRAM_ORIGIN}"||responseUrl.username!==""||responseUrl.password!==""||responseUrl.hash!==""||responseUrl.href!==expected.href)throw new Error("Instagram profile browser response escaped its exact route");const contentType=(response.headers.get("content-type")||"").split(";",1)[0].trim().toLowerCase();const contentTypeAllowed=input.kind==="json"?contentType==="application/json":contentType==="text/html";if(response.status!==200||!contentTypeAllowed){response.body?.cancel();return{authWall:false,bodyBase64:null,bodyBytes:0,bodySha256:null,contentType,status:response.status}}if(response.body===null)throw new Error("Instagram profile browser response omitted its body");const reader=response.body.getReader();const chunks=[];let bytes=0;while(true){const part=await reader.read();if(part.done)break;bytes+=part.value.byteLength;if(bytes>input.maxBytes){await reader.cancel();throw new Error("Instagram profile browser response exceeded its reviewed byte bound")}chunks.push(part.value)}const body=new Uint8Array(bytes);let cursor=0;for(const chunk of chunks){body.set(chunk,cursor);cursor+=chunk.byteLength}const text=new TextDecoder("utf-8",{fatal:true}).decode(body);const authWall=input.kind==="html"&&/<form[^>]+(?:login|sign-in)|href=["']\\/accounts\\/login\\/?["']/iu.test(text);if(authWall)return{authWall:true,bodyBase64:null,bodyBytes:0,bodySha256:null,contentType,status:response.status};const digest=Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256",body)),(value)=>value.toString(16).padStart(2,"0")).join("");let binary="";for(let offset=0;offset<body.length;offset+=32768)binary+=String.fromCharCode(...body.subarray(offset,Math.min(offset+32768,body.length)));return{authWall:false,bodyBase64:btoa(binary),bodyBytes:body.byteLength,bodySha256:digest,contentType,status:response.status}})()`;
157
+ }
158
+
159
+ function encodedBodyBound(bytes: number): number {
160
+ return Math.ceil(bytes / 3) * 4;
161
+ }
162
+
163
+ function decodedBody(
164
+ result: Readonly<Record<string, unknown>>,
165
+ maximumBytes: number,
166
+ ): string {
167
+ if (
168
+ typeof result.bodyBase64 !== "string"
169
+ || result.bodyBase64.length > encodedBodyBound(maximumBytes)
170
+ || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(
171
+ result.bodyBase64,
172
+ )
173
+ || !Number.isSafeInteger(result.bodyBytes)
174
+ || (result.bodyBytes as number) < 0
175
+ || (result.bodyBytes as number) > maximumBytes
176
+ || typeof result.bodySha256 !== "string"
177
+ || !/^[a-f0-9]{64}$/u.test(result.bodySha256)
178
+ ) throw new InstagramProfileBrowserFailure(
179
+ "body-envelope",
180
+ "Instagram profile browser body envelope changed shape",
181
+ );
182
+ const bytes = Buffer.from(result.bodyBase64, "base64");
183
+ if (
184
+ bytes.byteLength !== result.bodyBytes
185
+ || bytes.toString("base64") !== result.bodyBase64
186
+ || createHash("sha256").update(bytes).digest("hex") !== result.bodySha256
187
+ ) throw new InstagramProfileBrowserFailure(
188
+ "body-envelope",
189
+ "Instagram profile browser body envelope failed integrity verification",
190
+ );
191
+ try {
192
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
193
+ } catch {
194
+ throw new InstagramProfileBrowserFailure(
195
+ "body-envelope",
196
+ "Instagram profile browser body was not valid UTF-8",
197
+ );
198
+ }
199
+ }
200
+
201
+ function browserEvaluationResult(
202
+ value: Readonly<Record<string, unknown>>,
203
+ ): Readonly<Record<string, unknown>> {
204
+ const data = browserResultData(value as Record<string, unknown>);
205
+ if (!isRecord(data) || typeof data.origin !== "string" || !isRecord(data.result)) {
206
+ throw new InstagramProfileBrowserFailure(
207
+ "response-envelope",
208
+ "Instagram profile browser returned a malformed evaluation envelope",
209
+ );
210
+ }
211
+ let origin: URL;
212
+ try {
213
+ origin = new URL(data.origin);
214
+ } catch {
215
+ throw new InstagramProfileBrowserFailure(
216
+ "response-envelope",
217
+ "Instagram profile browser returned a malformed evaluation envelope",
218
+ );
219
+ }
220
+ if (
221
+ origin.origin !== INSTAGRAM_ORIGIN
222
+ || origin.username !== ""
223
+ || origin.password !== ""
224
+ ) throw new InstagramProfileBrowserFailure(
225
+ "response-envelope",
226
+ "Instagram profile browser returned a malformed evaluation envelope",
227
+ );
228
+ return data.result;
229
+ }
230
+
231
+ function hasNoDefaultExecutionContext(error: unknown): boolean {
232
+ let current: unknown = error;
233
+ for (let depth = 0; depth < 8 && current !== undefined; depth += 1) {
234
+ if (
235
+ current instanceof Error
236
+ && /(?:cannot find|no) default execution context/iu.test(current.message)
237
+ ) return true;
238
+ current = current instanceof Error ? current.cause : undefined;
239
+ }
240
+ return false;
241
+ }
242
+
243
+ function hasUnexpectedInstagramOrigin(error: unknown): boolean {
244
+ let current: unknown = error;
245
+ for (let depth = 0; depth < 8 && current !== undefined; depth += 1) {
246
+ if (
247
+ current instanceof Error
248
+ && /(?:^|: )unexpected Instagram origin(?:$|[\r\n])/u.test(current.message)
249
+ ) return true;
250
+ current = current instanceof Error ? current.cause : undefined;
251
+ }
252
+ return false;
253
+ }
254
+
255
+ function classifiedBrowserCommandFailure(
256
+ error: unknown,
257
+ ): InstagramProfileBrowserFailure {
258
+ const message = error instanceof Error && error.message.length <= 1_500
259
+ ? error.message
260
+ : "";
261
+ if (message.includes("Failed to fetch")) {
262
+ return new InstagramProfileBrowserFailure(
263
+ "provider-fetch",
264
+ "Instagram profile browser could not complete its first-party fetch",
265
+ );
266
+ }
267
+ if (
268
+ message.endsWith("Instagram profile browser response escaped its exact route")
269
+ || message.endsWith("Instagram profile browser request escaped its exact route")
270
+ || message.endsWith("Instagram viewer browser request escaped its exact route")
271
+ ) {
272
+ return new InstagramProfileBrowserFailure(
273
+ "response-envelope",
274
+ "Instagram profile browser request escaped its exact route",
275
+ );
276
+ }
277
+ if (
278
+ message.includes("process output exceeded")
279
+ || message.includes("response exceeded its reviewed byte bound")
280
+ ) {
281
+ return new InstagramProfileBrowserFailure(
282
+ "output-bound",
283
+ "Instagram profile browser exceeded a reviewed output bound",
284
+ );
285
+ }
286
+ if (
287
+ message.includes("malformed batch")
288
+ || message.includes("malformed batch entry")
289
+ || message.includes("did not return JSON")
290
+ || message.includes("command omitted its result")
291
+ ) {
292
+ return new InstagramProfileBrowserFailure(
293
+ "browser-envelope",
294
+ "Instagram profile browser command returned a malformed envelope",
295
+ );
296
+ }
297
+ return new InstagramProfileBrowserFailure(
298
+ "browser-command",
299
+ "Instagram profile browser command failed before a reviewed response",
300
+ );
301
+ }
302
+
303
+ function instagramProfileBrowserCommandRunner(
304
+ execute: typeof runCommand,
305
+ authKind: GhostgetAuth["kind"],
306
+ ): typeof runCommand {
307
+ let initialBatchPending = true;
308
+ return (command, options) => {
309
+ const rewroteInitialRoot = initialBatchPending
310
+ && options.stdin === INSTAGRAM_INITIAL_ROOT_BATCH;
311
+ const rewroteInitialBlank = initialBatchPending
312
+ && authKind === "browser-profile"
313
+ && options.stdin === INSTAGRAM_INITIAL_BLANK_BATCH;
314
+ initialBatchPending = false;
315
+ return execute(command, rewroteInitialRoot || rewroteInitialBlank
316
+ ? { ...options, stdin: INSTAGRAM_INITIAL_REALM_BATCH }
317
+ : options);
318
+ };
319
+ }
320
+
321
+ async function finalizeBrowserSession(session: BrowserSession): Promise<void> {
322
+ const failures: unknown[] = [];
323
+ let closeVerified = false;
324
+ try {
325
+ await session.close();
326
+ closeVerified = true;
327
+ } catch (error) {
328
+ failures.push(error);
329
+ }
330
+ let cleanupVerified = false;
331
+ try {
332
+ await session.cleanup();
333
+ cleanupVerified = true;
334
+ } catch (error) {
335
+ failures.push(error);
336
+ }
337
+ // BrowserSession cleanup can resolve only after close was acknowledged or
338
+ // the same pinned resource was independently proved quiescent.
339
+ if (cleanupVerified) return;
340
+ const cleanupEvidence = (
341
+ closeVerified
342
+ && !cleanupVerified
343
+ && session.cleanupResourceIdentity !== undefined
344
+ )
345
+ ? Object.freeze({
346
+ kind: "agent-browser-closed-artifacts-v1" as const,
347
+ resource: session.cleanupResourceIdentity,
348
+ })
349
+ : undefined;
350
+ throw new PreservedBrowserArtifactsError(
351
+ "Instagram profile browser finalization failed; private artifacts were preserved",
352
+ session.recoveryHandle ?? "session=instagram-profile-runtime;artifacts=unknown",
353
+ new AggregateError(failures, "Instagram profile browser finalization failed"),
354
+ cleanupEvidence,
355
+ );
356
+ }
357
+
358
+ function assertSupportedAuth(auth: GhostgetAuth): void {
359
+ if (
360
+ auth.kind !== "cookie-source"
361
+ && auth.kind !== "cookies-file"
362
+ && auth.kind !== "browser-profile"
363
+ ) throw new Error(
364
+ "Instagram profile browser requires browser-session or cookie auth",
365
+ );
366
+ }
367
+
368
+ export async function createInstagramProfileBrowserTransport(
369
+ auth: GhostgetAuth,
370
+ options: {
371
+ readonly timeoutMs: number;
372
+ readonly maxOutputBytes: number;
373
+ readonly operationDeadline?: WebSessionOperationDeadline;
374
+ readonly publishCleanupResource?: WebSessionCleanupResourcePublisher;
375
+ readonly dependencies?: Partial<InstagramProfileBrowserDependencies>;
376
+ },
377
+ ): Promise<InstagramProfileBrowserTransport> {
378
+ assertSupportedAuth(auth);
379
+ if (
380
+ !Number.isSafeInteger(options.maxOutputBytes)
381
+ || options.maxOutputBytes < 1
382
+ || options.maxOutputBytes > MAX_VIEWER_HTML_BYTES
383
+ ) throw new Error("Instagram profile browser output bound is invalid");
384
+ const createSession = options.dependencies?.createBrowserSession
385
+ ?? createBrowserSession;
386
+ const browserOutputBytes = encodedBodyBound(options.maxOutputBytes)
387
+ + BROWSER_ENVELOPE_BYTES;
388
+ const sessionOptions: CreateBrowserSessionOptions = {
389
+ allowCodeOwnedEvaluation: true,
390
+ headed: true,
391
+ maxOutputBytes: browserOutputBytes,
392
+ timeoutMs: options.timeoutMs,
393
+ dependencies: {
394
+ runCommand: instagramProfileBrowserCommandRunner(
395
+ options.dependencies?.runCommand ?? runCommand,
396
+ auth.kind,
397
+ ),
398
+ },
399
+ ...(options.operationDeadline === undefined
400
+ ? {}
401
+ : { operationDeadline: options.operationDeadline }),
402
+ ...(options.publishCleanupResource === undefined
403
+ ? {}
404
+ : { publishCleanupResource: options.publishCleanupResource }),
405
+ };
406
+ let session: BrowserSession;
407
+ try {
408
+ session = await createSession(profileBrowserManifest, auth, sessionOptions);
409
+ } catch (error) {
410
+ if (error instanceof PreservedBrowserArtifactsError) throw error;
411
+ options.operationDeadline?.throwIfUnavailable(
412
+ "Instagram profile browser startup",
413
+ );
414
+ throw new InstagramProfileBrowserFailure(
415
+ "startup",
416
+ "Instagram profile browser could not start its contained session",
417
+ );
418
+ }
419
+ let closed = false;
420
+ let state: BrowserReadState = "ready";
421
+
422
+ const remainingTimeMs = (): number =>
423
+ options.operationDeadline?.remainingTimeMs() ?? options.timeoutMs;
424
+ const run = async (
425
+ binding: BrowserReadBinding,
426
+ ): Promise<Readonly<Record<string, unknown>>> => {
427
+ if (closed) throw new Error("Instagram profile browser transport is closed");
428
+ const source = browserReadEvaluationSource(binding);
429
+ let records: readonly Readonly<Record<string, unknown>>[];
430
+ try {
431
+ records = await session.runBatch(
432
+ [["eval", source]],
433
+ remainingTimeMs(),
434
+ Math.min(
435
+ encodedBodyBound(binding.maxBytes) + BROWSER_ENVELOPE_BYTES,
436
+ browserOutputBytes,
437
+ ),
438
+ );
439
+ } catch (error) {
440
+ if (error instanceof PreservedBrowserArtifactsError) throw error;
441
+ if (error instanceof InstagramProfileBrowserFailure) throw error;
442
+ options.operationDeadline?.throwIfUnavailable(
443
+ "Instagram profile browser operation",
444
+ );
445
+ if (hasNoDefaultExecutionContext(error)) {
446
+ throw new InstagramProfileBrowserFailure(
447
+ "execution-context",
448
+ "Instagram profile browser lost its reviewed execution context",
449
+ );
450
+ }
451
+ if (hasUnexpectedInstagramOrigin(error)) {
452
+ throw new InstagramProfileBrowserFailure(
453
+ "bootstrap",
454
+ "Instagram profile browser was not on its reviewed signed-in origin",
455
+ );
456
+ }
457
+ throw classifiedBrowserCommandFailure(error);
458
+ }
459
+ const first = records[0];
460
+ if (first === undefined) {
461
+ throw new InstagramProfileBrowserFailure(
462
+ "response-envelope",
463
+ "Instagram profile browser omitted its response",
464
+ );
465
+ }
466
+ const result = browserEvaluationResult(first);
467
+ try {
468
+ exactKeys(
469
+ result,
470
+ [
471
+ "authWall",
472
+ "bodyBase64",
473
+ "bodyBytes",
474
+ "bodySha256",
475
+ "contentType",
476
+ "status",
477
+ ],
478
+ "Instagram profile browser request",
479
+ );
480
+ } catch {
481
+ throw new InstagramProfileBrowserFailure(
482
+ "response-envelope",
483
+ "Instagram profile browser request returned an unexpected result shape",
484
+ );
485
+ }
486
+ const expectedContentType = binding.kind === "json"
487
+ ? "application/json"
488
+ : "text/html";
489
+ if (
490
+ typeof result.status !== "number"
491
+ || !Number.isSafeInteger(result.status)
492
+ || result.status < 100
493
+ || result.status > 599
494
+ || typeof result.contentType !== "string"
495
+ || result.contentType.length > 128
496
+ || (result.contentType !== ""
497
+ && !INSTAGRAM_RESPONSE_MEDIA_TYPE.test(result.contentType))
498
+ ) throw new InstagramProfileBrowserFailure(
499
+ "response-envelope",
500
+ "Instagram profile browser returned a malformed response category",
501
+ );
502
+ if (result.authWall === true) {
503
+ if (
504
+ binding.kind !== "html"
505
+ || result.status !== 200
506
+ || result.contentType !== "text/html"
507
+ || result.bodyBase64 !== null
508
+ || result.bodyBytes !== 0
509
+ || result.bodySha256 !== null
510
+ ) throw new InstagramProfileBrowserFailure(
511
+ "response-envelope",
512
+ "Instagram profile browser returned a malformed authwall envelope",
513
+ );
514
+ throw new InstagramProfileBrowserFailure(
515
+ "authwall",
516
+ "Instagram profile browser reached the signed-out authwall",
517
+ );
518
+ }
519
+ if (result.authWall !== false) {
520
+ throw new InstagramProfileBrowserFailure(
521
+ "response-envelope",
522
+ "Instagram profile browser returned a malformed authwall envelope",
523
+ );
524
+ }
525
+ if (
526
+ result.status !== 200
527
+ || result.contentType !== expectedContentType
528
+ ) {
529
+ if (
530
+ result.bodyBase64 !== null
531
+ || result.bodyBytes !== 0
532
+ || result.bodySha256 !== null
533
+ ) throw new InstagramProfileBrowserFailure(
534
+ "response-envelope",
535
+ "Instagram profile browser returned a malformed rejection envelope",
536
+ );
537
+ throw new InstagramProfileBrowserResponseRejectedError(
538
+ result.status,
539
+ result.contentType,
540
+ );
541
+ }
542
+ return result;
543
+ };
544
+
545
+ return Object.freeze({
546
+ readCurrentViewerHtml: async () => {
547
+ if (state !== "ready") {
548
+ throw new Error("Instagram profile browser viewer read is out of order");
549
+ }
550
+ state = "viewer-pending";
551
+ const maximumBytes = Math.min(
552
+ MAX_VIEWER_HTML_BYTES,
553
+ options.maxOutputBytes,
554
+ );
555
+ const result = await run({
556
+ kind: "html",
557
+ maxBytes: maximumBytes,
558
+ path: "/",
559
+ referrer: INSTAGRAM_ROOT_URL,
560
+ });
561
+ const html = decodedBody(result, maximumBytes);
562
+ state = "viewer";
563
+ return html;
564
+ },
565
+ readProfileJson: async (profileValue: string) => {
566
+ if (state !== "viewer") {
567
+ throw new Error("Instagram profile browser target read is out of order");
568
+ }
569
+ const profile = exactInstagramProfile(profileValue);
570
+ state = "profile-pending";
571
+ const maximumBytes = Math.min(
572
+ MAX_PROFILE_JSON_BYTES,
573
+ options.maxOutputBytes,
574
+ );
575
+ const result = await run({
576
+ kind: "json",
577
+ maxBytes: maximumBytes,
578
+ path: profilePath(profile),
579
+ referrer: `${INSTAGRAM_ORIGIN}/${profile}/`,
580
+ });
581
+ const text = decodedBody(result, maximumBytes);
582
+ let output: unknown;
583
+ try {
584
+ output = JSON.parse(text) as unknown;
585
+ } catch {
586
+ throw new InstagramProfileBrowserFailure(
587
+ "profile-json",
588
+ "Instagram profile browser response was not valid JSON",
589
+ );
590
+ }
591
+ state = "complete";
592
+ return output;
593
+ },
594
+ close: async () => {
595
+ if (closed) return;
596
+ closed = true;
597
+ await finalizeBrowserSession(session);
598
+ },
599
+ });
600
+ }