@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,500 @@
1
+ import {
2
+ createDecipheriv,
3
+ createHash,
4
+ pbkdf2Sync,
5
+ timingSafeEqual,
6
+ } from "node:crypto";
7
+ import {
8
+ copyFileSync,
9
+ existsSync,
10
+ lstatSync,
11
+ mkdtempSync,
12
+ readFileSync,
13
+ realpathSync,
14
+ rmSync,
15
+ } from "node:fs";
16
+ import { tmpdir } from "node:os";
17
+ import { basename, dirname, isAbsolute, join, resolve } from "node:path";
18
+
19
+ import {
20
+ acquireCookieRecords,
21
+ type CookieRecordReader,
22
+ type CookieSelection,
23
+ } from "@hraness/kb/clip/acquire";
24
+ import {
25
+ filterCookies,
26
+ MAX_COOKIE_RECORDS,
27
+ type StrictCookie,
28
+ } from "@hraness/kb/clip/cookies";
29
+
30
+ import type { GhostgetAuth } from "./auth";
31
+ import { listDerivations } from "./derive";
32
+
33
+ const MAX_CHROMIUM_COOKIE_DB_BYTES = 256 * 1024 * 1024;
34
+ const MAX_CHROMIUM_COOKIE_SIDECAR_BYTES = 128 * 1024 * 1024;
35
+ const CHROMIUM_EPOCH_OFFSET_MICROSECONDS = 11_644_473_600_000_000n;
36
+ const MICROSECONDS_PER_SECOND = 1_000_000n;
37
+ const UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
38
+ const MOCK_KEYCHAIN_KEY = pbkdf2Sync(
39
+ "mock_password",
40
+ "saltysalt",
41
+ 1_003,
42
+ 16,
43
+ "sha1",
44
+ );
45
+ const DERIVATION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
46
+
47
+ type ChromiumCookieRow = {
48
+ readonly name: string;
49
+ readonly value: string;
50
+ readonly hostKey: string;
51
+ readonly path: string;
52
+ readonly expiresUtc: string;
53
+ readonly sameSite: number;
54
+ readonly encryptedValue: Uint8Array;
55
+ readonly secure: boolean;
56
+ readonly httpOnly: boolean;
57
+ readonly topFrameSiteKey: string;
58
+ readonly hasCrossSiteAncestor: boolean;
59
+ };
60
+
61
+ function ownedByCurrentUser(uid: bigint): boolean {
62
+ const currentUid = typeof process.getuid === "function" ? process.getuid() : undefined;
63
+ return currentUid === undefined || uid === BigInt(currentUid);
64
+ }
65
+
66
+ type FileIdentity = {
67
+ readonly device: string;
68
+ readonly inode: string;
69
+ readonly byteLength: bigint;
70
+ readonly modifiedAtNanoseconds: bigint;
71
+ };
72
+
73
+ function inspectPrivateRegularFile(
74
+ path: string,
75
+ maximumBytes: number,
76
+ label: string,
77
+ ): FileIdentity {
78
+ let stats: ReturnType<typeof lstatSync>;
79
+ try {
80
+ stats = lstatSync(path, { bigint: true });
81
+ } catch (error) {
82
+ throw new Error(`${label} is unavailable`, { cause: error });
83
+ }
84
+ if (
85
+ !stats.isFile()
86
+ || stats.isSymbolicLink()
87
+ || !ownedByCurrentUser(stats.uid)
88
+ || (stats.mode & 0o077n) !== 0n
89
+ || stats.size < 1n
90
+ || stats.size > BigInt(maximumBytes)
91
+ || realpathSync(path) !== path
92
+ ) throw new Error(`${label} is unsafe`);
93
+ return {
94
+ device: stats.dev.toString(),
95
+ inode: stats.ino.toString(),
96
+ byteLength: stats.size,
97
+ modifiedAtNanoseconds: stats.mtimeNs,
98
+ };
99
+ }
100
+
101
+ function sameFileIdentity(left: FileIdentity, right: FileIdentity): boolean {
102
+ return left.device === right.device
103
+ && left.inode === right.inode
104
+ && left.byteLength === right.byteLength
105
+ && left.modifiedAtNanoseconds === right.modifiedAtNanoseconds;
106
+ }
107
+
108
+ function inspectPrivateDirectory(path: string, label: string): void {
109
+ let stats: ReturnType<typeof lstatSync>;
110
+ try {
111
+ stats = lstatSync(path, { bigint: true });
112
+ } catch (error) {
113
+ throw new Error(`${label} is unavailable`, { cause: error });
114
+ }
115
+ if (
116
+ !stats.isDirectory()
117
+ || stats.isSymbolicLink()
118
+ || !ownedByCurrentUser(stats.uid)
119
+ || (stats.mode & 0o777n) !== 0o700n
120
+ || realpathSync(path) !== path
121
+ ) throw new Error(`${label} is unsafe`);
122
+ }
123
+
124
+ /**
125
+ * Select only the exact ready profile layout created inside Ghostget's private
126
+ * derivation state. A path that merely resembles that layout cannot opt an
127
+ * arbitrary Chromium profile into the fixed mock-keychain credential.
128
+ */
129
+ function managedDerivationChromiumCookieDatabase(
130
+ browserProfile: string,
131
+ cookieProfile: string | undefined,
132
+ ): string | null {
133
+ if (!isAbsolute(browserProfile) || cookieProfile === undefined || !isAbsolute(cookieProfile)) {
134
+ return null;
135
+ }
136
+ const profile = resolve(browserProfile);
137
+ if (basename(profile) !== "profile") return null;
138
+ const directory = dirname(profile);
139
+ const id = basename(directory);
140
+ const derivationsDirectory = dirname(directory);
141
+ if (
142
+ !DERIVATION_ID_PATTERN.test(id)
143
+ || basename(derivationsDirectory) !== "derivations"
144
+ || resolve(cookieProfile) !== join(profile, "Default")
145
+ ) return null;
146
+
147
+ const sessionPath = join(directory, "session.json");
148
+ const before = inspectPrivateRegularFile(
149
+ sessionPath,
150
+ 64 * 1024,
151
+ "managed Chromium derivation metadata",
152
+ );
153
+ const summaries = listDerivations({
154
+ GHOSTGET_STATE_HOME: dirname(derivationsDirectory),
155
+ });
156
+ const summary = summaries.find((candidate) => candidate.id === id);
157
+ if (summary === undefined || summary.invalid === true || summary.ready !== true) {
158
+ throw new Error("managed Chromium profile is not bound to one ready Ghostget derivation");
159
+ }
160
+ let metadata: unknown;
161
+ try {
162
+ metadata = JSON.parse(readFileSync(sessionPath, "utf8")) as unknown;
163
+ } catch (error) {
164
+ throw new Error("managed Chromium derivation metadata is malformed", { cause: error });
165
+ }
166
+ const after = inspectPrivateRegularFile(
167
+ sessionPath,
168
+ 64 * 1024,
169
+ "managed Chromium derivation metadata",
170
+ );
171
+ if (!sameFileIdentity(before, after)) {
172
+ throw new Error("managed Chromium derivation metadata changed during validation");
173
+ }
174
+ if (
175
+ typeof metadata !== "object"
176
+ || metadata === null
177
+ || Array.isArray(metadata)
178
+ || (metadata as Record<string, unknown>).id !== id
179
+ || (metadata as Record<string, unknown>).directory !== directory
180
+ || (metadata as Record<string, unknown>).profilePath !== profile
181
+ ) throw new Error("managed Chromium profile does not match its derivation metadata");
182
+ inspectPrivateDirectory(profile, "managed Chromium profile");
183
+ inspectPrivateDirectory(join(profile, "Default"), "managed Chromium Default profile");
184
+ return join(profile, "Default", "Cookies");
185
+ }
186
+
187
+ function snapshotPrivateRegularFile(
188
+ source: string,
189
+ target: string,
190
+ maximumBytes: number,
191
+ label: string,
192
+ optional = false,
193
+ ): void {
194
+ if (optional && !existsSync(source)) return;
195
+ const before = inspectPrivateRegularFile(source, maximumBytes, label);
196
+ try {
197
+ copyFileSync(source, target);
198
+ } catch (error) {
199
+ throw new Error(`${label} could not be snapshotted`, { cause: error });
200
+ }
201
+ const after = inspectPrivateRegularFile(source, maximumBytes, label);
202
+ if (!sameFileIdentity(before, after)) {
203
+ throw new Error(`${label} changed while it was being snapshotted`);
204
+ }
205
+ }
206
+
207
+ function chromiumHostCandidates(hostname: string): readonly string[] {
208
+ const parts = hostname.toLowerCase().split(".").filter(Boolean);
209
+ if (parts.length < 2) return [hostname.toLowerCase()];
210
+ const candidates = new Set<string>([parts.join(".")]);
211
+ for (let index = 0; index <= parts.length - 2; index += 1) {
212
+ candidates.add(`.${parts.slice(index).join(".")}`);
213
+ }
214
+ return [...candidates];
215
+ }
216
+
217
+ function booleanColumn(value: unknown): boolean | null {
218
+ if (value === true || value === 1 || value === 1n || value === "1") return true;
219
+ if (value === false || value === 0 || value === 0n || value === "0") return false;
220
+ return null;
221
+ }
222
+
223
+ function integerColumn(value: unknown): number | null {
224
+ if (typeof value === "number" && Number.isSafeInteger(value)) return value;
225
+ if (typeof value === "bigint") {
226
+ const parsed = Number(value);
227
+ return Number.isSafeInteger(parsed) ? parsed : null;
228
+ }
229
+ if (typeof value === "string" && /^-?[0-9]+$/u.test(value)) {
230
+ const parsed = Number(value);
231
+ return Number.isSafeInteger(parsed) ? parsed : null;
232
+ }
233
+ return null;
234
+ }
235
+
236
+ function parseChromiumCookieRow(value: unknown): ChromiumCookieRow | null {
237
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
238
+ const row = value as Record<string, unknown>;
239
+ const secure = booleanColumn(row.is_secure);
240
+ const httpOnly = booleanColumn(row.is_httponly);
241
+ const hasCrossSiteAncestor = booleanColumn(row.has_cross_site_ancestor);
242
+ const sameSite = integerColumn(row.samesite);
243
+ if (
244
+ typeof row.name !== "string"
245
+ || typeof row.value !== "string"
246
+ || typeof row.host_key !== "string"
247
+ || typeof row.path !== "string"
248
+ || typeof row.expires_utc !== "string"
249
+ || !(row.encrypted_value instanceof Uint8Array)
250
+ || typeof row.top_frame_site_key !== "string"
251
+ || secure === null
252
+ || httpOnly === null
253
+ || hasCrossSiteAncestor === null
254
+ || sameSite === null
255
+ ) return null;
256
+ return {
257
+ name: row.name,
258
+ value: row.value,
259
+ hostKey: row.host_key,
260
+ path: row.path,
261
+ expiresUtc: row.expires_utc,
262
+ sameSite,
263
+ encryptedValue: row.encrypted_value,
264
+ secure,
265
+ httpOnly,
266
+ topFrameSiteKey: row.top_frame_site_key,
267
+ hasCrossSiteAncestor,
268
+ };
269
+ }
270
+
271
+ function removePkcs7Padding(value: Buffer): Buffer | null {
272
+ if (value.length === 0) return null;
273
+ const padding = value[value.length - 1] ?? 0;
274
+ if (padding < 1 || padding > 16 || padding > value.length) return null;
275
+ for (let index = value.length - padding; index < value.length; index += 1) {
276
+ if (value[index] !== padding) return null;
277
+ }
278
+ return value.subarray(0, value.length - padding);
279
+ }
280
+
281
+ function decryptMockKeychainCookie(
282
+ encryptedValue: Uint8Array,
283
+ hostKey: string,
284
+ requireHostHash: boolean,
285
+ ): string | null {
286
+ const bytes = Buffer.from(encryptedValue);
287
+ if (bytes.length < 19) return null;
288
+ const prefix = bytes.subarray(0, 3).toString("ascii");
289
+ if (prefix !== "v10" && prefix !== "v11") return null;
290
+ try {
291
+ const decipher = createDecipheriv(
292
+ "aes-128-cbc",
293
+ MOCK_KEYCHAIN_KEY,
294
+ Buffer.alloc(16, 0x20),
295
+ );
296
+ decipher.setAutoPadding(false);
297
+ const padded = Buffer.concat([
298
+ decipher.update(bytes.subarray(3)),
299
+ decipher.final(),
300
+ ]);
301
+ const plaintext = removePkcs7Padding(padded);
302
+ if (plaintext === null) return null;
303
+ let value = plaintext;
304
+ if (requireHostHash) {
305
+ if (plaintext.length < 32) return null;
306
+ const expected = createHash("sha256").update(hostKey, "utf8").digest();
307
+ if (!timingSafeEqual(plaintext.subarray(0, 32), expected)) return null;
308
+ value = plaintext.subarray(32);
309
+ }
310
+ return UTF8_DECODER.decode(value);
311
+ } catch {
312
+ return null;
313
+ }
314
+ }
315
+
316
+ function chromiumExpiry(value: string): number | null {
317
+ if (!/^[0-9]+$/u.test(value)) return null;
318
+ const microseconds = BigInt(value);
319
+ if (microseconds === 0n) return 0;
320
+ if (microseconds <= CHROMIUM_EPOCH_OFFSET_MICROSECONDS) return null;
321
+ const seconds = (microseconds - CHROMIUM_EPOCH_OFFSET_MICROSECONDS)
322
+ / MICROSECONDS_PER_SECOND;
323
+ const parsed = Number(seconds);
324
+ return Number.isSafeInteger(parsed) ? parsed : null;
325
+ }
326
+
327
+ function chromiumSameSite(value: number): StrictCookie["sameSite"] {
328
+ if (value === 2) return "Strict";
329
+ if (value === 1) return "Lax";
330
+ if (value === 0) return "None";
331
+ return null;
332
+ }
333
+
334
+ async function readManagedChromiumCookies(
335
+ databasePath: string,
336
+ target: URL,
337
+ ): Promise<{ readonly cookies: readonly StrictCookie[]; readonly warnings: readonly string[] }> {
338
+ const snapshotDirectory = mkdtempSync(join(tmpdir(), "wrench-managed-chromium-"));
339
+ const snapshotPath = join(snapshotDirectory, "Cookies");
340
+ try {
341
+ snapshotPrivateRegularFile(
342
+ databasePath,
343
+ snapshotPath,
344
+ MAX_CHROMIUM_COOKIE_DB_BYTES,
345
+ "managed Chromium cookie database",
346
+ );
347
+ snapshotPrivateRegularFile(
348
+ `${databasePath}-wal`,
349
+ `${snapshotPath}-wal`,
350
+ MAX_CHROMIUM_COOKIE_SIDECAR_BYTES,
351
+ "managed Chromium cookie WAL",
352
+ true,
353
+ );
354
+ snapshotPrivateRegularFile(
355
+ `${databasePath}-shm`,
356
+ `${snapshotPath}-shm`,
357
+ MAX_CHROMIUM_COOKIE_SIDECAR_BYTES,
358
+ "managed Chromium cookie shared-memory file",
359
+ true,
360
+ );
361
+
362
+ const { Database } = await import("bun:sqlite");
363
+ const database = new Database(snapshotPath, { readonly: true });
364
+ try {
365
+ const meta = database.query("SELECT value FROM meta WHERE key = ? LIMIT 1").get("version");
366
+ const metaVersion = typeof meta === "object" && meta !== null
367
+ ? integerColumn((meta as Record<string, unknown>).value)
368
+ : null;
369
+ if (metaVersion === null) throw new Error("managed Chromium cookie database has no valid schema version");
370
+ const hostCandidates = chromiumHostCandidates(target.hostname);
371
+ const placeholders = hostCandidates.map(() => "?").join(", ");
372
+ const rows = database.query(
373
+ "SELECT name, value, host_key, path, CAST(expires_utc AS TEXT) AS expires_utc, "
374
+ + "samesite, encrypted_value, is_secure, is_httponly, top_frame_site_key, "
375
+ + "has_cross_site_ancestor FROM cookies "
376
+ + `WHERE host_key IN (${placeholders}) ORDER BY expires_utc DESC LIMIT ?`,
377
+ ).all(...hostCandidates, MAX_COOKIE_RECORDS + 1);
378
+ if (rows.length > MAX_COOKIE_RECORDS) {
379
+ throw new Error("managed Chromium cookie selection exceeded its record bound");
380
+ }
381
+
382
+ let malformed = 0;
383
+ let partitioned = 0;
384
+ const candidates = new Map<string, Readonly<Record<string, unknown>>>();
385
+ for (const value of rows) {
386
+ const row = parseChromiumCookieRow(value);
387
+ if (row === null) {
388
+ malformed += 1;
389
+ continue;
390
+ }
391
+ if (row.topFrameSiteKey.trim() !== "") {
392
+ partitioned += 1;
393
+ continue;
394
+ }
395
+ const cookieValue = row.value.length > 0
396
+ ? row.value
397
+ : decryptMockKeychainCookie(
398
+ row.encryptedValue,
399
+ row.hostKey,
400
+ metaVersion >= 24,
401
+ );
402
+ const expires = chromiumExpiry(row.expiresUtc);
403
+ if (cookieValue === null || expires === null) {
404
+ malformed += 1;
405
+ continue;
406
+ }
407
+ const hostOnly = !row.hostKey.startsWith(".");
408
+ const domain = hostOnly ? row.hostKey : row.hostKey.slice(1);
409
+ const key = `${domain}\0${hostOnly ? "host" : "domain"}\0${row.path || "/"}\0${row.name}`;
410
+ if (candidates.has(key)) continue;
411
+ candidates.set(key, {
412
+ name: row.name,
413
+ value: cookieValue,
414
+ domain,
415
+ hostOnly,
416
+ path: row.path || "/",
417
+ secure: row.secure,
418
+ httpOnly: row.httpOnly,
419
+ sameSite: chromiumSameSite(row.sameSite),
420
+ expires,
421
+ top_frame_site_key: "",
422
+ has_cross_site_ancestor: false,
423
+ });
424
+ }
425
+ const filtered = filterCookies([...candidates.values()], target);
426
+ const rejected = malformed + filtered.rejected;
427
+ if (filtered.cookies.length === 0) {
428
+ throw new Error(rejected === 0
429
+ ? "no matching cookies were found in the managed Chromium profile"
430
+ : "the managed Chromium profile contained no usable origin-scoped cookies");
431
+ }
432
+ const warnings: string[] = [];
433
+ if (rejected > 0) warnings.push(`Ignored ${rejected} malformed, expired, or out-of-scope managed Chromium cookie record(s).`);
434
+ if (partitioned > 0) warnings.push(`Excluded ${partitioned} partitioned managed Chromium cookie record(s).`);
435
+ return { cookies: filtered.cookies, warnings };
436
+ } finally {
437
+ database.close();
438
+ }
439
+ } finally {
440
+ rmSync(snapshotDirectory, { recursive: true, force: true });
441
+ }
442
+ }
443
+
444
+ export function webSessionCookieSelection(
445
+ auth: GhostgetAuth,
446
+ timeoutMs: number,
447
+ ): CookieSelection {
448
+ if (auth.kind === "cookie-source") {
449
+ return {
450
+ cookieSources: [auth.source],
451
+ cookiesFile: undefined,
452
+ cookieProfile: auth.profile,
453
+ timeoutMs,
454
+ requireExplicitCookieScope: true,
455
+ };
456
+ }
457
+ if (auth.kind === "cookies-file") {
458
+ return {
459
+ cookieSources: [],
460
+ cookiesFile: auth.path,
461
+ cookieProfile: undefined,
462
+ timeoutMs,
463
+ requireExplicitCookieScope: true,
464
+ };
465
+ }
466
+ if (auth.kind === "browser-profile" && auth.cookieSource !== undefined) {
467
+ return {
468
+ cookieSources: [auth.cookieSource],
469
+ cookiesFile: undefined,
470
+ cookieProfile: auth.cookieProfile,
471
+ timeoutMs,
472
+ requireExplicitCookieScope: true,
473
+ };
474
+ }
475
+ if (auth.kind === "browser-profile") {
476
+ throw new Error("authenticated web API execution requires the browser auth locator to name a cookie source");
477
+ }
478
+ throw new Error("authenticated web API execution requires browser-session or cookie auth");
479
+ }
480
+
481
+ export async function acquireWebSessionCookieRecords(
482
+ auth: GhostgetAuth,
483
+ target: URL,
484
+ timeoutMs: number,
485
+ reader: CookieRecordReader = acquireCookieRecords,
486
+ ): Promise<{ readonly cookies: readonly StrictCookie[]; readonly warnings: readonly string[] }> {
487
+ if (
488
+ auth.kind === "browser-profile"
489
+ && auth.cookieSource === "chrome"
490
+ ) {
491
+ const databasePath = managedDerivationChromiumCookieDatabase(
492
+ auth.profile,
493
+ auth.cookieProfile,
494
+ );
495
+ if (databasePath !== null) {
496
+ return readManagedChromiumCookies(databasePath, target);
497
+ }
498
+ }
499
+ return reader(webSessionCookieSelection(auth, timeoutMs), target);
500
+ }