@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,355 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
canonicalJson,
|
|
4
|
+
sha256
|
|
5
|
+
} from "./index-gwk7rbyj.js";
|
|
6
|
+
|
|
7
|
+
// src/whatsapp-client.ts
|
|
8
|
+
import { spawnSync } from "child_process";
|
|
9
|
+
import { existsSync } from "fs";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
import { types as nodeTypes } from "util";
|
|
12
|
+
|
|
13
|
+
// src/whatsapp-client-binding.ts
|
|
14
|
+
function fail(message) {
|
|
15
|
+
throw new Error(`Ghostget WhatsApp client: ${message}`);
|
|
16
|
+
}
|
|
17
|
+
function requireWhatsAppMessageLikeMeReceiptRequestBinding(receipt, request) {
|
|
18
|
+
if (receipt.auth.id !== request.authId) {
|
|
19
|
+
return fail("receipt auth identity does not match the requested auth locator");
|
|
20
|
+
}
|
|
21
|
+
if (receipt.output.directory !== request.output) {
|
|
22
|
+
return fail("receipt output directory does not match the requested output");
|
|
23
|
+
}
|
|
24
|
+
return receipt;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/whatsapp-export-coordinate.ts
|
|
28
|
+
import { dirname, isAbsolute, resolve } from "path";
|
|
29
|
+
var WHATSAPP_EXPORT_AUTH_ID_PATTERN = /^[a-z][a-z0-9-]{0,47}$/u;
|
|
30
|
+
var MAX_WHATSAPP_EXPORT_OUTPUT_BYTES = 4096;
|
|
31
|
+
function isWhatsAppExportAuthId(value) {
|
|
32
|
+
return typeof value === "string" && WHATSAPP_EXPORT_AUTH_ID_PATTERN.test(value);
|
|
33
|
+
}
|
|
34
|
+
function isWhatsAppExportOutputDirectory(value) {
|
|
35
|
+
return typeof value === "string" && isAbsolute(value) && resolve(value) === value && dirname(value) !== value && Buffer.byteLength(value, "utf8") <= MAX_WHATSAPP_EXPORT_OUTPUT_BYTES && !/[\0\r\n]/u.test(value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/whatsapp-client.ts
|
|
39
|
+
var MAX_STDOUT_BYTES = 2 * 1024 * 1024;
|
|
40
|
+
var MAX_STDERR_BYTES = 8 * 1024;
|
|
41
|
+
var MAX_V2_RECORDS = 500000;
|
|
42
|
+
var PROCESS_TIMEOUT_MS = 6 * 60 * 60 * 1000 + 60000;
|
|
43
|
+
var DIGEST_PATTERN = /^[a-f0-9]{64}$/u;
|
|
44
|
+
function fail2(message) {
|
|
45
|
+
throw new Error(`Ghostget WhatsApp client: ${message}`);
|
|
46
|
+
}
|
|
47
|
+
function record(value, label) {
|
|
48
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || nodeTypes.isProxy(value) || Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)
|
|
49
|
+
return fail2(`${label} must be a plain data object`);
|
|
50
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
51
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
52
|
+
if (typeof key !== "string")
|
|
53
|
+
return fail2(`${label} must contain only enumerable string data fields`);
|
|
54
|
+
const descriptor = descriptors[key];
|
|
55
|
+
if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor)) {
|
|
56
|
+
return fail2(`${label} must contain only enumerable string data fields`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return Object.freeze(Object.fromEntries(Object.entries(descriptors).map(([key, descriptor]) => [key, descriptor.value])));
|
|
60
|
+
}
|
|
61
|
+
function exact(value, keys, label) {
|
|
62
|
+
const observed = Object.keys(value).sort();
|
|
63
|
+
const expected = [...keys].sort();
|
|
64
|
+
if (observed.length !== expected.length || observed.some((key, index) => key !== expected[index])) {
|
|
65
|
+
fail2(`${label} contains unsupported or missing fields`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function timestamp(value, label, nullable = false) {
|
|
69
|
+
if (nullable && value === null)
|
|
70
|
+
return null;
|
|
71
|
+
if (typeof value !== "string")
|
|
72
|
+
return fail2(`${label} must be a timestamp`);
|
|
73
|
+
const parsed = new Date(value);
|
|
74
|
+
if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== value) {
|
|
75
|
+
return fail2(`${label} must be a canonical UTC timestamp`);
|
|
76
|
+
}
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
function digest(value, label) {
|
|
80
|
+
if (typeof value !== "string" || !DIGEST_PATTERN.test(value)) {
|
|
81
|
+
return fail2(`${label} must be lowercase SHA-256`);
|
|
82
|
+
}
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
function nonNegative(value, label) {
|
|
86
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
87
|
+
return fail2(`${label} must be a non-negative safe integer`);
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
function denseDataArray(value, label, maximum) {
|
|
92
|
+
if (nodeTypes.isProxy(value) || !Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype)
|
|
93
|
+
return fail2(`${label} must be an ordinary non-proxy array`);
|
|
94
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
95
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
96
|
+
if (lengthDescriptor === undefined || !("value" in lengthDescriptor) || typeof lengthDescriptor.value !== "number" || !Number.isSafeInteger(lengthDescriptor.value) || lengthDescriptor.value < 0 || lengthDescriptor.value > maximum)
|
|
97
|
+
return fail2(`${label} length exceeds its reviewed bound`);
|
|
98
|
+
const length = lengthDescriptor.value;
|
|
99
|
+
const keys = Reflect.ownKeys(descriptors);
|
|
100
|
+
if (keys.length !== length + 1 || keys.some((key) => typeof key !== "string"))
|
|
101
|
+
return fail2(`${label} must be a dense array without named properties`);
|
|
102
|
+
const items = [];
|
|
103
|
+
for (let index = 0;index < length; index += 1) {
|
|
104
|
+
const descriptor = descriptors[String(index)];
|
|
105
|
+
if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor))
|
|
106
|
+
return fail2(`${label} must contain only dense enumerable data elements`);
|
|
107
|
+
items.push(descriptor.value);
|
|
108
|
+
}
|
|
109
|
+
if (keys.some((key) => key !== "length" && (typeof key !== "string" || !/^(0|[1-9][0-9]*)$/u.test(key)))) {
|
|
110
|
+
return fail2(`${label} must be a dense array without named properties`);
|
|
111
|
+
}
|
|
112
|
+
return Object.freeze(items);
|
|
113
|
+
}
|
|
114
|
+
function parseWhatsAppMessageLikeMeExportReceipt(value) {
|
|
115
|
+
const root = record(value, "receipt");
|
|
116
|
+
exact(root, [
|
|
117
|
+
"schemaVersion",
|
|
118
|
+
"format",
|
|
119
|
+
"runId",
|
|
120
|
+
"operation",
|
|
121
|
+
"status",
|
|
122
|
+
"transport",
|
|
123
|
+
"startedAt",
|
|
124
|
+
"finishedAt",
|
|
125
|
+
"auth",
|
|
126
|
+
"source",
|
|
127
|
+
"provider",
|
|
128
|
+
"completeness",
|
|
129
|
+
"warnings",
|
|
130
|
+
"counts",
|
|
131
|
+
"output",
|
|
132
|
+
"privacy",
|
|
133
|
+
"integrity"
|
|
134
|
+
], "receipt");
|
|
135
|
+
if (root.schemaVersion !== 1 || root.format !== "wrench.whatsapp-message-like-me-export-receipt" || typeof root.runId !== "string" || !/^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/u.test(root.runId) || root.operation !== "whatsapp.export-message-like-me" || root.status !== "succeeded" || root.transport !== "linked-device-local-store")
|
|
136
|
+
return fail2("receipt identity is unsupported");
|
|
137
|
+
const startedAt = timestamp(root.startedAt, "receipt.startedAt");
|
|
138
|
+
const finishedAt = timestamp(root.finishedAt, "receipt.finishedAt");
|
|
139
|
+
if (finishedAt < startedAt)
|
|
140
|
+
return fail2("receipt timestamps are reversed");
|
|
141
|
+
const auth = record(root.auth, "receipt.auth");
|
|
142
|
+
exact(auth, ["id", "provider", "identitySha256"], "receipt.auth");
|
|
143
|
+
if (!isWhatsAppExportAuthId(auth.id)) {
|
|
144
|
+
return fail2("receipt.auth.id is not a bounded lowercase auth coordinate");
|
|
145
|
+
}
|
|
146
|
+
const parsedAuthId = auth.id;
|
|
147
|
+
if (auth.provider !== "whatsapp")
|
|
148
|
+
return fail2("receipt auth identity is unsupported");
|
|
149
|
+
const identitySha256 = digest(auth.identitySha256, "receipt.auth.identitySha256");
|
|
150
|
+
const source = record(root.source, "receipt.source");
|
|
151
|
+
exact(source, ["id", "version"], "receipt.source");
|
|
152
|
+
const provider = record(root.provider, "receipt.provider");
|
|
153
|
+
exact(provider, ["id", "version"], "receipt.provider");
|
|
154
|
+
if (source.id !== "wacli-local" || source.version !== "1.0.0" || provider.id !== "whatsapp" || provider.version !== "0.15.0")
|
|
155
|
+
return fail2("receipt producer identity is unsupported");
|
|
156
|
+
const completeness = record(root.completeness, "receipt.completeness");
|
|
157
|
+
exact(completeness, ["kind", "reason", "observedFrom", "observedThrough"], "receipt.completeness");
|
|
158
|
+
if (completeness.kind !== "bounded-local" || completeness.reason !== "local-store-coverage-unknown")
|
|
159
|
+
return fail2("receipt completeness boundary is unsupported");
|
|
160
|
+
const observedFrom = timestamp(completeness.observedFrom, "receipt.completeness.observedFrom", true);
|
|
161
|
+
const observedThrough = timestamp(completeness.observedThrough, "receipt.completeness.observedThrough", true);
|
|
162
|
+
if (observedFrom === null !== (observedThrough === null) || observedFrom !== null && observedThrough !== null && observedThrough < observedFrom) {
|
|
163
|
+
return fail2("receipt completeness timestamps are reversed");
|
|
164
|
+
}
|
|
165
|
+
const warnings = denseDataArray(root.warnings, "receipt.warnings", 5);
|
|
166
|
+
const warningOrder = [
|
|
167
|
+
"remote-history-incomplete",
|
|
168
|
+
"reaction-state-unproven",
|
|
169
|
+
"self-chat-excluded",
|
|
170
|
+
"message-payload-purged",
|
|
171
|
+
"non-conversation-chats-excluded"
|
|
172
|
+
];
|
|
173
|
+
if (warnings[0] !== warningOrder[0] || warnings.some((item) => typeof item !== "string" || !warningOrder.includes(item)) || warnings.some((item, index) => index > 0 && warningOrder.indexOf(item) <= warningOrder.indexOf(warnings[index - 1])))
|
|
174
|
+
return fail2("receipt warnings are unsupported");
|
|
175
|
+
const warningTail = warnings.slice(1).map((warning) => warning);
|
|
176
|
+
const canonicalWarnings = Object.freeze([
|
|
177
|
+
"remote-history-incomplete",
|
|
178
|
+
...warningTail
|
|
179
|
+
]);
|
|
180
|
+
const counts = record(root.counts, "receipt.counts");
|
|
181
|
+
const countKinds = ["account", "participant", "conversation", "message", "reaction", "tombstone"];
|
|
182
|
+
exact(counts, countKinds, "receipt.counts");
|
|
183
|
+
const parsedCounts = Object.fromEntries(countKinds.map((kind) => [
|
|
184
|
+
kind,
|
|
185
|
+
nonNegative(counts[kind], `receipt.counts.${kind}`)
|
|
186
|
+
]));
|
|
187
|
+
const observed = observedFrom !== null;
|
|
188
|
+
const totalRecords = countKinds.reduce((total, kind) => total + parsedCounts[kind], 0);
|
|
189
|
+
if (parsedCounts.account !== 1 || parsedCounts.participant < 1 || parsedCounts.reaction !== 0 || parsedCounts.tombstone !== 0 || parsedCounts.message > 0 !== observed || parsedCounts.conversation > 0 !== observed || parsedCounts.conversation > parsedCounts.message || parsedCounts.message > MAX_V2_RECORDS || parsedCounts.participant > parsedCounts.message + 1 || !Number.isSafeInteger(totalRecords) || totalRecords > MAX_V2_RECORDS || warnings.includes("message-payload-purged") && parsedCounts.message === 0)
|
|
190
|
+
return fail2("receipt counts contradict the fixed producer");
|
|
191
|
+
const output = record(root.output, "receipt.output");
|
|
192
|
+
exact(output, ["schemaVersion", "format", "directory", "manifestSha256"], "receipt.output");
|
|
193
|
+
if (!isWhatsAppExportOutputDirectory(output.directory)) {
|
|
194
|
+
return fail2("receipt.output.directory is not a bounded normalized non-root absolute directory");
|
|
195
|
+
}
|
|
196
|
+
const parsedOutputDirectory = output.directory;
|
|
197
|
+
if (output.schemaVersion !== 2 || output.format !== "message-like-me.local-message-bundle")
|
|
198
|
+
return fail2("receipt output identity is unsupported");
|
|
199
|
+
const manifestSha256 = digest(output.manifestSha256, "receipt.output.manifestSha256");
|
|
200
|
+
const privacy = record(root.privacy, "receipt.privacy");
|
|
201
|
+
exact(privacy, [
|
|
202
|
+
"classification",
|
|
203
|
+
"attachments",
|
|
204
|
+
"credentials",
|
|
205
|
+
"sourcePaths",
|
|
206
|
+
"mediaBytes",
|
|
207
|
+
"cloudSync"
|
|
208
|
+
], "receipt.privacy");
|
|
209
|
+
if (privacy.classification !== "private-local" || privacy.attachments !== "metadata-only" || privacy.credentials !== "excluded" || privacy.sourcePaths !== "excluded" || privacy.mediaBytes !== "excluded" || privacy.cloudSync !== "none")
|
|
210
|
+
return fail2("receipt privacy boundary is unsupported");
|
|
211
|
+
const integrity = record(root.integrity, "receipt.integrity");
|
|
212
|
+
exact(integrity, ["algorithm", "receiptSha256"], "receipt.integrity");
|
|
213
|
+
if (integrity.algorithm !== "sha256")
|
|
214
|
+
return fail2("receipt integrity algorithm is unsupported");
|
|
215
|
+
const expectedDigest = digest(integrity.receiptSha256, "receipt.integrity.receiptSha256");
|
|
216
|
+
const projection = Object.freeze({
|
|
217
|
+
schemaVersion: 1,
|
|
218
|
+
format: "wrench.whatsapp-message-like-me-export-receipt",
|
|
219
|
+
runId: root.runId,
|
|
220
|
+
operation: "whatsapp.export-message-like-me",
|
|
221
|
+
status: "succeeded",
|
|
222
|
+
transport: "linked-device-local-store",
|
|
223
|
+
startedAt,
|
|
224
|
+
finishedAt,
|
|
225
|
+
auth: Object.freeze({
|
|
226
|
+
id: parsedAuthId,
|
|
227
|
+
provider: "whatsapp",
|
|
228
|
+
identitySha256
|
|
229
|
+
}),
|
|
230
|
+
source: Object.freeze({ id: "wacli-local", version: "1.0.0" }),
|
|
231
|
+
provider: Object.freeze({ id: "whatsapp", version: "0.15.0" }),
|
|
232
|
+
completeness: Object.freeze({
|
|
233
|
+
kind: "bounded-local",
|
|
234
|
+
reason: "local-store-coverage-unknown",
|
|
235
|
+
observedFrom,
|
|
236
|
+
observedThrough
|
|
237
|
+
}),
|
|
238
|
+
warnings: canonicalWarnings,
|
|
239
|
+
counts: Object.freeze({
|
|
240
|
+
account: 1,
|
|
241
|
+
participant: parsedCounts.participant,
|
|
242
|
+
conversation: parsedCounts.conversation,
|
|
243
|
+
message: parsedCounts.message,
|
|
244
|
+
reaction: 0,
|
|
245
|
+
tombstone: 0
|
|
246
|
+
}),
|
|
247
|
+
output: Object.freeze({
|
|
248
|
+
schemaVersion: 2,
|
|
249
|
+
format: "message-like-me.local-message-bundle",
|
|
250
|
+
directory: parsedOutputDirectory,
|
|
251
|
+
manifestSha256
|
|
252
|
+
}),
|
|
253
|
+
privacy: Object.freeze({
|
|
254
|
+
classification: "private-local",
|
|
255
|
+
attachments: "metadata-only",
|
|
256
|
+
credentials: "excluded",
|
|
257
|
+
sourcePaths: "excluded",
|
|
258
|
+
mediaBytes: "excluded",
|
|
259
|
+
cloudSync: "none"
|
|
260
|
+
})
|
|
261
|
+
});
|
|
262
|
+
if (sha256(canonicalJson(projection)) !== expectedDigest) {
|
|
263
|
+
return fail2("receipt digest does not match its canonical projection");
|
|
264
|
+
}
|
|
265
|
+
return Object.freeze({
|
|
266
|
+
...projection,
|
|
267
|
+
integrity: Object.freeze({ algorithm: "sha256", receiptSha256: expectedDigest })
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function cliSourcePath() {
|
|
271
|
+
const besideSource = fileURLToPath(new URL("./cli.ts", import.meta.url));
|
|
272
|
+
if (existsSync(besideSource))
|
|
273
|
+
return besideSource;
|
|
274
|
+
const packagedSource = fileURLToPath(new URL("../src/cli.ts", import.meta.url));
|
|
275
|
+
if (existsSync(packagedSource))
|
|
276
|
+
return packagedSource;
|
|
277
|
+
return fail2("the installed Ghostget CLI source is unavailable");
|
|
278
|
+
}
|
|
279
|
+
function preparedEnvironment(additions) {
|
|
280
|
+
const environment = Object.create(null);
|
|
281
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
282
|
+
if (typeof value === "string")
|
|
283
|
+
environment[key] = value;
|
|
284
|
+
}
|
|
285
|
+
if (additions === undefined)
|
|
286
|
+
return Object.freeze(environment);
|
|
287
|
+
const source = record(additions, "options.environment");
|
|
288
|
+
for (const [key, value] of Object.entries(source)) {
|
|
289
|
+
if (key.length < 1 || key.includes("=") || key.includes("\x00")) {
|
|
290
|
+
return fail2("environment name is malformed");
|
|
291
|
+
}
|
|
292
|
+
if (value === undefined)
|
|
293
|
+
delete environment[key];
|
|
294
|
+
else if (typeof value !== "string" || value.includes("\x00")) {
|
|
295
|
+
return fail2("environment value is malformed");
|
|
296
|
+
} else
|
|
297
|
+
environment[key] = value;
|
|
298
|
+
}
|
|
299
|
+
return Object.freeze(environment);
|
|
300
|
+
}
|
|
301
|
+
function exportWhatsAppMessageLikeMeSync(requestValue, optionsValue = {}) {
|
|
302
|
+
if (typeof process.versions.bun !== "string") {
|
|
303
|
+
return fail2("@hraness/ghostget/whatsapp requires Bun");
|
|
304
|
+
}
|
|
305
|
+
const request = record(requestValue, "request");
|
|
306
|
+
exact(request, ["authId", "output"], "request");
|
|
307
|
+
if (!isWhatsAppExportAuthId(request.authId)) {
|
|
308
|
+
return fail2("request.authId is not a bounded lowercase auth coordinate");
|
|
309
|
+
}
|
|
310
|
+
if (!isWhatsAppExportOutputDirectory(request.output)) {
|
|
311
|
+
return fail2("request.output is not a bounded normalized non-root absolute directory");
|
|
312
|
+
}
|
|
313
|
+
const requestedAuthId = request.authId;
|
|
314
|
+
const requestedOutput = request.output;
|
|
315
|
+
const requested = Object.freeze({
|
|
316
|
+
authId: requestedAuthId,
|
|
317
|
+
output: requestedOutput
|
|
318
|
+
});
|
|
319
|
+
const options = record(optionsValue, "options");
|
|
320
|
+
exact(options, Object.hasOwn(options, "environment") ? ["environment"] : [], "options");
|
|
321
|
+
const result = spawnSync(process.execPath, [
|
|
322
|
+
cliSourcePath(),
|
|
323
|
+
"whatsapp",
|
|
324
|
+
"export-message-like-me",
|
|
325
|
+
"--auth",
|
|
326
|
+
requestedAuthId,
|
|
327
|
+
"--output",
|
|
328
|
+
requestedOutput,
|
|
329
|
+
"--json"
|
|
330
|
+
], {
|
|
331
|
+
cwd: process.cwd(),
|
|
332
|
+
env: preparedEnvironment(options.environment),
|
|
333
|
+
encoding: "utf8",
|
|
334
|
+
maxBuffer: MAX_STDOUT_BYTES,
|
|
335
|
+
timeout: PROCESS_TIMEOUT_MS,
|
|
336
|
+
stdio: ["ignore", "pipe", "inherit"]
|
|
337
|
+
});
|
|
338
|
+
if (result.error !== undefined)
|
|
339
|
+
return fail2("installed CLI could not complete the export");
|
|
340
|
+
if (result.status !== 0 || result.signal !== null) {
|
|
341
|
+
const errorText = typeof result.stderr === "string" ? Buffer.from(result.stderr, "utf8").subarray(0, MAX_STDERR_BYTES).toString("utf8").trim() : "";
|
|
342
|
+
return fail2(errorText.length > 0 ? errorText : "installed CLI rejected the export");
|
|
343
|
+
}
|
|
344
|
+
let raw;
|
|
345
|
+
try {
|
|
346
|
+
raw = JSON.parse(result.stdout.trim());
|
|
347
|
+
} catch {
|
|
348
|
+
return fail2("installed CLI returned malformed JSON");
|
|
349
|
+
}
|
|
350
|
+
return requireWhatsAppMessageLikeMeReceiptRequestBinding(parseWhatsAppMessageLikeMeExportReceipt(raw), requested);
|
|
351
|
+
}
|
|
352
|
+
export {
|
|
353
|
+
parseWhatsAppMessageLikeMeExportReceipt,
|
|
354
|
+
exportWhatsAppMessageLikeMeSync
|
|
355
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Direct iMessage provider
|
|
2
|
+
|
|
3
|
+
The `imessage` source plugin wraps one exact reviewed `openclaw/imsg` build. It
|
|
4
|
+
supports bounded local chat and message reads plus confirmed one-to-eight-bubble
|
|
5
|
+
turns to an exact live chat GUID. The mutation path is fixed to AppleScript and
|
|
6
|
+
`iMessage`, with SMS fallback disabled.
|
|
7
|
+
|
|
8
|
+
The plugin does not select an Apple ID. Messages chooses the device-default
|
|
9
|
+
account and routing identity. `account_id`, `account_login`, and
|
|
10
|
+
`last_addressed_handle` from `chat.db` are observations for route inspection,
|
|
11
|
+
not a selectable or proven sending account. The auth subject binds a canonical
|
|
12
|
+
local Messages store coordinate and this device-default policy. It does not
|
|
13
|
+
claim to identify the signed-in Apple account.
|
|
14
|
+
|
|
15
|
+
## Reviewed transport
|
|
16
|
+
|
|
17
|
+
The vendored patch stack applies to `openclaw/imsg` 0.14.1 at commit
|
|
18
|
+
`25beb76c902b0acf2dd7ae392f1b0792f6813240`. The first commit,
|
|
19
|
+
`292db82d89293867ef847a2875667fea0fdd5dc1`, isolates AppleScript payloads.
|
|
20
|
+
The second, `c5994f00d17969fd7772fd2772e7b3591089513a`, adds the read-only exact
|
|
21
|
+
`chats.get(chat_id)` lookup required to revalidate a route without relying on a
|
|
22
|
+
bounded recent-chat scan. Patch SHA-256 values, changed files, and the full
|
|
23
|
+
artifact review boundary are recorded in
|
|
24
|
+
`src/plugins/imessage-direct/vendor/provenance.json`.
|
|
25
|
+
|
|
26
|
+
AppleScript source remains on standard input. Recipient, text, service,
|
|
27
|
+
attachment path, chat target, and the chat-routing flag live in checked
|
|
28
|
+
mode-0600 files under one random mode-0700 directory. The nested `osascript`
|
|
29
|
+
argv contains only fixed interpreter switches and an opaque random locator.
|
|
30
|
+
The patch rejects symlinks, file substitution, owner or mode changes, and
|
|
31
|
+
in-place content changes. It reaps the child before removing known private
|
|
32
|
+
files and the checked directory.
|
|
33
|
+
|
|
34
|
+
The Ghostget runtime adds an outer boundary. Every RPC child is a fresh detached
|
|
35
|
+
process group with fixed argv `imsg rpc`, bounded stdin/stdout/stderr, a fixed
|
|
36
|
+
minimal environment, a total deadline, and durable cleanup admission. Each
|
|
37
|
+
confirmed bubble produces one separately journaled send process and exactly one
|
|
38
|
+
send request. Private message text appears only in that process's RPC stdin.
|
|
39
|
+
Timeout, signal, malformed output, or lost output after spawn is indeterminate.
|
|
40
|
+
The runtime never retries.
|
|
41
|
+
|
|
42
|
+
`imsg rpc status` is the exact nonlaunching readiness probe. It retries the database open and
|
|
43
|
+
probes only an already-running private bridge. It does not launch, kill, or
|
|
44
|
+
relaunch Messages. Ghostget requires protocol 1, imsg 0.14.1, a readable database,
|
|
45
|
+
and the reviewed `chats.get`, read, and send methods. This proves the local contract is currently
|
|
46
|
+
usable, not which Apple account Messages will choose.
|
|
47
|
+
|
|
48
|
+
## Build and installation boundary
|
|
49
|
+
|
|
50
|
+
Apply both vendored mail patches in order to an otherwise clean checkout at the exact base,
|
|
51
|
+
then build `imsg` in release mode. The current reviewed macOS arm64 executable
|
|
52
|
+
SHA-256 is
|
|
53
|
+
`77a0db864dfd247cd0a9142dd98997960582e6f150f76ecd3bf1c38944f2bf71`.
|
|
54
|
+
Compiler, SDK, and command provenance are in the manifest. A different byte is
|
|
55
|
+
not this reviewed transport, even if it prints version 0.14.1.
|
|
56
|
+
|
|
57
|
+
Install only that byte sequence through the checked installer:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
ghostget imessage transport install --binary /absolute/path/to/imsg --json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The installer supports only the declared current platform, reads the source
|
|
64
|
+
without following a symlink, checks owner, mode, size, stability, and SHA-256,
|
|
65
|
+
and installs by an exclusive same-filesystem link. It never replaces mismatched
|
|
66
|
+
existing bytes. Software build, installation, Messages login, and account
|
|
67
|
+
recovery remain operator workflows, not provider operations.
|
|
68
|
+
|
|
69
|
+
Bind the local Messages store after the reviewed binary is installed:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
ghostget adapter sync-bundled --json
|
|
73
|
+
ghostget auth add imessage-main --linked-device imessage \
|
|
74
|
+
--device-store "${HOME}/Library/Messages"
|
|
75
|
+
ghostget auth bind imessage-main --site imessage
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
macOS must grant the invoking terminal Full Disk Access for `chat.db` and
|
|
79
|
+
Automation access to Messages for a send. No live send is part of installation
|
|
80
|
+
or subject binding.
|
|
81
|
+
|
|
82
|
+
## Outcome boundary
|
|
83
|
+
|
|
84
|
+
Stock AppleScript does not return a message GUID. A Ghostget result is
|
|
85
|
+
`submitted` only when the patched imsg process independently observes one
|
|
86
|
+
matching outgoing `chat.db` row and returns the exact message GUID, chat GUID,
|
|
87
|
+
row ID, `iMessage` service, and `applescript` transport. This is evidence of
|
|
88
|
+
local Messages acceptance, not network delivery or recipient receipt.
|
|
89
|
+
|
|
90
|
+
The transport preserves imsg's `not_started`, `may_have_completed`, and
|
|
91
|
+
`still_in_flight` categories in encrypted provider output. Once Ghostget has
|
|
92
|
+
started the confirmed child, every non-accepted result remains non-retryable at
|
|
93
|
+
the Ghostget boundary. The separately requested `messaging.delivery.read`
|
|
94
|
+
operation can inspect only a GUID already obtained from exact accepted evidence.
|
|
95
|
+
A missing row is not proof that an unobserved send did not happen.
|
|
96
|
+
|
|
97
|
+
Threaded replies are unsupported because AppleScript cannot express them. The
|
|
98
|
+
generic messaging facade owns one composite confirmation and a per-part
|
|
99
|
+
journal with a proven accepted prefix, at most one uncertain current part, and
|
|
100
|
+
an unattempted suffix. Before each remaining bubble it rereads the exact chat
|
|
101
|
+
and bounded history, accepting only the unchanged preview base, that same base
|
|
102
|
+
while an accepted bubble is not yet visible, or the exact visible own-message
|
|
103
|
+
prefix with bounded-window eviction. Foreign activity, edits, deletions,
|
|
104
|
+
reordering, or identity reuse stops the suffix. The provider hook
|
|
105
|
+
`executeImsgDirectMessagingPart` rejects non-send recipes and delegates every
|
|
106
|
+
bubble to the same supervised at-most-once operation boundary.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Rental listings
|
|
2
|
+
|
|
3
|
+
Ghostget exposes public Puerto Rico rental search as one named operation,
|
|
4
|
+
`listings.search`, on the built-in `clasificados-web` plugin.
|
|
5
|
+
|
|
6
|
+
The operation is read-only. It takes a location plus optional bedroom and rent
|
|
7
|
+
bounds, fetches the reviewed ClasificadosOnline list pages, and returns
|
|
8
|
+
structured rows. Each row includes the canonical listing URL, rent, beds, baths,
|
|
9
|
+
street address when the list card publishes one, ZIP when a reviewed ZIP table
|
|
10
|
+
or known-address override can prove it, and a neighborhood derived from that
|
|
11
|
+
address and ZIP or from the listing's own coordinates. Broker titles and
|
|
12
|
+
`BarrioCond` blurbs are never the neighborhood source.
|
|
13
|
+
|
|
14
|
+
## Input
|
|
15
|
+
|
|
16
|
+
Required key:
|
|
17
|
+
|
|
18
|
+
- `location` — a reviewed San Juan location token such as `San Juan, PR`,
|
|
19
|
+
`Hato Rey`, `Condado`, `Santurce`, `Río Piedras`, or `Viejo San Juan`.
|
|
20
|
+
|
|
21
|
+
Optional keys:
|
|
22
|
+
|
|
23
|
+
- `beds_min` — keep listings with at least this many bedrooms. ClasificadosOnline
|
|
24
|
+
treats its own bedroom field as an exact match, so Ghostget omits that field
|
|
25
|
+
from the query and applies the bound locally.
|
|
26
|
+
- `max_price` — keep listings at or below this monthly rent.
|
|
27
|
+
|
|
28
|
+
Unknown keys, extra fields, and locations outside the reviewed San Juan pueblo
|
|
29
|
+
set are rejected.
|
|
30
|
+
|
|
31
|
+
## Neighborhood verification
|
|
32
|
+
|
|
33
|
+
Listing text often lies. A Hato Rey tower can advertise Condado. Ghostget resolves
|
|
34
|
+
neighborhood in this order:
|
|
35
|
+
|
|
36
|
+
1. A reviewed street-plus-ZIP override for a known building.
|
|
37
|
+
2. A reviewed San Juan ZIP when the listing or override publishes one.
|
|
38
|
+
3. Coordinates published on the same list card as the listing.
|
|
39
|
+
|
|
40
|
+
Reviewed ZIP examples:
|
|
41
|
+
|
|
42
|
+
- `00918` — Hato Rey
|
|
43
|
+
- `00907` — Condado / Miramar
|
|
44
|
+
- `00901` — Old San Juan / Puerta de Tierra / Paseo Caribe
|
|
45
|
+
|
|
46
|
+
Coordinates on ClasificadosOnline detail pages include related listings. The
|
|
47
|
+
runtime therefore binds latitude and longitude from the list card only.
|
|
48
|
+
|
|
49
|
+
## Completeness
|
|
50
|
+
|
|
51
|
+
San Juan is five ClasificadosOnline pueblos, not one island-wide search.
|
|
52
|
+
`location=San Juan, PR` walks those five reviewed list pages. The pueblo query
|
|
53
|
+
uses the page's latin-1 form encoding, so `Río Piedras` is `R%EDo`, not UTF-8.
|
|
54
|
+
Completeness is `partial` when any page is full, when a card is skipped because
|
|
55
|
+
it is not a San Juan listing, or when a card is missing a reviewed field.
|
|
56
|
+
Completeness is `complete` only when every requested pueblo page is a short,
|
|
57
|
+
fully projected page. Bath counts keep a reviewed half-step (`2 1/2` is `2.5`).
|
|
58
|
+
Studios that omit the bedroom icon and name themselves estudio, efficiency, or
|
|
59
|
+
studio project as zero bedrooms.
|
|
60
|
+
|
|
61
|
+
## Live probe
|
|
62
|
+
|
|
63
|
+
A live ClasificadosOnline probe on 2026-09-03 for San Juan two-bedroom rentals
|
|
64
|
+
at or below $5500 returned 43 structured rows with canonical detail URLs,
|
|
65
|
+
including Hato Rey, Condado / Miramar, Santurce, Río Piedras, and Old San Juan
|
|
66
|
+
neighborhoods derived from coordinates or known-address overrides. First-page
|
|
67
|
+
pueblo results were full, so completeness was `partial`. Aquablue at 48 Luis
|
|
68
|
+
Muñoz Rivera is covered by the recorded fixture: the override binds Hato Rey
|
|
69
|
+
`00918` even when the title says Condado. Replay the live probe with:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
ghostget clasificados-web listings.search \
|
|
73
|
+
--input '{"location":"San Juan, PR","beds_min":2,"max_price":5500}' \
|
|
74
|
+
--json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The command is a live network probe. Tests cover the same input against recorded
|
|
78
|
+
list-card fixtures so CI does not depend on the site.
|
|
79
|
+
|
|
80
|
+
## Surfaces that are not shipped
|
|
81
|
+
|
|
82
|
+
Zillow-group HTML and first-party search endpoints from this environment
|
|
83
|
+
returned PerimeterX or equivalent bot challenges on zillow.com, hotpads.com,
|
|
84
|
+
trulia.com, apartments.com, and homes.com. No reviewed public search contract
|
|
85
|
+
exists for those hosts, so Ghostget does not ship a Zillow-group `listings.search`
|
|
86
|
+
and does not ship a DOM-click recipe.
|
|
87
|
+
|
|
88
|
+
Puerto Rico MLS public search is also not shipped. londonfoster.com's search
|
|
89
|
+
index is Florida inventory. puertoricorealtorsmls.com answered with a disk-full
|
|
90
|
+
error. stellarmls.com is a Florida member marketing site. Individual Puerto Rico
|
|
91
|
+
detail pages are not a reviewed search index.
|
|
92
|
+
|
|
93
|
+
Those blockers stay documented until a first-party public contract can be
|
|
94
|
+
reviewed. They are not capture-required stubs and they are not portable plugins.
|