@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,224 @@
1
+ import * as Cause from "effect/Cause";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Either from "effect/Either";
4
+ import * as Exit from "effect/Exit";
5
+ import * as Option from "effect/Option";
6
+ import { canonicalJson } from "./canonical-json";
7
+ import { ConfirmedWriteFailure, confirmedWriteFinally, type ConfirmedWritePhase } from "./confirmed-write-failure";
8
+ import { ConfirmedWritePlatform } from "./confirmed-write-platform";
9
+ import type { RunPreparedOptions } from "./confirmed-write-model";
10
+ import type { PreparedInvocation, StoredPlan, InvocationResult } from "./runtime";
11
+
12
+ type Platform = Effect.Effect.Success<typeof ConfirmedWritePlatform>;
13
+ type Execution = Effect.Effect.Success<ReturnType<Platform["execution"]>>;
14
+
15
+ const refuse = (phase: ConfirmedWritePhase, message: string) =>
16
+ Effect.fail(new ConfirmedWriteFailure({ phase, cause: new Error(message) }));
17
+ const wrapped = (failure: ConfirmedWriteFailure, message: string) =>
18
+ Effect.fail(new ConfirmedWriteFailure({
19
+ phase: failure.phase, cause: new Error(message, { cause: failure.cause }),
20
+ priorCause: Cause.fail(failure),
21
+ }));
22
+
23
+ /** The best-effort repair cannot replace the existing selected error. */
24
+ function finalizePreDispatchFailure(state: Execution, message: string) {
25
+ return Effect.gen(function*() {
26
+ if (!state.journalTerminal) {
27
+ yield* state.record({ type: "finished", status: "failed", finalOrigin: null, error: message, at: yield* state.clock() });
28
+ }
29
+ yield* state.projectJournal;
30
+ yield* state.refreshReceipt;
31
+ }).pipe(Effect.either, Effect.asVoid);
32
+ }
33
+
34
+ function prepareAndExecute(
35
+ platform: Platform, invocation: PreparedInvocation, digest: string, options: RunPreparedOptions,
36
+ ): Effect.Effect<InvocationResult, ConfirmedWriteFailure> {
37
+ return Effect.gen(function*() {
38
+ const checked = yield* platform.checkExecution(invocation, options);
39
+ if (checked.operation.risk === "R4") return yield* refuse("confirmation", "R4 capabilities are blocked by wrench");
40
+ const claims = yield* platform.repairClaims;
41
+ const journals = yield* platform.repairJournals(yield* platform.currentTime(options));
42
+ if (claims.invalid > 0 || journals.issues.length > 0) {
43
+ return yield* refuse("confirmation", "local execution recovery has unresolved state; run ghostget doctor before starting another write");
44
+ }
45
+ const state = yield* platform.execution(checked, digest, options);
46
+ const requestJournal = yield* state.journalRequest;
47
+ const created = yield* Effect.either(state.createJournal(requestJournal));
48
+ if (Either.isLeft(created)) {
49
+ return yield* confirmedWriteFinally(Effect.fail(created.left), state.releaseClaim);
50
+ }
51
+ if (!(yield* state.removePlan)) {
52
+ yield* confirmedWriteFinally(Effect.gen(function*() {
53
+ yield* state.record({ type: "finished", status: "failed", finalOrigin: null, error: "confirmation ownership was lost before plan consumption", at: state.startedAt });
54
+ yield* state.projectJournal;
55
+ }), state.releaseClaim);
56
+ return yield* refuse("confirmation", "confirmation plan was already consumed or cancelled");
57
+ }
58
+ const consumed = yield* Effect.either(state.record({ type: "confirmation-consumed", at: state.startedAt }));
59
+ if (Either.isLeft(consumed)) return yield* wrapped(consumed.left,
60
+ "confirmation plan was consumed, but its run journal could not claim ownership; run ghostget doctor before retrying");
61
+ if (!(yield* state.releaseClaim)) {
62
+ yield* Effect.either(Effect.gen(function*() {
63
+ yield* state.record({ type: "finished", status: "failed", finalOrigin: null, error: "confirmation ownership claim changed before release", at: state.startedAt });
64
+ yield* state.projectJournal;
65
+ })).pipe(Effect.asVoid);
66
+ return yield* refuse("confirmation", "confirmation ownership claim changed unexpectedly; run ghostget doctor before retrying");
67
+ }
68
+ yield* state.refreshReceipt;
69
+ const provisional = yield* Effect.either(state.persistProvisional);
70
+ if (Either.isLeft(provisional)) {
71
+ yield* Effect.either(Effect.gen(function*() {
72
+ yield* state.record({
73
+ type: "finished", status: "failed", finalOrigin: null,
74
+ error: "provisional receipt could not be projected before dispatch", at: yield* state.clock()
75
+ });
76
+ })).pipe(Effect.asVoid);
77
+ return yield* wrapped(provisional.left, "refusing to start execution because its provisional receipt could not be stored");
78
+ }
79
+ const request = yield* state.ledgerRequest;
80
+ const ledger = yield* Effect.either(state.acquireLedger(request));
81
+ if (Either.isLeft(ledger)) {
82
+ yield* finalizePreDispatchFailure(state, "idempotency state could not be inspected before dispatch");
83
+ return yield* wrapped(ledger.left, "refusing to start a remote write because its idempotency state could not be inspected");
84
+ }
85
+ const acquired = ledger.right;
86
+ if (!acquired.acquired) {
87
+ yield* finalizePreDispatchFailure(state, "another run already owns this idempotency scope");
88
+ if (acquired.existing.inputHash !== state.inputHash || acquired.existing.adapterHash !== state.adapter.hash || acquired.existing.authHash !== state.auth.hash) {
89
+ return yield* refuse("journal", "idempotency key was already used in a different action scope");
90
+ }
91
+ if (acquired.existing.status === "succeeded") return {
92
+ receipt: yield* state.readReceipt(acquired.existing.runId), output: null, replayed: true, privateArtifactsPreserved: false,
93
+ };
94
+ return yield* refuse("journal", `a prior attempt (${acquired.existing.runId}) may have reached the provider; inspect 'ghostget runs show ${acquired.existing.runId}' and reconcile it before retrying`);
95
+ }
96
+ const claimed = yield* Effect.either(Effect.gen(function*() {
97
+ yield* state.record({ type: "ledger-claimed", ledgerRelativePath: yield* state.ledgerRelativePath(acquired.snapshot.path), at: yield* state.clock() });
98
+ }));
99
+ if (Either.isLeft(claimed)) {
100
+ yield* finalizePreDispatchFailure(state, "idempotency claim could not be bound to the run journal");
101
+ return yield* wrapped(claimed.left, "refusing to start a remote write because its run journal could not claim the idempotency ledger");
102
+ }
103
+ const recovery = yield* Effect.either(Effect.gen(function*() {
104
+ yield* state.storeCapsule;
105
+ yield* state.record({ type: "recovery-stored", at: yield* state.clock() });
106
+ yield* state.refreshReceipt;
107
+ }));
108
+ if (Either.isLeft(recovery)) {
109
+ yield* finalizePreDispatchFailure(state, "encrypted recovery state could not be made durable before dispatch");
110
+ return yield* wrapped(recovery.left, "refusing to start a remote write because its encrypted recovery capsule could not be stored");
111
+ }
112
+ const maxOutputBytes = yield* state.outputLimit;
113
+ const dispatched = yield* Effect.either(state.dispatch);
114
+ const projected = yield* state.projectExecution(Either.isRight(dispatched)
115
+ ? { status: "fulfilled", value: dispatched.right }
116
+ : { status: "rejected", reason: dispatched.left.cause }, maxOutputBytes);
117
+ const terminal = yield* Effect.either(state.record(projected.terminalEvent));
118
+ if (Either.isLeft(terminal)) yield* Effect.either(state.reloadJournal).pipe(Effect.asVoid);
119
+ if (state.journalTerminal) yield* Effect.either(state.projectJournal).pipe(Effect.asVoid);
120
+ return yield* state.result(projected.result);
121
+ });
122
+ }
123
+
124
+ /** Admission and actual physical cleanup belong to the same program as dispatch. */
125
+ function withWriteAdmission(
126
+ platform: Platform, invocation: PreparedInvocation, digest: string, options: RunPreparedOptions,
127
+ ): Effect.Effect<InvocationResult, ConfirmedWriteFailure> {
128
+ return Effect.gen(function*() {
129
+ const selected = yield* platform.admission(invocation, options);
130
+ if (selected.cleanupIdentity !== null) {
131
+ const admission = yield* platform.acquireWeb(selected.cleanupIdentity, options.now);
132
+ const settlement = yield* Effect.exit(Effect.gen(function*() {
133
+ const outcome = yield* Effect.exit(prepareAndExecute(platform, selected.invocation, digest,
134
+ { ...selected.options, registerCleanupBarrier: admission.registerCleanupBarrier }));
135
+ const cleanup = Effect.gen(function*() {
136
+ yield* platform.cleanup(admission.closeRegistration);
137
+ const joined = yield* Effect.all(admission.barriers.map(barrier =>
138
+ Effect.either(platform.nativeCleanup(() => barrier))), { concurrency: "unbounded" });
139
+ yield* platform.cleanup(() => {
140
+ if (joined.some(Either.isLeft)) admission.cleanupUnsafe();
141
+ else { admission.cleanupComplete(); admission.release(); }
142
+ });
143
+ });
144
+ return yield* confirmedWriteFinally(outcome, cleanup);
145
+ }));
146
+ if (Exit.isFailure(settlement)) {
147
+ return yield* confirmedWriteFinally(settlement, platform.cleanup(() => {
148
+ admission.closeRegistration();
149
+ if (admission.current.claim.containment.status === "parent-owned") { admission.cleanupComplete(); admission.release(); }
150
+ }));
151
+ }
152
+ return settlement.value;
153
+ }
154
+ if (selected.portableIdentity !== null) {
155
+ const owned = yield* platform.acquirePortable(selected.portableIdentity, selected.options.runId, options.now);
156
+ const outcome = yield* Effect.exit(prepareAndExecute(platform, selected.invocation, digest, selected.options));
157
+ const cleanup = yield* Effect.exit(Effect.gen(function*() {
158
+ yield* platform.cleanup(owned.context.closeRegistration);
159
+ yield* platform.nativeCleanup(owned.context.join);
160
+ yield* platform.cleanup(owned.context.complete);
161
+ }));
162
+ if (Exit.isFailure(cleanup)) {
163
+ // A failed physical proof never releases the durable lease.
164
+ return yield* confirmedWriteFinally(confirmedWriteFinally(outcome, cleanup),
165
+ platform.cleanup(() => {
166
+ const selected = Cause.failureOption(cleanup.cause);
167
+ owned.context.unsafe(Option.isSome(selected) ? selected.value.cause : Cause.squash(cleanup.cause));
168
+ }));
169
+ }
170
+ return yield* confirmedWriteFinally(outcome, platform.cleanup(owned.release));
171
+ }
172
+ return yield* prepareAndExecute(platform, selected.invocation, digest, selected.options);
173
+ });
174
+ }
175
+
176
+ export function confirmedWriteProgram(digest: string): Effect.Effect<InvocationResult, ConfirmedWriteFailure, ConfirmedWritePlatform> {
177
+ return Effect.gen(function*() {
178
+ const platform = yield* ConfirmedWritePlatform;
179
+ const initial = yield* platform.initialPlan(digest);
180
+ if (initial.plan.messagingComposite !== undefined) return yield* refuse("confirmation",
181
+ "messaging composite execution is unavailable until a reviewed provider executor is installed");
182
+ const configured = yield* platform.configure;
183
+ const claims = yield* platform.repairClaims;
184
+ const journals = yield* platform.repairJournals(configured.observedAt);
185
+ if (claims.invalid > 0 || journals.issues.length > 0) return yield* refuse("confirmation",
186
+ "local execution recovery has unresolved state; run ghostget doctor before confirming");
187
+ const runId = yield* platform.newRunId;
188
+ const claim = yield* platform.claim(digest, runId, configured.observedAt);
189
+ let stored: StoredPlan | null = null;
190
+ let invocation: PreparedInvocation | null = null;
191
+ const selected = yield* confirmedWriteFinally(Effect.gen(function*() {
192
+ stored = yield* platform.initialPlan(digest);
193
+ invocation = yield* platform.freshPlan(stored, configured.registry, configured.observedAt);
194
+ if (stored.plan.duplicateRisk !== undefined) {
195
+ const current = yield* platform.duplicateRisk(stored);
196
+ if (current === undefined || canonicalJson(current) !== canonicalJson(stored.plan.duplicateRisk)) {
197
+ return yield* refuse("confirmation", "duplicate-risk source evidence changed after preview; inspect the source run and preview again");
198
+ }
199
+ }
200
+ return { stored, invocation };
201
+ }), Effect.gen(function*() {
202
+ // Preserve the existing duplicate-risk mismatch witness: invocation has
203
+ // already been assigned, so this branch deliberately retains its claim.
204
+ if (invocation === null && stored !== null) {
205
+ if (yield* platform.removePlan(digest)) {
206
+ if (!(yield* platform.retainingJournal(digest))) yield* platform.cleanupAssets(digest);
207
+ }
208
+ yield* platform.releaseClaim(claim);
209
+ } else if (invocation === null) yield* platform.releaseClaim(claim);
210
+ }));
211
+ let started = false;
212
+ return yield* confirmedWriteFinally(Effect.gen(function*() {
213
+ const prepared = yield* platform.prepareOptions(selected.invocation, selected.stored, claim, runId, configured.registry);
214
+ started = true;
215
+ return yield* withWriteAdmission(platform, selected.invocation, digest, prepared);
216
+ }), Effect.gen(function*() {
217
+ if (!started) {
218
+ yield* platform.removePlan(digest);
219
+ if (!(yield* platform.retainingJournal(digest))) yield* platform.cleanupAssets(digest);
220
+ }
221
+ yield* platform.releaseClaim(claim);
222
+ }));
223
+ }).pipe(Effect.uninterruptible);
224
+ }
@@ -0,0 +1,16 @@
1
+ import * as Cause from "effect/Cause";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Exit from "effect/Exit";
4
+ import * as Option from "effect/Option";
5
+ import type { ConfirmedWriteFailure } from "./confirmed-write-failure";
6
+
7
+ /** The only interpreter for a closed, complete confirmed-write invocation. */
8
+ export async function runConfirmedWrite<A>(
9
+ program: Effect.Effect<A, ConfirmedWriteFailure>,
10
+ ): Promise<A> {
11
+ const exit = await Effect.runPromiseExit(program.pipe(Effect.uninterruptible));
12
+ if (Exit.isSuccess(exit)) return exit.value;
13
+ const selected = Cause.failureOption(exit.cause);
14
+ if (Option.isSome(selected)) throw selected.value.cause;
15
+ throw Cause.squash(exit.cause);
16
+ }
@@ -0,0 +1,228 @@
1
+ import {
2
+ createCipheriv,
3
+ createDecipheriv,
4
+ randomBytes,
5
+ } from "node:crypto";
6
+ import { join } from "node:path";
7
+
8
+ import { canonicalJson } from "./canonical-json";
9
+ import {
10
+ createPrivateJsonIfAbsent,
11
+ readRegularFile,
12
+ ghostgetStateHome,
13
+ } from "./storage";
14
+
15
+ const TOKEN_VERSION = "smn1";
16
+ const TOKEN_PREFIX = `${TOKEN_VERSION}.`;
17
+ const KEY_FILE_NAME = ".cursor-encryption-key";
18
+ const KEY_BYTES = 32;
19
+ const IV_BYTES = 12;
20
+ const AUTH_TAG_BYTES = 16;
21
+ const MAX_KEY_FILE_BYTES = 128;
22
+ const MAX_TOKEN_CHARACTERS = 8192;
23
+ const MAX_ENVELOPE_BYTES = Math.floor(
24
+ ((MAX_TOKEN_CHARACTERS - TOKEN_PREFIX.length) * 3) / 4,
25
+ );
26
+ const MAX_PLAINTEXT_BYTES = MAX_ENVELOPE_BYTES - IV_BYTES - AUTH_TAG_BYTES;
27
+
28
+ type Environment = Readonly<Record<string, string | undefined>>;
29
+
30
+ function invalidToken(message: "authentication failed" | "is malformed"): Error {
31
+ return new Error(`cursor token ${message}`);
32
+ }
33
+
34
+ function validateCoordinates(
35
+ scope: string,
36
+ authId: string,
37
+ authHash: string,
38
+ ): void {
39
+ if (
40
+ typeof scope !== "string"
41
+ || !/^[a-z][a-z0-9-]{0,47}$/u.test(scope)
42
+ ) {
43
+ throw new Error("cursor-token scope must be lowercase kebab-case");
44
+ }
45
+ if (
46
+ typeof authId !== "string"
47
+ || !/^[a-z][a-z0-9-]{0,47}$/u.test(authId)
48
+ ) {
49
+ throw new Error("cursor-token auth ID must be lowercase kebab-case");
50
+ }
51
+ if (
52
+ typeof authHash !== "string"
53
+ || !/^[a-f0-9]{64}$/u.test(authHash)
54
+ ) {
55
+ throw new Error("cursor-token auth hash is malformed");
56
+ }
57
+ }
58
+
59
+ function isRecord(value: unknown): value is Record<string, unknown> {
60
+ return typeof value === "object" && value !== null && !Array.isArray(value);
61
+ }
62
+
63
+ function keyPath(environment: Environment): string {
64
+ return join(ghostgetStateHome(environment), KEY_FILE_NAME);
65
+ }
66
+
67
+ function cursorEncryptionKey(
68
+ environment: Environment,
69
+ createIfMissing: boolean,
70
+ ): Buffer {
71
+ const path = keyPath(environment);
72
+ if (createIfMissing) {
73
+ createPrivateJsonIfAbsent(path, {
74
+ schemaVersion: 1,
75
+ key: randomBytes(KEY_BYTES).toString("hex"),
76
+ }, {
77
+ environment,
78
+ privateParent: true,
79
+ });
80
+ }
81
+ const text = readRegularFile(path, MAX_KEY_FILE_BYTES, "cursor encryption key");
82
+ let parsed: unknown;
83
+ try {
84
+ parsed = JSON.parse(text) as unknown;
85
+ } catch {
86
+ throw new Error("cursor encryption key is malformed");
87
+ }
88
+ if (
89
+ !isRecord(parsed)
90
+ || Object.keys(parsed).sort().join(",") !== "key,schemaVersion"
91
+ || parsed.schemaVersion !== 1
92
+ || typeof parsed.key !== "string"
93
+ || !/^[a-f0-9]{64}$/u.test(parsed.key)
94
+ ) {
95
+ throw new Error("cursor encryption key is malformed");
96
+ }
97
+ return Buffer.from(parsed.key, "hex");
98
+ }
99
+
100
+ function additionalData(
101
+ scope: string,
102
+ authId: string,
103
+ authHash: string,
104
+ ): Buffer {
105
+ return Buffer.from(
106
+ `io-cursor-token\0${TOKEN_VERSION}\0${scope}\0${authId}\0${authHash}`,
107
+ "utf8",
108
+ );
109
+ }
110
+
111
+ function canonicalPayload(payload: unknown): Buffer {
112
+ let encoded: string;
113
+ try {
114
+ encoded = canonicalJson(payload);
115
+ } catch {
116
+ throw new Error("cursor-token payload must be JSON-compatible");
117
+ }
118
+ const bytes = Buffer.from(encoded, "utf8");
119
+ if (bytes.byteLength > MAX_PLAINTEXT_BYTES) {
120
+ throw new Error("cursor-token payload exceeds its size bound");
121
+ }
122
+ return bytes;
123
+ }
124
+
125
+ function decodeEnvelope(token: string): {
126
+ readonly ciphertext: Buffer;
127
+ readonly iv: Buffer;
128
+ readonly tag: Buffer;
129
+ } {
130
+ if (
131
+ typeof token !== "string"
132
+ || token.length > MAX_TOKEN_CHARACTERS
133
+ || !token.startsWith(TOKEN_PREFIX)
134
+ ) {
135
+ throw invalidToken("is malformed");
136
+ }
137
+ const encoded = token.slice(TOKEN_PREFIX.length);
138
+ if (encoded === "" || /[^A-Za-z0-9_-]/u.test(encoded)) {
139
+ throw invalidToken("is malformed");
140
+ }
141
+ let envelope: Buffer;
142
+ try {
143
+ envelope = Buffer.from(encoded, "base64url");
144
+ } catch {
145
+ throw invalidToken("is malformed");
146
+ }
147
+ if (
148
+ envelope.toString("base64url") !== encoded
149
+ || envelope.byteLength <= IV_BYTES + AUTH_TAG_BYTES
150
+ || envelope.byteLength > MAX_ENVELOPE_BYTES
151
+ ) {
152
+ throw invalidToken("is malformed");
153
+ }
154
+ return {
155
+ iv: envelope.subarray(0, IV_BYTES),
156
+ ciphertext: envelope.subarray(IV_BYTES, -AUTH_TAG_BYTES),
157
+ tag: envelope.subarray(-AUTH_TAG_BYTES),
158
+ };
159
+ }
160
+
161
+ export function sealCursorToken(
162
+ scope: string,
163
+ authId: string,
164
+ authHash: string,
165
+ payload: unknown,
166
+ environment: Environment = process.env,
167
+ ): string {
168
+ validateCoordinates(scope, authId, authHash);
169
+ const plaintext = canonicalPayload(payload);
170
+ const iv = randomBytes(IV_BYTES);
171
+ const cipher = createCipheriv(
172
+ "aes-256-gcm",
173
+ cursorEncryptionKey(environment, true),
174
+ iv,
175
+ );
176
+ cipher.setAAD(additionalData(scope, authId, authHash));
177
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
178
+ const envelope = Buffer.concat([iv, ciphertext, cipher.getAuthTag()]);
179
+ const token = `${TOKEN_PREFIX}${envelope.toString("base64url")}`;
180
+ if (token.length > MAX_TOKEN_CHARACTERS) {
181
+ throw new Error("cursor-token payload exceeds its size bound");
182
+ }
183
+ return token;
184
+ }
185
+
186
+ export function openCursorToken(
187
+ scope: string,
188
+ authId: string,
189
+ authHash: string,
190
+ token: string,
191
+ environment: Environment = process.env,
192
+ ): unknown {
193
+ validateCoordinates(scope, authId, authHash);
194
+ const { ciphertext, iv, tag } = decodeEnvelope(token);
195
+ let plaintext: Buffer;
196
+ try {
197
+ const decipher = createDecipheriv(
198
+ "aes-256-gcm",
199
+ cursorEncryptionKey(environment, false),
200
+ iv,
201
+ );
202
+ decipher.setAAD(additionalData(scope, authId, authHash));
203
+ decipher.setAuthTag(tag);
204
+ plaintext = Buffer.concat([
205
+ decipher.update(ciphertext),
206
+ decipher.final(),
207
+ ]);
208
+ } catch {
209
+ throw invalidToken("authentication failed");
210
+ }
211
+ if (plaintext.byteLength > MAX_PLAINTEXT_BYTES) {
212
+ throw invalidToken("is malformed");
213
+ }
214
+ let text: string;
215
+ let payload: unknown;
216
+ try {
217
+ text = new TextDecoder("utf-8", { fatal: true }).decode(plaintext);
218
+ payload = JSON.parse(text) as unknown;
219
+ } catch {
220
+ throw invalidToken("is malformed");
221
+ }
222
+ try {
223
+ if (canonicalJson(payload) !== text) throw invalidToken("is malformed");
224
+ } catch {
225
+ throw invalidToken("is malformed");
226
+ }
227
+ return payload;
228
+ }