@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,1130 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_DESCRIPTOR,
|
|
4
|
+
GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_HASH,
|
|
5
|
+
GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID,
|
|
6
|
+
GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_DESCRIPTOR,
|
|
7
|
+
GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH,
|
|
8
|
+
GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID,
|
|
9
|
+
GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_DESCRIPTOR,
|
|
10
|
+
GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH,
|
|
11
|
+
GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID,
|
|
12
|
+
GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_DESCRIPTOR,
|
|
13
|
+
GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_HASH,
|
|
14
|
+
GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID,
|
|
15
|
+
parseGhostgetMessagingContextBindingV1,
|
|
16
|
+
parseGhostgetMessagingContextBindingV2
|
|
17
|
+
} from "./index-9t13b4k7.js";
|
|
18
|
+
import {
|
|
19
|
+
canonicalJson,
|
|
20
|
+
sha256
|
|
21
|
+
} from "./index-gwk7rbyj.js";
|
|
22
|
+
|
|
23
|
+
// src/messaging.ts
|
|
24
|
+
import { spawn } from "child_process";
|
|
25
|
+
import {
|
|
26
|
+
existsSync,
|
|
27
|
+
lstatSync,
|
|
28
|
+
mkdtempSync,
|
|
29
|
+
readFileSync,
|
|
30
|
+
rmSync
|
|
31
|
+
} from "fs";
|
|
32
|
+
import { tmpdir } from "os";
|
|
33
|
+
import { join } from "path";
|
|
34
|
+
import { fileURLToPath } from "url";
|
|
35
|
+
|
|
36
|
+
// src/messaging-types.ts
|
|
37
|
+
import { types as nodeTypes } from "util";
|
|
38
|
+
var MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID = GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID;
|
|
39
|
+
var MESSAGING_CONTEXT_BINDING_V1_CONTRACT_DESCRIPTOR = GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_DESCRIPTOR;
|
|
40
|
+
var MESSAGING_CONTEXT_BINDING_V1_CONTRACT_HASH = GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_HASH;
|
|
41
|
+
var MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID = GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID;
|
|
42
|
+
var MESSAGING_CONTEXT_BINDING_V2_CONTRACT_DESCRIPTOR = GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_DESCRIPTOR;
|
|
43
|
+
var MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH = GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH;
|
|
44
|
+
var MESSAGING_CONTEXT_BINDING_CONTRACT_ID = MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID;
|
|
45
|
+
var MESSAGING_CONTEXT_BINDING_CONTRACT_DESCRIPTOR = MESSAGING_CONTEXT_BINDING_V2_CONTRACT_DESCRIPTOR;
|
|
46
|
+
var MESSAGING_CONTEXT_BINDING_CONTRACT_HASH = MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH;
|
|
47
|
+
var MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID = GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID;
|
|
48
|
+
var MESSAGING_RECEIPT_BINDING_V1_CONTRACT_DESCRIPTOR = GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_DESCRIPTOR;
|
|
49
|
+
var MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH = GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH;
|
|
50
|
+
var MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID = GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID;
|
|
51
|
+
var MESSAGING_RECEIPT_BINDING_V2_CONTRACT_DESCRIPTOR = GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_DESCRIPTOR;
|
|
52
|
+
var MESSAGING_RECEIPT_BINDING_V2_CONTRACT_HASH = GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_HASH;
|
|
53
|
+
var MESSAGING_RECEIPT_BINDING_CONTRACT_ID = MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID;
|
|
54
|
+
var MESSAGING_RECEIPT_BINDING_CONTRACT_DESCRIPTOR = MESSAGING_RECEIPT_BINDING_V2_CONTRACT_DESCRIPTOR;
|
|
55
|
+
var MESSAGING_RECEIPT_BINDING_CONTRACT_HASH = MESSAGING_RECEIPT_BINDING_V2_CONTRACT_HASH;
|
|
56
|
+
function fail(label, detail) {
|
|
57
|
+
throw new Error(`${label} ${detail}`);
|
|
58
|
+
}
|
|
59
|
+
function record(value, label) {
|
|
60
|
+
if (nodeTypes.isProxy(value) || typeof value !== "object" || value === null || Array.isArray(value) || Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)
|
|
61
|
+
return fail(label, "must be a plain non-proxy object");
|
|
62
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
63
|
+
if (Reflect.ownKeys(descriptors).some((key) => typeof key !== "string")) {
|
|
64
|
+
return fail(label, "must not contain symbol properties");
|
|
65
|
+
}
|
|
66
|
+
const result = {};
|
|
67
|
+
for (const [key, descriptor] of Object.entries(descriptors)) {
|
|
68
|
+
if (!descriptor.enumerable || !("value" in descriptor)) {
|
|
69
|
+
return fail(`${label}.${key}`, "must be an enumerable data property");
|
|
70
|
+
}
|
|
71
|
+
result[key] = descriptor.value;
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
function exactKeys(value, required, optional, label) {
|
|
76
|
+
const allowed = new Set([...required, ...optional]);
|
|
77
|
+
if (required.some((key) => !Object.hasOwn(value, key)) || Object.keys(value).some((key) => !allowed.has(key)))
|
|
78
|
+
return fail(label, "has unsupported or missing fields");
|
|
79
|
+
}
|
|
80
|
+
function denseArray(value, label, maximum) {
|
|
81
|
+
if (nodeTypes.isProxy(value) || !Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length > maximum)
|
|
82
|
+
return fail(label, `must be a dense array of at most ${maximum} items`);
|
|
83
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
84
|
+
if (!Object.hasOwn(value, index))
|
|
85
|
+
return fail(label, "must not be sparse");
|
|
86
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, index);
|
|
87
|
+
if (descriptor === undefined || !("value" in descriptor) || !descriptor.enumerable) {
|
|
88
|
+
return fail(label, "must contain only enumerable data items");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const expected = new Set([
|
|
92
|
+
"length",
|
|
93
|
+
...Array.from({ length: value.length }, (_unused, index) => String(index))
|
|
94
|
+
]);
|
|
95
|
+
if (Reflect.ownKeys(value).some((key) => !expected.has(key))) {
|
|
96
|
+
return fail(label, "must not contain named or symbol properties");
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
function hasUnpairedSurrogate(value) {
|
|
101
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
102
|
+
const code = value.charCodeAt(index);
|
|
103
|
+
if (code >= 55296 && code <= 56319) {
|
|
104
|
+
const next = value.charCodeAt(index + 1);
|
|
105
|
+
if (next < 56320 || next > 57343)
|
|
106
|
+
return true;
|
|
107
|
+
index += 1;
|
|
108
|
+
} else if (code >= 56320 && code <= 57343)
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
function text(value, label, maximumBytes, options = {}) {
|
|
114
|
+
if (typeof value !== "string" || !options.allowEmpty && value.length === 0 || Buffer.byteLength(value, "utf8") > maximumBytes || hasUnpairedSurrogate(value) || /[\0\u007f-\u009f\u061c\u200e\u200f\u2028-\u202e\u2066-\u2069]/u.test(value) || !options.allowNewlines && /[\r\n]/u.test(value))
|
|
115
|
+
return fail(label, "must be bounded well-formed inert text");
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
function id(value, label, maximum = 256) {
|
|
119
|
+
const result = text(value, label, maximum);
|
|
120
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._~-]*$/u.test(result)) {
|
|
121
|
+
return fail(label, "must be an exact opaque identifier");
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
function routeRef(value, label) {
|
|
126
|
+
const result = id(value, label, 128);
|
|
127
|
+
if (!/^wmroute_[A-Za-z0-9_-]{22}$/u.test(result)) {
|
|
128
|
+
return fail(label, "must be a Ghostget messaging route reference");
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
function contextRef(value, label) {
|
|
133
|
+
const result = id(value, label, 128);
|
|
134
|
+
if (!/^wmcontext_[A-Za-z0-9_-]{22}$/u.test(result)) {
|
|
135
|
+
return fail(label, "must be a Ghostget messaging context reference");
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
function replyRef(value, label) {
|
|
140
|
+
const result = id(value, label, 128);
|
|
141
|
+
if (!/^wmreply_[A-Za-z0-9_-]{22}$/u.test(result)) {
|
|
142
|
+
return fail(label, "must be a Ghostget messaging reply reference");
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
function integer(value, label, minimum, maximum) {
|
|
147
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum || value > maximum)
|
|
148
|
+
return fail(label, `must be an integer from ${minimum} through ${maximum}`);
|
|
149
|
+
return value;
|
|
150
|
+
}
|
|
151
|
+
function cloneJsonRecord(value, label) {
|
|
152
|
+
let nodes = 0;
|
|
153
|
+
const ancestors = new WeakSet;
|
|
154
|
+
const visit = (candidate, path, depth) => {
|
|
155
|
+
nodes += 1;
|
|
156
|
+
if (nodes > 1e4 || depth > 32)
|
|
157
|
+
return fail(label, "exceeds its structural bound");
|
|
158
|
+
if (candidate === null || typeof candidate === "boolean" || typeof candidate === "string") {
|
|
159
|
+
if (typeof candidate === "string")
|
|
160
|
+
text(candidate, path, 256 * 1024, { allowEmpty: true, allowNewlines: true });
|
|
161
|
+
return candidate;
|
|
162
|
+
}
|
|
163
|
+
if (typeof candidate === "number") {
|
|
164
|
+
if (!Number.isFinite(candidate))
|
|
165
|
+
return fail(path, "must contain finite JSON numbers");
|
|
166
|
+
return candidate;
|
|
167
|
+
}
|
|
168
|
+
if (typeof candidate !== "object")
|
|
169
|
+
return fail(path, "must contain only JSON data");
|
|
170
|
+
if (ancestors.has(candidate))
|
|
171
|
+
return fail(label, "must not contain cycles");
|
|
172
|
+
ancestors.add(candidate);
|
|
173
|
+
try {
|
|
174
|
+
if (Array.isArray(candidate)) {
|
|
175
|
+
return Object.freeze(denseArray(candidate, path, 1000).map((item, index) => visit(item, `${path}[${index}]`, depth + 1)));
|
|
176
|
+
}
|
|
177
|
+
const source = record(candidate, path);
|
|
178
|
+
const entries = Object.entries(source).map(([key, item]) => {
|
|
179
|
+
text(key, `${path} key`, 256);
|
|
180
|
+
return [key, visit(item, `${path}.${key}`, depth + 1)];
|
|
181
|
+
});
|
|
182
|
+
return Object.freeze(Object.fromEntries(entries));
|
|
183
|
+
} finally {
|
|
184
|
+
ancestors.delete(candidate);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
return record(visit(value, label, 0), label);
|
|
188
|
+
}
|
|
189
|
+
function parseRoutesSource(value, label) {
|
|
190
|
+
const item = record(value, label);
|
|
191
|
+
exactKeys(item, ["adapterId", "authId", "listInput"], [], label);
|
|
192
|
+
const exactSource = Object.freeze({
|
|
193
|
+
adapterId: id(item.adapterId, `${label}.adapterId`, 64),
|
|
194
|
+
authId: id(item.authId, `${label}.authId`, 48),
|
|
195
|
+
listInput: cloneJsonRecord(item.listInput, `${label}.listInput`)
|
|
196
|
+
});
|
|
197
|
+
return exactSource;
|
|
198
|
+
}
|
|
199
|
+
function parseMessagingRoutesRequestV1(value) {
|
|
200
|
+
const source = record(value, "messaging routes request");
|
|
201
|
+
exactKeys(source, ["schemaVersion", "format", "source"], [], "messaging routes request");
|
|
202
|
+
if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-routes-request") {
|
|
203
|
+
return fail("messaging routes request", "has an unsupported contract");
|
|
204
|
+
}
|
|
205
|
+
return Object.freeze({
|
|
206
|
+
schemaVersion: 1,
|
|
207
|
+
format: "wrench.messaging-routes-request",
|
|
208
|
+
source: parseRoutesSource(source.source, "messaging routes request.source")
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function parseMessagingRouteResolveRequestV1(value) {
|
|
212
|
+
const source = record(value, "messaging route resolve request");
|
|
213
|
+
exactKeys(source, ["schemaVersion", "format", "source", "candidate"], [], "messaging route resolve request");
|
|
214
|
+
if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-route-resolve-request")
|
|
215
|
+
return fail("messaging route resolve request", "has an unsupported contract");
|
|
216
|
+
const candidate = record(source.candidate, "messaging route resolve request.candidate");
|
|
217
|
+
exactKeys(candidate, ["coordinate"], [], "messaging route resolve request.candidate");
|
|
218
|
+
const coordinateSource = record(candidate.coordinate, "messaging route resolve request.candidate.coordinate");
|
|
219
|
+
let coordinate;
|
|
220
|
+
if (coordinateSource.kind === "beeperConversation") {
|
|
221
|
+
exactKeys(coordinateSource, ["kind", "network", "conversationId"], [], "messaging route resolve request.candidate.coordinate");
|
|
222
|
+
coordinate = Object.freeze({
|
|
223
|
+
kind: "beeperConversation",
|
|
224
|
+
network: id(coordinateSource.network, "messaging route resolve request.candidate.coordinate.network", 64),
|
|
225
|
+
conversationId: text(coordinateSource.conversationId, "messaging route resolve request.candidate.coordinate.conversationId", 2048)
|
|
226
|
+
});
|
|
227
|
+
} else if (coordinateSource.kind === "imessageChat") {
|
|
228
|
+
exactKeys(coordinateSource, ["kind", "chatGuid", "service", "observedChatRowId"], [], "messaging route resolve request.candidate.coordinate");
|
|
229
|
+
coordinate = Object.freeze({
|
|
230
|
+
kind: "imessageChat",
|
|
231
|
+
chatGuid: text(coordinateSource.chatGuid, "messaging route resolve request.candidate.coordinate.chatGuid", 2048),
|
|
232
|
+
service: coordinateSource.service === null ? null : id(coordinateSource.service, "messaging route resolve request.candidate.coordinate.service", 64),
|
|
233
|
+
observedChatRowId: coordinateSource.observedChatRowId === null ? null : integer(coordinateSource.observedChatRowId, "messaging route resolve request.candidate.coordinate.observedChatRowId", 1, Number.MAX_SAFE_INTEGER)
|
|
234
|
+
});
|
|
235
|
+
} else if (coordinateSource.kind === "whatsappJid") {
|
|
236
|
+
exactKeys(coordinateSource, ["kind", "jid"], [], "messaging route resolve request.candidate.coordinate");
|
|
237
|
+
coordinate = Object.freeze({
|
|
238
|
+
kind: "whatsappJid",
|
|
239
|
+
jid: text(coordinateSource.jid, "messaging route resolve request.candidate.coordinate.jid", 512)
|
|
240
|
+
});
|
|
241
|
+
} else {
|
|
242
|
+
return fail("messaging route resolve request.candidate.coordinate.kind", "is unsupported");
|
|
243
|
+
}
|
|
244
|
+
return Object.freeze({
|
|
245
|
+
schemaVersion: 1,
|
|
246
|
+
format: "wrench.messaging-route-resolve-request",
|
|
247
|
+
source: parseRoutesSource(source.source, "messaging route resolve request.source"),
|
|
248
|
+
candidate: Object.freeze({ coordinate })
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
function parseMessagingRouteResolveRequestV2(value) {
|
|
252
|
+
const source = record(value, "messaging route resolve request V2");
|
|
253
|
+
exactKeys(source, ["schemaVersion", "format", "routeRef"], [], "messaging route resolve request V2");
|
|
254
|
+
if (source.schemaVersion !== 2 || source.format !== "wrench.messaging-route-resolve-request")
|
|
255
|
+
return fail("messaging route resolve request V2", "has an unsupported contract");
|
|
256
|
+
return Object.freeze({
|
|
257
|
+
schemaVersion: 2,
|
|
258
|
+
format: "wrench.messaging-route-resolve-request",
|
|
259
|
+
routeRef: routeRef(source.routeRef, "messaging route resolve request V2.routeRef")
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
function parseMessagingContextRequestV1(value) {
|
|
263
|
+
const source = record(value, "messaging context request");
|
|
264
|
+
exactKeys(source, ["schemaVersion", "format", "routeRef", "limit"], [], "messaging context request");
|
|
265
|
+
if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-context-request") {
|
|
266
|
+
return fail("messaging context request", "has an unsupported contract");
|
|
267
|
+
}
|
|
268
|
+
return Object.freeze({
|
|
269
|
+
schemaVersion: 1,
|
|
270
|
+
format: "wrench.messaging-context-request",
|
|
271
|
+
routeRef: routeRef(source.routeRef, "messaging context request.routeRef"),
|
|
272
|
+
limit: integer(source.limit, "messaging context request.limit", 1, 200)
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
function parseMessagingTurnV1(value) {
|
|
276
|
+
const source = record(value, "messaging turn");
|
|
277
|
+
exactKeys(source, [
|
|
278
|
+
"schemaVersion",
|
|
279
|
+
"format",
|
|
280
|
+
"clientIntentSha256",
|
|
281
|
+
"routeRef",
|
|
282
|
+
"contextRef",
|
|
283
|
+
"parts"
|
|
284
|
+
], [], "messaging turn");
|
|
285
|
+
if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-turn") {
|
|
286
|
+
return fail("messaging turn", "has an unsupported contract");
|
|
287
|
+
}
|
|
288
|
+
const seen = new Set;
|
|
289
|
+
const parts = denseArray(source.parts, "messaging turn.parts", 8).map((candidate, index) => {
|
|
290
|
+
const item = record(candidate, `messaging turn.parts[${index}]`);
|
|
291
|
+
exactKeys(item, ["partId", "text", "replyRef"], [], `messaging turn.parts[${index}]`);
|
|
292
|
+
const partId = id(item.partId, `messaging turn.parts[${index}].partId`, 64);
|
|
293
|
+
if (seen.has(partId))
|
|
294
|
+
return fail("messaging turn.parts", "must not repeat part IDs");
|
|
295
|
+
seen.add(partId);
|
|
296
|
+
return Object.freeze({
|
|
297
|
+
partId,
|
|
298
|
+
text: text(item.text, `messaging turn.parts[${index}].text`, 65536, { allowNewlines: true }),
|
|
299
|
+
replyRef: item.replyRef === null ? null : replyRef(item.replyRef, `messaging turn.parts[${index}].replyRef`)
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
if (parts.length === 0)
|
|
303
|
+
return fail("messaging turn.parts", "must not be empty");
|
|
304
|
+
return Object.freeze({
|
|
305
|
+
schemaVersion: 1,
|
|
306
|
+
format: "wrench.messaging-turn",
|
|
307
|
+
clientIntentSha256: sha256Digest(source.clientIntentSha256, "messaging turn.clientIntentSha256"),
|
|
308
|
+
routeRef: routeRef(source.routeRef, "messaging turn.routeRef"),
|
|
309
|
+
contextRef: contextRef(source.contextRef, "messaging turn.contextRef"),
|
|
310
|
+
parts: Object.freeze(parts)
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
function messagingTurnDigest(turn) {
|
|
314
|
+
const parsed = parseMessagingTurnV1(turn);
|
|
315
|
+
return sha256(canonicalJson({
|
|
316
|
+
schemaVersion: 1,
|
|
317
|
+
format: "wrench.messaging-turn",
|
|
318
|
+
clientIntentSha256: parsed.clientIntentSha256,
|
|
319
|
+
routeRef: parsed.routeRef,
|
|
320
|
+
contextRef: parsed.contextRef,
|
|
321
|
+
parts: parsed.parts.map((part) => ({
|
|
322
|
+
partId: part.partId,
|
|
323
|
+
text: part.text,
|
|
324
|
+
replyRef: part.replyRef
|
|
325
|
+
}))
|
|
326
|
+
}));
|
|
327
|
+
}
|
|
328
|
+
function parseMessagingContextBindingV1(value) {
|
|
329
|
+
const parsed = parseGhostgetMessagingContextBindingV1(value);
|
|
330
|
+
routeRef(parsed.routeRef, "messaging context binding.routeRef");
|
|
331
|
+
contextRef(parsed.contextRef, "messaging context binding.contextRef");
|
|
332
|
+
return parsed;
|
|
333
|
+
}
|
|
334
|
+
function parseMessagingContextBindingV2(value) {
|
|
335
|
+
const parsed = parseGhostgetMessagingContextBindingV2(value);
|
|
336
|
+
routeRef(parsed.routeRef, "messaging context binding.routeRef");
|
|
337
|
+
contextRef(parsed.contextRef, "messaging context binding.contextRef");
|
|
338
|
+
return parsed;
|
|
339
|
+
}
|
|
340
|
+
function parseMessagingContextBinding(value) {
|
|
341
|
+
const source = record(value, "messaging context binding");
|
|
342
|
+
return source.contractId === MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID ? parseMessagingContextBindingV1(source) : parseMessagingContextBindingV2(source);
|
|
343
|
+
}
|
|
344
|
+
function sha256Digest(candidate, label) {
|
|
345
|
+
const result = text(candidate, label, 64);
|
|
346
|
+
if (!/^[a-f0-9]{64}$/u.test(result))
|
|
347
|
+
return fail(label, "must be a SHA-256 digest");
|
|
348
|
+
return result;
|
|
349
|
+
}
|
|
350
|
+
function utcTimestamp(candidate, label) {
|
|
351
|
+
const result = text(candidate, label, 64);
|
|
352
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/u.test(result) || !Number.isFinite(Date.parse(result)))
|
|
353
|
+
return fail(label, "must be an RFC3339 UTC timestamp");
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
function nullableBoundedText(candidate, label, maximumBytes, allowNewlines = false) {
|
|
357
|
+
return candidate === null ? null : text(candidate, label, maximumBytes, {
|
|
358
|
+
allowEmpty: true,
|
|
359
|
+
allowNewlines
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
function bool(candidate, label) {
|
|
363
|
+
if (typeof candidate !== "boolean")
|
|
364
|
+
return fail(label, "must be boolean");
|
|
365
|
+
return candidate;
|
|
366
|
+
}
|
|
367
|
+
function completeness(value, label) {
|
|
368
|
+
const source = record(value, label);
|
|
369
|
+
exactKeys(source, ["kind", "reason"], [], label);
|
|
370
|
+
const kinds = new Set([
|
|
371
|
+
"complete",
|
|
372
|
+
"page",
|
|
373
|
+
"unknown",
|
|
374
|
+
"first-page-only",
|
|
375
|
+
"bounded-local",
|
|
376
|
+
"search-window",
|
|
377
|
+
"truncated"
|
|
378
|
+
]);
|
|
379
|
+
if (!kinds.has(source.kind)) {
|
|
380
|
+
return fail(`${label}.kind`, "is unsupported");
|
|
381
|
+
}
|
|
382
|
+
return Object.freeze({
|
|
383
|
+
kind: source.kind,
|
|
384
|
+
reason: nullableBoundedText(source.reason, `${label}.reason`, 2048)
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
function conversation(value, label) {
|
|
388
|
+
const source = record(value, label);
|
|
389
|
+
exactKeys(source, ["kind", "title", "participantCount"], [], label);
|
|
390
|
+
if (source.kind !== "single" && source.kind !== "group" && source.kind !== "unknown")
|
|
391
|
+
return fail(`${label}.kind`, "is unsupported");
|
|
392
|
+
return Object.freeze({
|
|
393
|
+
kind: source.kind,
|
|
394
|
+
title: nullableBoundedText(source.title, `${label}.title`, 4096, true),
|
|
395
|
+
participantCount: integer(source.participantCount, `${label}.participantCount`, 0, 1e4)
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
function parseMessagingRouteV1(value) {
|
|
399
|
+
const source = record(value, "messaging route");
|
|
400
|
+
exactKeys(source, [
|
|
401
|
+
"schemaVersion",
|
|
402
|
+
"format",
|
|
403
|
+
"routeRef",
|
|
404
|
+
"network",
|
|
405
|
+
"conversation",
|
|
406
|
+
"readiness",
|
|
407
|
+
"completeness",
|
|
408
|
+
"expiresAt"
|
|
409
|
+
], [], "messaging route");
|
|
410
|
+
if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-route") {
|
|
411
|
+
return fail("messaging route", "has an unsupported contract");
|
|
412
|
+
}
|
|
413
|
+
const readiness = record(source.readiness, "messaging route.readiness");
|
|
414
|
+
exactKeys(readiness, ["context", "turn", "reply", "reason"], [], "messaging route.readiness");
|
|
415
|
+
if (readiness.context !== "ready" && readiness.context !== "historical-readable")
|
|
416
|
+
return fail("messaging route.readiness.context", "is unsupported");
|
|
417
|
+
if (readiness.turn !== "ready" && readiness.turn !== "unavailable") {
|
|
418
|
+
return fail("messaging route.readiness.turn", "is unsupported");
|
|
419
|
+
}
|
|
420
|
+
if (readiness.reply !== "supported" && readiness.reply !== "unsupported") {
|
|
421
|
+
return fail("messaging route.readiness.reply", "is unsupported");
|
|
422
|
+
}
|
|
423
|
+
if (readiness.context === "historical-readable" && (readiness.turn !== "unavailable" || readiness.reply !== "unsupported"))
|
|
424
|
+
return fail("messaging route.readiness", "must block actions when freshness is unproven");
|
|
425
|
+
return Object.freeze({
|
|
426
|
+
schemaVersion: 1,
|
|
427
|
+
format: "wrench.messaging-route",
|
|
428
|
+
routeRef: routeRef(source.routeRef, "messaging route.routeRef"),
|
|
429
|
+
network: id(source.network, "messaging route.network", 64),
|
|
430
|
+
conversation: conversation(source.conversation, "messaging route.conversation"),
|
|
431
|
+
readiness: Object.freeze({
|
|
432
|
+
context: readiness.context,
|
|
433
|
+
turn: readiness.turn,
|
|
434
|
+
reply: readiness.reply,
|
|
435
|
+
reason: nullableBoundedText(readiness.reason, "messaging route.readiness.reason", 1000)
|
|
436
|
+
}),
|
|
437
|
+
completeness: completeness(source.completeness, "messaging route.completeness"),
|
|
438
|
+
expiresAt: utcTimestamp(source.expiresAt, "messaging route.expiresAt")
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function parseMessagingRouteV2(value) {
|
|
442
|
+
const source = record(value, "messaging route V2");
|
|
443
|
+
exactKeys(source, [
|
|
444
|
+
"schemaVersion",
|
|
445
|
+
"format",
|
|
446
|
+
"routeRef",
|
|
447
|
+
"network",
|
|
448
|
+
"conversation",
|
|
449
|
+
"readiness",
|
|
450
|
+
"completeness",
|
|
451
|
+
"expiresAt"
|
|
452
|
+
], [], "messaging route V2");
|
|
453
|
+
if (source.schemaVersion !== 2 || source.format !== "wrench.messaging-route") {
|
|
454
|
+
return fail("messaging route V2", "has an unsupported contract");
|
|
455
|
+
}
|
|
456
|
+
const readiness = record(source.readiness, "messaging route V2.readiness");
|
|
457
|
+
exactKeys(readiness, ["context", "turn", "reply", "reason"], [], "messaging route V2.readiness");
|
|
458
|
+
if (readiness.context !== "ready" && readiness.context !== "historical-readable" && readiness.context !== "resolution-required" && readiness.context !== "unavailable")
|
|
459
|
+
return fail("messaging route V2.readiness.context", "is unsupported");
|
|
460
|
+
if (readiness.turn !== "ready" && readiness.turn !== "unavailable") {
|
|
461
|
+
return fail("messaging route V2.readiness.turn", "is unsupported");
|
|
462
|
+
}
|
|
463
|
+
if (readiness.reply !== "supported" && readiness.reply !== "unsupported") {
|
|
464
|
+
return fail("messaging route V2.readiness.reply", "is unsupported");
|
|
465
|
+
}
|
|
466
|
+
if (readiness.context !== "ready" && (readiness.turn !== "unavailable" || readiness.reply !== "unsupported"))
|
|
467
|
+
return fail("messaging route V2.readiness", "must block actions when freshness is unproven");
|
|
468
|
+
if (readiness.context !== "ready" && readiness.reason === null) {
|
|
469
|
+
return fail("messaging route V2.readiness.reason", "is required when context is not actionable");
|
|
470
|
+
}
|
|
471
|
+
return Object.freeze({
|
|
472
|
+
schemaVersion: 2,
|
|
473
|
+
format: "wrench.messaging-route",
|
|
474
|
+
routeRef: routeRef(source.routeRef, "messaging route V2.routeRef"),
|
|
475
|
+
network: id(source.network, "messaging route V2.network", 64),
|
|
476
|
+
conversation: conversation(source.conversation, "messaging route V2.conversation"),
|
|
477
|
+
readiness: Object.freeze({
|
|
478
|
+
context: readiness.context,
|
|
479
|
+
turn: readiness.turn,
|
|
480
|
+
reply: readiness.reply,
|
|
481
|
+
reason: nullableBoundedText(readiness.reason, "messaging route V2.readiness.reason", 1000)
|
|
482
|
+
}),
|
|
483
|
+
completeness: completeness(source.completeness, "messaging route V2.completeness"),
|
|
484
|
+
expiresAt: utcTimestamp(source.expiresAt, "messaging route V2.expiresAt")
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
function parseMessagingRoutesV1(value) {
|
|
488
|
+
const source = record(value, "messaging routes");
|
|
489
|
+
exactKeys(source, [
|
|
490
|
+
"schemaVersion",
|
|
491
|
+
"format",
|
|
492
|
+
"generatedAt",
|
|
493
|
+
"completeness",
|
|
494
|
+
"continuation",
|
|
495
|
+
"routes"
|
|
496
|
+
], [], "messaging routes");
|
|
497
|
+
if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-routes") {
|
|
498
|
+
return fail("messaging routes", "has an unsupported contract");
|
|
499
|
+
}
|
|
500
|
+
const continuation = record(source.continuation, "messaging routes.continuation");
|
|
501
|
+
exactKeys(continuation, ["direction", "request", "nextInput"], [], "messaging routes.continuation");
|
|
502
|
+
if (continuation.direction !== "forward" && continuation.direction !== "backward" && continuation.direction !== "none")
|
|
503
|
+
return fail("messaging routes.continuation.direction", "is unsupported");
|
|
504
|
+
const request = nullableBoundedText(continuation.request, "messaging routes.continuation.request", 8192);
|
|
505
|
+
const nextInput = continuation.nextInput === null ? null : cloneJsonRecord(continuation.nextInput, "messaging routes.continuation.nextInput");
|
|
506
|
+
if (continuation.direction === "none" && (request !== null || nextInput !== null))
|
|
507
|
+
return fail("messaging routes.continuation", "must be empty when direction is none");
|
|
508
|
+
const routes = denseArray(source.routes, "messaging routes.routes", 1000).map(parseMessagingRouteV1);
|
|
509
|
+
return Object.freeze({
|
|
510
|
+
schemaVersion: 1,
|
|
511
|
+
format: "wrench.messaging-routes",
|
|
512
|
+
generatedAt: utcTimestamp(source.generatedAt, "messaging routes.generatedAt"),
|
|
513
|
+
completeness: completeness(source.completeness, "messaging routes.completeness"),
|
|
514
|
+
continuation: Object.freeze({
|
|
515
|
+
direction: continuation.direction,
|
|
516
|
+
request,
|
|
517
|
+
nextInput
|
|
518
|
+
}),
|
|
519
|
+
routes: Object.freeze(routes)
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
function parseMessagingRoutesV2(value) {
|
|
523
|
+
const source = record(value, "messaging routes V2");
|
|
524
|
+
exactKeys(source, [
|
|
525
|
+
"schemaVersion",
|
|
526
|
+
"format",
|
|
527
|
+
"generatedAt",
|
|
528
|
+
"completeness",
|
|
529
|
+
"continuation",
|
|
530
|
+
"routes"
|
|
531
|
+
], [], "messaging routes V2");
|
|
532
|
+
if (source.schemaVersion !== 2 || source.format !== "wrench.messaging-routes") {
|
|
533
|
+
return fail("messaging routes V2", "has an unsupported contract");
|
|
534
|
+
}
|
|
535
|
+
const continuation = record(source.continuation, "messaging routes V2.continuation");
|
|
536
|
+
exactKeys(continuation, ["direction", "request", "nextInput"], [], "messaging routes V2.continuation");
|
|
537
|
+
if (continuation.direction !== "forward" && continuation.direction !== "backward" && continuation.direction !== "none")
|
|
538
|
+
return fail("messaging routes V2.continuation.direction", "is unsupported");
|
|
539
|
+
const request = nullableBoundedText(continuation.request, "messaging routes V2.continuation.request", 8192);
|
|
540
|
+
const nextInput = continuation.nextInput === null ? null : cloneJsonRecord(continuation.nextInput, "messaging routes V2.continuation.nextInput");
|
|
541
|
+
if (continuation.direction === "none" && (request !== null || nextInput !== null))
|
|
542
|
+
return fail("messaging routes V2.continuation", "must be empty when direction is none");
|
|
543
|
+
const routes = denseArray(source.routes, "messaging routes V2.routes", 1000).map(parseMessagingRouteV2);
|
|
544
|
+
return Object.freeze({
|
|
545
|
+
schemaVersion: 2,
|
|
546
|
+
format: "wrench.messaging-routes",
|
|
547
|
+
generatedAt: utcTimestamp(source.generatedAt, "messaging routes V2.generatedAt"),
|
|
548
|
+
completeness: completeness(source.completeness, "messaging routes V2.completeness"),
|
|
549
|
+
continuation: Object.freeze({
|
|
550
|
+
direction: continuation.direction,
|
|
551
|
+
request,
|
|
552
|
+
nextInput
|
|
553
|
+
}),
|
|
554
|
+
routes: Object.freeze(routes)
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
function parseMessagingContextV1(value) {
|
|
558
|
+
const source = record(value, "messaging context");
|
|
559
|
+
exactKeys(source, [
|
|
560
|
+
"schemaVersion",
|
|
561
|
+
"format",
|
|
562
|
+
"binding",
|
|
563
|
+
"network",
|
|
564
|
+
"liveness",
|
|
565
|
+
"truncated",
|
|
566
|
+
"completeness",
|
|
567
|
+
"messages",
|
|
568
|
+
"warnings"
|
|
569
|
+
], [], "messaging context");
|
|
570
|
+
if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-context") {
|
|
571
|
+
return fail("messaging context", "has an unsupported contract");
|
|
572
|
+
}
|
|
573
|
+
if (source.liveness !== "fresh-as-of-live-preflight" && source.liveness !== "freshness-unproven")
|
|
574
|
+
return fail("messaging context.liveness", "is unsupported");
|
|
575
|
+
const messages = denseArray(source.messages, "messaging context.messages", 200).map((value_, index) => {
|
|
576
|
+
const label = `messaging context.messages[${index}]`;
|
|
577
|
+
const message = record(value_, label);
|
|
578
|
+
exactKeys(message, [
|
|
579
|
+
"messageRef",
|
|
580
|
+
"direction",
|
|
581
|
+
"time",
|
|
582
|
+
"author",
|
|
583
|
+
"body",
|
|
584
|
+
"bodyTruncated",
|
|
585
|
+
"edited",
|
|
586
|
+
"retracted",
|
|
587
|
+
"reply",
|
|
588
|
+
"attachments",
|
|
589
|
+
"untrustedData"
|
|
590
|
+
], [], label);
|
|
591
|
+
if (message.direction !== "incoming" && message.direction !== "outgoing" && message.direction !== "unknown")
|
|
592
|
+
return fail(`${label}.direction`, "is unsupported");
|
|
593
|
+
if (message.edited !== "unknown" && message.edited !== "observed" && message.edited !== "not-observed")
|
|
594
|
+
return fail(`${label}.edited`, "is unsupported");
|
|
595
|
+
if (message.retracted !== "unknown" && message.retracted !== "observed" && message.retracted !== "not-observed")
|
|
596
|
+
return fail(`${label}.retracted`, "is unsupported");
|
|
597
|
+
if (message.untrustedData !== true) {
|
|
598
|
+
return fail(`${label}.untrustedData`, "must be true");
|
|
599
|
+
}
|
|
600
|
+
const authorSource = message.author === null ? null : record(message.author, `${label}.author`);
|
|
601
|
+
if (authorSource !== null) {
|
|
602
|
+
exactKeys(authorSource, ["displayName", "handle"], [], `${label}.author`);
|
|
603
|
+
}
|
|
604
|
+
const reply = record(message.reply, `${label}.reply`);
|
|
605
|
+
exactKeys(reply, ["toMessageRef"], [], `${label}.reply`);
|
|
606
|
+
const attachments = denseArray(message.attachments, `${label}.attachments`, 64).map((value__, attachmentIndex) => {
|
|
607
|
+
const attachmentLabel = `${label}.attachments[${attachmentIndex}]`;
|
|
608
|
+
const attachment = record(value__, attachmentLabel);
|
|
609
|
+
exactKeys(attachment, ["kind", "mimeType", "name", "sizeBytes"], [], attachmentLabel);
|
|
610
|
+
const kinds = new Set([
|
|
611
|
+
"audio",
|
|
612
|
+
"document",
|
|
613
|
+
"image",
|
|
614
|
+
"link",
|
|
615
|
+
"sticker",
|
|
616
|
+
"video",
|
|
617
|
+
"unknown"
|
|
618
|
+
]);
|
|
619
|
+
if (!kinds.has(attachment.kind)) {
|
|
620
|
+
return fail(`${attachmentLabel}.kind`, "is unsupported");
|
|
621
|
+
}
|
|
622
|
+
return Object.freeze({
|
|
623
|
+
kind: attachment.kind,
|
|
624
|
+
mimeType: nullableBoundedText(attachment.mimeType, `${attachmentLabel}.mimeType`, 256),
|
|
625
|
+
name: nullableBoundedText(attachment.name, `${attachmentLabel}.name`, 1024, true),
|
|
626
|
+
sizeBytes: attachment.sizeBytes === null ? null : integer(attachment.sizeBytes, `${attachmentLabel}.sizeBytes`, 0, Number.MAX_SAFE_INTEGER)
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
return Object.freeze({
|
|
630
|
+
messageRef: replyRef(message.messageRef, `${label}.messageRef`),
|
|
631
|
+
direction: message.direction,
|
|
632
|
+
time: message.time === null ? null : utcTimestamp(message.time, `${label}.time`),
|
|
633
|
+
author: authorSource === null ? null : Object.freeze({
|
|
634
|
+
displayName: nullableBoundedText(authorSource.displayName, `${label}.author.displayName`, 2048, true),
|
|
635
|
+
handle: nullableBoundedText(authorSource.handle, `${label}.author.handle`, 512)
|
|
636
|
+
}),
|
|
637
|
+
body: nullableBoundedText(message.body, `${label}.body`, 256 * 1024, true),
|
|
638
|
+
bodyTruncated: bool(message.bodyTruncated, `${label}.bodyTruncated`),
|
|
639
|
+
edited: message.edited,
|
|
640
|
+
retracted: message.retracted,
|
|
641
|
+
reply: Object.freeze({
|
|
642
|
+
toMessageRef: reply.toMessageRef === null ? null : replyRef(reply.toMessageRef, `${label}.reply.toMessageRef`)
|
|
643
|
+
}),
|
|
644
|
+
attachments: Object.freeze(attachments),
|
|
645
|
+
untrustedData: true
|
|
646
|
+
});
|
|
647
|
+
});
|
|
648
|
+
const warnings = denseArray(source.warnings, "messaging context.warnings", 32).map((value_, index) => text(value_, `messaging context.warnings[${index}]`, 512));
|
|
649
|
+
return Object.freeze({
|
|
650
|
+
schemaVersion: 1,
|
|
651
|
+
format: "wrench.messaging-context",
|
|
652
|
+
binding: source.binding === null ? null : parseMessagingContextBinding(source.binding),
|
|
653
|
+
network: id(source.network, "messaging context.network", 64),
|
|
654
|
+
liveness: source.liveness,
|
|
655
|
+
truncated: bool(source.truncated, "messaging context.truncated"),
|
|
656
|
+
completeness: completeness(source.completeness, "messaging context.completeness"),
|
|
657
|
+
messages: Object.freeze(messages),
|
|
658
|
+
warnings: Object.freeze(warnings)
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
function parseMessagingPreviewV1(value) {
|
|
662
|
+
const source = record(value, "messaging preview");
|
|
663
|
+
exactKeys(source, [
|
|
664
|
+
"schemaVersion",
|
|
665
|
+
"format",
|
|
666
|
+
"status",
|
|
667
|
+
"planDigest",
|
|
668
|
+
"expiresAt",
|
|
669
|
+
"routeRef",
|
|
670
|
+
"contextRef",
|
|
671
|
+
"clientIntentSha256",
|
|
672
|
+
"turnDigest",
|
|
673
|
+
"recipient",
|
|
674
|
+
"partCount",
|
|
675
|
+
"bubbles",
|
|
676
|
+
"risk",
|
|
677
|
+
"sideEffect"
|
|
678
|
+
], [], "messaging preview");
|
|
679
|
+
if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-preview" || source.status !== "confirmation-required" || source.risk !== "R3")
|
|
680
|
+
return fail("messaging preview", "has an unsupported contract");
|
|
681
|
+
const recipient = record(source.recipient, "messaging preview.recipient");
|
|
682
|
+
exactKeys(recipient, ["network", "conversation"], [], "messaging preview.recipient");
|
|
683
|
+
const bubbles = denseArray(source.bubbles, "messaging preview.bubbles", 8).map((value_, index) => {
|
|
684
|
+
const label = `messaging preview.bubbles[${index}]`;
|
|
685
|
+
const bubble = record(value_, label);
|
|
686
|
+
exactKeys(bubble, ["partId", "text", "replyRef"], [], label);
|
|
687
|
+
return Object.freeze({
|
|
688
|
+
partId: id(bubble.partId, `${label}.partId`, 64),
|
|
689
|
+
text: text(bubble.text, `${label}.text`, 65536, { allowNewlines: true }),
|
|
690
|
+
replyRef: bubble.replyRef === null ? null : replyRef(bubble.replyRef, `${label}.replyRef`)
|
|
691
|
+
});
|
|
692
|
+
});
|
|
693
|
+
const partCount = integer(source.partCount, "messaging preview.partCount", 1, 8);
|
|
694
|
+
if (bubbles.length !== partCount) {
|
|
695
|
+
return fail("messaging preview.bubbles", "must match partCount");
|
|
696
|
+
}
|
|
697
|
+
return Object.freeze({
|
|
698
|
+
schemaVersion: 1,
|
|
699
|
+
format: "wrench.messaging-preview",
|
|
700
|
+
status: "confirmation-required",
|
|
701
|
+
planDigest: sha256Digest(source.planDigest, "messaging preview.planDigest"),
|
|
702
|
+
expiresAt: utcTimestamp(source.expiresAt, "messaging preview.expiresAt"),
|
|
703
|
+
routeRef: routeRef(source.routeRef, "messaging preview.routeRef"),
|
|
704
|
+
contextRef: contextRef(source.contextRef, "messaging preview.contextRef"),
|
|
705
|
+
clientIntentSha256: sha256Digest(source.clientIntentSha256, "messaging preview.clientIntentSha256"),
|
|
706
|
+
turnDigest: sha256Digest(source.turnDigest, "messaging preview.turnDigest"),
|
|
707
|
+
recipient: Object.freeze({
|
|
708
|
+
network: id(recipient.network, "messaging preview.recipient.network", 64),
|
|
709
|
+
conversation: conversation(recipient.conversation, "messaging preview.recipient.conversation")
|
|
710
|
+
}),
|
|
711
|
+
partCount,
|
|
712
|
+
bubbles: Object.freeze(bubbles),
|
|
713
|
+
risk: "R3",
|
|
714
|
+
sideEffect: text(source.sideEffect, "messaging preview.sideEffect", 1024)
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
function parseMessagingPrivateOutputReceiptV1(value) {
|
|
718
|
+
const source = record(value, "messaging private output receipt");
|
|
719
|
+
exactKeys(source, [
|
|
720
|
+
"schemaVersion",
|
|
721
|
+
"format",
|
|
722
|
+
"artifactFormat",
|
|
723
|
+
"artifactSha256",
|
|
724
|
+
"itemCount",
|
|
725
|
+
"generatedAt",
|
|
726
|
+
"expiresAt"
|
|
727
|
+
], [], "messaging private output receipt");
|
|
728
|
+
const formats = new Set([
|
|
729
|
+
"wrench.messaging-route",
|
|
730
|
+
"wrench.messaging-routes",
|
|
731
|
+
"wrench.messaging-context",
|
|
732
|
+
"wrench.messaging-preview",
|
|
733
|
+
"wrench.messaging-run",
|
|
734
|
+
"wrench.messaging-receipt-binding"
|
|
735
|
+
]);
|
|
736
|
+
if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-private-output-receipt" || !formats.has(source.artifactFormat))
|
|
737
|
+
return fail("messaging private output receipt", "has an unsupported contract");
|
|
738
|
+
return Object.freeze({
|
|
739
|
+
schemaVersion: 1,
|
|
740
|
+
format: "wrench.messaging-private-output-receipt",
|
|
741
|
+
artifactFormat: source.artifactFormat,
|
|
742
|
+
artifactSha256: sha256Digest(source.artifactSha256, "messaging private output receipt.artifactSha256"),
|
|
743
|
+
itemCount: integer(source.itemCount, "messaging private output receipt.itemCount", 0, 1000),
|
|
744
|
+
generatedAt: utcTimestamp(source.generatedAt, "messaging private output receipt.generatedAt"),
|
|
745
|
+
expiresAt: source.expiresAt === null ? null : utcTimestamp(source.expiresAt, "messaging private output receipt.expiresAt")
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
function parseMessagingReceiptBindingV1(value) {
|
|
749
|
+
const source = record(value, "messaging receipt binding V1");
|
|
750
|
+
exactKeys(source, [
|
|
751
|
+
"schemaVersion",
|
|
752
|
+
"format",
|
|
753
|
+
"contractId",
|
|
754
|
+
"contractHash",
|
|
755
|
+
"clientIntentSha256",
|
|
756
|
+
"routeRefSha256",
|
|
757
|
+
"contextRefSha256",
|
|
758
|
+
"turnDigest",
|
|
759
|
+
"previewDigest",
|
|
760
|
+
"runId",
|
|
761
|
+
"state",
|
|
762
|
+
"partCount",
|
|
763
|
+
"provenPartCount",
|
|
764
|
+
"receiptSha256",
|
|
765
|
+
"recordedAt"
|
|
766
|
+
], [], "messaging receipt binding V1");
|
|
767
|
+
if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-receipt-binding" || source.contractId !== MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID || source.contractHash !== MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH || source.state !== "submitted" && source.state !== "failed" && source.state !== "partial" && source.state !== "indeterminate")
|
|
768
|
+
return fail("messaging receipt binding V1", "has an unsupported contract");
|
|
769
|
+
const partCount = integer(source.partCount, "messaging receipt binding V1.partCount", 1, 8);
|
|
770
|
+
const provenPartCount = integer(source.provenPartCount, "messaging receipt binding V1.provenPartCount", 0, partCount);
|
|
771
|
+
const validPrefix = source.state === "submitted" ? provenPartCount === partCount : source.state === "failed" ? provenPartCount === 0 : source.state === "partial" ? provenPartCount >= 1 && provenPartCount < partCount : provenPartCount < partCount;
|
|
772
|
+
if (!validPrefix) {
|
|
773
|
+
return fail("messaging receipt binding V1", "violates the proven-prefix state law");
|
|
774
|
+
}
|
|
775
|
+
const normalizedWithoutReceipt = Object.freeze({
|
|
776
|
+
schemaVersion: 1,
|
|
777
|
+
format: "wrench.messaging-receipt-binding",
|
|
778
|
+
contractId: MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID,
|
|
779
|
+
contractHash: MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH,
|
|
780
|
+
clientIntentSha256: sha256Digest(source.clientIntentSha256, "messaging receipt binding V1.clientIntentSha256"),
|
|
781
|
+
routeRefSha256: sha256Digest(source.routeRefSha256, "messaging receipt binding V1.routeRefSha256"),
|
|
782
|
+
contextRefSha256: sha256Digest(source.contextRefSha256, "messaging receipt binding V1.contextRefSha256"),
|
|
783
|
+
turnDigest: sha256Digest(source.turnDigest, "messaging receipt binding V1.turnDigest"),
|
|
784
|
+
previewDigest: sha256Digest(source.previewDigest, "messaging receipt binding V1.previewDigest"),
|
|
785
|
+
runId: id(source.runId, "messaging receipt binding V1.runId", 256),
|
|
786
|
+
state: source.state,
|
|
787
|
+
partCount,
|
|
788
|
+
provenPartCount,
|
|
789
|
+
recordedAt: utcTimestamp(source.recordedAt, "messaging receipt binding V1.recordedAt")
|
|
790
|
+
});
|
|
791
|
+
const receiptSha256 = sha256Digest(source.receiptSha256, "messaging receipt binding V1.receiptSha256");
|
|
792
|
+
if (sha256(canonicalJson(normalizedWithoutReceipt)) !== receiptSha256) {
|
|
793
|
+
return fail("messaging receipt binding V1.receiptSha256", "does not bind the canonical receipt");
|
|
794
|
+
}
|
|
795
|
+
return Object.freeze({ ...normalizedWithoutReceipt, receiptSha256 });
|
|
796
|
+
}
|
|
797
|
+
function parseMessagingReceiptBindingV2(value) {
|
|
798
|
+
const source = record(value, "messaging receipt binding");
|
|
799
|
+
exactKeys(source, [
|
|
800
|
+
"schemaVersion",
|
|
801
|
+
"format",
|
|
802
|
+
"contractId",
|
|
803
|
+
"contractHash",
|
|
804
|
+
"clientIntentSha256",
|
|
805
|
+
"contextBindingSha256",
|
|
806
|
+
"sourceConversationCoordinateSha256",
|
|
807
|
+
"routeRefSha256",
|
|
808
|
+
"contextRefSha256",
|
|
809
|
+
"turnDigest",
|
|
810
|
+
"previewDigest",
|
|
811
|
+
"runId",
|
|
812
|
+
"state",
|
|
813
|
+
"partCount",
|
|
814
|
+
"provenPartCount",
|
|
815
|
+
"receiptSha256",
|
|
816
|
+
"recordedAt"
|
|
817
|
+
], [], "messaging receipt binding");
|
|
818
|
+
if (source.schemaVersion !== 2 || source.format !== "wrench.messaging-receipt-binding" || source.contractId !== MESSAGING_RECEIPT_BINDING_CONTRACT_ID || source.contractHash !== MESSAGING_RECEIPT_BINDING_CONTRACT_HASH || source.state !== "submitted" && source.state !== "failed" && source.state !== "partial" && source.state !== "indeterminate")
|
|
819
|
+
return fail("messaging receipt binding", "has an unsupported contract");
|
|
820
|
+
const partCount = integer(source.partCount, "messaging receipt binding.partCount", 1, 8);
|
|
821
|
+
const provenPartCount = integer(source.provenPartCount, "messaging receipt binding.provenPartCount", 0, partCount);
|
|
822
|
+
const validPrefix = source.state === "submitted" ? provenPartCount === partCount : source.state === "failed" ? provenPartCount === 0 : source.state === "partial" ? provenPartCount >= 1 && provenPartCount < partCount : provenPartCount < partCount;
|
|
823
|
+
if (!validPrefix) {
|
|
824
|
+
return fail("messaging receipt binding", "violates the proven-prefix state law");
|
|
825
|
+
}
|
|
826
|
+
const runId = id(source.runId, "messaging receipt binding.runId", 256);
|
|
827
|
+
const normalizedWithoutReceipt = Object.freeze({
|
|
828
|
+
schemaVersion: 2,
|
|
829
|
+
format: "wrench.messaging-receipt-binding",
|
|
830
|
+
contractId: MESSAGING_RECEIPT_BINDING_CONTRACT_ID,
|
|
831
|
+
contractHash: MESSAGING_RECEIPT_BINDING_CONTRACT_HASH,
|
|
832
|
+
clientIntentSha256: sha256Digest(source.clientIntentSha256, "messaging receipt binding.clientIntentSha256"),
|
|
833
|
+
contextBindingSha256: sha256Digest(source.contextBindingSha256, "messaging receipt binding.contextBindingSha256"),
|
|
834
|
+
sourceConversationCoordinateSha256: sha256Digest(source.sourceConversationCoordinateSha256, "messaging receipt binding.sourceConversationCoordinateSha256"),
|
|
835
|
+
routeRefSha256: sha256Digest(source.routeRefSha256, "messaging receipt binding.routeRefSha256"),
|
|
836
|
+
contextRefSha256: sha256Digest(source.contextRefSha256, "messaging receipt binding.contextRefSha256"),
|
|
837
|
+
turnDigest: sha256Digest(source.turnDigest, "messaging receipt binding.turnDigest"),
|
|
838
|
+
previewDigest: sha256Digest(source.previewDigest, "messaging receipt binding.previewDigest"),
|
|
839
|
+
runId,
|
|
840
|
+
state: source.state,
|
|
841
|
+
partCount,
|
|
842
|
+
provenPartCount,
|
|
843
|
+
recordedAt: utcTimestamp(source.recordedAt, "messaging receipt binding.recordedAt")
|
|
844
|
+
});
|
|
845
|
+
const receiptSha256 = sha256Digest(source.receiptSha256, "messaging receipt binding.receiptSha256");
|
|
846
|
+
if (sha256(canonicalJson(normalizedWithoutReceipt)) !== receiptSha256) {
|
|
847
|
+
return fail("messaging receipt binding.receiptSha256", "does not bind the canonical receipt");
|
|
848
|
+
}
|
|
849
|
+
return Object.freeze({
|
|
850
|
+
...normalizedWithoutReceipt,
|
|
851
|
+
receiptSha256
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
function parseMessagingReceiptBinding(value) {
|
|
855
|
+
const source = record(value, "messaging receipt binding");
|
|
856
|
+
return source.contractId === MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID ? parseMessagingReceiptBindingV1(source) : parseMessagingReceiptBindingV2(source);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// src/messaging.ts
|
|
860
|
+
var MAX_STDOUT_BYTES = 64 * 1024;
|
|
861
|
+
var MAX_STDERR_BYTES = 64 * 1024;
|
|
862
|
+
var MAX_ARTIFACT_BYTES = 4 * 1024 * 1024;
|
|
863
|
+
var COMMAND_TIMEOUT_MS = 120000;
|
|
864
|
+
var COMMAND_TERMINATION_GRACE_MS = 1000;
|
|
865
|
+
function cliSourcePath() {
|
|
866
|
+
const besideSource = fileURLToPath(new URL("./cli.ts", import.meta.url));
|
|
867
|
+
if (existsSync(besideSource))
|
|
868
|
+
return besideSource;
|
|
869
|
+
const packagedSource = fileURLToPath(new URL("../src/cli.ts", import.meta.url));
|
|
870
|
+
if (existsSync(packagedSource))
|
|
871
|
+
return packagedSource;
|
|
872
|
+
throw new Error("the installed Ghostget CLI source is unavailable");
|
|
873
|
+
}
|
|
874
|
+
function environmentSnapshot(overrides) {
|
|
875
|
+
const result = {};
|
|
876
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
877
|
+
if (value !== undefined)
|
|
878
|
+
result[key] = value;
|
|
879
|
+
}
|
|
880
|
+
if (overrides !== undefined) {
|
|
881
|
+
if (typeof overrides !== "object" || overrides === null || Array.isArray(overrides) || Object.getPrototypeOf(overrides) !== Object.prototype)
|
|
882
|
+
throw new Error("Ghostget messaging environment must be a plain object");
|
|
883
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
884
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(key)) {
|
|
885
|
+
throw new Error("Ghostget messaging environment contains an invalid name");
|
|
886
|
+
}
|
|
887
|
+
if (value === undefined)
|
|
888
|
+
delete result[key];
|
|
889
|
+
else if (typeof value !== "string" || Buffer.byteLength(value, "utf8") > 128 * 1024 || value.includes("\x00"))
|
|
890
|
+
throw new Error("Ghostget messaging environment contains an invalid value");
|
|
891
|
+
else
|
|
892
|
+
result[key] = value;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
return Object.freeze(result);
|
|
896
|
+
}
|
|
897
|
+
function options(value) {
|
|
898
|
+
if (value === undefined)
|
|
899
|
+
return Object.freeze({ environment: environmentSnapshot(undefined) });
|
|
900
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || Object.getPrototypeOf(value) !== Object.prototype || Object.keys(value).some((key) => key !== "environment" && key !== "signal"))
|
|
901
|
+
throw new Error("Ghostget messaging options are malformed");
|
|
902
|
+
if (value.signal !== undefined && !(value.signal instanceof AbortSignal)) {
|
|
903
|
+
throw new Error("Ghostget messaging signal is malformed");
|
|
904
|
+
}
|
|
905
|
+
return Object.freeze({
|
|
906
|
+
environment: environmentSnapshot(value.environment),
|
|
907
|
+
...value.signal === undefined ? {} : { signal: value.signal }
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
function boundedError(chunks) {
|
|
911
|
+
return Buffer.concat(chunks).toString("utf8").slice(0, MAX_STDERR_BYTES).trim();
|
|
912
|
+
}
|
|
913
|
+
async function runCli(operation, request, clientOptions) {
|
|
914
|
+
if (typeof process.versions.bun !== "string") {
|
|
915
|
+
throw new Error("@hraness/ghostget/messaging requires Bun to run the installed Ghostget CLI");
|
|
916
|
+
}
|
|
917
|
+
const prepared = options(clientOptions);
|
|
918
|
+
if (prepared.signal?.aborted === true) {
|
|
919
|
+
throw prepared.signal.reason instanceof Error ? prepared.signal.reason : new DOMException("Ghostget messaging operation was aborted", "AbortError");
|
|
920
|
+
}
|
|
921
|
+
const temporaryDirectory = mkdtempSync(join(tmpdir(), "wrench-messaging-"));
|
|
922
|
+
const privateOutput = join(temporaryDirectory, "artifact.json");
|
|
923
|
+
try {
|
|
924
|
+
const result = await new Promise((resolve, reject) => {
|
|
925
|
+
const child = spawn(process.execPath, [
|
|
926
|
+
cliSourcePath(),
|
|
927
|
+
"messaging",
|
|
928
|
+
operation,
|
|
929
|
+
"--input",
|
|
930
|
+
"-",
|
|
931
|
+
"--private-output",
|
|
932
|
+
privateOutput,
|
|
933
|
+
"--json"
|
|
934
|
+
], {
|
|
935
|
+
env: prepared.environment,
|
|
936
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
937
|
+
detached: process.platform !== "win32"
|
|
938
|
+
});
|
|
939
|
+
const stdout = [];
|
|
940
|
+
const stderr = [];
|
|
941
|
+
let stdoutBytes = 0;
|
|
942
|
+
let stderrBytes = 0;
|
|
943
|
+
let settled = false;
|
|
944
|
+
let pendingError = null;
|
|
945
|
+
let terminationTimer = null;
|
|
946
|
+
const settleFailure = (error) => {
|
|
947
|
+
if (settled)
|
|
948
|
+
return;
|
|
949
|
+
settled = true;
|
|
950
|
+
clearTimeout(timer);
|
|
951
|
+
if (terminationTimer !== null)
|
|
952
|
+
clearTimeout(terminationTimer);
|
|
953
|
+
prepared.signal?.removeEventListener("abort", abort);
|
|
954
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
955
|
+
};
|
|
956
|
+
const signalOwnedTree = (signal) => {
|
|
957
|
+
try {
|
|
958
|
+
if (process.platform !== "win32" && child.pid !== undefined) {
|
|
959
|
+
process.kill(-child.pid, signal);
|
|
960
|
+
} else {
|
|
961
|
+
child.kill(signal);
|
|
962
|
+
}
|
|
963
|
+
} catch {}
|
|
964
|
+
};
|
|
965
|
+
const ownedTreeIsAlive = () => {
|
|
966
|
+
if (process.platform === "win32" || child.pid === undefined)
|
|
967
|
+
return false;
|
|
968
|
+
try {
|
|
969
|
+
process.kill(-child.pid, 0);
|
|
970
|
+
return true;
|
|
971
|
+
} catch {
|
|
972
|
+
return false;
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
const rejectAfterOwnedTreeExit = () => {
|
|
976
|
+
signalOwnedTree("SIGKILL");
|
|
977
|
+
if (!ownedTreeIsAlive()) {
|
|
978
|
+
settleFailure(pendingError ?? new Error("Ghostget messaging operation failed"));
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
setTimeout(rejectAfterOwnedTreeExit, 10);
|
|
982
|
+
};
|
|
983
|
+
const requestTermination = (error) => {
|
|
984
|
+
pendingError ??= error;
|
|
985
|
+
if (child.pid === undefined) {
|
|
986
|
+
settleFailure(pendingError);
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
if (terminationTimer !== null)
|
|
990
|
+
return;
|
|
991
|
+
signalOwnedTree("SIGTERM");
|
|
992
|
+
terminationTimer = setTimeout(() => signalOwnedTree("SIGKILL"), COMMAND_TERMINATION_GRACE_MS);
|
|
993
|
+
terminationTimer.unref?.();
|
|
994
|
+
};
|
|
995
|
+
const abort = () => {
|
|
996
|
+
requestTermination(prepared.signal?.reason instanceof Error ? prepared.signal.reason : new DOMException("Ghostget messaging operation was aborted", "AbortError"));
|
|
997
|
+
};
|
|
998
|
+
const timer = setTimeout(() => {
|
|
999
|
+
requestTermination(new Error("Ghostget messaging operation timed out"));
|
|
1000
|
+
}, COMMAND_TIMEOUT_MS);
|
|
1001
|
+
timer.unref?.();
|
|
1002
|
+
prepared.signal?.addEventListener("abort", abort, { once: true });
|
|
1003
|
+
child.on("error", (error) => requestTermination(error));
|
|
1004
|
+
child.stdout.on("data", (chunk) => {
|
|
1005
|
+
stdoutBytes += chunk.byteLength;
|
|
1006
|
+
if (stdoutBytes > MAX_STDOUT_BYTES) {
|
|
1007
|
+
requestTermination(new Error("Ghostget messaging receipt exceeded its byte bound"));
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
stdout.push(Buffer.from(chunk));
|
|
1011
|
+
});
|
|
1012
|
+
child.stderr.on("data", (chunk) => {
|
|
1013
|
+
stderrBytes += chunk.byteLength;
|
|
1014
|
+
if (stderrBytes > MAX_STDERR_BYTES) {
|
|
1015
|
+
requestTermination(new Error("Ghostget messaging diagnostic exceeded its byte bound"));
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
stderr.push(Buffer.from(chunk));
|
|
1019
|
+
});
|
|
1020
|
+
child.on("close", (code) => {
|
|
1021
|
+
if (settled)
|
|
1022
|
+
return;
|
|
1023
|
+
clearTimeout(timer);
|
|
1024
|
+
if (terminationTimer !== null)
|
|
1025
|
+
clearTimeout(terminationTimer);
|
|
1026
|
+
prepared.signal?.removeEventListener("abort", abort);
|
|
1027
|
+
if (pendingError !== null) {
|
|
1028
|
+
rejectAfterOwnedTreeExit();
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
settled = true;
|
|
1032
|
+
resolve(Object.freeze({
|
|
1033
|
+
code: code ?? 3,
|
|
1034
|
+
stdout: Buffer.concat(stdout),
|
|
1035
|
+
stderr: Object.freeze(stderr)
|
|
1036
|
+
}));
|
|
1037
|
+
});
|
|
1038
|
+
child.stdin.on("error", (error) => requestTermination(error));
|
|
1039
|
+
child.stdin.end(`${canonicalJson(request)}
|
|
1040
|
+
`, "utf8");
|
|
1041
|
+
});
|
|
1042
|
+
if (result.code !== 0) {
|
|
1043
|
+
throw new Error(boundedError(result.stderr) || `Ghostget messaging exited ${result.code}`);
|
|
1044
|
+
}
|
|
1045
|
+
let receiptValue;
|
|
1046
|
+
try {
|
|
1047
|
+
receiptValue = JSON.parse(result.stdout.toString("utf8"));
|
|
1048
|
+
} catch {
|
|
1049
|
+
throw new Error("Ghostget messaging returned a malformed receipt");
|
|
1050
|
+
}
|
|
1051
|
+
const receipt = parseMessagingPrivateOutputReceiptV1(receiptValue);
|
|
1052
|
+
const expectedFormat = operation === "routes" ? "wrench.messaging-routes" : operation === "resolve" ? "wrench.messaging-route" : operation === "context" ? "wrench.messaging-context" : "wrench.messaging-preview";
|
|
1053
|
+
if (receipt.artifactFormat !== expectedFormat) {
|
|
1054
|
+
throw new Error("Ghostget messaging returned another private artifact contract");
|
|
1055
|
+
}
|
|
1056
|
+
const stats = lstatSync(privateOutput);
|
|
1057
|
+
const currentUid = process.getuid?.();
|
|
1058
|
+
if (!stats.isFile() || currentUid === undefined || stats.uid !== currentUid || (stats.mode & 511) !== 384 || stats.size > MAX_ARTIFACT_BYTES)
|
|
1059
|
+
throw new Error("Ghostget messaging private artifact is not an owned mode-0600 file");
|
|
1060
|
+
const artifactText = readFileSync(privateOutput, "utf8");
|
|
1061
|
+
let artifact;
|
|
1062
|
+
try {
|
|
1063
|
+
artifact = JSON.parse(artifactText);
|
|
1064
|
+
} catch {
|
|
1065
|
+
throw new Error("Ghostget messaging private artifact is malformed JSON");
|
|
1066
|
+
}
|
|
1067
|
+
if (sha256(canonicalJson(artifact)) !== receipt.artifactSha256) {
|
|
1068
|
+
throw new Error("Ghostget messaging private artifact does not match its receipt");
|
|
1069
|
+
}
|
|
1070
|
+
return artifact;
|
|
1071
|
+
} finally {
|
|
1072
|
+
rmSync(temporaryDirectory, { recursive: true, force: true });
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
async function discoverMessagingRoutes(request, clientOptions) {
|
|
1076
|
+
const value = parseMessagingRoutesV2(await runCli("routes", parseMessagingRoutesRequestV1(request), clientOptions));
|
|
1077
|
+
return value;
|
|
1078
|
+
}
|
|
1079
|
+
async function resolveMessagingRoute(request, clientOptions) {
|
|
1080
|
+
return parseMessagingRouteV2(await runCli("resolve", parseMessagingRouteResolveRequestV2(request), clientOptions));
|
|
1081
|
+
}
|
|
1082
|
+
async function readMessagingContext(request, clientOptions) {
|
|
1083
|
+
return parseMessagingContextV1(await runCli("context", parseMessagingContextRequestV1(request), clientOptions));
|
|
1084
|
+
}
|
|
1085
|
+
async function previewMessagingTurn(request, clientOptions) {
|
|
1086
|
+
return parseMessagingPreviewV1(await runCli("preview", parseMessagingTurnV1(request), clientOptions));
|
|
1087
|
+
}
|
|
1088
|
+
export {
|
|
1089
|
+
resolveMessagingRoute,
|
|
1090
|
+
readMessagingContext,
|
|
1091
|
+
previewMessagingTurn,
|
|
1092
|
+
parseMessagingTurnV1,
|
|
1093
|
+
parseMessagingRoutesV2,
|
|
1094
|
+
parseMessagingRoutesV1,
|
|
1095
|
+
parseMessagingRoutesRequestV1,
|
|
1096
|
+
parseMessagingRouteV2,
|
|
1097
|
+
parseMessagingRouteV1,
|
|
1098
|
+
parseMessagingRouteResolveRequestV2,
|
|
1099
|
+
parseMessagingRouteResolveRequestV1,
|
|
1100
|
+
parseMessagingReceiptBindingV2,
|
|
1101
|
+
parseMessagingReceiptBindingV1,
|
|
1102
|
+
parseMessagingReceiptBinding,
|
|
1103
|
+
parseMessagingPrivateOutputReceiptV1,
|
|
1104
|
+
parseMessagingPreviewV1,
|
|
1105
|
+
parseMessagingContextV1,
|
|
1106
|
+
parseMessagingContextRequestV1,
|
|
1107
|
+
parseMessagingContextBindingV2,
|
|
1108
|
+
parseMessagingContextBindingV1,
|
|
1109
|
+
parseMessagingContextBinding,
|
|
1110
|
+
messagingTurnDigest,
|
|
1111
|
+
discoverMessagingRoutes,
|
|
1112
|
+
MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID,
|
|
1113
|
+
MESSAGING_RECEIPT_BINDING_V2_CONTRACT_HASH,
|
|
1114
|
+
MESSAGING_RECEIPT_BINDING_V2_CONTRACT_DESCRIPTOR,
|
|
1115
|
+
MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID,
|
|
1116
|
+
MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH,
|
|
1117
|
+
MESSAGING_RECEIPT_BINDING_V1_CONTRACT_DESCRIPTOR,
|
|
1118
|
+
MESSAGING_RECEIPT_BINDING_CONTRACT_ID,
|
|
1119
|
+
MESSAGING_RECEIPT_BINDING_CONTRACT_HASH,
|
|
1120
|
+
MESSAGING_RECEIPT_BINDING_CONTRACT_DESCRIPTOR,
|
|
1121
|
+
MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID,
|
|
1122
|
+
MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH,
|
|
1123
|
+
MESSAGING_CONTEXT_BINDING_V2_CONTRACT_DESCRIPTOR,
|
|
1124
|
+
MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID,
|
|
1125
|
+
MESSAGING_CONTEXT_BINDING_V1_CONTRACT_HASH,
|
|
1126
|
+
MESSAGING_CONTEXT_BINDING_V1_CONTRACT_DESCRIPTOR,
|
|
1127
|
+
MESSAGING_CONTEXT_BINDING_CONTRACT_ID,
|
|
1128
|
+
MESSAGING_CONTEXT_BINDING_CONTRACT_HASH,
|
|
1129
|
+
MESSAGING_CONTEXT_BINDING_CONTRACT_DESCRIPTOR
|
|
1130
|
+
};
|