@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,835 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { existsSync, lstatSync } from "node:fs";
3
+ import { connect as connectLocal, type Socket } from "node:net";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ import {
8
+ createDerivationGuardExtension,
9
+ DERIVATION_GUARD_PROXY_CONFIG,
10
+ DERIVATION_GUARD_PROXY_READY,
11
+ derivationGuardControlSocketPath,
12
+ derivationProxyPolicySha256,
13
+ parseProxyHelperConfig,
14
+ parseProxyHelperReady,
15
+ proxyHelperConfigContent,
16
+ proxyHelperReadyContent,
17
+ readGuardPrivateFile,
18
+ verifyDerivationGuardExtension,
19
+ verifyGuardPrivateFile,
20
+ writeGuardPrivateFile,
21
+ type DerivationNetworkGuard,
22
+ type DerivationProxyHelperConfig,
23
+ type GuardDirectoryIdentity,
24
+ } from "./derivation-network-guard";
25
+ import { sha256 } from "./canonical-json";
26
+ import {
27
+ captureProcessOwnerIdentity,
28
+ processOwnerStatus,
29
+ type ProcessOwnerIdentity,
30
+ type ProcessOwnerStatus,
31
+ } from "./process-identity";
32
+
33
+ const proxyHelperPath = join(
34
+ dirname(fileURLToPath(import.meta.url)),
35
+ "derivation-network-proxy-helper.ts",
36
+ );
37
+ const trustedBunConfigPath = join(
38
+ dirname(fileURLToPath(import.meta.url)),
39
+ "state-helper.bunfig.toml",
40
+ );
41
+ const PROXY_START_TIMEOUT_MS = 15_000;
42
+ const PROXY_CONTROL_TIMEOUT_MS = 5_000;
43
+ const PROXY_CONTROL_MAX_BYTES = 64 * 1024;
44
+ const helperFailureEnvironmentKey = "GHOSTGET_DERIVATION_PROXY_FAIL_FOR_TEST";
45
+
46
+ export type DerivationProxyHelperFailureForTest =
47
+ | "after-proxy"
48
+ | "after-control-listen"
49
+ | "after-ready-write";
50
+
51
+ type ProxyControlResponse = {
52
+ readonly schemaVersion: 1;
53
+ readonly ok: true;
54
+ readonly derivationId: string;
55
+ readonly policySha256: string;
56
+ readonly port: number;
57
+ readonly owner: ProcessOwnerIdentity;
58
+ readonly adopted: boolean;
59
+ };
60
+
61
+ type BoundaryDependencies = {
62
+ readonly inspectOwner: (owner: ProcessOwnerIdentity) => ProcessOwnerStatus;
63
+ readonly monotonicNow: () => number;
64
+ };
65
+
66
+ export class UnsafeDerivationNetworkBoundaryCleanupError extends Error {
67
+ constructor(cause: unknown) {
68
+ super(
69
+ "derivation network helper could not be proved quiescent; private state was preserved",
70
+ { cause },
71
+ );
72
+ this.name = "UnsafeDerivationNetworkBoundaryCleanupError";
73
+ }
74
+ }
75
+
76
+ class DerivationProxyControlUnavailableError extends Error {
77
+ readonly code: "ENOENT" | "ECONNREFUSED";
78
+
79
+ constructor(code: "ENOENT" | "ECONNREFUSED", cause: unknown) {
80
+ super("derivation proxy control is unavailable", { cause });
81
+ this.name = "DerivationProxyControlUnavailableError";
82
+ this.code = code;
83
+ }
84
+ }
85
+
86
+ class DerivationProxyControlMissingError extends Error {
87
+ constructor() {
88
+ super("derivation proxy control is missing");
89
+ this.name = "DerivationProxyControlMissingError";
90
+ }
91
+ }
92
+
93
+ function exactKeys(record: Record<string, unknown>, expected: readonly string[]): boolean {
94
+ const actual = Object.keys(record).sort();
95
+ const sorted = [...expected].sort();
96
+ return actual.length === sorted.length
97
+ && actual.every((key, index) => key === sorted[index]);
98
+ }
99
+
100
+ function parseOwner(value: unknown): ProcessOwnerIdentity {
101
+ if (
102
+ typeof value !== "object"
103
+ || value === null
104
+ || Array.isArray(value)
105
+ || !exactKeys(value as Record<string, unknown>, ["pid", "bootId", "processStartId"])
106
+ ) throw new Error("derivation proxy control response is malformed");
107
+ const record = value as Record<string, unknown>;
108
+ if (
109
+ !Number.isSafeInteger(record.pid)
110
+ || (record.pid as number) < 1
111
+ || typeof record.bootId !== "string"
112
+ || !/^[a-f0-9]{64}$/u.test(record.bootId)
113
+ || typeof record.processStartId !== "string"
114
+ || !/^[a-f0-9]{64}$/u.test(record.processStartId)
115
+ ) throw new Error("derivation proxy control response is malformed");
116
+ return {
117
+ pid: record.pid as number,
118
+ bootId: record.bootId,
119
+ processStartId: record.processStartId,
120
+ };
121
+ }
122
+
123
+ function parseControlResponse(value: unknown): ProxyControlResponse {
124
+ if (
125
+ typeof value !== "object"
126
+ || value === null
127
+ || Array.isArray(value)
128
+ || !exactKeys(value as Record<string, unknown>, [
129
+ "schemaVersion", "ok", "derivationId", "policySha256", "port", "owner", "adopted",
130
+ ])
131
+ ) throw new Error("derivation proxy control response is malformed");
132
+ const record = value as Record<string, unknown>;
133
+ if (
134
+ record.schemaVersion !== 1
135
+ || record.ok !== true
136
+ || typeof record.derivationId !== "string"
137
+ || !/^[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(record.derivationId)
138
+ || typeof record.policySha256 !== "string"
139
+ || !/^[a-f0-9]{64}$/u.test(record.policySha256)
140
+ || !Number.isSafeInteger(record.port)
141
+ || (record.port as number) < 1
142
+ || (record.port as number) > 65_535
143
+ || typeof record.adopted !== "boolean"
144
+ ) throw new Error("derivation proxy control response is malformed");
145
+ return {
146
+ schemaVersion: 1,
147
+ ok: true,
148
+ derivationId: record.derivationId,
149
+ policySha256: record.policySha256,
150
+ port: record.port as number,
151
+ owner: parseOwner(record.owner),
152
+ adopted: record.adopted,
153
+ };
154
+ }
155
+
156
+ function sameOwner(left: ProcessOwnerIdentity, right: ProcessOwnerIdentity): boolean {
157
+ return left.pid === right.pid
158
+ && left.bootId === right.bootId
159
+ && left.processStartId === right.processStartId;
160
+ }
161
+
162
+ function currentUserOwns(uid: number | bigint): boolean {
163
+ return typeof process.getuid !== "function" || BigInt(process.getuid()) === BigInt(uid);
164
+ }
165
+
166
+ function pathIsAbsent(path: string): boolean {
167
+ try {
168
+ lstatSync(path);
169
+ return false;
170
+ } catch (error) {
171
+ if (
172
+ typeof error === "object"
173
+ && error !== null
174
+ && "code" in error
175
+ && error.code === "ENOENT"
176
+ ) return true;
177
+ throw error;
178
+ }
179
+ }
180
+
181
+ function assertPrivateDirectoryIdentity(
182
+ path: string,
183
+ expected: GuardDirectoryIdentity,
184
+ ): void {
185
+ const stats = lstatSync(path, { bigint: true });
186
+ if (
187
+ !stats.isDirectory()
188
+ || stats.isSymbolicLink()
189
+ || !currentUserOwns(stats.uid)
190
+ || (stats.mode & 0o777n) !== 0o700n
191
+ || stats.dev.toString() !== expected.device
192
+ || stats.ino.toString() !== expected.inode
193
+ ) throw new Error("derivation proxy evidence directory changed identity");
194
+ }
195
+
196
+ /**
197
+ * Return true only for an absent endpoint. An existing path must be the exact
198
+ * private socket shape before Ghostget will connect to it or consider fallback.
199
+ */
200
+ function privateControlSocketIsAbsent(path: string): boolean {
201
+ try {
202
+ const stats = lstatSync(path, { bigint: true });
203
+ if (
204
+ !stats.isSocket()
205
+ || stats.isSymbolicLink()
206
+ || !currentUserOwns(stats.uid)
207
+ || (stats.mode & 0o777n) !== 0o600n
208
+ ) throw new Error("derivation proxy control path is unsafe");
209
+ return false;
210
+ } catch (error) {
211
+ if (
212
+ typeof error === "object"
213
+ && error !== null
214
+ && "code" in error
215
+ && error.code === "ENOENT"
216
+ ) return true;
217
+ throw error;
218
+ }
219
+ }
220
+
221
+ type ProxyControlBinding = {
222
+ readonly controlNonce: string;
223
+ readonly policySha256: string;
224
+ };
225
+
226
+ function proxyControl(
227
+ socketPath: string,
228
+ derivationId: string,
229
+ command: "status" | "adopt" | "close",
230
+ binding: ProxyControlBinding,
231
+ ): Promise<ProxyControlResponse> {
232
+ return new Promise((resolve, reject) => {
233
+ const socket: Socket = connectLocal({ path: socketPath });
234
+ const chunks: Buffer[] = [];
235
+ let total = 0;
236
+ let settled = false;
237
+ let connected = false;
238
+ const timer = setTimeout(() => fail(new Error("derivation proxy control timed out")), PROXY_CONTROL_TIMEOUT_MS);
239
+ const cleanup = (): void => {
240
+ clearTimeout(timer);
241
+ socket.off("connect", onConnect);
242
+ socket.off("data", onData);
243
+ socket.off("end", onEnd);
244
+ socket.off("error", onError);
245
+ };
246
+ const fail = (error: Error): void => {
247
+ if (settled) return;
248
+ settled = true;
249
+ cleanup();
250
+ socket.destroy();
251
+ reject(error);
252
+ };
253
+ const onConnect = (): void => {
254
+ connected = true;
255
+ socket.write(`${JSON.stringify({
256
+ schemaVersion: 1,
257
+ command,
258
+ derivationId,
259
+ nonce: binding.controlNonce,
260
+ policySha256: binding.policySha256,
261
+ })}\n`);
262
+ };
263
+ const onData = (chunk: Buffer): void => {
264
+ total += chunk.byteLength;
265
+ if (total > PROXY_CONTROL_MAX_BYTES) {
266
+ fail(new Error("derivation proxy control response is too large"));
267
+ return;
268
+ }
269
+ chunks.push(Buffer.from(chunk));
270
+ };
271
+ const onEnd = (): void => {
272
+ if (settled) return;
273
+ settled = true;
274
+ cleanup();
275
+ try {
276
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(
277
+ Buffer.concat(chunks, total),
278
+ );
279
+ resolve(parseControlResponse(JSON.parse(text) as unknown));
280
+ } catch (error) {
281
+ reject(error instanceof Error ? error : new Error("derivation proxy control failed"));
282
+ }
283
+ };
284
+ const onError = (error: Error): void => {
285
+ const code = "code" in error ? error.code : undefined;
286
+ if (!connected && (code === "ENOENT" || code === "ECONNREFUSED")) {
287
+ fail(new DerivationProxyControlUnavailableError(code, error));
288
+ return;
289
+ }
290
+ fail(new Error("derivation proxy control failed", { cause: error }));
291
+ };
292
+ socket.once("connect", onConnect);
293
+ socket.on("data", onData);
294
+ socket.once("end", onEnd);
295
+ socket.once("error", onError);
296
+ });
297
+ }
298
+
299
+ async function proxyControlAtBoundPath(input: {
300
+ readonly derivationId: string;
301
+ readonly command: "status" | "adopt" | "close";
302
+ readonly binding: ProxyControlBinding;
303
+ }): Promise<ProxyControlResponse> {
304
+ const path = derivationGuardControlSocketPath(input.derivationId);
305
+ if (privateControlSocketIsAbsent(path)) throw new DerivationProxyControlMissingError();
306
+ try {
307
+ const response = await proxyControl(
308
+ path,
309
+ input.derivationId,
310
+ input.command,
311
+ input.binding,
312
+ );
313
+ if (response.derivationId !== input.derivationId) {
314
+ throw new Error("derivation proxy control changed derivation identity");
315
+ }
316
+ return response;
317
+ } catch (error) {
318
+ // Only an absent/refused current endpoint permits the exact-owner cleanup
319
+ // migration. Malformed, foreign, reset, and timed-out responses preserve.
320
+ if (error instanceof DerivationProxyControlUnavailableError) {
321
+ const absent = privateControlSocketIsAbsent(path);
322
+ if (error.code === "ENOENT" && absent) {
323
+ throw new DerivationProxyControlMissingError();
324
+ }
325
+ if (absent) throw new DerivationProxyControlMissingError();
326
+ }
327
+ throw error;
328
+ }
329
+ }
330
+
331
+ async function waitForProxyOwnerQuiescence(
332
+ owner: ProcessOwnerIdentity,
333
+ inspectOwner: BoundaryDependencies["inspectOwner"],
334
+ monotonicNow: BoundaryDependencies["monotonicNow"] = () => performance.now(),
335
+ ): Promise<void> {
336
+ const startedAt = monotonicNow();
337
+ if (!Number.isFinite(startedAt)) {
338
+ throw new Error("derivation network proxy cleanup monotonic clock is non-finite");
339
+ }
340
+ let previous = startedAt;
341
+ let waited = false;
342
+ for (;;) {
343
+ const status = inspectOwner(owner);
344
+ if (status === "different-or-dead") return;
345
+ const current = monotonicNow();
346
+ if (!Number.isFinite(current)) {
347
+ throw new Error("derivation network proxy cleanup monotonic clock is non-finite");
348
+ }
349
+ if (current < previous) {
350
+ throw new Error("derivation network proxy cleanup monotonic clock moved backward");
351
+ }
352
+ if (waited && current === previous) {
353
+ throw new Error("derivation network proxy cleanup monotonic clock did not advance");
354
+ }
355
+ previous = current;
356
+ if (current - startedAt >= PROXY_CONTROL_TIMEOUT_MS) {
357
+ throw new Error(
358
+ status === "unknown"
359
+ ? "derivation network proxy cleanup cannot prove process quiescence"
360
+ : "derivation network proxy cleanup did not settle",
361
+ );
362
+ }
363
+ await Bun.sleep(25);
364
+ waited = true;
365
+ }
366
+ }
367
+
368
+ async function signalBoundOwnerAtAbsentControl(input: {
369
+ readonly derivationId: string;
370
+ readonly owner: ProcessOwnerIdentity;
371
+ readonly dependencies?: Partial<BoundaryDependencies>;
372
+ }): Promise<void> {
373
+ const controlPath = derivationGuardControlSocketPath(input.derivationId);
374
+ if (!privateControlSocketIsAbsent(controlPath)) {
375
+ throw new Error("derivation proxy control endpoint could not be authenticated for cleanup");
376
+ }
377
+ const inspectOwner = input.dependencies?.inspectOwner ?? processOwnerStatus;
378
+ const initial = inspectOwner(input.owner);
379
+ if (initial === "different-or-dead") return;
380
+ if (initial !== "exact-live-owner") {
381
+ throw new Error("derivation network proxy owner cannot be verified for cleanup");
382
+ }
383
+ const rebound = inspectOwner(input.owner);
384
+ if (rebound === "different-or-dead") return;
385
+ if (rebound !== "exact-live-owner") {
386
+ throw new Error("derivation network proxy owner cannot be verified for cleanup");
387
+ }
388
+ if (!privateControlSocketIsAbsent(controlPath)) {
389
+ throw new Error("derivation proxy control endpoint appeared before cleanup");
390
+ }
391
+ try {
392
+ process.kill(input.owner.pid, "SIGTERM");
393
+ } catch (signalError) {
394
+ const afterSignal = inspectOwner(input.owner);
395
+ if (afterSignal === "different-or-dead") return;
396
+ throw new Error("derivation network proxy owner could not be terminated", {
397
+ cause: signalError,
398
+ });
399
+ }
400
+ await waitForProxyOwnerQuiescence(
401
+ input.owner,
402
+ inspectOwner,
403
+ input.dependencies?.monotonicNow,
404
+ );
405
+ if (!privateControlSocketIsAbsent(controlPath)) {
406
+ throw new Error("derivation proxy control endpoint appeared during cleanup");
407
+ }
408
+ }
409
+
410
+ /**
411
+ * Recover an interrupted initialization only from its exact private config and
412
+ * readiness records. No caller-supplied PID is accepted by this boundary.
413
+ */
414
+ export async function closeInterruptedDerivationNetworkBoundary(input: {
415
+ readonly derivationId: string;
416
+ readonly directory: string;
417
+ readonly directoryIdentity: GuardDirectoryIdentity;
418
+ readonly socketDirectory: string;
419
+ readonly socketIdentity: GuardDirectoryIdentity;
420
+ readonly dependencies?: Partial<BoundaryDependencies>;
421
+ }): Promise<ProcessOwnerIdentity | null> {
422
+ const controlPath = derivationGuardControlSocketPath(input.derivationId);
423
+ assertPrivateDirectoryIdentity(input.directory, input.directoryIdentity);
424
+ const readyPath = join(input.directory, DERIVATION_GUARD_PROXY_READY);
425
+ if (pathIsAbsent(readyPath)) {
426
+ assertPrivateDirectoryIdentity(input.directory, input.directoryIdentity);
427
+ if (!privateControlSocketIsAbsent(controlPath)) {
428
+ throw new Error("interrupted derivation has an unbound network control endpoint");
429
+ }
430
+ return null;
431
+ }
432
+
433
+ const configPath = join(input.directory, DERIVATION_GUARD_PROXY_CONFIG);
434
+ const configRead = readGuardPrivateFile(configPath, 128 * 1024);
435
+ const readyRead = readGuardPrivateFile(readyPath, 64 * 1024);
436
+ let configValue: unknown;
437
+ let readyValue: unknown;
438
+ try {
439
+ configValue = JSON.parse(configRead.content) as unknown;
440
+ readyValue = JSON.parse(readyRead.content) as unknown;
441
+ } catch (error) {
442
+ throw new Error("interrupted derivation network evidence is malformed", { cause: error });
443
+ }
444
+ const config = parseProxyHelperConfig(configValue);
445
+ const ready = parseProxyHelperReady(readyValue);
446
+ if (
447
+ config.derivationId !== input.derivationId
448
+ || ready.derivationId !== input.derivationId
449
+ || config.directoryIdentity.device !== input.directoryIdentity.device
450
+ || config.directoryIdentity.inode !== input.directoryIdentity.inode
451
+ || config.socketDirectory !== input.socketDirectory
452
+ || config.socketIdentity.device !== input.socketIdentity.device
453
+ || config.socketIdentity.inode !== input.socketIdentity.inode
454
+ || ready.policySha256 !== config.policySha256
455
+ ) throw new Error("interrupted derivation network evidence changed identity");
456
+ verifyGuardPrivateFile(
457
+ configPath,
458
+ configRead.evidence,
459
+ proxyHelperConfigContent(config),
460
+ );
461
+ verifyGuardPrivateFile(
462
+ readyPath,
463
+ readyRead.evidence,
464
+ proxyHelperReadyContent(ready),
465
+ );
466
+ assertPrivateDirectoryIdentity(input.directory, input.directoryIdentity);
467
+
468
+ const inspectOwner = input.dependencies?.inspectOwner ?? processOwnerStatus;
469
+ let statusResponse: ProxyControlResponse;
470
+ try {
471
+ statusResponse = await proxyControlAtBoundPath({
472
+ derivationId: input.derivationId,
473
+ command: "status",
474
+ binding: config,
475
+ });
476
+ } catch (error) {
477
+ if (!(error instanceof DerivationProxyControlMissingError)) throw error;
478
+ await signalBoundOwnerAtAbsentControl({
479
+ derivationId: input.derivationId,
480
+ owner: ready.owner,
481
+ dependencies: { ...input.dependencies, inspectOwner },
482
+ });
483
+ return ready.owner;
484
+ }
485
+ if (
486
+ statusResponse.policySha256 !== ready.policySha256
487
+ || statusResponse.port !== ready.port
488
+ || !sameOwner(statusResponse.owner, ready.owner)
489
+ ) throw new Error("interrupted derivation network status changed identity");
490
+ if (inspectOwner(ready.owner) !== "exact-live-owner") {
491
+ throw new Error("interrupted derivation network owner cannot be verified");
492
+ }
493
+
494
+ let closeResponse: ProxyControlResponse;
495
+ try {
496
+ closeResponse = await proxyControlAtBoundPath({
497
+ derivationId: input.derivationId,
498
+ command: "close",
499
+ binding: config,
500
+ });
501
+ } catch (error) {
502
+ if (!(error instanceof DerivationProxyControlMissingError)) throw error;
503
+ await signalBoundOwnerAtAbsentControl({
504
+ derivationId: input.derivationId,
505
+ owner: ready.owner,
506
+ dependencies: { ...input.dependencies, inspectOwner },
507
+ });
508
+ return ready.owner;
509
+ }
510
+ if (
511
+ closeResponse.policySha256 !== ready.policySha256
512
+ || closeResponse.port !== ready.port
513
+ || !sameOwner(closeResponse.owner, ready.owner)
514
+ ) throw new Error("interrupted derivation network control changed identity");
515
+ await waitForProxyOwnerQuiescence(
516
+ ready.owner,
517
+ inspectOwner,
518
+ input.dependencies?.monotonicNow,
519
+ );
520
+ if (!privateControlSocketIsAbsent(controlPath)) {
521
+ throw new Error("interrupted derivation control endpoint remains after cleanup");
522
+ }
523
+ return ready.owner;
524
+ }
525
+
526
+ function expectedProxyConfig(input: {
527
+ readonly derivationId: string;
528
+ readonly directoryIdentity: GuardDirectoryIdentity;
529
+ readonly socketDirectory: string;
530
+ readonly socketIdentity: GuardDirectoryIdentity;
531
+ readonly browserDomains: readonly string[];
532
+ readonly parentOwner: ProcessOwnerIdentity;
533
+ readonly controlNonce: string;
534
+ readonly policySha256: string;
535
+ }): DerivationProxyHelperConfig {
536
+ return Object.freeze({
537
+ schemaVersion: 1,
538
+ kind: "wrench-derivation-proxy-config",
539
+ ...input,
540
+ });
541
+ }
542
+
543
+ export async function createDerivationNetworkBoundary(input: {
544
+ readonly derivationId: string;
545
+ readonly directory: string;
546
+ readonly directoryIdentity: GuardDirectoryIdentity;
547
+ readonly socketDirectory: string;
548
+ readonly socketIdentity: GuardDirectoryIdentity;
549
+ readonly browserDomains: readonly string[];
550
+ readonly failHelperAtForTest?: DerivationProxyHelperFailureForTest;
551
+ }): Promise<DerivationNetworkGuard> {
552
+ derivationGuardControlSocketPath(input.derivationId);
553
+ if (input.failHelperAtForTest !== undefined && process.env.NODE_ENV !== "test") {
554
+ throw new Error("derivation proxy helper fault injection is available only in tests");
555
+ }
556
+ const extension = createDerivationGuardExtension(input.directory, input.browserDomains);
557
+ const policySha256 = derivationProxyPolicySha256(input.browserDomains);
558
+ const controlNonce = randomBytes(32).toString("hex");
559
+ const parentOwner = captureProcessOwnerIdentity(process.pid);
560
+ const config = expectedProxyConfig({
561
+ derivationId: input.derivationId,
562
+ directoryIdentity: input.directoryIdentity,
563
+ socketDirectory: input.socketDirectory,
564
+ socketIdentity: input.socketIdentity,
565
+ browserDomains: input.browserDomains,
566
+ parentOwner,
567
+ controlNonce,
568
+ policySha256,
569
+ });
570
+ const configFile = writeGuardPrivateFile(
571
+ join(input.directory, DERIVATION_GUARD_PROXY_CONFIG),
572
+ proxyHelperConfigContent(config),
573
+ );
574
+ const child = Bun.spawn([
575
+ process.execPath,
576
+ "--no-env-file",
577
+ "--no-install",
578
+ "--no-macros",
579
+ "--no-addons",
580
+ `--config=${trustedBunConfigPath}`,
581
+ proxyHelperPath,
582
+ ], {
583
+ cwd: input.directory,
584
+ env: input.failHelperAtForTest === undefined
585
+ ? { NODE_ENV: "production" }
586
+ : {
587
+ NODE_ENV: "test",
588
+ [helperFailureEnvironmentKey]: input.failHelperAtForTest,
589
+ },
590
+ stdin: "ignore",
591
+ stdout: "ignore",
592
+ stderr: "ignore",
593
+ detached: true,
594
+ });
595
+ const readyPath = join(input.directory, DERIVATION_GUARD_PROXY_READY);
596
+ const deadline = Date.now() + PROXY_START_TIMEOUT_MS;
597
+ let readyRead: ReturnType<typeof readGuardPrivateFile> | null = null;
598
+ try {
599
+ for (;;) {
600
+ if (existsSync(readyPath)) {
601
+ try {
602
+ readyRead = readGuardPrivateFile(readyPath, 64 * 1024);
603
+ break;
604
+ } catch {
605
+ // The helper creates the ready path with O_EXCL before the write
606
+ // finishes. Keep polling until the file is complete or the deadline.
607
+ }
608
+ }
609
+ const exit = await Promise.race([
610
+ child.exited.then((code) => ({ exited: true as const, code })),
611
+ Bun.sleep(25).then(() => ({ exited: false as const, code: 0 })),
612
+ ]);
613
+ if (exit.exited) throw new Error("derivation proxy helper exited before readiness");
614
+ if (Date.now() >= deadline) throw new Error("derivation proxy helper readiness timed out");
615
+ }
616
+ const ready = parseProxyHelperReady(JSON.parse(readyRead.content) as unknown);
617
+ if (
618
+ ready.derivationId !== input.derivationId
619
+ || ready.policySha256 !== policySha256
620
+ || ready.owner.pid !== child.pid
621
+ || processOwnerStatus(ready.owner) !== "exact-live-owner"
622
+ ) throw new Error("derivation proxy helper readiness did not match its launch");
623
+ const guard: DerivationNetworkGuard = Object.freeze({
624
+ schemaVersion: 1,
625
+ kind: "contained-mv3-dnr-proxy",
626
+ extension,
627
+ proxy: Object.freeze({
628
+ policySha256,
629
+ controlNonce,
630
+ port: ready.port,
631
+ owner: ready.owner,
632
+ parentOwner,
633
+ configFile,
634
+ readyFile: readyRead.evidence,
635
+ }),
636
+ });
637
+ const response = await proxyControlAtBoundPath({
638
+ derivationId: input.derivationId,
639
+ command: "status",
640
+ binding: guard.proxy,
641
+ });
642
+ if (
643
+ response.policySha256 !== policySha256
644
+ || response.port !== ready.port
645
+ || !sameOwner(response.owner, ready.owner)
646
+ || response.adopted
647
+ ) throw new Error("derivation proxy helper control did not match readiness");
648
+ child.unref();
649
+ return guard;
650
+ } catch (error) {
651
+ const settlesWithin = async (milliseconds: number): Promise<boolean> => {
652
+ let timer: ReturnType<typeof setTimeout> | undefined;
653
+ try {
654
+ return await Promise.race([
655
+ child.exited.then(() => true as const, () => true as const),
656
+ new Promise<false>((resolve) => {
657
+ timer = setTimeout(() => resolve(false), milliseconds);
658
+ }),
659
+ ]);
660
+ } finally {
661
+ if (timer !== undefined) clearTimeout(timer);
662
+ }
663
+ };
664
+ try {
665
+ process.kill(child.pid, "SIGTERM");
666
+ } catch {
667
+ // The helper may already have exited.
668
+ }
669
+ if (!await settlesWithin(2_000)) {
670
+ try {
671
+ process.kill(child.pid, "SIGKILL");
672
+ } catch {
673
+ // A concurrently exited helper is safe once child.exited settles.
674
+ }
675
+ if (!await settlesWithin(2_000)) {
676
+ child.unref();
677
+ throw new UnsafeDerivationNetworkBoundaryCleanupError(error);
678
+ }
679
+ }
680
+ throw error;
681
+ }
682
+ }
683
+
684
+ /** Bind the helper to durable session metadata before the creating CLI exits. */
685
+ export async function adoptDerivationNetworkBoundary(input: {
686
+ readonly derivationId: string;
687
+ readonly guard: DerivationNetworkGuard;
688
+ }): Promise<void> {
689
+ derivationGuardControlSocketPath(input.derivationId);
690
+ assertGuardDerivationIdentity(input.guard, input.derivationId);
691
+ if (processOwnerStatus(input.guard.proxy.owner) !== "exact-live-owner") {
692
+ throw new Error("derivation network proxy helper is unavailable before adoption");
693
+ }
694
+ if (processOwnerStatus(input.guard.proxy.parentOwner) !== "exact-live-owner") {
695
+ throw new Error("derivation network proxy parent changed before adoption");
696
+ }
697
+ const response = await proxyControlAtBoundPath({
698
+ derivationId: input.derivationId,
699
+ command: "adopt",
700
+ binding: input.guard.proxy,
701
+ });
702
+ if (
703
+ response.policySha256 !== input.guard.proxy.policySha256
704
+ || response.port !== input.guard.proxy.port
705
+ || !sameOwner(response.owner, input.guard.proxy.owner)
706
+ || !response.adopted
707
+ ) throw new Error("derivation network proxy adoption changed identity");
708
+ }
709
+
710
+ function expectedReady(guard: DerivationNetworkGuard, derivationId: string) {
711
+ return {
712
+ schemaVersion: 1 as const,
713
+ kind: "wrench-derivation-proxy-ready" as const,
714
+ derivationId,
715
+ policySha256: guard.proxy.policySha256,
716
+ port: guard.proxy.port,
717
+ owner: guard.proxy.owner,
718
+ };
719
+ }
720
+
721
+ function assertGuardDerivationIdentity(
722
+ guard: DerivationNetworkGuard,
723
+ derivationId: string,
724
+ ): void {
725
+ const content = proxyHelperReadyContent(expectedReady(guard, derivationId));
726
+ if (
727
+ Buffer.byteLength(content, "utf8") !== guard.proxy.readyFile.byteLength
728
+ || sha256(content) !== guard.proxy.readyFile.sha256
729
+ ) throw new Error("derivation network guard does not match its derivation ID");
730
+ }
731
+
732
+ export async function verifyDerivationNetworkBoundary(input: {
733
+ readonly derivationId: string;
734
+ readonly directory: string;
735
+ readonly directoryIdentity: GuardDirectoryIdentity;
736
+ readonly socketDirectory: string;
737
+ readonly socketIdentity: GuardDirectoryIdentity;
738
+ readonly browserDomains: readonly string[];
739
+ readonly guard: DerivationNetworkGuard;
740
+ readonly dependencies?: Partial<BoundaryDependencies>;
741
+ }): Promise<void> {
742
+ derivationGuardControlSocketPath(input.derivationId);
743
+ const inspectOwner = input.dependencies?.inspectOwner ?? processOwnerStatus;
744
+ assertGuardDerivationIdentity(input.guard, input.derivationId);
745
+ if (input.guard.proxy.policySha256 !== derivationProxyPolicySha256(input.browserDomains)) {
746
+ throw new Error("derivation network proxy policy changed");
747
+ }
748
+ verifyDerivationGuardExtension(input.directory, input.browserDomains, input.guard.extension);
749
+ const config = expectedProxyConfig({
750
+ derivationId: input.derivationId,
751
+ directoryIdentity: input.directoryIdentity,
752
+ socketDirectory: input.socketDirectory,
753
+ socketIdentity: input.socketIdentity,
754
+ browserDomains: input.browserDomains,
755
+ parentOwner: input.guard.proxy.parentOwner,
756
+ controlNonce: input.guard.proxy.controlNonce,
757
+ policySha256: input.guard.proxy.policySha256,
758
+ });
759
+ verifyGuardPrivateFile(
760
+ join(input.directory, DERIVATION_GUARD_PROXY_CONFIG),
761
+ input.guard.proxy.configFile,
762
+ proxyHelperConfigContent(config),
763
+ );
764
+ verifyGuardPrivateFile(
765
+ join(input.directory, DERIVATION_GUARD_PROXY_READY),
766
+ input.guard.proxy.readyFile,
767
+ proxyHelperReadyContent(expectedReady(input.guard, input.derivationId)),
768
+ );
769
+ if (inspectOwner(input.guard.proxy.owner) !== "exact-live-owner") {
770
+ throw new Error("derivation network proxy helper is not the exact live owner");
771
+ }
772
+ const response = await proxyControlAtBoundPath({
773
+ derivationId: input.derivationId,
774
+ command: "status",
775
+ binding: input.guard.proxy,
776
+ });
777
+ if (
778
+ response.policySha256 !== input.guard.proxy.policySha256
779
+ || response.port !== input.guard.proxy.port
780
+ || !sameOwner(response.owner, input.guard.proxy.owner)
781
+ || !response.adopted
782
+ ) throw new Error("derivation network proxy helper changed identity");
783
+ }
784
+
785
+ export async function closeDerivationNetworkBoundary(input: {
786
+ readonly derivationId: string;
787
+ readonly guard: DerivationNetworkGuard;
788
+ readonly dependencies?: Partial<BoundaryDependencies>;
789
+ }): Promise<void> {
790
+ const controlPath = derivationGuardControlSocketPath(input.derivationId);
791
+ const inspectOwner = input.dependencies?.inspectOwner ?? processOwnerStatus;
792
+ assertGuardDerivationIdentity(input.guard, input.derivationId);
793
+ const initial = inspectOwner(input.guard.proxy.owner);
794
+ if (initial === "different-or-dead") {
795
+ if (!privateControlSocketIsAbsent(controlPath)) {
796
+ throw new Error("derivation proxy control endpoint remains after its owner changed");
797
+ }
798
+ return;
799
+ }
800
+ if (initial !== "exact-live-owner") {
801
+ throw new Error("derivation network proxy owner cannot be verified for cleanup");
802
+ }
803
+ let response: ProxyControlResponse;
804
+ try {
805
+ response = await proxyControlAtBoundPath({
806
+ derivationId: input.derivationId,
807
+ command: "close",
808
+ binding: input.guard.proxy,
809
+ });
810
+ } catch (error) {
811
+ if (!(error instanceof DerivationProxyControlMissingError)) throw error;
812
+ // Legacy schema-v1 sessions may retain an exact-live owner after their old
813
+ // agent-browser-owned control socket vanished. Never launch or speak the
814
+ // new protocol on that old path; terminate only the revalidated owner.
815
+ await signalBoundOwnerAtAbsentControl({
816
+ derivationId: input.derivationId,
817
+ owner: input.guard.proxy.owner,
818
+ dependencies: { ...input.dependencies, inspectOwner },
819
+ });
820
+ return;
821
+ }
822
+ if (
823
+ response.policySha256 !== input.guard.proxy.policySha256
824
+ || response.port !== input.guard.proxy.port
825
+ || !sameOwner(response.owner, input.guard.proxy.owner)
826
+ ) throw new Error("derivation network proxy close acknowledgement changed identity");
827
+ await waitForProxyOwnerQuiescence(
828
+ input.guard.proxy.owner,
829
+ inspectOwner,
830
+ input.dependencies?.monotonicNow,
831
+ );
832
+ if (!privateControlSocketIsAbsent(controlPath)) {
833
+ throw new Error("derivation proxy control endpoint remains after authenticated cleanup");
834
+ }
835
+ }