@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,693 @@
1
+ #!/usr/bin/env bun
2
+ import {
3
+ closeSync,
4
+ constants,
5
+ fstatSync,
6
+ lstatSync,
7
+ openSync,
8
+ readSync,
9
+ } from "node:fs";
10
+ import { isAbsolute } from "node:path";
11
+
12
+ import {
13
+ agentBrowserCommand,
14
+ isolatedEnvironment,
15
+ parseLastJsonWithExactLaunchHashes,
16
+ runCommand,
17
+ } from "./browser";
18
+ import {
19
+ derivationActionPolicy,
20
+ derivationBrowserConfig,
21
+ derivationConfirmedPolicyActions,
22
+ } from "./derive";
23
+ import {
24
+ processOwnerStatus,
25
+ type ProcessOwnerIdentity,
26
+ } from "./process-identity";
27
+
28
+ const MAX_REQUEST_BYTES = 10 * 1024 * 1024;
29
+ const maximumU64 = (1n << 64n) - 1n;
30
+
31
+ type Identity = { readonly device: string; readonly inode: string };
32
+ export type BrowserIdentity = {
33
+ readonly engine: "chrome";
34
+ readonly launchHash: string;
35
+ };
36
+
37
+ export type BrowserPin = {
38
+ readonly sessionName: string;
39
+ readonly cdpUrl: string;
40
+ readonly browserIdentity: BrowserIdentity | null;
41
+ readonly confirmationAction: string | null;
42
+ readonly daemonOwner: ProcessOwnerIdentity | null;
43
+ };
44
+
45
+ type CodeOwnedBrowserRequest =
46
+ | "none"
47
+ | "initial-contained-launch"
48
+ | "readiness-context"
49
+ | "pin-context"
50
+ | "pinned-batch"
51
+ | "pinned-confirm"
52
+ | "profile-confirm";
53
+
54
+ type Request = {
55
+ readonly schemaVersion: 1;
56
+ readonly requestId: string;
57
+ readonly expectedDirectory: Identity;
58
+ readonly socketDirectory: string;
59
+ readonly expectedSocketDirectory: Identity;
60
+ readonly ownerSessionName: string;
61
+ readonly allowRemoteActions: boolean;
62
+ readonly allowFixtureUpload: boolean;
63
+ readonly guardedBrowserConfig: boolean;
64
+ readonly codeOwnedBrowserRequest: CodeOwnedBrowserRequest;
65
+ readonly browserPin: BrowserPin | null;
66
+ readonly timeoutMs: number;
67
+ readonly maxOutputBytes: number;
68
+ readonly arguments: readonly string[];
69
+ readonly browserStdin: string | null;
70
+ };
71
+
72
+ function isRecord(value: unknown): value is Record<string, unknown> {
73
+ return typeof value === "object" && value !== null && !Array.isArray(value);
74
+ }
75
+
76
+ function exactKeys(value: Record<string, unknown>, expected: readonly string[]): boolean {
77
+ const actual = Object.keys(value).sort();
78
+ const sorted = [...expected].sort();
79
+ return actual.length === sorted.length && actual.every((key, index) => key === sorted[index]);
80
+ }
81
+
82
+ function parseIdentity(value: unknown): Identity {
83
+ if (
84
+ !isRecord(value)
85
+ || !exactKeys(value, ["device", "inode"])
86
+ || typeof value.device !== "string"
87
+ || !/^\d{1,40}$/u.test(value.device)
88
+ || typeof value.inode !== "string"
89
+ || !/^\d{1,40}$/u.test(value.inode)
90
+ ) throw new Error("expected derivation directory identity is invalid");
91
+ return { device: value.device, inode: value.inode };
92
+ }
93
+
94
+ function parseBrowserIdentity(value: unknown): BrowserIdentity {
95
+ if (
96
+ !isRecord(value)
97
+ || !exactKeys(value, ["engine", "launchHash"])
98
+ || value.engine !== "chrome"
99
+ || typeof value.launchHash !== "string"
100
+ || !/^(?:0|[1-9][0-9]{0,19})$/u.test(value.launchHash)
101
+ || BigInt(value.launchHash) > maximumU64
102
+ ) throw new Error("pinned derivation browser identity is invalid");
103
+ return { engine: "chrome", launchHash: value.launchHash };
104
+ }
105
+
106
+ function parseDaemonOwner(value: unknown): ProcessOwnerIdentity {
107
+ if (
108
+ !isRecord(value)
109
+ || !exactKeys(value, ["bootId", "pid", "processStartId"])
110
+ || !Number.isSafeInteger(value.pid)
111
+ || (value.pid as number) < 1
112
+ || typeof value.bootId !== "string"
113
+ || value.bootId.length < 1
114
+ || value.bootId.length > 512
115
+ || /[\u0000-\u001f\u007f]/u.test(value.bootId)
116
+ || typeof value.processStartId !== "string"
117
+ || value.processStartId.length < 1
118
+ || value.processStartId.length > 512
119
+ || /[\u0000-\u001f\u007f]/u.test(value.processStartId)
120
+ ) throw new Error("pinned derivation daemon owner is invalid");
121
+ return {
122
+ pid: value.pid as number,
123
+ bootId: value.bootId,
124
+ processStartId: value.processStartId,
125
+ };
126
+ }
127
+
128
+ function parseBrowserPin(value: unknown): BrowserPin {
129
+ if (
130
+ !isRecord(value)
131
+ || !exactKeys(value, ["browserIdentity", "cdpUrl", "confirmationAction", "daemonOwner", "sessionName"])
132
+ || typeof value.sessionName !== "string"
133
+ || !/^io-derive-pin-[0-9a-f]{12}$/u.test(value.sessionName)
134
+ || typeof value.cdpUrl !== "string"
135
+ || value.cdpUrl.length > 4_096
136
+ ) throw new Error("pinned derivation browser descriptor is invalid");
137
+ let cdpUrl: URL;
138
+ try {
139
+ cdpUrl = new URL(value.cdpUrl);
140
+ } catch {
141
+ throw new Error("pinned derivation browser descriptor is invalid");
142
+ }
143
+ if (
144
+ cdpUrl.protocol !== "ws:"
145
+ || (cdpUrl.hostname !== "127.0.0.1" && cdpUrl.hostname !== "[::1]" && cdpUrl.hostname !== "localhost")
146
+ || cdpUrl.port === ""
147
+ || cdpUrl.username !== ""
148
+ || cdpUrl.password !== ""
149
+ || cdpUrl.search !== ""
150
+ || cdpUrl.hash !== ""
151
+ || !/^\/devtools\/browser\/[A-Za-z0-9_-]{1,256}$/u.test(cdpUrl.pathname)
152
+ ) throw new Error("pinned derivation browser descriptor is invalid");
153
+ return {
154
+ sessionName: value.sessionName,
155
+ cdpUrl: cdpUrl.href,
156
+ browserIdentity: value.browserIdentity === null ? null : parseBrowserIdentity(value.browserIdentity),
157
+ confirmationAction: value.confirmationAction === null
158
+ ? null
159
+ : typeof value.confirmationAction === "string"
160
+ && derivationConfirmedPolicyActions.includes(
161
+ value.confirmationAction as (typeof derivationConfirmedPolicyActions)[number],
162
+ )
163
+ ? value.confirmationAction
164
+ : (() => { throw new Error("pinned derivation confirmation action is invalid"); })(),
165
+ daemonOwner: value.daemonOwner === null ? null : parseDaemonOwner(value.daemonOwner),
166
+ };
167
+ }
168
+
169
+ function pinnedBrowserStdin(kind: CodeOwnedBrowserRequest): string | null {
170
+ if (kind === "none") return null;
171
+ if (kind === "initial-contained-launch") return JSON.stringify([["open", "about:blank"]]);
172
+ if (kind === "readiness-context") return JSON.stringify([["get", "cdp-url"], ["get", "url"]]);
173
+ if (kind === "pin-context") return JSON.stringify([["get", "cdp-url"], ["get", "url"]]);
174
+ return null;
175
+ }
176
+
177
+ function parsePinnedCommands(value: string | null): readonly (readonly string[])[] {
178
+ if (value === null) throw new Error("pinned derivation browser commands are invalid");
179
+ let parsed: unknown;
180
+ try {
181
+ parsed = JSON.parse(value) as unknown;
182
+ } catch {
183
+ throw new Error("pinned derivation browser commands are invalid");
184
+ }
185
+ if (
186
+ !Array.isArray(parsed)
187
+ || parsed.length < 1
188
+ || parsed.length > 500
189
+ || parsed.some((command) => (
190
+ !Array.isArray(command)
191
+ || command.length < 1
192
+ || command.length > 100
193
+ || command.some((argument) => typeof argument !== "string")
194
+ ))
195
+ ) throw new Error("pinned derivation browser commands are invalid");
196
+ return parsed as readonly (readonly string[])[];
197
+ }
198
+
199
+ function parseRequest(value: unknown): Request {
200
+ if (
201
+ !isRecord(value)
202
+ || !exactKeys(value, [
203
+ "schemaVersion",
204
+ "requestId",
205
+ "expectedDirectory",
206
+ "socketDirectory",
207
+ "expectedSocketDirectory",
208
+ "ownerSessionName",
209
+ "allowRemoteActions",
210
+ "allowFixtureUpload",
211
+ "guardedBrowserConfig",
212
+ "codeOwnedBrowserRequest",
213
+ "browserPin",
214
+ "timeoutMs",
215
+ "maxOutputBytes",
216
+ "arguments",
217
+ "browserStdin",
218
+ ])
219
+ || value.schemaVersion !== 1
220
+ || typeof value.requestId !== "string"
221
+ || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(value.requestId)
222
+ || typeof value.socketDirectory !== "string"
223
+ || !isAbsolute(value.socketDirectory)
224
+ || value.socketDirectory.length > 4_096
225
+ || value.socketDirectory.includes("\u0000")
226
+ || typeof value.ownerSessionName !== "string"
227
+ || !/^io-derive-[a-f0-9]{12}$/u.test(value.ownerSessionName)
228
+ || typeof value.allowRemoteActions !== "boolean"
229
+ || typeof value.allowFixtureUpload !== "boolean"
230
+ || typeof value.guardedBrowserConfig !== "boolean"
231
+ || (value.allowFixtureUpload && !value.allowRemoteActions)
232
+ || !["none", "initial-contained-launch", "readiness-context", "pin-context", "pinned-batch", "pinned-confirm", "profile-confirm"].includes(
233
+ typeof value.codeOwnedBrowserRequest === "string" ? value.codeOwnedBrowserRequest : "",
234
+ )
235
+ || typeof value.timeoutMs !== "number"
236
+ || !Number.isSafeInteger(value.timeoutMs)
237
+ || value.timeoutMs < 1
238
+ || value.timeoutMs > 120_000
239
+ || typeof value.maxOutputBytes !== "number"
240
+ || !Number.isSafeInteger(value.maxOutputBytes)
241
+ || value.maxOutputBytes < 1
242
+ || value.maxOutputBytes > 10 * 1024 * 1024
243
+ || !Array.isArray(value.arguments)
244
+ || value.arguments.length < 1
245
+ || value.arguments.length > 200
246
+ || value.arguments.some((argument) => (
247
+ typeof argument !== "string"
248
+ || argument.length > 64 * 1024
249
+ || argument.includes("\u0000")
250
+ || argument === "--config"
251
+ || argument.startsWith("--config=")
252
+ || argument === "--action-policy"
253
+ || argument.startsWith("--action-policy=")
254
+ ))
255
+ || (value.browserStdin !== null && (typeof value.browserStdin !== "string" || Buffer.byteLength(value.browserStdin, "utf8") > 5 * 1024 * 1024))
256
+ || (
257
+ value.codeOwnedBrowserRequest !== "none"
258
+ && value.codeOwnedBrowserRequest !== "pinned-confirm"
259
+ && value.codeOwnedBrowserRequest !== "profile-confirm"
260
+ && (
261
+ (
262
+ value.codeOwnedBrowserRequest !== "pinned-batch"
263
+ && value.browserStdin !== pinnedBrowserStdin(value.codeOwnedBrowserRequest as CodeOwnedBrowserRequest)
264
+ )
265
+ || value.arguments.length < 3
266
+ || value.arguments.at(-3) !== "batch"
267
+ || value.arguments.at(-2) !== "--bail"
268
+ || value.arguments.at(-1) !== "--json"
269
+ )
270
+ )
271
+ ) throw new Error("derivation command request is invalid");
272
+ const codeOwnedBrowserRequest = value.codeOwnedBrowserRequest as CodeOwnedBrowserRequest;
273
+ const browserPin = value.browserPin === null ? null : parseBrowserPin(value.browserPin);
274
+ if (
275
+ (codeOwnedBrowserRequest === "initial-contained-launch" && value.guardedBrowserConfig)
276
+ || (["readiness-context", "pin-context", "pinned-batch", "pinned-confirm"].includes(codeOwnedBrowserRequest) && !value.guardedBrowserConfig)
277
+ || (codeOwnedBrowserRequest === "profile-confirm" && value.guardedBrowserConfig)
278
+ || (
279
+ codeOwnedBrowserRequest === "readiness-context"
280
+ && JSON.stringify(value.arguments) !== JSON.stringify([
281
+ "--session",
282
+ value.ownerSessionName,
283
+ "--content-boundaries",
284
+ "--max-output",
285
+ String(5 * 1024 * 1024),
286
+ "batch",
287
+ "--bail",
288
+ "--json",
289
+ ])
290
+ )
291
+ || (["pin-context", "pinned-batch", "pinned-confirm"].includes(codeOwnedBrowserRequest)) !== (browserPin !== null)
292
+ || (
293
+ browserPin !== null
294
+ && browserPin.sessionName !== value.ownerSessionName.replace(
295
+ /^io-derive-/u,
296
+ "io-derive-pin-",
297
+ )
298
+ )
299
+ || (codeOwnedBrowserRequest === "pin-context" && (browserPin === null || browserPin.browserIdentity !== null || browserPin.confirmationAction !== null || browserPin.daemonOwner !== null))
300
+ || (codeOwnedBrowserRequest === "pinned-batch" && (browserPin === null || browserPin.browserIdentity === null || browserPin.confirmationAction !== null || browserPin.daemonOwner !== null))
301
+ || (codeOwnedBrowserRequest === "pinned-batch" && value.browserStdin === null)
302
+ || (codeOwnedBrowserRequest === "pinned-batch" && parsePinnedCommands(value.browserStdin).length !== 1)
303
+ || (codeOwnedBrowserRequest === "pinned-confirm" && (browserPin === null || browserPin.browserIdentity === null || browserPin.confirmationAction === null || browserPin.daemonOwner === null))
304
+ || (
305
+ codeOwnedBrowserRequest === "profile-confirm"
306
+ && (
307
+ browserPin !== null
308
+ || !value.allowRemoteActions
309
+ || value.browserStdin !== null
310
+ || value.arguments.length !== 3
311
+ || value.arguments[0] !== "confirm"
312
+ || typeof value.arguments[1] !== "string"
313
+ || !/^r\d{1,6}$/u.test(value.arguments[1])
314
+ || value.arguments[2] !== "--json"
315
+ )
316
+ )
317
+ || (
318
+ codeOwnedBrowserRequest === "pinned-confirm"
319
+ && (
320
+ value.browserStdin !== null
321
+ || value.arguments.length !== 3
322
+ || value.arguments[0] !== "confirm"
323
+ || typeof value.arguments[1] !== "string"
324
+ || !/^r\d{1,6}$/u.test(value.arguments[1])
325
+ || value.arguments[2] !== "--json"
326
+ )
327
+ )
328
+ || (
329
+ browserPin !== null
330
+ && codeOwnedBrowserRequest !== "pinned-confirm"
331
+ && (
332
+ value.arguments.length !== 3
333
+ || value.arguments[0] !== "batch"
334
+ || value.arguments[1] !== "--bail"
335
+ || value.arguments[2] !== "--json"
336
+ )
337
+ )
338
+ ) throw new Error("derivation command request is invalid");
339
+ return {
340
+ schemaVersion: 1,
341
+ requestId: value.requestId,
342
+ expectedDirectory: parseIdentity(value.expectedDirectory),
343
+ socketDirectory: value.socketDirectory,
344
+ expectedSocketDirectory: parseIdentity(value.expectedSocketDirectory),
345
+ ownerSessionName: value.ownerSessionName,
346
+ allowRemoteActions: value.allowRemoteActions,
347
+ allowFixtureUpload: value.allowFixtureUpload,
348
+ guardedBrowserConfig: value.guardedBrowserConfig,
349
+ codeOwnedBrowserRequest,
350
+ browserPin,
351
+ timeoutMs: value.timeoutMs,
352
+ maxOutputBytes: value.maxOutputBytes,
353
+ arguments: value.arguments.map((argument) => String(argument)),
354
+ browserStdin: value.browserStdin,
355
+ };
356
+ }
357
+
358
+ function pinnedLifecycle(
359
+ value: unknown,
360
+ expected: BrowserIdentity | null,
361
+ ): BrowserIdentity {
362
+ if (!isRecord(value) || !exactKeys(value, [
363
+ "effectiveLaunch",
364
+ "launched",
365
+ "relaunchedBrowser",
366
+ "restartedBackground",
367
+ "restoreStatus",
368
+ "reused",
369
+ "saveStatus",
370
+ ])) throw new Error("pinned derivation browser lifecycle changed shape");
371
+ const effectiveLaunch = value.effectiveLaunch;
372
+ if (
373
+ !isRecord(effectiveLaunch)
374
+ || !exactKeys(effectiveLaunch, ["browserLaunched", "engine", "launchHash"])
375
+ ) throw new Error("pinned derivation browser lifecycle changed shape");
376
+ const identity = parseBrowserIdentity({
377
+ engine: effectiveLaunch.engine,
378
+ launchHash: effectiveLaunch.launchHash,
379
+ });
380
+ if (
381
+ effectiveLaunch.browserLaunched !== true
382
+ || value.launched !== false
383
+ || value.relaunchedBrowser !== false
384
+ || value.restartedBackground !== false
385
+ || value.restoreStatus !== "not_configured"
386
+ || typeof value.reused !== "boolean"
387
+ || value.saveStatus !== "not_attempted"
388
+ || (
389
+ expected !== null
390
+ && (identity.engine !== expected.engine || identity.launchHash !== expected.launchHash)
391
+ )
392
+ ) throw new Error("pinned derivation browser lifecycle changed identity or was relaunched");
393
+ return identity;
394
+ }
395
+
396
+ export function validatePinnedDerivationBrowserOutput(
397
+ value: unknown,
398
+ commands: readonly (readonly string[])[],
399
+ pin: BrowserPin,
400
+ ): BrowserIdentity {
401
+ if (!Array.isArray(value) || value.length !== commands.length) {
402
+ throw new Error("pinned derivation browser result changed shape");
403
+ }
404
+ let observed: BrowserIdentity | null = null;
405
+ for (let index = 0; index < value.length; index += 1) {
406
+ const entry = value[index];
407
+ const command = commands[index];
408
+ if (
409
+ !isRecord(entry)
410
+ || !exactKeys(entry, ["command", "error", "result", "success"])
411
+ || JSON.stringify(entry.command) !== JSON.stringify(command)
412
+ || entry.success !== true
413
+ || entry.error !== null
414
+ || !isRecord(entry.result)
415
+ || !("lifecycle" in entry.result)
416
+ ) throw new Error("pinned derivation browser result changed shape");
417
+ const identity = pinnedLifecycle(entry.result.lifecycle, pin.browserIdentity);
418
+ if (
419
+ observed !== null
420
+ && (observed.engine !== identity.engine || observed.launchHash !== identity.launchHash)
421
+ ) throw new Error("pinned derivation browser lifecycle changed within its batch");
422
+ observed = identity;
423
+ if (
424
+ command?.length === 2
425
+ && command[0] === "get"
426
+ && command[1] === "cdp-url"
427
+ && entry.result.cdpUrl !== pin.cdpUrl
428
+ ) throw new Error("pinned derivation browser returned a different private CDP URL");
429
+ }
430
+ if (observed === null) throw new Error("pinned derivation browser result changed shape");
431
+ return observed;
432
+ }
433
+
434
+ export type PinnedDerivationConfirmation = {
435
+ readonly action: string;
436
+ readonly confirmationId: string;
437
+ };
438
+
439
+ export function parsePinnedDerivationConfirmation(
440
+ value: unknown,
441
+ command: readonly string[],
442
+ ): PinnedDerivationConfirmation | null {
443
+ if (!Array.isArray(value) || value.length !== 1) {
444
+ throw new Error("pinned derivation browser result changed shape");
445
+ }
446
+ const entry = value[0];
447
+ if (
448
+ !isRecord(entry)
449
+ || !exactKeys(entry, ["command", "error", "result", "success"])
450
+ || JSON.stringify(entry.command) !== JSON.stringify(command)
451
+ || entry.success !== true
452
+ || entry.error !== null
453
+ || !isRecord(entry.result)
454
+ ) throw new Error("pinned derivation browser result changed shape");
455
+ if ("lifecycle" in entry.result) return null;
456
+ if (
457
+ !exactKeys(entry.result, ["action", "confirmation_id", "confirmation_required"])
458
+ || entry.result.confirmation_required !== true
459
+ || typeof entry.result.confirmation_id !== "string"
460
+ || !/^r\d{1,6}$/u.test(entry.result.confirmation_id)
461
+ || typeof entry.result.action !== "string"
462
+ || !derivationConfirmedPolicyActions.includes(
463
+ entry.result.action as (typeof derivationConfirmedPolicyActions)[number],
464
+ )
465
+ ) throw new Error("pinned derivation browser confirmation changed shape");
466
+ return {
467
+ action: entry.result.action,
468
+ confirmationId: entry.result.confirmation_id,
469
+ };
470
+ }
471
+
472
+ export function parsePinnedDerivationConfirmationResult(
473
+ value: unknown,
474
+ confirmation: PinnedDerivationConfirmation,
475
+ pin: BrowserPin & {
476
+ readonly browserIdentity: BrowserIdentity;
477
+ readonly daemonOwner: ProcessOwnerIdentity;
478
+ },
479
+ ): Record<string, unknown> {
480
+ if (
481
+ !isRecord(value)
482
+ || !exactKeys(value, ["_boundary", "data", "error", "success"])
483
+ || value.success !== true
484
+ || value.error !== null
485
+ || !isRecord(value._boundary)
486
+ || !exactKeys(value._boundary, ["nonce", "origin"])
487
+ || typeof value._boundary.nonce !== "string"
488
+ || !/^[a-f0-9]{32}$/u.test(value._boundary.nonce)
489
+ || value._boundary.origin !== "unknown"
490
+ || !isRecord(value.data)
491
+ || !exactKeys(value.data, ["action", "confirmed", "lifecycle", "result"])
492
+ || value.data.confirmed !== true
493
+ || value.data.action !== confirmation.action
494
+ || !isRecord(value.data.result)
495
+ || !exactKeys(value.data.result, ["data", "id", "success"])
496
+ || value.data.result.id !== confirmation.confirmationId
497
+ || value.data.result.success !== true
498
+ || !isRecord(value.data.result.data)
499
+ ) throw new Error("pinned derivation browser confirmation result changed shape");
500
+ const confirmedData = value.data.result.data;
501
+ if (
502
+ !("lifecycle" in confirmedData)
503
+ || ["confirmation_required", "confirmation_id", "confirmed", "denied"].some(
504
+ (key) => key in confirmedData,
505
+ )
506
+ ) throw new Error("pinned derivation browser confirmation result changed shape");
507
+ pinnedLifecycle(value.data.lifecycle, pin.browserIdentity);
508
+ pinnedLifecycle(confirmedData.lifecycle, pin.browserIdentity);
509
+ return confirmedData;
510
+ }
511
+
512
+ function readBoundedStdin(): string {
513
+ const chunks: Buffer[] = [];
514
+ const buffer = Buffer.allocUnsafe(64 * 1024);
515
+ let total = 0;
516
+ for (;;) {
517
+ const count = readSync(0, buffer, 0, buffer.byteLength, null);
518
+ if (count === 0) break;
519
+ total += count;
520
+ if (total > MAX_REQUEST_BYTES) throw new Error("derivation command request exceeds its byte bound");
521
+ chunks.push(Buffer.from(buffer.subarray(0, count)));
522
+ }
523
+ return new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks, total));
524
+ }
525
+
526
+ function assertBoundDirectory(expected: Identity): void {
527
+ const descriptor = openSync(
528
+ ".",
529
+ constants.O_RDONLY
530
+ | ("O_NOFOLLOW" in constants ? constants.O_NOFOLLOW : 0)
531
+ | ("O_DIRECTORY" in constants ? constants.O_DIRECTORY : 0),
532
+ );
533
+ try {
534
+ const stats = fstatSync(descriptor, { bigint: true });
535
+ const currentUid = typeof process.getuid === "function" ? BigInt(process.getuid()) : stats.uid;
536
+ if (
537
+ !stats.isDirectory()
538
+ || stats.uid !== currentUid
539
+ || (stats.mode & 0o777n) !== 0o700n
540
+ || stats.dev.toString() !== expected.device
541
+ || stats.ino.toString() !== expected.inode
542
+ ) throw new Error("bound derivation directory no longer matches its private identity");
543
+ } finally {
544
+ closeSync(descriptor);
545
+ }
546
+ }
547
+
548
+ function assertPrivateDirectoryPath(path: string, expected: Identity): void {
549
+ const stats = lstatSync(path, { bigint: true });
550
+ const currentUid = typeof process.getuid === "function" ? BigInt(process.getuid()) : stats.uid;
551
+ if (
552
+ !stats.isDirectory()
553
+ || stats.isSymbolicLink()
554
+ || stats.uid !== currentUid
555
+ || (stats.mode & 0o777n) !== 0o700n
556
+ || stats.dev.toString() !== expected.device
557
+ || stats.ino.toString() !== expected.inode
558
+ ) throw new Error("derivation socket directory no longer matches its private identity");
559
+ }
560
+
561
+ function assertPrivateFile(path: string, expected: string): void {
562
+ const descriptor = openSync(path, constants.O_RDONLY | ("O_NOFOLLOW" in constants ? constants.O_NOFOLLOW : 0));
563
+ try {
564
+ const stats = fstatSync(descriptor, { bigint: true });
565
+ const currentUid = typeof process.getuid === "function" ? BigInt(process.getuid()) : stats.uid;
566
+ const expectedBytes = Buffer.from(expected, "utf8");
567
+ if (
568
+ !stats.isFile()
569
+ || stats.uid !== currentUid
570
+ || (stats.mode & 0o777n) !== 0o600n
571
+ || stats.size !== BigInt(expectedBytes.byteLength)
572
+ ) throw new Error(`bound derivation ${path} is unavailable or unsafe`);
573
+ const actual = Buffer.alloc(expectedBytes.byteLength);
574
+ let offset = 0;
575
+ while (offset < actual.byteLength) {
576
+ const count = readSync(descriptor, actual, offset, actual.byteLength - offset, null);
577
+ if (count === 0) throw new Error(`bound derivation ${path} changed while reading`);
578
+ offset += count;
579
+ }
580
+ const after = fstatSync(descriptor, { bigint: true });
581
+ if (
582
+ after.dev !== stats.dev
583
+ || after.ino !== stats.ino
584
+ || after.size !== stats.size
585
+ || after.mtimeNs !== stats.mtimeNs
586
+ || after.ctimeNs !== stats.ctimeNs
587
+ || !actual.equals(expectedBytes)
588
+ ) throw new Error(`bound derivation ${path} changed or is invalid`);
589
+ } finally {
590
+ closeSync(descriptor);
591
+ }
592
+ }
593
+
594
+ async function main(): Promise<void> {
595
+ const request = parseRequest(JSON.parse(readBoundedStdin()) as unknown);
596
+ process.umask(0o077);
597
+ assertBoundDirectory(request.expectedDirectory);
598
+ const config = "agent-browser.json";
599
+ const policy = "action-policy.json";
600
+ assertPrivateFile(
601
+ config,
602
+ `${JSON.stringify(derivationBrowserConfig(request.guardedBrowserConfig))}\n`,
603
+ );
604
+ assertPrivateFile(
605
+ policy,
606
+ `${JSON.stringify(derivationActionPolicy(
607
+ request.allowRemoteActions,
608
+ request.allowFixtureUpload,
609
+ ))}\n`,
610
+ );
611
+ assertBoundDirectory(request.expectedDirectory);
612
+ assertPrivateDirectoryPath(request.socketDirectory, request.expectedSocketDirectory);
613
+ const arguments_ = request.codeOwnedBrowserRequest === "profile-confirm"
614
+ ? [
615
+ "--session",
616
+ request.ownerSessionName,
617
+ "--content-boundaries",
618
+ "--max-output",
619
+ String(5 * 1024 * 1024),
620
+ ...request.arguments,
621
+ ]
622
+ : request.browserPin === null
623
+ ? request.arguments
624
+ : request.codeOwnedBrowserRequest === "pinned-confirm"
625
+ ? [
626
+ "--session",
627
+ request.browserPin.sessionName,
628
+ "--content-boundaries",
629
+ "--max-output",
630
+ String(5 * 1024 * 1024),
631
+ ...request.arguments,
632
+ ]
633
+ : [
634
+ "--session",
635
+ request.browserPin.sessionName,
636
+ "--cdp",
637
+ request.browserPin.cdpUrl,
638
+ "--content-boundaries",
639
+ "--max-output",
640
+ String(5 * 1024 * 1024),
641
+ ...request.arguments,
642
+ ];
643
+ if (
644
+ request.codeOwnedBrowserRequest === "pinned-confirm"
645
+ && processOwnerStatus(request.browserPin?.daemonOwner as ProcessOwnerIdentity) !== "exact-live-owner"
646
+ ) throw new Error("pinned derivation daemon changed before confirmation");
647
+ const result = await runCommand(
648
+ [...agentBrowserCommand(), "--config", config, "--action-policy", policy, ...arguments_],
649
+ {
650
+ cwd: ".",
651
+ environment: isolatedEnvironment(request.socketDirectory),
652
+ timeoutMs: request.timeoutMs,
653
+ maxOutputBytes: request.maxOutputBytes,
654
+ ...(request.browserStdin === null ? {} : { stdin: request.browserStdin }),
655
+ },
656
+ );
657
+ if (
658
+ request.codeOwnedBrowserRequest === "pinned-confirm"
659
+ && processOwnerStatus(request.browserPin?.daemonOwner as ProcessOwnerIdentity) !== "exact-live-owner"
660
+ ) throw new Error("pinned derivation daemon changed during confirmation");
661
+ if (request.browserPin !== null && result.exitCode === 0) {
662
+ const parsed = parseLastJsonWithExactLaunchHashes(result.stdout);
663
+ if (request.codeOwnedBrowserRequest === "pinned-confirm") {
664
+ parsePinnedDerivationConfirmationResult(
665
+ parsed,
666
+ {
667
+ action: request.browserPin.confirmationAction as string,
668
+ confirmationId: request.arguments[1] as string,
669
+ },
670
+ request.browserPin as BrowserPin & {
671
+ readonly browserIdentity: BrowserIdentity;
672
+ readonly daemonOwner: ProcessOwnerIdentity;
673
+ },
674
+ );
675
+ } else {
676
+ const commands = parsePinnedCommands(request.browserStdin);
677
+ if (
678
+ request.codeOwnedBrowserRequest !== "pinned-batch"
679
+ || parsePinnedDerivationConfirmation(parsed, commands[0] ?? []) === null
680
+ ) validatePinnedDerivationBrowserOutput(parsed, commands, request.browserPin);
681
+ }
682
+ }
683
+ process.stdout.write(result.stdout);
684
+ process.stderr.write(result.stderr);
685
+ process.exitCode = result.exitCode;
686
+ }
687
+
688
+ if (import.meta.main) {
689
+ main().catch((error: unknown) => {
690
+ process.stderr.write(`derivation command helper: ${error instanceof Error ? error.message : "unknown failure"}\n`);
691
+ process.exitCode = 1;
692
+ });
693
+ }