@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.
- package/CHANGELOG.md +458 -0
- package/LICENSE +21 -0
- package/README.md +1359 -0
- package/bunfig.toml +2 -0
- package/dist/apple-photos-client.js +704 -0
- package/dist/beeper-client.js +4602 -0
- package/dist/client.js +1605 -0
- package/dist/index-26yq8q16.js +31 -0
- package/dist/index-9t13b4k7.js +508 -0
- package/dist/index-axwm9b3v.js +5 -0
- package/dist/index-gwk7rbyj.js +30 -0
- package/dist/index.js +701 -0
- package/dist/messaging.js +1130 -0
- package/dist/omni-client.js +1406 -0
- package/dist/whatsapp-client.js +355 -0
- package/docs/imessage-direct-provider.md +106 -0
- package/docs/rental-listings.md +94 -0
- package/package.json +600 -0
- package/skills/ghostget/SKILL.md +251 -0
- package/skills/ghostget/agents/openai.yaml +4 -0
- package/skills/ghostget/references/adapter-contract.md +181 -0
- package/skills/ghostget/references/apple-photos.md +115 -0
- package/skills/ghostget/references/article-drafts.md +235 -0
- package/skills/ghostget/references/code-owned-provider-scaffold.md +134 -0
- package/skills/ghostget/references/cross-posting-video.md +139 -0
- package/skills/ghostget/references/cross-posting.md +82 -0
- package/skills/ghostget/references/derivation.md +390 -0
- package/skills/ghostget/references/hraness-social-profile-stats.json +269 -0
- package/skills/ghostget/references/install.md +66 -0
- package/skills/ghostget/references/linkedin-adapter.md +442 -0
- package/skills/ghostget/references/messaging.md +168 -0
- package/skills/ghostget/references/meta-comet-contract.md +153 -0
- package/skills/ghostget/references/platform-patterns.md +122 -0
- package/skills/ghostget/references/provider-plugins.md +496 -0
- package/skills/ghostget/references/reddit-flair.md +78 -0
- package/skills/ghostget/references/safety-and-state.md +214 -0
- package/skills/ghostget/references/settlement-and-duplicate-cleanup.md +79 -0
- package/skills/ghostget/references/social-platform-routing.md +66 -0
- package/skills/ghostget/references/social-profile-stats.md +195 -0
- package/skills/ghostget/references/social-video-platform-routing.md +89 -0
- package/skills/ghostget/references/whatsapp-adapter.md +101 -0
- package/skills/ghostget/references/x-adapter.md +332 -0
- package/skills/ghostget/references/x-ai-disclosure.md +71 -0
- package/src/apple-photos-cli.ts +639 -0
- package/src/apple-photos-client-types.ts +168 -0
- package/src/apple-photos-client.ts +201 -0
- package/src/apple-photos-contact-evidence.ts +838 -0
- package/src/apple-photos-local-source.ts +1131 -0
- package/src/args.ts +2053 -0
- package/src/article-draft-document.ts +417 -0
- package/src/article-draft-embeds.ts +123 -0
- package/src/article-draft-images.ts +214 -0
- package/src/assets/adapter-template/wrench-adapter.json +40 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.json +1500 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v1.0.0.json +129 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v1.1.0.json +207 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.0.0.json +1463 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.1.0.json +1463 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.2.0.json +1475 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.3.0.json +1488 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.0.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.1.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.2.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.3.0.json +615 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.4.0.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.5.0.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.6.0.json +684 -0
- package/src/assets/adapters/clasificados/wrench-web-adapter.json +54 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.json +750 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.0.0.json +706 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.1.0.json +703 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.2.0.json +750 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.json +682 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.0.0.json +583 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.1.0.json +682 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.json +437 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.0.0.json +383 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.0.json +437 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.1.json +437 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.json +880 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.0.0.json +740 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.1.0.json +880 -0
- package/src/assets/adapters/github/wrench-web-adapter.json +69 -0
- package/src/assets/adapters/github/wrench-web-adapter.v1.0.0.json +42 -0
- package/src/assets/adapters/github/wrench-web-adapter.v1.1.0.json +69 -0
- package/src/assets/adapters/gmail/wrench-adapter.json +161 -0
- package/src/assets/adapters/gmail/wrench-adapter.v1.2.0.json +157 -0
- package/src/assets/adapters/hacker-news/wrench-web-adapter.json +315 -0
- package/src/assets/adapters/hacker-news/wrench-web-adapter.v1.0.0.json +315 -0
- package/src/assets/adapters/imessage/wrench-web-adapter.json +236 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.json +719 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.0.0.json +627 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.1.0.json +609 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.2.0.json +640 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.3.0.json +671 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.4.0.json +710 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.5.0.json +710 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.6.0.json +719 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.7.0.json +719 -0
- package/src/assets/adapters/linkedin/wrench-adapter.json +442 -0
- package/src/assets/adapters/linkedin/wrench-adapter.v0.4.0.json +124 -0
- package/src/assets/adapters/linkedin/wrench-adapter.v1.0.0.json +411 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.json +914 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.0.0.json +604 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.1.0.json +604 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.10.0.json +809 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.11.0.json +809 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.12.0.json +820 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.13.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.14.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.15.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.16.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.17.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.18.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.19.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.2.0.json +731 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.3.0.json +768 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.4.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.5.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.6.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.7.0.json +825 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.8.0.json +792 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.9.0.json +809 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.json +855 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.0.0.json +577 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.1.0.json +604 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.10.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.11.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.12.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.2.0.json +653 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.3.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.4.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.5.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.6.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.7.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.8.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.9.0.json +713 -0
- package/src/assets/adapters/substack/wrench-web-adapter.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.0.0.json +817 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.1.0.json +812 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.2.0.json +812 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.3.0.json +902 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.4.0.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.5.0.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.6.0.json +936 -0
- package/src/assets/adapters/threads/wrench-web-adapter.json +692 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.0.0.json +629 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.1.0.json +623 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.2.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.3.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.4.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.5.0.json +699 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.6.0.json +693 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.7.0.json +692 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.json +762 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.0.0.json +681 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.1.0.json +708 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.2.0.json +762 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.3.0.json +762 -0
- package/src/assets/adapters/twitch/wrench-web-adapter.json +42 -0
- package/src/assets/adapters/twitch/wrench-web-adapter.v1.0.0.json +42 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.json +400 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.0.0.json +355 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.1.0.json +386 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.2.0.json +386 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.3.0.json +400 -0
- package/src/assets/adapters/x/wrench-adapter.json +749 -0
- package/src/assets/adapters/x/wrench-adapter.v1.0.0.json +700 -0
- package/src/assets/adapters/x/wrench-adapter.v1.1.0.json +707 -0
- package/src/assets/adapters/x/wrench-web-adapter.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.1.0.json +579 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.10.0.json +685 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.11.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.12.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.13.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.2.0.json +578 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.3.0.json +610 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.4.0.json +617 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.5.0.json +634 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.6.0.json +632 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.7.0.json +649 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.8.0.json +649 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.9.0.json +683 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.json +561 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.0.0.json +465 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.1.0.json +519 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.2.0.json +561 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.3.0.json +561 -0
- package/src/assets/code-owned-provider-template/plugin.test.ts.template +54 -0
- package/src/assets/code-owned-provider-template/plugin.ts.template +102 -0
- package/src/assets/code-owned-provider-template/runtime.internal.test.ts.template +44 -0
- package/src/assets/code-owned-provider-template/runtime.ts.template +59 -0
- package/src/assets/code-owned-provider-template/wrench-adapter.json.template +33 -0
- package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/PROVENANCE.md +14 -0
- package/src/assets.d.ts +4 -0
- package/src/auth.ts +1157 -0
- package/src/beeper-client-types.ts +169 -0
- package/src/beeper-client.ts +250 -0
- package/src/beeper-contact-interactions-cli.ts +180 -0
- package/src/beeper-contact-interactions.ts +1612 -0
- package/src/beeper-message-bundle-v1.ts +257 -0
- package/src/beeper-message-like-me-cli.ts +84 -0
- package/src/beeper-message-like-me-export.ts +2274 -0
- package/src/beeper-message-like-me-recovery.ts +931 -0
- package/src/beeper-message-like-me-source.ts +4034 -0
- package/src/browser-admission.ts +928 -0
- package/src/browser-snapshots.ts +260 -0
- package/src/browser.ts +3873 -0
- package/src/canonical-json.ts +60 -0
- package/src/catalog-cli.ts +638 -0
- package/src/cli.ts +208 -0
- package/src/client-types.ts +301 -0
- package/src/client.ts +2726 -0
- package/src/confirmed-write-failure.ts +44 -0
- package/src/confirmed-write-model.ts +72 -0
- package/src/confirmed-write-platform.ts +1055 -0
- package/src/confirmed-write-program.ts +224 -0
- package/src/confirmed-write-runtime.ts +16 -0
- package/src/cursor-token.ts +228 -0
- package/src/derivation-dnr-readiness.ts +367 -0
- package/src/derivation-file-chooser.ts +384 -0
- package/src/derivation-network-boundary.ts +835 -0
- package/src/derivation-network-guard.ts +782 -0
- package/src/derivation-network-proxy-helper.ts +440 -0
- package/src/derivation-network-proxy.ts +385 -0
- package/src/derive-command-helper.ts +693 -0
- package/src/derive-fixtures.ts +343 -0
- package/src/derive-review.ts +980 -0
- package/src/derive.ts +4754 -0
- package/src/fixtures/beeper-message-like-me-v1/accounts.ndjson +2 -0
- package/src/fixtures/beeper-message-like-me-v1/conversations.ndjson +1 -0
- package/src/fixtures/beeper-message-like-me-v1/manifest.json +1 -0
- package/src/fixtures/beeper-message-like-me-v1/messages.ndjson +2 -0
- package/src/fixtures/beeper-message-like-me-v1/participants.ndjson +3 -0
- package/src/fixtures/beeper-message-like-me-v1/reactions.ndjson +1 -0
- package/src/fixtures/beeper-message-like-me-v1/tombstones.ndjson +1 -0
- package/src/ghostget.ts +3672 -0
- package/src/gmail-capture.ts +1244 -0
- package/src/har-internal.ts +1242 -0
- package/src/har.ts +743 -0
- package/src/index.ts +218 -0
- package/src/invocation-read-platform.ts +55 -0
- package/src/invocation-read-program.ts +93 -0
- package/src/invocation-read-runtime.ts +11 -0
- package/src/linked-device-lifecycle-admission.ts +450 -0
- package/src/linked-device-lifecycle-journal.ts +1560 -0
- package/src/linked-device-lifecycle-runtime.ts +1602 -0
- package/src/local-cli-admission.ts +135 -0
- package/src/local-cli-contracts.ts +271 -0
- package/src/local-cli-execution.ts +98 -0
- package/src/local-cli-surface-contract.ts +1510 -0
- package/src/local-cli-tool-identity.ts +336 -0
- package/src/media/archive.ts +2325 -0
- package/src/media/args.ts +303 -0
- package/src/media/cli.ts +287 -0
- package/src/media/doctor.ts +475 -0
- package/src/media/ffmpeg.ts +1330 -0
- package/src/media/http-capture.ts +711 -0
- package/src/media/http-probe.ts +593 -0
- package/src/media/http.ts +387 -0
- package/src/media/index.ts +10 -0
- package/src/media/local-transcription.ts +613 -0
- package/src/media/lock.ts +321 -0
- package/src/media/manifest.ts +1712 -0
- package/src/media/metadata.ts +906 -0
- package/src/media/process.ts +723 -0
- package/src/media/revision.ts +166 -0
- package/src/media/runtime-closure.ts +1063 -0
- package/src/media/source-router.ts +187 -0
- package/src/media/transcriber-config.ts +1122 -0
- package/src/media/transcript-persistence-model.ts +15 -0
- package/src/media/transcript-persistence-platform.ts +89 -0
- package/src/media/transcript-persistence-program.ts +39 -0
- package/src/media/transcript-persistence-runtime.ts +24 -0
- package/src/media/transcript.ts +616 -0
- package/src/media/utf8-order.ts +30 -0
- package/src/media/whisper-cpp.ts +464 -0
- package/src/media/whisper-language.ts +58 -0
- package/src/media/yt-dlp.ts +439 -0
- package/src/message-like-me-agentic-messaging.ts +882 -0
- package/src/messaging-action-store.ts +793 -0
- package/src/messaging-runtime.ts +2136 -0
- package/src/messaging-store.ts +539 -0
- package/src/messaging-types.ts +1663 -0
- package/src/messaging.ts +334 -0
- package/src/model.ts +2671 -0
- package/src/oauth-google.ts +888 -0
- package/src/omni-client-types.ts +306 -0
- package/src/omni-client.ts +2225 -0
- package/src/omni-limits.ts +34 -0
- package/src/omni-model.ts +1614 -0
- package/src/omni-request.ts +379 -0
- package/src/omni-runtime.ts +1632 -0
- package/src/operation-deadline.ts +189 -0
- package/src/path-helper.ts +1656 -0
- package/src/pinned-https.ts +194 -0
- package/src/plan-assets.ts +832 -0
- package/src/platform-catalog.ts +1832 -0
- package/src/plugins/beeper-linked-device/plugin.ts +310 -0
- package/src/plugins/bluesky-web/plugin.ts +178 -0
- package/src/plugins/clasificados-web/plugin.ts +66 -0
- package/src/plugins/github-web/plugin.ts +80 -0
- package/src/plugins/gmail-official/plugin.ts +110 -0
- package/src/plugins/hacker-news-web/plugin.ts +52 -0
- package/src/plugins/imessage-direct/plugin.ts +195 -0
- package/src/plugins/imessage-direct/vendor/0001-fix-keep-AppleScript-send-payloads-out-of-child-argv.patch +1430 -0
- package/src/plugins/imessage-direct/vendor/0002-feat-rpc-add-exact-chat-lookup.patch +225 -0
- package/src/plugins/imessage-direct/vendor/provenance.json +80 -0
- package/src/plugins/linkedin-official/plugin.ts +84 -0
- package/src/plugins/linkedin-web/plugin.ts +412 -0
- package/src/plugins/meta-web/plugin.ts +317 -0
- package/src/plugins/reddit-web/flair.ts +492 -0
- package/src/plugins/reddit-web/plugin.ts +477 -0
- package/src/plugins/substack-web/plugin.ts +124 -0
- package/src/plugins/tiktok-web/plugin.ts +67 -0
- package/src/plugins/twitch-web/plugin.ts +54 -0
- package/src/plugins/whatsapp-linked-device/plugin.ts +105 -0
- package/src/plugins/x-official/plugin.ts +64 -0
- package/src/plugins/x-web/plugin.ts +488 -0
- package/src/plugins/youtube-web/plugin.ts +101 -0
- package/src/portable-run-recovery.ts +466 -0
- package/src/process-identity.ts +501 -0
- package/src/profile-clone-helper.ts +104 -0
- package/src/provider-catalog-views.ts +51 -0
- package/src/provider-context.ts +69 -0
- package/src/provider-contract-definitions-x.ts +45 -0
- package/src/provider-contract-definitions.ts +948 -0
- package/src/provider-contract-input-gmail.ts +176 -0
- package/src/provider-contract-input-linkedin.ts +77 -0
- package/src/provider-contract-input-x.ts +304 -0
- package/src/provider-contract-planning.ts +23 -0
- package/src/provider-contract-semantic-identity.ts +54 -0
- package/src/provider-contracts.ts +251 -0
- package/src/provider-http.ts +723 -0
- package/src/provider-plugin-auth.ts +36 -0
- package/src/provider-plugin-builtins.ts +229 -0
- package/src/provider-plugin-cleanup-barrier.ts +229 -0
- package/src/provider-plugin-cleanup-execution.ts +79 -0
- package/src/provider-plugin-cleanup-resource.ts +470 -0
- package/src/provider-plugin-contract-identity.ts +707 -0
- package/src/provider-plugin-host.ts +1972 -0
- package/src/provider-plugin-identifiers.ts +60 -0
- package/src/provider-plugin-invocation-lease.ts +889 -0
- package/src/provider-plugin-lifecycle-kernel.ts +566 -0
- package/src/provider-plugin-lifecycle.ts +1455 -0
- package/src/provider-plugin-module-analysis.ts +336 -0
- package/src/provider-plugin-package.ts +2530 -0
- package/src/provider-plugin-portable-authority.ts +106 -0
- package/src/provider-plugin-portable-catalog.ts +94 -0
- package/src/provider-plugin-portable-identity.ts +315 -0
- package/src/provider-plugin-portable-runtime.ts +2057 -0
- package/src/provider-plugin-protocol.ts +1625 -0
- package/src/provider-plugin-registry.ts +3342 -0
- package/src/provider-plugin-store.ts +2155 -0
- package/src/provider-plugin-test-fixture.ts +5 -0
- package/src/provider-plugin.ts +4907 -0
- package/src/provider-plugins.generated.ts +43 -0
- package/src/provider-plugins.ts +8 -0
- package/src/provider-subject.ts +41 -0
- package/src/provider.ts +315 -0
- package/src/providers/beeper-local-runtime.ts +5426 -0
- package/src/providers/beeper-local.ts +2739 -0
- package/src/providers/beeper-messaging.ts +462 -0
- package/src/providers/beeper-omni.ts +1159 -0
- package/src/providers/bluesky-web-runtime.ts +2895 -0
- package/src/providers/bluesky-web.ts +1614 -0
- package/src/providers/clasificados-web-runtime.ts +255 -0
- package/src/providers/clasificados-web.ts +503 -0
- package/src/providers/contact-projection.ts +301 -0
- package/src/providers/github-read-model.ts +94 -0
- package/src/providers/github-read-platform.ts +165 -0
- package/src/providers/github-read-program.ts +97 -0
- package/src/providers/github-web-runtime.ts +320 -0
- package/src/providers/github-web.ts +403 -0
- package/src/providers/gmail-api.ts +2834 -0
- package/src/providers/gmail-omni.ts +830 -0
- package/src/providers/gmail.ts +1130 -0
- package/src/providers/hacker-news-web-runtime.ts +220 -0
- package/src/providers/hacker-news-web.ts +809 -0
- package/src/providers/imessage-direct-install.ts +354 -0
- package/src/providers/imessage-direct-messaging.ts +532 -0
- package/src/providers/imessage-direct-omni.ts +400 -0
- package/src/providers/imessage-direct-runtime.ts +1498 -0
- package/src/providers/imessage-direct.ts +351 -0
- package/src/providers/instagram-video-foundations.ts +913 -0
- package/src/providers/instagram-web-profile-browser.ts +600 -0
- package/src/providers/iso-bmff.ts +338 -0
- package/src/providers/linkedin-company-platform.ts +42 -0
- package/src/providers/linkedin-company-program.ts +88 -0
- package/src/providers/linkedin-contact-failure.ts +84 -0
- package/src/providers/linkedin-contact-platform.ts +45 -0
- package/src/providers/linkedin-contact-program.ts +91 -0
- package/src/providers/linkedin-profile-activity-failure.ts +76 -0
- package/src/providers/linkedin-profile-activity-platform.ts +41 -0
- package/src/providers/linkedin-profile-activity-program.ts +67 -0
- package/src/providers/linkedin-read-failure.ts +52 -0
- package/src/providers/linkedin-self-platform.ts +53 -0
- package/src/providers/linkedin-self-program.ts +122 -0
- package/src/providers/linkedin-web-article-browser.ts +794 -0
- package/src/providers/linkedin-web-bootstrap.ts +146 -0
- package/src/providers/linkedin-web-contact.ts +830 -0
- package/src/providers/linkedin-web-feed-browser.ts +832 -0
- package/src/providers/linkedin-web-feed.ts +864 -0
- package/src/providers/linkedin-web-post-browser.ts +653 -0
- package/src/providers/linkedin-web-profile-browser.ts +683 -0
- package/src/providers/linkedin-web-runtime.ts +2388 -0
- package/src/providers/linkedin-web.ts +3305 -0
- package/src/providers/linkedin.ts +1885 -0
- package/src/providers/meta-bootstrap.ts +1043 -0
- package/src/providers/meta-facebook-group.ts +690 -0
- package/src/providers/meta-marketplace-relay.ts +747 -0
- package/src/providers/meta-omni.ts +373 -0
- package/src/providers/meta-relay-bundle.ts +1049 -0
- package/src/providers/meta-web-descriptors.ts +1740 -0
- package/src/providers/meta-web-runtime.ts +3388 -0
- package/src/providers/meta-web.ts +2931 -0
- package/src/providers/read-failure.ts +139 -0
- package/src/providers/reddit-omni.ts +462 -0
- package/src/providers/reddit-web-runtime.ts +1929 -0
- package/src/providers/reddit-web.ts +1611 -0
- package/src/providers/rental-listings.ts +460 -0
- package/src/providers/substack-omni.ts +406 -0
- package/src/providers/substack-video-mp4.ts +33 -0
- package/src/providers/substack-web-runtime.ts +2812 -0
- package/src/providers/substack-web.ts +1502 -0
- package/src/providers/tiktok-video-mp4.ts +33 -0
- package/src/providers/tiktok-web-runtime.ts +770 -0
- package/src/providers/tiktok-web.ts +2786 -0
- package/src/providers/twitch-web-runtime.ts +226 -0
- package/src/providers/twitch-web.ts +274 -0
- package/src/providers/whatsapp-account-identity.ts +55 -0
- package/src/providers/whatsapp-contact-projection-helper.ts +874 -0
- package/src/providers/whatsapp-contact-projection-protocol.ts +403 -0
- package/src/providers/whatsapp-interaction-projection-helper.ts +1452 -0
- package/src/providers/whatsapp-interaction-projection-protocol.ts +462 -0
- package/src/providers/whatsapp-message-export-projection-protocol.ts +532 -0
- package/src/providers/whatsapp-messaging.ts +132 -0
- package/src/providers/whatsapp-omni.ts +586 -0
- package/src/providers/whatsapp-web-runtime.ts +3108 -0
- package/src/providers/whatsapp-web.ts +1037 -0
- package/src/providers/x-image-provenance.ts +523 -0
- package/src/providers/x-made-with-ai.ts +106 -0
- package/src/providers/x-transaction-id.ts +413 -0
- package/src/providers/x-web-runtime.ts +3485 -0
- package/src/providers/x-web.ts +2332 -0
- package/src/providers/x.ts +1889 -0
- package/src/providers/youtube-web-runtime.ts +1400 -0
- package/src/providers/youtube-web.ts +1221 -0
- package/src/read-admission-runtime.ts +91 -0
- package/src/read-client.ts +521 -0
- package/src/read-effect-platform.ts +7 -0
- package/src/read-effect-runtime.ts +14 -0
- package/src/read-effect.ts +52 -0
- package/src/read-projection-admission.ts +866 -0
- package/src/read-projections.ts +3151 -0
- package/src/recovery.ts +1492 -0
- package/src/reviewed-template.ts +146 -0
- package/src/run-journal.ts +1272 -0
- package/src/runtime.ts +5116 -0
- package/src/scripts/generate-provider-plugin-catalog.ts +121 -0
- package/src/scripts/install-whatsapp-protocol.sh +232 -0
- package/src/scripts/recover-install-stages.ts +1491 -0
- package/src/scripts/resolve-state-home.ts +30 -0
- package/src/scripts/scaffold-web-provider.ts +973 -0
- package/src/scripts/sync-bundled-adapters.ts +695 -0
- package/src/session-secrets.ts +1183 -0
- package/src/state-helper.bunfig.toml +2 -0
- package/src/state-helper.ts +2914 -0
- package/src/storage.ts +3842 -0
- package/src/transport-policy.ts +2 -0
- package/src/usage.ts +182 -0
- package/src/version.ts +2 -0
- package/src/web-session-authentication-policy.ts +136 -0
- package/src/web-session-cleanup-admission.ts +2344 -0
- package/src/web-session-cleanup-join.ts +99 -0
- package/src/web-session-client.ts +996 -0
- package/src/web-session-contract-definitions.ts +820 -0
- package/src/web-session-contract-planning.ts +42 -0
- package/src/web-session-contracts.ts +228 -0
- package/src/web-session-cookies.ts +500 -0
- package/src/web-session-execution.ts +385 -0
- package/src/web-session-http.ts +373 -0
- package/src/web-session-read-errors.ts +19 -0
- package/src/web-session-read-runtime.ts +124 -0
- package/src/web-session-recovery.ts +818 -0
- package/src/web-session-sites.ts +58 -0
- package/src/web-session-template.ts +994 -0
- package/src/web-session.ts +93 -0
- package/src/whatsapp-client-binding.ts +21 -0
- package/src/whatsapp-client-types.ts +75 -0
- package/src/whatsapp-client.ts +395 -0
- package/src/whatsapp-export-coordinate.ts +17 -0
- package/src/whatsapp-message-bundle-v2.ts +195 -0
- package/src/whatsapp-message-like-me-cli.ts +195 -0
- package/src/whatsapp-message-like-me-export.ts +1072 -0
- package/src/whatsapp-message-like-me-source.ts +1012 -0
- package/tsconfig.json +67 -0
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
import { types as nodeTypes } from "node:util";
|
|
2
|
+
|
|
3
|
+
import { canonicalJson, sha256 } from "./canonical-json";
|
|
4
|
+
|
|
5
|
+
// Historical wire names and digest inputs are intentionally unchanged: existing
|
|
6
|
+
// Message Like Me bindings and encrypted receipts survive the Ghostget rename.
|
|
7
|
+
export const MESSAGE_LIKE_ME_SOURCE_CONVERSATION_COORDINATE_V1_SCHEMA_VERSION = 1 as const;
|
|
8
|
+
export const MESSAGE_LIKE_ME_SOURCE_CONVERSATION_COORDINATE_V1_CONTRACT_ID =
|
|
9
|
+
"wrench.message-like-me.source-conversation-coordinate.v1" as const;
|
|
10
|
+
|
|
11
|
+
/** Exact read-only predecessor shipped by Ghostget #72. */
|
|
12
|
+
export const GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_FORMAT =
|
|
13
|
+
"wrench.messaging-context-binding" as const;
|
|
14
|
+
export const GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID =
|
|
15
|
+
"wrench.messaging-context-binding.v1" as const;
|
|
16
|
+
export const GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_DESCRIPTOR = Object.freeze({
|
|
17
|
+
contractId: GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID,
|
|
18
|
+
fields: Object.freeze([
|
|
19
|
+
"schemaVersion:1",
|
|
20
|
+
"format:wrench.messaging-context-binding",
|
|
21
|
+
"contractId:wrench.messaging-context-binding.v1",
|
|
22
|
+
"contractHash:sha256",
|
|
23
|
+
"routeRef:opaque",
|
|
24
|
+
"contextRef:opaque",
|
|
25
|
+
"exactDataRevision:sha256",
|
|
26
|
+
"latestMessageRevision:sha256",
|
|
27
|
+
"validatedAt:rfc3339",
|
|
28
|
+
"expiresAt:rfc3339",
|
|
29
|
+
]),
|
|
30
|
+
format: "wrench.messaging-contract-descriptor" as const,
|
|
31
|
+
schemaVersion: 1 as const,
|
|
32
|
+
});
|
|
33
|
+
export const GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_HASH =
|
|
34
|
+
"5e64da6a3d826e7f6fa3db7dca0a4ba92c10cfb784981e71a25aed9513a5c687" as const;
|
|
35
|
+
|
|
36
|
+
/** Exact terminal-receipt predecessor shipped by Ghostget #72. */
|
|
37
|
+
export const GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_FORMAT =
|
|
38
|
+
"wrench.messaging-receipt-binding" as const;
|
|
39
|
+
export const GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID =
|
|
40
|
+
"wrench.messaging-receipt-binding.v1" as const;
|
|
41
|
+
export const GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_DESCRIPTOR = Object.freeze({
|
|
42
|
+
contractId: GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID,
|
|
43
|
+
fields: Object.freeze([
|
|
44
|
+
"schemaVersion:1",
|
|
45
|
+
"format:wrench.messaging-receipt-binding",
|
|
46
|
+
"contractId:wrench.messaging-receipt-binding.v1",
|
|
47
|
+
"contractHash:sha256",
|
|
48
|
+
"clientIntentSha256:sha256",
|
|
49
|
+
"routeRefSha256:sha256",
|
|
50
|
+
"contextRefSha256:sha256",
|
|
51
|
+
"turnDigest:sha256",
|
|
52
|
+
"previewDigest:sha256",
|
|
53
|
+
"runId:opaque",
|
|
54
|
+
"state:submitted|failed|partial|indeterminate",
|
|
55
|
+
"partCount:uint",
|
|
56
|
+
"provenPartCount:uint",
|
|
57
|
+
"receiptSha256:sha256",
|
|
58
|
+
"recordedAt:rfc3339",
|
|
59
|
+
]),
|
|
60
|
+
format: "wrench.messaging-contract-descriptor" as const,
|
|
61
|
+
schemaVersion: 1 as const,
|
|
62
|
+
});
|
|
63
|
+
export const GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH =
|
|
64
|
+
"7f6cf724f0200b2399e4f4641c637b20b48914fc5c9b13755127a8ec69fe66f4" as const;
|
|
65
|
+
|
|
66
|
+
export const GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_FORMAT =
|
|
67
|
+
"wrench.messaging-context-binding" as const;
|
|
68
|
+
export const GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID =
|
|
69
|
+
"wrench.messaging-context-binding.v2" as const;
|
|
70
|
+
export const GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_DESCRIPTOR = Object.freeze({
|
|
71
|
+
contractId: GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID,
|
|
72
|
+
fields: Object.freeze([
|
|
73
|
+
"schemaVersion:2",
|
|
74
|
+
"format:wrench.messaging-context-binding",
|
|
75
|
+
"contractId:wrench.messaging-context-binding.v2",
|
|
76
|
+
"contractHash:sha256",
|
|
77
|
+
"sourceConversationCoordinate:{contractId,schemaVersion,sha256}",
|
|
78
|
+
"routeRef:opaque",
|
|
79
|
+
"contextRef:opaque",
|
|
80
|
+
"exactDataRevision:sha256",
|
|
81
|
+
"latestMessageRevision:sha256",
|
|
82
|
+
"validatedAt:rfc3339",
|
|
83
|
+
"expiresAt:rfc3339",
|
|
84
|
+
]),
|
|
85
|
+
format: "wrench.messaging-contract-descriptor" as const,
|
|
86
|
+
schemaVersion: 2 as const,
|
|
87
|
+
});
|
|
88
|
+
export const GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH = sha256(
|
|
89
|
+
canonicalJson(GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_DESCRIPTOR),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
export const GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_FORMAT =
|
|
93
|
+
"wrench.messaging-receipt-binding" as const;
|
|
94
|
+
export const GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID =
|
|
95
|
+
"wrench.messaging-receipt-binding.v2" as const;
|
|
96
|
+
export const GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_DESCRIPTOR = Object.freeze({
|
|
97
|
+
contractId: GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID,
|
|
98
|
+
fields: Object.freeze([
|
|
99
|
+
"schemaVersion:2",
|
|
100
|
+
"format:wrench.messaging-receipt-binding",
|
|
101
|
+
"contractId:wrench.messaging-receipt-binding.v2",
|
|
102
|
+
"contractHash:sha256",
|
|
103
|
+
"clientIntentSha256:sha256",
|
|
104
|
+
"contextBindingSha256:sha256",
|
|
105
|
+
"sourceConversationCoordinateSha256:sha256",
|
|
106
|
+
"routeRefSha256:sha256",
|
|
107
|
+
"contextRefSha256:sha256",
|
|
108
|
+
"turnDigest:sha256",
|
|
109
|
+
"previewDigest:sha256",
|
|
110
|
+
"runId:opaque",
|
|
111
|
+
"state:submitted|failed|partial|indeterminate",
|
|
112
|
+
"partCount:uint",
|
|
113
|
+
"provenPartCount:uint",
|
|
114
|
+
"receiptSha256:sha256",
|
|
115
|
+
"recordedAt:rfc3339",
|
|
116
|
+
]),
|
|
117
|
+
format: "wrench.messaging-contract-descriptor" as const,
|
|
118
|
+
schemaVersion: 2 as const,
|
|
119
|
+
});
|
|
120
|
+
export const GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_HASH = sha256(
|
|
121
|
+
canonicalJson(GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_DESCRIPTOR),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
export const GHOSTGET_MESSAGING_CLIENT_INTENT_BINDING_V1_FORMAT =
|
|
125
|
+
"wrench.messaging-client-intent-binding" as const;
|
|
126
|
+
export const GHOSTGET_MESSAGING_CLIENT_INTENT_BINDING_V1_CONTRACT_ID =
|
|
127
|
+
"wrench.messaging-client-intent-binding.v1" as const;
|
|
128
|
+
export const GHOSTGET_MESSAGING_CONTEXT_INSTANCE_V1_CONTRACT_ID =
|
|
129
|
+
"wrench.messaging-context-instance.v1" as const;
|
|
130
|
+
export const GHOSTGET_MESSAGING_CONTEXT_INSTANCE_V2_CONTRACT_ID =
|
|
131
|
+
"wrench.messaging-context-instance.v2" as const;
|
|
132
|
+
|
|
133
|
+
export type MessageLikeMeSourceConversationCoordinateV1 = Readonly<{
|
|
134
|
+
sourceAccountId: string | null;
|
|
135
|
+
sourceExternalId: string;
|
|
136
|
+
coordinate:
|
|
137
|
+
| Readonly<{
|
|
138
|
+
kind: "beeperConversation";
|
|
139
|
+
network: string;
|
|
140
|
+
conversationId: string;
|
|
141
|
+
}>
|
|
142
|
+
| Readonly<{
|
|
143
|
+
kind: "imessageChat";
|
|
144
|
+
chatGuid: string;
|
|
145
|
+
service: string | null;
|
|
146
|
+
observedChatRowId: number | null;
|
|
147
|
+
}>;
|
|
148
|
+
}>;
|
|
149
|
+
|
|
150
|
+
export type MessageLikeMeSourceConversationCoordinateBindingV1 = Readonly<{
|
|
151
|
+
contractId: typeof MESSAGE_LIKE_ME_SOURCE_CONVERSATION_COORDINATE_V1_CONTRACT_ID;
|
|
152
|
+
schemaVersion: typeof MESSAGE_LIKE_ME_SOURCE_CONVERSATION_COORDINATE_V1_SCHEMA_VERSION;
|
|
153
|
+
sha256: string;
|
|
154
|
+
}>;
|
|
155
|
+
|
|
156
|
+
export type GhostgetMessagingContextBindingV1 = Readonly<{
|
|
157
|
+
schemaVersion: 1;
|
|
158
|
+
format: typeof GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_FORMAT;
|
|
159
|
+
contractId: typeof GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID;
|
|
160
|
+
contractHash: typeof GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_HASH;
|
|
161
|
+
routeRef: string;
|
|
162
|
+
contextRef: string;
|
|
163
|
+
exactDataRevision: string;
|
|
164
|
+
latestMessageRevision: string;
|
|
165
|
+
validatedAt: string;
|
|
166
|
+
expiresAt: string;
|
|
167
|
+
}>;
|
|
168
|
+
|
|
169
|
+
export type GhostgetMessagingContextBindingV2 = Readonly<{
|
|
170
|
+
schemaVersion: 2;
|
|
171
|
+
format: typeof GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_FORMAT;
|
|
172
|
+
contractId: typeof GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID;
|
|
173
|
+
contractHash: string;
|
|
174
|
+
sourceConversationCoordinate: MessageLikeMeSourceConversationCoordinateBindingV1;
|
|
175
|
+
routeRef: string;
|
|
176
|
+
contextRef: string;
|
|
177
|
+
exactDataRevision: string;
|
|
178
|
+
latestMessageRevision: string;
|
|
179
|
+
validatedAt: string;
|
|
180
|
+
expiresAt: string;
|
|
181
|
+
}>;
|
|
182
|
+
|
|
183
|
+
export type GhostgetMessagingReceiptStateV2 =
|
|
184
|
+
| "failed"
|
|
185
|
+
| "indeterminate"
|
|
186
|
+
| "partial"
|
|
187
|
+
| "submitted";
|
|
188
|
+
|
|
189
|
+
export type GhostgetMessagingReceiptStateV1 = GhostgetMessagingReceiptStateV2;
|
|
190
|
+
|
|
191
|
+
export type GhostgetMessagingClientIntentBindingV1 = Readonly<{
|
|
192
|
+
schemaVersion: 1;
|
|
193
|
+
format: typeof GHOSTGET_MESSAGING_CLIENT_INTENT_BINDING_V1_FORMAT;
|
|
194
|
+
contractId: typeof GHOSTGET_MESSAGING_CLIENT_INTENT_BINDING_V1_CONTRACT_ID;
|
|
195
|
+
clientIntentSha256: string;
|
|
196
|
+
contextBindingSha256: string;
|
|
197
|
+
sourceConversationCoordinateSha256: string;
|
|
198
|
+
routeRefSha256: string;
|
|
199
|
+
contextRefSha256: string;
|
|
200
|
+
turnDigest: string;
|
|
201
|
+
partCount: number;
|
|
202
|
+
}>;
|
|
203
|
+
|
|
204
|
+
export type GhostgetMessagingReceiptBindingV2 = Readonly<{
|
|
205
|
+
schemaVersion: 2;
|
|
206
|
+
format: typeof GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_FORMAT;
|
|
207
|
+
contractId: typeof GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID;
|
|
208
|
+
contractHash: string;
|
|
209
|
+
clientIntentSha256: string;
|
|
210
|
+
contextBindingSha256: string;
|
|
211
|
+
sourceConversationCoordinateSha256: string;
|
|
212
|
+
routeRefSha256: string;
|
|
213
|
+
contextRefSha256: string;
|
|
214
|
+
turnDigest: string;
|
|
215
|
+
previewDigest: string;
|
|
216
|
+
runId: string;
|
|
217
|
+
state: GhostgetMessagingReceiptStateV2;
|
|
218
|
+
partCount: number;
|
|
219
|
+
provenPartCount: number;
|
|
220
|
+
receiptSha256: string;
|
|
221
|
+
recordedAt: string;
|
|
222
|
+
}>;
|
|
223
|
+
|
|
224
|
+
export type GhostgetMessagingReceiptBindingV1 = Readonly<{
|
|
225
|
+
schemaVersion: 1;
|
|
226
|
+
format: typeof GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_FORMAT;
|
|
227
|
+
contractId: typeof GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID;
|
|
228
|
+
contractHash: typeof GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH;
|
|
229
|
+
clientIntentSha256: string;
|
|
230
|
+
routeRefSha256: string;
|
|
231
|
+
contextRefSha256: string;
|
|
232
|
+
turnDigest: string;
|
|
233
|
+
previewDigest: string;
|
|
234
|
+
runId: string;
|
|
235
|
+
state: GhostgetMessagingReceiptStateV1;
|
|
236
|
+
partCount: number;
|
|
237
|
+
provenPartCount: number;
|
|
238
|
+
receiptSha256: string;
|
|
239
|
+
recordedAt: string;
|
|
240
|
+
}>;
|
|
241
|
+
|
|
242
|
+
type PlainObject = Record<string, unknown>;
|
|
243
|
+
|
|
244
|
+
function fail(message: string): never {
|
|
245
|
+
throw new TypeError(message);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function plainObject(value: unknown, label: string): PlainObject {
|
|
249
|
+
if (
|
|
250
|
+
value === null
|
|
251
|
+
|| typeof value !== "object"
|
|
252
|
+
|| Array.isArray(value)
|
|
253
|
+
|| nodeTypes.isProxy(value)
|
|
254
|
+
|| (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)
|
|
255
|
+
) return fail(`${label} must be a plain object`);
|
|
256
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
257
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
258
|
+
if (
|
|
259
|
+
typeof key !== "string"
|
|
260
|
+
|| descriptor === undefined
|
|
261
|
+
|| !("value" in descriptor)
|
|
262
|
+
|| !descriptor.enumerable
|
|
263
|
+
) return fail(`${label} must contain only enumerable string-keyed data properties`);
|
|
264
|
+
}
|
|
265
|
+
return value as PlainObject;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function exactKeys(value: PlainObject, expected: readonly string[], label: string): void {
|
|
269
|
+
const actual = Object.keys(value).sort();
|
|
270
|
+
const sortedExpected = [...expected].sort();
|
|
271
|
+
if (
|
|
272
|
+
actual.length !== sortedExpected.length
|
|
273
|
+
|| actual.some((key, index) => key !== sortedExpected[index])
|
|
274
|
+
) return fail(`${label} must contain exactly ${sortedExpected.join(", ")}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function isWellFormedUnicode(value: string): boolean {
|
|
278
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
279
|
+
const unit = value.charCodeAt(index);
|
|
280
|
+
if (unit >= 0xd800 && unit <= 0xdbff) {
|
|
281
|
+
const next = value.charCodeAt(index + 1);
|
|
282
|
+
if (!(next >= 0xdc00 && next <= 0xdfff)) return false;
|
|
283
|
+
index += 1;
|
|
284
|
+
} else if (unit >= 0xdc00 && unit <= 0xdfff) {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function text(value: unknown, label: string, maximumBytes = 1_024): string {
|
|
292
|
+
if (
|
|
293
|
+
typeof value !== "string"
|
|
294
|
+
|| value.length === 0
|
|
295
|
+
|| Buffer.byteLength(value, "utf8") > maximumBytes
|
|
296
|
+
|| /\p{Cc}|\p{Zl}|\p{Zp}/u.test(value)
|
|
297
|
+
|| value !== value.trim()
|
|
298
|
+
|| !isWellFormedUnicode(value)
|
|
299
|
+
) return fail(`${label} must be bounded well-formed text without controls or surrounding space`);
|
|
300
|
+
return value;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function digest(value: unknown, label: string): string {
|
|
304
|
+
if (typeof value !== "string" || !/^[a-f0-9]{64}$/u.test(value)) {
|
|
305
|
+
return fail(`${label} must be a lowercase SHA-256 digest`);
|
|
306
|
+
}
|
|
307
|
+
return value;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function timestamp(value: unknown, label: string): string {
|
|
311
|
+
if (
|
|
312
|
+
typeof value !== "string"
|
|
313
|
+
|| !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value)
|
|
314
|
+
|| new Date(value).toISOString() !== value
|
|
315
|
+
) return fail(`${label} must be a canonical RFC 3339 timestamp`);
|
|
316
|
+
return value;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function legacyTimestamp(value: unknown, label: string): string {
|
|
320
|
+
if (
|
|
321
|
+
typeof value !== "string"
|
|
322
|
+
|| !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/u.test(value)
|
|
323
|
+
|| !Number.isFinite(Date.parse(value))
|
|
324
|
+
) return fail(`${label} must be an RFC 3339 UTC timestamp`);
|
|
325
|
+
return value;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export function parseMessageLikeMeSourceConversationCoordinateV1(
|
|
329
|
+
value: unknown,
|
|
330
|
+
): MessageLikeMeSourceConversationCoordinateV1 {
|
|
331
|
+
const source = plainObject(value, "Message Like Me source-conversation coordinate");
|
|
332
|
+
exactKeys(
|
|
333
|
+
source,
|
|
334
|
+
["sourceAccountId", "sourceExternalId", "coordinate"],
|
|
335
|
+
"Message Like Me source-conversation coordinate",
|
|
336
|
+
);
|
|
337
|
+
const sourceExternalId = text(
|
|
338
|
+
source.sourceExternalId,
|
|
339
|
+
"Message Like Me source-conversation coordinate.sourceExternalId",
|
|
340
|
+
);
|
|
341
|
+
const coordinate = plainObject(
|
|
342
|
+
source.coordinate,
|
|
343
|
+
"Message Like Me source-conversation coordinate.coordinate",
|
|
344
|
+
);
|
|
345
|
+
if (coordinate.kind === "beeperConversation") {
|
|
346
|
+
exactKeys(
|
|
347
|
+
coordinate,
|
|
348
|
+
["kind", "network", "conversationId"],
|
|
349
|
+
"Message Like Me Beeper coordinate",
|
|
350
|
+
);
|
|
351
|
+
const sourceAccountId = text(
|
|
352
|
+
source.sourceAccountId,
|
|
353
|
+
"Message Like Me source-conversation coordinate.sourceAccountId",
|
|
354
|
+
);
|
|
355
|
+
if (sourceExternalId !== sourceAccountId) {
|
|
356
|
+
return fail("Message Like Me Beeper source account and external identity must match");
|
|
357
|
+
}
|
|
358
|
+
return Object.freeze({
|
|
359
|
+
sourceAccountId,
|
|
360
|
+
sourceExternalId,
|
|
361
|
+
coordinate: Object.freeze({
|
|
362
|
+
kind: "beeperConversation" as const,
|
|
363
|
+
network: text(coordinate.network, "Message Like Me Beeper coordinate.network", 512),
|
|
364
|
+
conversationId: text(
|
|
365
|
+
coordinate.conversationId,
|
|
366
|
+
"Message Like Me Beeper coordinate.conversationId",
|
|
367
|
+
),
|
|
368
|
+
}),
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
if (coordinate.kind === "imessageChat") {
|
|
372
|
+
exactKeys(
|
|
373
|
+
coordinate,
|
|
374
|
+
["kind", "chatGuid", "service", "observedChatRowId"],
|
|
375
|
+
"Message Like Me iMessage coordinate",
|
|
376
|
+
);
|
|
377
|
+
if (source.sourceAccountId !== null) {
|
|
378
|
+
return fail("Message Like Me iMessage sourceAccountId must be null");
|
|
379
|
+
}
|
|
380
|
+
let observedChatRowId: number | null;
|
|
381
|
+
if (coordinate.observedChatRowId === null) {
|
|
382
|
+
observedChatRowId = null;
|
|
383
|
+
} else if (
|
|
384
|
+
typeof coordinate.observedChatRowId === "number"
|
|
385
|
+
&& Number.isFinite(coordinate.observedChatRowId)
|
|
386
|
+
&& Number.isSafeInteger(coordinate.observedChatRowId)
|
|
387
|
+
&& coordinate.observedChatRowId >= 1
|
|
388
|
+
) {
|
|
389
|
+
observedChatRowId = coordinate.observedChatRowId;
|
|
390
|
+
} else {
|
|
391
|
+
return fail("Message Like Me iMessage observedChatRowId must be a positive safe integer or null");
|
|
392
|
+
}
|
|
393
|
+
return Object.freeze({
|
|
394
|
+
sourceAccountId: null,
|
|
395
|
+
sourceExternalId,
|
|
396
|
+
coordinate: Object.freeze({
|
|
397
|
+
kind: "imessageChat" as const,
|
|
398
|
+
chatGuid: text(coordinate.chatGuid, "Message Like Me iMessage coordinate.chatGuid"),
|
|
399
|
+
service: coordinate.service === null
|
|
400
|
+
? null
|
|
401
|
+
: text(coordinate.service, "Message Like Me iMessage coordinate.service", 512),
|
|
402
|
+
observedChatRowId,
|
|
403
|
+
}),
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
return fail("Message Like Me source-conversation coordinate has an unsupported tag");
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
export function messageLikeMeSourceConversationCoordinateBindingV1(
|
|
410
|
+
value: unknown,
|
|
411
|
+
): MessageLikeMeSourceConversationCoordinateBindingV1 {
|
|
412
|
+
const coordinate = parseMessageLikeMeSourceConversationCoordinateV1(value);
|
|
413
|
+
const canonicalPreimage = Object.freeze({
|
|
414
|
+
contractId: MESSAGE_LIKE_ME_SOURCE_CONVERSATION_COORDINATE_V1_CONTRACT_ID,
|
|
415
|
+
schemaVersion: MESSAGE_LIKE_ME_SOURCE_CONVERSATION_COORDINATE_V1_SCHEMA_VERSION,
|
|
416
|
+
value: coordinate,
|
|
417
|
+
});
|
|
418
|
+
return Object.freeze({
|
|
419
|
+
contractId: MESSAGE_LIKE_ME_SOURCE_CONVERSATION_COORDINATE_V1_CONTRACT_ID,
|
|
420
|
+
schemaVersion: MESSAGE_LIKE_ME_SOURCE_CONVERSATION_COORDINATE_V1_SCHEMA_VERSION,
|
|
421
|
+
sha256: sha256(canonicalJson(canonicalPreimage)),
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export function parseMessageLikeMeSourceConversationCoordinateBindingV1(
|
|
426
|
+
value: unknown,
|
|
427
|
+
): MessageLikeMeSourceConversationCoordinateBindingV1 {
|
|
428
|
+
const binding = plainObject(value, "Ghostget source-conversation coordinate binding");
|
|
429
|
+
exactKeys(
|
|
430
|
+
binding,
|
|
431
|
+
["contractId", "schemaVersion", "sha256"],
|
|
432
|
+
"Ghostget source-conversation coordinate binding",
|
|
433
|
+
);
|
|
434
|
+
if (
|
|
435
|
+
binding.contractId !== MESSAGE_LIKE_ME_SOURCE_CONVERSATION_COORDINATE_V1_CONTRACT_ID
|
|
436
|
+
|| binding.schemaVersion !== MESSAGE_LIKE_ME_SOURCE_CONVERSATION_COORDINATE_V1_SCHEMA_VERSION
|
|
437
|
+
) return fail("Ghostget source-conversation coordinate binding has an unsupported contract identity");
|
|
438
|
+
return Object.freeze({
|
|
439
|
+
contractId: MESSAGE_LIKE_ME_SOURCE_CONVERSATION_COORDINATE_V1_CONTRACT_ID,
|
|
440
|
+
schemaVersion: MESSAGE_LIKE_ME_SOURCE_CONVERSATION_COORDINATE_V1_SCHEMA_VERSION,
|
|
441
|
+
sha256: digest(binding.sha256, "Ghostget source-conversation coordinate binding.sha256"),
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export function parseGhostgetMessagingContextBindingV1(
|
|
446
|
+
value: unknown,
|
|
447
|
+
): GhostgetMessagingContextBindingV1 {
|
|
448
|
+
const context = plainObject(value, "Ghostget messaging context binding V1");
|
|
449
|
+
exactKeys(context, [
|
|
450
|
+
"schemaVersion",
|
|
451
|
+
"format",
|
|
452
|
+
"contractId",
|
|
453
|
+
"contractHash",
|
|
454
|
+
"routeRef",
|
|
455
|
+
"contextRef",
|
|
456
|
+
"exactDataRevision",
|
|
457
|
+
"latestMessageRevision",
|
|
458
|
+
"validatedAt",
|
|
459
|
+
"expiresAt",
|
|
460
|
+
], "Ghostget messaging context binding V1");
|
|
461
|
+
if (
|
|
462
|
+
context.schemaVersion !== 1
|
|
463
|
+
|| context.format !== GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_FORMAT
|
|
464
|
+
|| context.contractId !== GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID
|
|
465
|
+
|| context.contractHash !== GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_HASH
|
|
466
|
+
) return fail("Ghostget messaging context binding V1 has an unsupported contract identity");
|
|
467
|
+
const validatedAt = legacyTimestamp(
|
|
468
|
+
context.validatedAt,
|
|
469
|
+
"Ghostget messaging context binding V1.validatedAt",
|
|
470
|
+
);
|
|
471
|
+
const expiresAt = legacyTimestamp(
|
|
472
|
+
context.expiresAt,
|
|
473
|
+
"Ghostget messaging context binding V1.expiresAt",
|
|
474
|
+
);
|
|
475
|
+
if (Date.parse(expiresAt) <= Date.parse(validatedAt)) {
|
|
476
|
+
return fail("Ghostget messaging context binding V1.expiresAt must follow validatedAt");
|
|
477
|
+
}
|
|
478
|
+
return Object.freeze({
|
|
479
|
+
schemaVersion: 1 as const,
|
|
480
|
+
format: GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_FORMAT,
|
|
481
|
+
contractId: GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID,
|
|
482
|
+
contractHash: GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_HASH,
|
|
483
|
+
routeRef: text(context.routeRef, "Ghostget messaging context binding V1.routeRef", 2_048),
|
|
484
|
+
contextRef: text(context.contextRef, "Ghostget messaging context binding V1.contextRef", 2_048),
|
|
485
|
+
exactDataRevision: digest(
|
|
486
|
+
context.exactDataRevision,
|
|
487
|
+
"Ghostget messaging context binding V1.exactDataRevision",
|
|
488
|
+
),
|
|
489
|
+
latestMessageRevision: digest(
|
|
490
|
+
context.latestMessageRevision,
|
|
491
|
+
"Ghostget messaging context binding V1.latestMessageRevision",
|
|
492
|
+
),
|
|
493
|
+
validatedAt,
|
|
494
|
+
expiresAt,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export function parseGhostgetMessagingContextBindingV2(
|
|
499
|
+
value: unknown,
|
|
500
|
+
): GhostgetMessagingContextBindingV2 {
|
|
501
|
+
const context = plainObject(value, "Ghostget messaging context binding");
|
|
502
|
+
exactKeys(context, [
|
|
503
|
+
"schemaVersion",
|
|
504
|
+
"format",
|
|
505
|
+
"contractId",
|
|
506
|
+
"contractHash",
|
|
507
|
+
"sourceConversationCoordinate",
|
|
508
|
+
"routeRef",
|
|
509
|
+
"contextRef",
|
|
510
|
+
"exactDataRevision",
|
|
511
|
+
"latestMessageRevision",
|
|
512
|
+
"validatedAt",
|
|
513
|
+
"expiresAt",
|
|
514
|
+
], "Ghostget messaging context binding");
|
|
515
|
+
if (
|
|
516
|
+
context.schemaVersion !== 2
|
|
517
|
+
|| context.format !== GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_FORMAT
|
|
518
|
+
|| context.contractId !== GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID
|
|
519
|
+
|| context.contractHash !== GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH
|
|
520
|
+
) return fail("Ghostget messaging context binding has an unsupported contract identity");
|
|
521
|
+
const validatedAt = timestamp(context.validatedAt, "Ghostget messaging context binding.validatedAt");
|
|
522
|
+
const expiresAt = timestamp(context.expiresAt, "Ghostget messaging context binding.expiresAt");
|
|
523
|
+
const lifetime = Date.parse(expiresAt) - Date.parse(validatedAt);
|
|
524
|
+
if (lifetime <= 0 || lifetime > 24 * 60 * 60 * 1_000) {
|
|
525
|
+
return fail("Ghostget messaging context binding has an invalid lifetime");
|
|
526
|
+
}
|
|
527
|
+
return Object.freeze({
|
|
528
|
+
schemaVersion: 2 as const,
|
|
529
|
+
format: GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_FORMAT,
|
|
530
|
+
contractId: GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID,
|
|
531
|
+
contractHash: GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH,
|
|
532
|
+
sourceConversationCoordinate: parseMessageLikeMeSourceConversationCoordinateBindingV1(
|
|
533
|
+
context.sourceConversationCoordinate,
|
|
534
|
+
),
|
|
535
|
+
routeRef: text(context.routeRef, "Ghostget messaging context binding.routeRef", 2_048),
|
|
536
|
+
contextRef: text(context.contextRef, "Ghostget messaging context binding.contextRef", 2_048),
|
|
537
|
+
exactDataRevision: digest(
|
|
538
|
+
context.exactDataRevision,
|
|
539
|
+
"Ghostget messaging context binding.exactDataRevision",
|
|
540
|
+
),
|
|
541
|
+
latestMessageRevision: digest(
|
|
542
|
+
context.latestMessageRevision,
|
|
543
|
+
"Ghostget messaging context binding.latestMessageRevision",
|
|
544
|
+
),
|
|
545
|
+
validatedAt,
|
|
546
|
+
expiresAt,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export function ghostgetMessagingContextBindingSha256V1(value: unknown): string {
|
|
551
|
+
const context = parseGhostgetMessagingContextBindingV1(value);
|
|
552
|
+
return sha256(canonicalJson({
|
|
553
|
+
contractId: GHOSTGET_MESSAGING_CONTEXT_INSTANCE_V1_CONTRACT_ID,
|
|
554
|
+
schemaVersion: 1,
|
|
555
|
+
value: context,
|
|
556
|
+
}));
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function parsedContextBindingSha256V2(context: GhostgetMessagingContextBindingV2): string {
|
|
560
|
+
return sha256(canonicalJson({
|
|
561
|
+
contractId: GHOSTGET_MESSAGING_CONTEXT_INSTANCE_V2_CONTRACT_ID,
|
|
562
|
+
schemaVersion: 2,
|
|
563
|
+
value: context,
|
|
564
|
+
}));
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export function ghostgetMessagingContextBindingSha256V2(value: unknown): string {
|
|
568
|
+
return parsedContextBindingSha256V2(parseGhostgetMessagingContextBindingV2(value));
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const BEEPER_EXACT_CONVERSATION_KEYS = Object.freeze([
|
|
572
|
+
"id",
|
|
573
|
+
"localChatId",
|
|
574
|
+
"accountId",
|
|
575
|
+
"network",
|
|
576
|
+
"title",
|
|
577
|
+
"type",
|
|
578
|
+
"description",
|
|
579
|
+
"descriptionObserved",
|
|
580
|
+
"hasAvatar",
|
|
581
|
+
"avatarObserved",
|
|
582
|
+
"lastReadMessageSortKey",
|
|
583
|
+
"lastActivity",
|
|
584
|
+
"unreadCount",
|
|
585
|
+
"unreadMentionsCount",
|
|
586
|
+
"isMarkedUnread",
|
|
587
|
+
"isArchived",
|
|
588
|
+
"isLowPriority",
|
|
589
|
+
"isMuted",
|
|
590
|
+
"isPinned",
|
|
591
|
+
"isReadOnly",
|
|
592
|
+
"messageExpirySeconds",
|
|
593
|
+
"messageExpiryObserved",
|
|
594
|
+
"draft",
|
|
595
|
+
"draftObserved",
|
|
596
|
+
"reminder",
|
|
597
|
+
"reminderObserved",
|
|
598
|
+
"participants",
|
|
599
|
+
]);
|
|
600
|
+
|
|
601
|
+
export function createBeeperMessageLikeMeSourceConversationCoordinateBindingV1(
|
|
602
|
+
input: Readonly<{
|
|
603
|
+
conversationRead: unknown;
|
|
604
|
+
expectedAccountSubject: unknown;
|
|
605
|
+
}>,
|
|
606
|
+
): MessageLikeMeSourceConversationCoordinateBindingV1 {
|
|
607
|
+
const read = plainObject(input.conversationRead, "Ghostget Beeper exact conversation read");
|
|
608
|
+
exactKeys(
|
|
609
|
+
read,
|
|
610
|
+
["provider", "operation", "accountSubject", "conversation"],
|
|
611
|
+
"Ghostget Beeper exact conversation read",
|
|
612
|
+
);
|
|
613
|
+
if (read.provider !== "beeper" || read.operation !== "conversations.read") {
|
|
614
|
+
return fail("Ghostget context requires an exact Beeper conversations.read projection");
|
|
615
|
+
}
|
|
616
|
+
const expectedAccountSubject = text(
|
|
617
|
+
input.expectedAccountSubject,
|
|
618
|
+
"Ghostget Beeper expected account subject",
|
|
619
|
+
);
|
|
620
|
+
if (
|
|
621
|
+
text(read.accountSubject, "Ghostget Beeper exact conversation read.accountSubject")
|
|
622
|
+
!== expectedAccountSubject
|
|
623
|
+
) return fail("Ghostget Beeper exact conversation read did not bind the expected account subject");
|
|
624
|
+
const conversation = plainObject(
|
|
625
|
+
read.conversation,
|
|
626
|
+
"Ghostget Beeper exact conversation read.conversation",
|
|
627
|
+
);
|
|
628
|
+
exactKeys(
|
|
629
|
+
conversation,
|
|
630
|
+
BEEPER_EXACT_CONVERSATION_KEYS,
|
|
631
|
+
"Ghostget Beeper exact conversation read.conversation",
|
|
632
|
+
);
|
|
633
|
+
if (conversation.type !== "single") {
|
|
634
|
+
return fail("Ghostget Message Like Me context requires a direct Beeper conversation");
|
|
635
|
+
}
|
|
636
|
+
if (conversation.isReadOnly !== false) {
|
|
637
|
+
return fail("Ghostget Message Like Me context requires a writable Beeper conversation");
|
|
638
|
+
}
|
|
639
|
+
const accountId = text(
|
|
640
|
+
conversation.accountId,
|
|
641
|
+
"Ghostget Beeper exact conversation read.conversation.accountId",
|
|
642
|
+
);
|
|
643
|
+
return messageLikeMeSourceConversationCoordinateBindingV1({
|
|
644
|
+
sourceAccountId: accountId,
|
|
645
|
+
sourceExternalId: accountId,
|
|
646
|
+
coordinate: {
|
|
647
|
+
kind: "beeperConversation",
|
|
648
|
+
network: text(
|
|
649
|
+
conversation.network,
|
|
650
|
+
"Ghostget Beeper exact conversation read.conversation.network",
|
|
651
|
+
512,
|
|
652
|
+
),
|
|
653
|
+
conversationId: text(
|
|
654
|
+
conversation.id,
|
|
655
|
+
"Ghostget Beeper exact conversation read.conversation.id",
|
|
656
|
+
),
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Compatibility writer for the exact #72 context shape. New checked actions
|
|
663
|
+
* must use V2 so the source-conversation coordinate is retained durably.
|
|
664
|
+
*/
|
|
665
|
+
export function createBeeperMessageLikeMeContextBindingV1(input: Readonly<{
|
|
666
|
+
conversationRead: unknown;
|
|
667
|
+
expectedAccountSubject: unknown;
|
|
668
|
+
routeRef: unknown;
|
|
669
|
+
contextRef: unknown;
|
|
670
|
+
exactDataRevision: unknown;
|
|
671
|
+
latestMessageRevision: unknown;
|
|
672
|
+
validatedAt: unknown;
|
|
673
|
+
expiresAt: unknown;
|
|
674
|
+
}>): GhostgetMessagingContextBindingV1 {
|
|
675
|
+
createBeeperMessageLikeMeSourceConversationCoordinateBindingV1({
|
|
676
|
+
conversationRead: input.conversationRead,
|
|
677
|
+
expectedAccountSubject: input.expectedAccountSubject,
|
|
678
|
+
});
|
|
679
|
+
return parseGhostgetMessagingContextBindingV1(Object.freeze({
|
|
680
|
+
schemaVersion: 1 as const,
|
|
681
|
+
format: GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_FORMAT,
|
|
682
|
+
contractId: GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID,
|
|
683
|
+
contractHash: GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_HASH,
|
|
684
|
+
routeRef: text(input.routeRef, "Ghostget messaging context binding V1.routeRef", 2_048),
|
|
685
|
+
contextRef: text(input.contextRef, "Ghostget messaging context binding V1.contextRef", 2_048),
|
|
686
|
+
exactDataRevision: digest(
|
|
687
|
+
input.exactDataRevision,
|
|
688
|
+
"Ghostget messaging context binding V1.exactDataRevision",
|
|
689
|
+
),
|
|
690
|
+
latestMessageRevision: digest(
|
|
691
|
+
input.latestMessageRevision,
|
|
692
|
+
"Ghostget messaging context binding V1.latestMessageRevision",
|
|
693
|
+
),
|
|
694
|
+
validatedAt: input.validatedAt,
|
|
695
|
+
expiresAt: input.expiresAt,
|
|
696
|
+
}));
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
export function createBeeperMessageLikeMeContextBindingV2(input: Readonly<{
|
|
700
|
+
conversationRead: unknown;
|
|
701
|
+
expectedAccountSubject: unknown;
|
|
702
|
+
routeRef: unknown;
|
|
703
|
+
contextRef: unknown;
|
|
704
|
+
exactDataRevision: unknown;
|
|
705
|
+
latestMessageRevision: unknown;
|
|
706
|
+
validatedAt: unknown;
|
|
707
|
+
expiresAt: unknown;
|
|
708
|
+
}>): GhostgetMessagingContextBindingV2 {
|
|
709
|
+
const sourceConversationCoordinate =
|
|
710
|
+
createBeeperMessageLikeMeSourceConversationCoordinateBindingV1({
|
|
711
|
+
conversationRead: input.conversationRead,
|
|
712
|
+
expectedAccountSubject: input.expectedAccountSubject,
|
|
713
|
+
});
|
|
714
|
+
return parseGhostgetMessagingContextBindingV2(Object.freeze({
|
|
715
|
+
schemaVersion: 2 as const,
|
|
716
|
+
format: GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_FORMAT,
|
|
717
|
+
contractId: GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID,
|
|
718
|
+
contractHash: GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH,
|
|
719
|
+
sourceConversationCoordinate,
|
|
720
|
+
routeRef: text(input.routeRef, "Ghostget messaging context binding.routeRef", 2_048),
|
|
721
|
+
contextRef: text(input.contextRef, "Ghostget messaging context binding.contextRef", 2_048),
|
|
722
|
+
exactDataRevision: digest(
|
|
723
|
+
input.exactDataRevision,
|
|
724
|
+
"Ghostget messaging context binding.exactDataRevision",
|
|
725
|
+
),
|
|
726
|
+
latestMessageRevision: digest(
|
|
727
|
+
input.latestMessageRevision,
|
|
728
|
+
"Ghostget messaging context binding.latestMessageRevision",
|
|
729
|
+
),
|
|
730
|
+
validatedAt: input.validatedAt,
|
|
731
|
+
expiresAt: input.expiresAt,
|
|
732
|
+
}));
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
export function parseGhostgetMessagingClientIntentBindingV1(
|
|
736
|
+
value: unknown,
|
|
737
|
+
contextValue: unknown,
|
|
738
|
+
): GhostgetMessagingClientIntentBindingV1 {
|
|
739
|
+
const context = parseGhostgetMessagingContextBindingV2(contextValue);
|
|
740
|
+
const intent = plainObject(value, "Ghostget messaging client-intent binding");
|
|
741
|
+
exactKeys(intent, [
|
|
742
|
+
"schemaVersion",
|
|
743
|
+
"format",
|
|
744
|
+
"contractId",
|
|
745
|
+
"clientIntentSha256",
|
|
746
|
+
"contextBindingSha256",
|
|
747
|
+
"sourceConversationCoordinateSha256",
|
|
748
|
+
"routeRefSha256",
|
|
749
|
+
"contextRefSha256",
|
|
750
|
+
"turnDigest",
|
|
751
|
+
"partCount",
|
|
752
|
+
], "Ghostget messaging client-intent binding");
|
|
753
|
+
if (
|
|
754
|
+
intent.schemaVersion !== 1
|
|
755
|
+
|| intent.format !== GHOSTGET_MESSAGING_CLIENT_INTENT_BINDING_V1_FORMAT
|
|
756
|
+
|| intent.contractId !== GHOSTGET_MESSAGING_CLIENT_INTENT_BINDING_V1_CONTRACT_ID
|
|
757
|
+
) return fail("Ghostget messaging client-intent binding has an unsupported contract identity");
|
|
758
|
+
if (!Number.isSafeInteger(intent.partCount) || (intent.partCount as number) < 1 || (intent.partCount as number) > 8) {
|
|
759
|
+
return fail("Ghostget messaging client-intent binding.partCount must be from 1 through 8");
|
|
760
|
+
}
|
|
761
|
+
const parsed = Object.freeze({
|
|
762
|
+
schemaVersion: 1 as const,
|
|
763
|
+
format: GHOSTGET_MESSAGING_CLIENT_INTENT_BINDING_V1_FORMAT,
|
|
764
|
+
contractId: GHOSTGET_MESSAGING_CLIENT_INTENT_BINDING_V1_CONTRACT_ID,
|
|
765
|
+
clientIntentSha256: digest(
|
|
766
|
+
intent.clientIntentSha256,
|
|
767
|
+
"Ghostget messaging client-intent binding.clientIntentSha256",
|
|
768
|
+
),
|
|
769
|
+
contextBindingSha256: digest(
|
|
770
|
+
intent.contextBindingSha256,
|
|
771
|
+
"Ghostget messaging client-intent binding.contextBindingSha256",
|
|
772
|
+
),
|
|
773
|
+
sourceConversationCoordinateSha256: digest(
|
|
774
|
+
intent.sourceConversationCoordinateSha256,
|
|
775
|
+
"Ghostget messaging client-intent binding.sourceConversationCoordinateSha256",
|
|
776
|
+
),
|
|
777
|
+
routeRefSha256: digest(
|
|
778
|
+
intent.routeRefSha256,
|
|
779
|
+
"Ghostget messaging client-intent binding.routeRefSha256",
|
|
780
|
+
),
|
|
781
|
+
contextRefSha256: digest(
|
|
782
|
+
intent.contextRefSha256,
|
|
783
|
+
"Ghostget messaging client-intent binding.contextRefSha256",
|
|
784
|
+
),
|
|
785
|
+
turnDigest: digest(intent.turnDigest, "Ghostget messaging client-intent binding.turnDigest"),
|
|
786
|
+
partCount: intent.partCount as number,
|
|
787
|
+
});
|
|
788
|
+
if (
|
|
789
|
+
parsed.contextBindingSha256 !== parsedContextBindingSha256V2(context)
|
|
790
|
+
|| parsed.sourceConversationCoordinateSha256
|
|
791
|
+
!== context.sourceConversationCoordinate.sha256
|
|
792
|
+
|| parsed.routeRefSha256 !== sha256(context.routeRef)
|
|
793
|
+
|| parsed.contextRefSha256 !== sha256(context.contextRef)
|
|
794
|
+
) return fail("Ghostget messaging client intent does not bind the exact context instance");
|
|
795
|
+
return parsed;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
export function createGhostgetMessagingReceiptBindingV2(input: Readonly<{
|
|
799
|
+
context: unknown;
|
|
800
|
+
clientIntent: unknown;
|
|
801
|
+
previewDigest: unknown;
|
|
802
|
+
runId: unknown;
|
|
803
|
+
state: unknown;
|
|
804
|
+
provenPartCount: unknown;
|
|
805
|
+
recordedAt: unknown;
|
|
806
|
+
}>): GhostgetMessagingReceiptBindingV2 {
|
|
807
|
+
const intent = parseGhostgetMessagingClientIntentBindingV1(input.clientIntent, input.context);
|
|
808
|
+
if (
|
|
809
|
+
input.state !== "submitted"
|
|
810
|
+
&& input.state !== "failed"
|
|
811
|
+
&& input.state !== "partial"
|
|
812
|
+
&& input.state !== "indeterminate"
|
|
813
|
+
) return fail("Ghostget messaging receipt binding has an invalid state");
|
|
814
|
+
if (
|
|
815
|
+
!Number.isSafeInteger(input.provenPartCount)
|
|
816
|
+
|| (input.provenPartCount as number) < 0
|
|
817
|
+
|| (input.provenPartCount as number) > intent.partCount
|
|
818
|
+
) return fail("Ghostget messaging receipt binding.provenPartCount is out of range");
|
|
819
|
+
const provenPartCount = input.provenPartCount as number;
|
|
820
|
+
if (
|
|
821
|
+
(input.state === "submitted" && provenPartCount !== intent.partCount)
|
|
822
|
+
|| (input.state === "failed" && provenPartCount !== 0)
|
|
823
|
+
|| (input.state === "partial"
|
|
824
|
+
&& (provenPartCount < 1 || provenPartCount >= intent.partCount))
|
|
825
|
+
|| (input.state === "indeterminate" && provenPartCount >= intent.partCount)
|
|
826
|
+
) return fail("Ghostget messaging receipt binding state does not match its proven prefix");
|
|
827
|
+
const core = Object.freeze({
|
|
828
|
+
schemaVersion: 2 as const,
|
|
829
|
+
format: GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_FORMAT,
|
|
830
|
+
contractId: GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID,
|
|
831
|
+
contractHash: GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_HASH,
|
|
832
|
+
clientIntentSha256: intent.clientIntentSha256,
|
|
833
|
+
contextBindingSha256: intent.contextBindingSha256,
|
|
834
|
+
sourceConversationCoordinateSha256: intent.sourceConversationCoordinateSha256,
|
|
835
|
+
routeRefSha256: intent.routeRefSha256,
|
|
836
|
+
contextRefSha256: intent.contextRefSha256,
|
|
837
|
+
turnDigest: intent.turnDigest,
|
|
838
|
+
previewDigest: digest(input.previewDigest, "Ghostget receipt.previewDigest"),
|
|
839
|
+
runId: text(input.runId, "Ghostget receipt.runId", 256),
|
|
840
|
+
state: input.state,
|
|
841
|
+
partCount: intent.partCount,
|
|
842
|
+
provenPartCount,
|
|
843
|
+
recordedAt: timestamp(input.recordedAt, "Ghostget receipt.recordedAt"),
|
|
844
|
+
});
|
|
845
|
+
return Object.freeze({
|
|
846
|
+
...core,
|
|
847
|
+
receiptSha256: sha256(canonicalJson(core)),
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/** Exact #72 receipt shape retained only for compatibility tooling. */
|
|
852
|
+
export function createGhostgetMessagingReceiptBindingV1(input: Readonly<{
|
|
853
|
+
context: unknown;
|
|
854
|
+
clientIntent: unknown;
|
|
855
|
+
previewDigest: unknown;
|
|
856
|
+
runId: unknown;
|
|
857
|
+
state: unknown;
|
|
858
|
+
provenPartCount: unknown;
|
|
859
|
+
recordedAt: unknown;
|
|
860
|
+
}>): GhostgetMessagingReceiptBindingV1 {
|
|
861
|
+
const enriched = createGhostgetMessagingReceiptBindingV2(input);
|
|
862
|
+
const core = Object.freeze({
|
|
863
|
+
schemaVersion: 1 as const,
|
|
864
|
+
format: GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_FORMAT,
|
|
865
|
+
contractId: GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID,
|
|
866
|
+
contractHash: GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH,
|
|
867
|
+
clientIntentSha256: enriched.clientIntentSha256,
|
|
868
|
+
routeRefSha256: enriched.routeRefSha256,
|
|
869
|
+
contextRefSha256: enriched.contextRefSha256,
|
|
870
|
+
turnDigest: enriched.turnDigest,
|
|
871
|
+
previewDigest: enriched.previewDigest,
|
|
872
|
+
runId: enriched.runId,
|
|
873
|
+
state: enriched.state,
|
|
874
|
+
partCount: enriched.partCount,
|
|
875
|
+
provenPartCount: enriched.provenPartCount,
|
|
876
|
+
recordedAt: enriched.recordedAt,
|
|
877
|
+
});
|
|
878
|
+
return Object.freeze({
|
|
879
|
+
...core,
|
|
880
|
+
receiptSha256: sha256(canonicalJson(core)),
|
|
881
|
+
});
|
|
882
|
+
}
|