@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,176 @@
1
+ import type { OperationInput } from "./model";
2
+ import type { ProviderPluginOperationName } from "./provider-plugin-identifiers";
3
+
4
+ function hasUnsafeControl(value: string): boolean {
5
+ for (let index = 0; index < value.length; index += 1) {
6
+ const code = value.charCodeAt(index);
7
+ if (
8
+ code <= 0x1f
9
+ || code === 0x7f
10
+ || (code >= 0x80 && code <= 0x9f)
11
+ || code === 0x061c
12
+ || code === 0x200e
13
+ || code === 0x200f
14
+ || (code >= 0x2028 && code <= 0x202e)
15
+ || (code >= 0x2066 && code <= 0x2069)
16
+ ) {
17
+ return true;
18
+ }
19
+ }
20
+ return false;
21
+ }
22
+
23
+ function isWellFormedText(value: string): boolean {
24
+ for (let index = 0; index < value.length; index += 1) {
25
+ const code = value.charCodeAt(index);
26
+ if (code >= 0xd800 && code <= 0xdbff) {
27
+ const next = value.charCodeAt(index + 1);
28
+ if (!(next >= 0xdc00 && next <= 0xdfff)) return false;
29
+ index += 1;
30
+ } else if (code >= 0xdc00 && code <= 0xdfff) return false;
31
+ }
32
+ return true;
33
+ }
34
+
35
+ function boundedTextIssue(
36
+ name: string,
37
+ value: unknown,
38
+ maximum: number,
39
+ ): string | null {
40
+ if (typeof value !== "string") return null;
41
+ return value.length < 1
42
+ || value.length > maximum
43
+ || !isWellFormedText(value)
44
+ || hasUnsafeControl(value)
45
+ ? `input.${name} must be a 1-${maximum} character string without unsafe controls`
46
+ : null;
47
+ }
48
+
49
+ function safeIntegerIssue(name: string, value: unknown): string | null {
50
+ return typeof value === "number" && !Number.isSafeInteger(value)
51
+ ? `input.${name} must be a safe integer`
52
+ : null;
53
+ }
54
+
55
+ export function gmailProviderConditionalInputIssues(
56
+ action: ProviderPluginOperationName,
57
+ input: OperationInput,
58
+ contractVersion = 5,
59
+ ): readonly string[] {
60
+ const issues: string[] = [];
61
+ const cursorIssue = boundedTextIssue("cursor", input.cursor, 4_096);
62
+ if (cursorIssue !== null) issues.push(cursorIssue);
63
+
64
+ if (action === "contacts.list") {
65
+ const limitIssue = safeIntegerIssue("limit", input.limit);
66
+ if (limitIssue !== null) issues.push(limitIssue);
67
+ if (contractVersion === 4 && input.include_dates !== undefined) {
68
+ issues.push("input.include_dates is available only in contacts.list contract v5");
69
+ }
70
+ if (input.collection === "interactions") {
71
+ if (contractVersion !== 4 && input.include_dates !== undefined) {
72
+ issues.push("input.include_dates is not accepted for the interactions collection");
73
+ }
74
+ if (input.include_stats !== undefined) {
75
+ issues.push("input.include_stats is not accepted for the interactions collection");
76
+ }
77
+ if (input.stats_scan_limit !== undefined) {
78
+ issues.push("input.stats_scan_limit is not accepted for the interactions collection");
79
+ }
80
+ if (typeof input.before !== "string") {
81
+ issues.push("input.before is required for the interactions collection");
82
+ } else {
83
+ const parsed = new Date(input.before);
84
+ if (
85
+ !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.000Z$/u.test(input.before)
86
+ || !Number.isFinite(parsed.getTime())
87
+ || parsed.toISOString() !== input.before
88
+ ) issues.push("input.before must be a canonical whole-second UTC timestamp");
89
+ }
90
+ if (input.after !== undefined) {
91
+ if (typeof input.after !== "string") {
92
+ issues.push("input.after must be a canonical whole-second UTC timestamp");
93
+ } else {
94
+ const parsed = new Date(input.after);
95
+ if (
96
+ !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.000Z$/u.test(input.after)
97
+ || !Number.isFinite(parsed.getTime())
98
+ || parsed.toISOString() !== input.after
99
+ ) issues.push("input.after must be a canonical whole-second UTC timestamp");
100
+ }
101
+ if (
102
+ typeof input.before === "string"
103
+ && typeof input.after === "string"
104
+ && new Date(input.after).getTime() >= new Date(input.before).getTime()
105
+ ) issues.push("input.after must precede input.before");
106
+ }
107
+ } else {
108
+ if (input.before !== undefined) {
109
+ issues.push("input.before is accepted only for the interactions collection");
110
+ }
111
+ if (input.after !== undefined) {
112
+ issues.push("input.after is accepted only for the interactions collection");
113
+ }
114
+ const statsScanLimitIssue = safeIntegerIssue("stats_scan_limit", input.stats_scan_limit);
115
+ if (statsScanLimitIssue !== null) issues.push(statsScanLimitIssue);
116
+ if (input.include_stats === false && input.stats_scan_limit !== undefined) {
117
+ issues.push("input.stats_scan_limit is accepted only when include_stats is true");
118
+ }
119
+ if (
120
+ contractVersion !== 4
121
+ && input.collection === "other-contacts"
122
+ && input.include_dates === true
123
+ ) {
124
+ issues.push("input.include_dates is supported only for saved contacts");
125
+ }
126
+ const limit = input.limit ?? 20;
127
+ const statsScanLimit = input.stats_scan_limit ?? 100;
128
+ if (
129
+ input.include_stats !== false
130
+ &&
131
+ typeof limit === "number"
132
+ && Number.isSafeInteger(limit)
133
+ && limit >= 1
134
+ && limit <= 100
135
+ && typeof statsScanLimit === "number"
136
+ && Number.isSafeInteger(statsScanLimit)
137
+ && statsScanLimit >= 1
138
+ && statsScanLimit <= 2_000
139
+ && limit * statsScanLimit > 2_000
140
+ ) {
141
+ issues.push("input.limit multiplied by input.stats_scan_limit must not exceed 2000");
142
+ }
143
+ }
144
+ }
145
+
146
+ if (action === "messaging.list") {
147
+ const limitIssue = safeIntegerIssue("limit", input.limit);
148
+ if (limitIssue !== null) issues.push(limitIssue);
149
+ if (input.view === "search") {
150
+ if (typeof input.query !== "string") {
151
+ issues.push("input.query is required when view is search");
152
+ } else {
153
+ const queryIssue = boundedTextIssue("query", input.query, 512);
154
+ if (queryIssue !== null) issues.push(queryIssue);
155
+ else if (input.query.trim().length === 0) {
156
+ issues.push("input.query must contain a non-whitespace search expression");
157
+ }
158
+ }
159
+ }
160
+ if (input.view === "inbox" && input.query !== undefined) {
161
+ issues.push("input.query is not accepted when view is inbox");
162
+ }
163
+ if (input.view === "inbox" && input.include_spam_trash !== undefined) {
164
+ issues.push("input.include_spam_trash is accepted only when view is search");
165
+ }
166
+ }
167
+
168
+ if (
169
+ action === "messaging.read"
170
+ && typeof input.thread_id === "string"
171
+ && !/^[A-Za-z0-9_-]{1,256}$/u.test(input.thread_id)
172
+ ) {
173
+ issues.push("input.thread_id must be an exact bounded Gmail thread ID");
174
+ }
175
+ return issues;
176
+ }
@@ -0,0 +1,77 @@
1
+ import type { OperationInput } from "./model";
2
+ import type { ProviderPluginOperationName } from "./provider-plugin-identifiers";
3
+
4
+ export function linkedinProviderConditionalInputIssues(
5
+ action: ProviderPluginOperationName,
6
+ input: OperationInput,
7
+ ): readonly string[] {
8
+ const issues: string[] = [];
9
+ if (action === "contacts.list") {
10
+ for (const name of ["start", "count"] as const) {
11
+ if (typeof input[name] === "number" && !Number.isSafeInteger(input[name])) {
12
+ issues.push(`input.${name} must be a safe integer`);
13
+ }
14
+ }
15
+ }
16
+ if (action === "posts.read") {
17
+ if (input.mode === "one") {
18
+ if (typeof input.post_urn !== "string") {
19
+ issues.push("input.post_urn is required when mode is one");
20
+ }
21
+ if (input.author !== undefined) {
22
+ issues.push("input.author is not accepted when mode is one");
23
+ }
24
+ }
25
+ if (input.mode === "author") {
26
+ if (typeof input.author !== "string") {
27
+ issues.push("input.author is required when mode is author");
28
+ }
29
+ if (input.post_urn !== undefined) {
30
+ issues.push("input.post_urn is not accepted when mode is author");
31
+ }
32
+ }
33
+ }
34
+ if (action === "posts.publish") {
35
+ const media = input.media;
36
+ if (Array.isArray(media) && typeof input.article_url === "string") {
37
+ issues.push("input.media and input.article_url are mutually exclusive");
38
+ }
39
+ if (typeof input.article_url === "string") {
40
+ try {
41
+ const url = new URL(input.article_url);
42
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "") {
43
+ issues.push("input.article_url must be a credential-free HTTPS URL");
44
+ }
45
+ } catch {
46
+ issues.push("input.article_url must be a credential-free HTTPS URL");
47
+ }
48
+ if (typeof input.article_title !== "string") {
49
+ issues.push("input.article_title is required with input.article_url");
50
+ }
51
+ } else if (
52
+ input.article_title !== undefined
53
+ || input.article_description !== undefined
54
+ ) {
55
+ issues.push(
56
+ "input.article_title and input.article_description require input.article_url",
57
+ );
58
+ }
59
+ if (input.alt_text !== undefined && !Array.isArray(media)) {
60
+ issues.push("input.alt_text requires input.media");
61
+ }
62
+ if (input.media_title !== undefined && !Array.isArray(media)) {
63
+ issues.push("input.media_title requires input.media");
64
+ }
65
+ }
66
+ if (action === "reactions.set") {
67
+ if (input.enabled === true && typeof input.reaction !== "string") {
68
+ issues.push("input.reaction is required when input.enabled is true");
69
+ }
70
+ if (input.enabled === false && input.reaction !== undefined) {
71
+ issues.push(
72
+ "input.reaction is not accepted when input.enabled is false because the operation clears any current reaction",
73
+ );
74
+ }
75
+ }
76
+ return issues;
77
+ }
@@ -0,0 +1,304 @@
1
+ import type { OperationInput } from "./model";
2
+ import type { ProviderPluginOperationName } from "./provider-plugin-identifiers";
3
+
4
+ function isFileInputValue(value: unknown): boolean {
5
+ return typeof value === "object"
6
+ && value !== null
7
+ && !Array.isArray(value)
8
+ && (value as { readonly kind?: unknown }).kind === "file"
9
+ && typeof (value as { readonly reference?: unknown }).reference === "string";
10
+ }
11
+
12
+ export function xProviderConditionalInputIssues(
13
+ action: ProviderPluginOperationName,
14
+ input: OperationInput,
15
+ ): readonly string[] {
16
+ const issues: string[] = [];
17
+ const xId = (name: string, value: unknown): void => {
18
+ if (typeof value === "string" && !/^[0-9]{1,19}$/u.test(value)) {
19
+ issues.push(`input.${name} must be a 1-19 digit X object ID`);
20
+ }
21
+ };
22
+ const xConversationId = (name: string, value: unknown): void => {
23
+ if (
24
+ typeof value === "string"
25
+ && !/^(?:[0-9]{15,19}|[0-9]{1,19}-[0-9]{1,19})$/u.test(value)
26
+ ) {
27
+ issues.push(`input.${name} must be an exact legacy X DM conversation ID`);
28
+ }
29
+ };
30
+ if (action === "feeds.read") {
31
+ const feedLabel = typeof input.feed === "string" ? input.feed : "unknown feed";
32
+ if (
33
+ (input.feed === "user" || input.feed === "mentions")
34
+ && typeof input.user_id !== "string"
35
+ ) {
36
+ issues.push("input.user_id is required for user and mentions feeds");
37
+ }
38
+ if (input.feed === "list" && typeof input.list_id !== "string") {
39
+ issues.push("input.list_id is required for the list feed");
40
+ }
41
+ if (input.feed === "recent-search" && typeof input.query !== "string") {
42
+ issues.push("input.query is required for recent-search");
43
+ }
44
+ if (
45
+ input.feed !== "user"
46
+ && input.feed !== "mentions"
47
+ && input.user_id !== undefined
48
+ ) {
49
+ issues.push("input.user_id is accepted only for user and mentions feeds");
50
+ }
51
+ if (input.feed !== "list" && input.list_id !== undefined) {
52
+ issues.push("input.list_id is accepted only for the list feed");
53
+ }
54
+ if (input.feed !== "recent-search" && input.query !== undefined) {
55
+ issues.push("input.query is accepted only for recent-search");
56
+ }
57
+ const supportsTimeAndIdBounds = input.feed === "home-reverse-chronological"
58
+ || input.feed === "user"
59
+ || input.feed === "mentions"
60
+ || input.feed === "recent-search";
61
+ for (const name of ["since_id", "until_id", "start_time", "end_time"] as const) {
62
+ if (!supportsTimeAndIdBounds && input[name] !== undefined) {
63
+ issues.push(`input.${name} is not accepted for ${feedLabel}`);
64
+ }
65
+ }
66
+ if (input.feed !== "home-reverse-chronological" && input.feed !== "user") {
67
+ if (input.exclude_replies !== undefined) {
68
+ issues.push(`input.exclude_replies is not accepted for ${feedLabel}`);
69
+ }
70
+ if (input.exclude_reposts !== undefined) {
71
+ issues.push(`input.exclude_reposts is not accepted for ${feedLabel}`);
72
+ }
73
+ }
74
+ if (input.feed !== "recent-search" && input.sort !== undefined) {
75
+ issues.push("input.sort is accepted only for recent-search");
76
+ }
77
+ xId("user_id", input.user_id);
78
+ xId("list_id", input.list_id);
79
+ xId("since_id", input.since_id);
80
+ xId("until_id", input.until_id);
81
+ if (
82
+ (input.feed === "user" || input.feed === "mentions")
83
+ && typeof input.limit === "number"
84
+ && input.limit < 5
85
+ ) {
86
+ issues.push("input.limit must be at least 5 for user and mentions feeds");
87
+ }
88
+ if (
89
+ input.feed === "recent-search"
90
+ && typeof input.limit === "number"
91
+ && input.limit < 10
92
+ ) {
93
+ issues.push("input.limit must be at least 10 for recent-search");
94
+ }
95
+ for (const name of ["start_time", "end_time"] as const) {
96
+ const value = input[name];
97
+ if (
98
+ typeof value === "string"
99
+ && (
100
+ !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?Z$/u.test(value)
101
+ || !Number.isFinite(Date.parse(value))
102
+ )
103
+ ) {
104
+ issues.push(`input.${name} must be a valid UTC RFC 3339 timestamp`);
105
+ }
106
+ }
107
+ }
108
+ if (action === "posts.read" && Array.isArray(input.post_ids)) {
109
+ for (const [index, value] of input.post_ids.entries()) {
110
+ xId(`post_ids[${index}]`, value);
111
+ }
112
+ if (
113
+ input.post_ids.every((value) => typeof value === "string")
114
+ && new Set(input.post_ids).size !== input.post_ids.length
115
+ ) {
116
+ issues.push("input.post_ids must contain unique X post IDs");
117
+ }
118
+ }
119
+ if (action === "comments.read") xId("post_id", input.post_id);
120
+ if (action === "messaging.list") {
121
+ const needsTarget = input.view === "participant"
122
+ || input.view === "conversation"
123
+ || input.view === "chat-events";
124
+ if (needsTarget && typeof input.target_id !== "string") {
125
+ issues.push(
126
+ "input.target_id is required for participant, conversation, and chat-events views",
127
+ );
128
+ }
129
+ if (
130
+ (input.view === "all" || input.view === "chat-conversations")
131
+ && input.target_id !== undefined
132
+ ) {
133
+ issues.push(`input.target_id is not accepted when view is ${input.view}`);
134
+ }
135
+ if (input.view === "participant") xId("target_id", input.target_id);
136
+ if (input.view === "conversation") {
137
+ xConversationId("target_id", input.target_id);
138
+ }
139
+ if (
140
+ input.view === "chat-events"
141
+ && typeof input.target_id === "string"
142
+ && !/^(?:[0-9]{1,19}|[0-9]{1,19}-[0-9]{1,19}|g[0-9]{1,19})$/u.test(
143
+ input.target_id,
144
+ )
145
+ ) {
146
+ issues.push(
147
+ "input.target_id must be an exact X Chat recipient or conversation ID",
148
+ );
149
+ }
150
+ if (
151
+ input.view === "chat-events"
152
+ && typeof input.target_id === "string"
153
+ && input.target_id.includes("-")
154
+ ) {
155
+ const [left, right] = input.target_id.split("-");
156
+ if (left === right) {
157
+ issues.push(
158
+ "input.target_id must identify two different X Chat participants",
159
+ );
160
+ }
161
+ }
162
+ }
163
+ if (action === "messaging.read") xId("event_id", input.event_id);
164
+ if (action === "messaging.send") {
165
+ if (input.target_kind === "participant") xId("target_id", input.target_id);
166
+ if (input.target_kind === "conversation") {
167
+ xConversationId("target_id", input.target_id);
168
+ }
169
+ if (
170
+ input.target_kind === "conversation"
171
+ && typeof input.target_id === "string"
172
+ && input.target_id.includes("-")
173
+ ) {
174
+ const [left, right] = input.target_id.split("-");
175
+ if (left === right) {
176
+ issues.push(
177
+ "input.target_id must identify two different legacy X DM participants",
178
+ );
179
+ }
180
+ }
181
+ if (typeof input.body !== "string" && !isFileInputValue(input.media)) {
182
+ issues.push("input.body or input.media is required for an X Direct Message");
183
+ }
184
+ if (input.media_alt_text !== undefined && !isFileInputValue(input.media)) {
185
+ issues.push("input.media_alt_text requires input.media");
186
+ }
187
+ }
188
+ if (action === "posts.publish") {
189
+ const hasPoll = Array.isArray(input.poll_options)
190
+ || input.poll_duration_minutes !== undefined;
191
+ if (
192
+ hasPoll
193
+ && (
194
+ !Array.isArray(input.poll_options)
195
+ || typeof input.poll_duration_minutes !== "number"
196
+ )
197
+ ) {
198
+ issues.push(
199
+ "input.poll_options and input.poll_duration_minutes must be supplied together",
200
+ );
201
+ }
202
+ if (hasPoll && Array.isArray(input.media)) {
203
+ issues.push("input.media and poll inputs are mutually exclusive");
204
+ }
205
+ if (
206
+ typeof input.body !== "string"
207
+ && !Array.isArray(input.media)
208
+ && !hasPoll
209
+ ) {
210
+ issues.push(
211
+ "input.body, input.media, or complete poll inputs are required for an X post",
212
+ );
213
+ }
214
+ if (input.made_with_ai === true && !Array.isArray(input.media)) {
215
+ issues.push(
216
+ "input.made_with_ai can be true only when reviewed media is attached",
217
+ );
218
+ }
219
+ if (input.media_alt_texts !== undefined) {
220
+ if (!Array.isArray(input.media)) {
221
+ issues.push("input.media_alt_texts requires input.media");
222
+ } else if (
223
+ !Array.isArray(input.media_alt_texts)
224
+ || input.media_alt_texts.length !== input.media.length
225
+ ) {
226
+ issues.push("input.media_alt_texts must align one-to-one with input.media");
227
+ }
228
+ }
229
+ xId("community_id", input.community_id);
230
+ }
231
+ if (action === "replies.create") {
232
+ xId("target_post_id", input.target_post_id);
233
+ if (typeof input.body !== "string" && !Array.isArray(input.media)) {
234
+ issues.push("input.body or input.media is required for an X reply");
235
+ }
236
+ if (input.media_alt_texts !== undefined) {
237
+ if (!Array.isArray(input.media)) {
238
+ issues.push("input.media_alt_texts requires input.media");
239
+ } else if (
240
+ !Array.isArray(input.media_alt_texts)
241
+ || input.media_alt_texts.length !== input.media.length
242
+ ) {
243
+ issues.push("input.media_alt_texts must align one-to-one with input.media");
244
+ }
245
+ }
246
+ }
247
+ if (action === "threads.publish") {
248
+ const media = input.media;
249
+ const indices = input.media_item_indices;
250
+ if (Array.isArray(media) !== Array.isArray(indices)) {
251
+ issues.push("input.media and input.media_item_indices must be supplied together");
252
+ }
253
+ if (
254
+ Array.isArray(media)
255
+ && Array.isArray(indices)
256
+ && media.length !== indices.length
257
+ ) {
258
+ issues.push("input.media_item_indices must align one-to-one with input.media");
259
+ }
260
+ if (Array.isArray(indices) && Array.isArray(input.items)) {
261
+ for (const [index, value] of indices.entries()) {
262
+ if (
263
+ typeof value === "number"
264
+ && (
265
+ !Number.isSafeInteger(value)
266
+ || value < 1
267
+ || value > input.items.length
268
+ )
269
+ ) {
270
+ issues.push(
271
+ `input.media_item_indices[${index}] must name an existing one-based thread item`,
272
+ );
273
+ }
274
+ }
275
+ }
276
+ if (input.media_alt_texts !== undefined) {
277
+ if (!Array.isArray(media)) {
278
+ issues.push("input.media_alt_texts requires input.media");
279
+ } else if (
280
+ !Array.isArray(input.media_alt_texts)
281
+ || input.media_alt_texts.length !== media.length
282
+ ) {
283
+ issues.push("input.media_alt_texts must align one-to-one with input.media");
284
+ }
285
+ }
286
+ }
287
+ if (action === "posts.repost" || action === "content.save") {
288
+ xId("post_id", input.post_id);
289
+ }
290
+ for (const name of ["limit", "poll_duration_minutes"] as const) {
291
+ const value = input[name];
292
+ if (typeof value === "number" && !Number.isSafeInteger(value)) {
293
+ issues.push(`input.${name} must be a safe integer`);
294
+ }
295
+ }
296
+ if (
297
+ (action === "articles.draft.save" || action === "articles.publish")
298
+ && input.cover_alt_text !== undefined
299
+ && !isFileInputValue(input.cover)
300
+ ) {
301
+ issues.push("input.cover_alt_text requires input.cover");
302
+ }
303
+ return issues;
304
+ }
@@ -0,0 +1,23 @@
1
+ import type { OperationInput } from "./model";
2
+ import type { ProviderContract } from "./provider-contract-definitions";
3
+
4
+ export function planProviderContractDispatches(
5
+ contract: ProviderContract,
6
+ input: OperationInput,
7
+ ): readonly { readonly id: string; readonly description: string }[] {
8
+ if (contract.dispatch === "none") return [];
9
+ if (contract.dispatch === "single") {
10
+ return [{
11
+ id: contract.operation.replaceAll(".", "-"),
12
+ description: `Execute ${contract.provider} ${contract.operation}`,
13
+ }];
14
+ }
15
+ const items = input.items;
16
+ if (!Array.isArray(items)) {
17
+ throw new Error("thread provider input must contain a validated items array");
18
+ }
19
+ return items.map((_item, index) => ({
20
+ id: `publish-item[${index + 1}]`,
21
+ description: `Publish confirmed thread item ${index + 1}`,
22
+ }));
23
+ }
@@ -0,0 +1,54 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ function stableJson(value: unknown): string {
4
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
5
+ return JSON.stringify(value);
6
+ }
7
+ if (typeof value === "number") {
8
+ if (!Number.isFinite(value)) {
9
+ throw new Error("provider plugin semantic identity contains a non-finite number");
10
+ }
11
+ return JSON.stringify(value);
12
+ }
13
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
14
+ if (typeof value === "object") {
15
+ const record = value as Readonly<Record<string, unknown>>;
16
+ return `{${Object.keys(record).sort().map((key) =>
17
+ `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
18
+ }
19
+ throw new Error("provider plugin semantic identity contains an unsupported value");
20
+ }
21
+
22
+ /**
23
+ * Hash an exact provider-owned contract projection. Functions are deliberately
24
+ * excluded: shared planners are implementation sources, while provider-local
25
+ * validators are declared as provider-specific implementation sources.
26
+ */
27
+ export function contractSemanticIdentity(
28
+ contracts: readonly object[],
29
+ ): string {
30
+ const ordered = [...contracts].sort((left, right) => {
31
+ const leftRecord = left as Readonly<Record<string, unknown>>;
32
+ const rightRecord = right as Readonly<Record<string, unknown>>;
33
+ const leftKey = `${String(leftRecord.provider ?? leftRecord.site)}/${String(leftRecord.operation)}@${String(leftRecord.contractVersion)}`;
34
+ const rightKey = `${String(rightRecord.provider ?? rightRecord.site)}/${String(rightRecord.operation)}@${String(rightRecord.contractVersion)}`;
35
+ return leftKey.localeCompare(rightKey);
36
+ });
37
+ return createHash("sha256").update(stableJson(ordered)).digest("hex");
38
+ }
39
+
40
+ export function assertContractSemanticIdentity(
41
+ owner: string,
42
+ contracts: readonly object[],
43
+ expected: string,
44
+ ): void {
45
+ if (!/^[a-f0-9]{64}$/u.test(expected)) {
46
+ throw new Error(`${owner} contract semantic identity must be a lowercase SHA-256 digest`);
47
+ }
48
+ const actual = contractSemanticIdentity(contracts);
49
+ if (actual !== expected) {
50
+ throw new Error(
51
+ `${owner} contract semantics changed: expected ${expected}, received ${actual}`,
52
+ );
53
+ }
54
+ }