@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
package/README.md
ADDED
|
@@ -0,0 +1,1359 @@
|
|
|
1
|
+
# Ghostget
|
|
2
|
+
|
|
3
|
+
[](https://ghostget.com)
|
|
4
|
+
|
|
5
|
+
[](https://skills.sh/hraness/ghostget)
|
|
6
|
+
|
|
7
|
+
**Give agents bounded access to pages, media, and connected accounts.**
|
|
8
|
+
|
|
9
|
+
Ghostget is an open-source, bring-your-own-agent CLI and TypeScript SDK. It is the
|
|
10
|
+
capability and custody layer beneath any AI agent that can run a command: a way
|
|
11
|
+
to capture pages, preserve media, query encrypted snapshots, and use reviewed
|
|
12
|
+
account capabilities without handing the model a mouse, keyboard, cookie jar,
|
|
13
|
+
arbitrary HTTP client, or every signed-in tab.
|
|
14
|
+
|
|
15
|
+
The caller asks for a named outcome such as `messaging.list`. Ghostget binds that
|
|
16
|
+
operation to one exact provider, transport, account realm, contract version,
|
|
17
|
+
implementation, and risk level. If those facts drift, the operation stops. It
|
|
18
|
+
does not silently fall back to general browser control.
|
|
19
|
+
|
|
20
|
+
Bring the model, planner, tool loop, approval interface, and application shell
|
|
21
|
+
you prefer. Ghostget supplies precise web capabilities with local custody and
|
|
22
|
+
explicit evidence.
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
ghostget https://example.com/article
|
|
26
|
+
ghostget capabilities
|
|
27
|
+
ghostget plugin list
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
[Install](#install) · [GitHub Releases](https://github.com/hraness/ghostget/releases) · [Project site](https://ghostget.com) · [Privacy and data custody](https://ghostget.com/privacy/) · [Security policy](SECURITY.md) · [Plugin guide](docs/plugins.md) · [Local CLI transport guide](docs/local-cli-providers.md)
|
|
31
|
+
|
|
32
|
+
## What Ghostget does
|
|
33
|
+
|
|
34
|
+
- **Capture knowledge.** Turn a public URL into durable Markdown, inspect it
|
|
35
|
+
without saving, and search the knowledge you keep locally.
|
|
36
|
+
- **Preserve media.** Archive one authorized, accessible, finite media item
|
|
37
|
+
with source bytes, requested derivatives, transcript, manifest, and SHA-256
|
|
38
|
+
integrity records.
|
|
39
|
+
- **Read connected services.** Store validated account-bound reads as encrypted
|
|
40
|
+
exact-query snapshots, then load the last verified state without reopening a
|
|
41
|
+
browser or contacting the provider.
|
|
42
|
+
- **Add one capability.** Turn a reviewed first-party exchange into a typed,
|
|
43
|
+
semantic operation with strict inputs, bounded outputs, and explicit trust.
|
|
44
|
+
|
|
45
|
+
## Built-in provider catalog
|
|
46
|
+
|
|
47
|
+
This v0.17.2 source tree supports executable actions for 20 services: Beeper,
|
|
48
|
+
Bluesky, ClasificadosOnline, Facebook, Facebook Groups, Facebook Marketplace,
|
|
49
|
+
GitHub, Gmail, Hacker News, Instagram, iMessage, LinkedIn, Reddit, Substack,
|
|
50
|
+
Threads, TikTok, Twitch, WhatsApp, X, and YouTube.
|
|
51
|
+
LinkedIn and X each have separate official and authenticated-web adapters. The
|
|
52
|
+
[release-bound provider directory](https://ghostget.com/provider-capabilities/)
|
|
53
|
+
lists only executable actions, grouped by the tasks each service supports and
|
|
54
|
+
the access method each action uses. Inspect `ghostget capabilities --json` for
|
|
55
|
+
the exact installed contract state.
|
|
56
|
+
|
|
57
|
+
Beeper is Ghostget's first provider adapter with a pinned local-CLI transport. Its
|
|
58
|
+
32 supported actions read accounts, contacts, conversations, and messages;
|
|
59
|
+
manage reactions, drafts, reminders, and conversation state; and preview and
|
|
60
|
+
confirm sends, edits, group changes, and presence. Of those, 26 operations use
|
|
61
|
+
the authoritative `@beeper/cli` 0.6.2 executable; six reads use fixed Beeper
|
|
62
|
+
Desktop loopback endpoints. Ghostget binds one Desktop target and does not expose
|
|
63
|
+
a generic command runner. Submission is not a claim of network delivery.
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
ghostget messaging routes --input @/absolute/private/beeper-routes-request.json \
|
|
67
|
+
--private-output /absolute/private/beeper-routes.json --json
|
|
68
|
+
ghostget messaging resolve --input @/absolute/private/beeper-resolve-request.json \
|
|
69
|
+
--private-output /absolute/private/beeper-route.json --json
|
|
70
|
+
ghostget messaging context --input @/absolute/private/beeper-context-request.json \
|
|
71
|
+
--private-output /absolute/private/beeper-context.json --json
|
|
72
|
+
ghostget messaging preview --input @/absolute/private/beeper-turn.json \
|
|
73
|
+
--private-output /absolute/private/beeper-preview.json --json
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`routes` returns bounded discovery evidence and opaque candidate references.
|
|
77
|
+
Put one candidate `routeRef` in the private resolve request. Ghostget loads the
|
|
78
|
+
checked provider target from encrypted private state and performs the exact
|
|
79
|
+
provider read. The caller never resupplies an account, network, conversation
|
|
80
|
+
ID, name, handle, title, or participant match. Preview is draft-only until the
|
|
81
|
+
owner reviews the exact private recipient and bubbles and makes a fresh
|
|
82
|
+
same-turn send request.
|
|
83
|
+
|
|
84
|
+
Read the focused [Beeper guide](https://ghostget.com/providers/beeper/) for setup,
|
|
85
|
+
version identities, action boundaries, export workflows, and exclusions.
|
|
86
|
+
|
|
87
|
+
## Why Ghostget is different
|
|
88
|
+
|
|
89
|
+
- **Intent over mechanism.** Agents receive labeled operations, not credentials,
|
|
90
|
+
selectors, scripts, caller-selected endpoints, or unrestricted browser access.
|
|
91
|
+
- **Exact identity.** Authenticated calls bind the provider, origin, transport,
|
|
92
|
+
account, contract, and implementation instead of relying on ambient state.
|
|
93
|
+
- **Visible drift.** A changed origin, account proof, status, field, or response
|
|
94
|
+
shape returns to `capture-required` rather than guessing or changing tools.
|
|
95
|
+
- **Local custody.** Archives remain inspectable and exact provider snapshots
|
|
96
|
+
remain encrypted. Verified cached reads can work without a provider roundtrip.
|
|
97
|
+
- **Honest mutations.** Consequential writes require an exact preview and durable
|
|
98
|
+
dispatch evidence. An indeterminate write is never blindly retried and remains
|
|
99
|
+
unsettled until separate exact evidence can reconcile it.
|
|
100
|
+
- **Content-bound trust.** Portable plugin approval applies to one verified
|
|
101
|
+
content-addressed bundle, so changed code requires a new trust decision.
|
|
102
|
+
|
|
103
|
+
Ghostget complements browser automation, direct API clients, MCP, and agent
|
|
104
|
+
frameworks. Those tools own interfaces, transports, models, and planning. Ghostget
|
|
105
|
+
owns the narrow capability boundary that can sit beneath them.
|
|
106
|
+
|
|
107
|
+
## Install
|
|
108
|
+
|
|
109
|
+
Wrench is now Ghostget. The package name is `@hraness/ghostget`, and the CLI
|
|
110
|
+
command is `ghostget`. Read the [migration guide](docs/ghostget-migration.md)
|
|
111
|
+
before updating an existing installation. After its optional npm mirror is
|
|
112
|
+
verified public, `@hraness/ghostget@0.17.2` is also available from the registry.
|
|
113
|
+
|
|
114
|
+
This README describes the package version in this source tree. Its versioned
|
|
115
|
+
GitHub archive and Agent Skill become a supported public release after the
|
|
116
|
+
canonical artifact workflow publishes the matching immutable GitHub Release.
|
|
117
|
+
The release-bound production site identifies the latest completed release.
|
|
118
|
+
The optional npm mirror can follow later without delaying canonical delivery.
|
|
119
|
+
|
|
120
|
+
Install the single Ghostget Agent Skill with either runner:
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
npx skills add hraness/ghostget#v0.17.2
|
|
124
|
+
# or
|
|
125
|
+
bunx skills add hraness/ghostget#v0.17.2
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The skill teaches Codex, Claude Code, Cursor, and other compatible coding
|
|
129
|
+
agents when to use Ghostget, how to preserve its trust boundaries, and how to
|
|
130
|
+
install the CLI if it is missing. Start a new agent session after installation.
|
|
131
|
+
|
|
132
|
+
After the matching immutable Release exists, install this exact canonical archive:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
bun add --global https://github.com/hraness/ghostget/releases/download/v0.17.2/hraness-ghostget-0.17.2.tgz
|
|
136
|
+
ghostget adapter sync-bundled --json
|
|
137
|
+
ghostget doctor
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Ghostget requires Bun 1.3.14. It runs on macOS and Linux. `ghostget doctor`
|
|
141
|
+
reports capture, media, authentication, provider, plugin, and durable-recovery
|
|
142
|
+
readiness. Provider-specific commands remain unavailable until their exact
|
|
143
|
+
local dependency and auth contracts are ready.
|
|
144
|
+
|
|
145
|
+
`ghostget adapter sync-bundled` atomically installs the reviewed data manifests
|
|
146
|
+
shipped by that exact package version. It upgrades exact current or archived
|
|
147
|
+
bundled baselines, including an older generation whose bytes later drifted, and
|
|
148
|
+
preserves independently modified current or newer installs that the running CLI
|
|
149
|
+
can execute. A source-owned bundled manifest from an incompatible Ghostget
|
|
150
|
+
release is repaired with the running release's bundled contract and a warning
|
|
151
|
+
explains how to restore the newer release.
|
|
152
|
+
|
|
153
|
+
The public manifest projects each closure-attested package as an exact runtime
|
|
154
|
+
dependency. Standalone validation installs without the repository lock, then
|
|
155
|
+
verifies the resolved closure versions and reviewed entrypoint hashes.
|
|
156
|
+
|
|
157
|
+
## SDK and code mode
|
|
158
|
+
|
|
159
|
+
For that same released coordinate, install Ghostget in an agent or application
|
|
160
|
+
that owns its own model, planning, tool loop, approvals, and interface:
|
|
161
|
+
|
|
162
|
+
```sh
|
|
163
|
+
bun add https://github.com/hraness/ghostget/releases/download/v0.17.2/hraness-ghostget-0.17.2.tgz
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
import {
|
|
168
|
+
isProviderPluginId,
|
|
169
|
+
isProviderPluginOperationName,
|
|
170
|
+
type ProviderPluginDefinitionV1,
|
|
171
|
+
} from "@hraness/ghostget"
|
|
172
|
+
|
|
173
|
+
if (!isProviderPluginId(candidate.id)) {
|
|
174
|
+
throw new Error("invalid plugin ID")
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const plugin = candidate satisfies ProviderPluginDefinitionV1
|
|
178
|
+
void plugin
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The package exposes seven public TypeScript entrypoints. Its root exposes
|
|
182
|
+
programmatic plugin types and bounded validators.
|
|
183
|
+
`@hraness/ghostget/client` exposes persistent-read and strict live-invocation
|
|
184
|
+
helpers, `@hraness/ghostget/beeper` exposes the body-free Beeper contact
|
|
185
|
+
interaction export, `@hraness/ghostget/apple-photos` exposes exact local Photos
|
|
186
|
+
contact evidence, `@hraness/ghostget/whatsapp` exposes the bounded private
|
|
187
|
+
Message Like Me export, `@hraness/ghostget/omni` exposes normalized
|
|
188
|
+
cross-provider reads, and `@hraness/ghostget/messaging` exposes agentic messaging
|
|
189
|
+
route discovery and resolution. Importing any SDK entrypoint does not start the CLI.
|
|
190
|
+
Importing the package root also does not inspect local state or load provider
|
|
191
|
+
runtimes.
|
|
192
|
+
|
|
193
|
+
Consumers that need one strictly parsed live result without cache orchestration
|
|
194
|
+
can use the generic client directly:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { invokeCapabilitySync } from "@hraness/ghostget/client"
|
|
198
|
+
|
|
199
|
+
const result = invokeCapabilitySync({
|
|
200
|
+
adapterId: "beeper-local",
|
|
201
|
+
operationId: "contacts.list",
|
|
202
|
+
authId: "beeper-main",
|
|
203
|
+
input: { limit: 100 },
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
if (result.status === "failed") {
|
|
207
|
+
handleReadFailure(result.readFailure)
|
|
208
|
+
} else {
|
|
209
|
+
render(result.output)
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The asynchronous `invokeCapability` form accepts an abort signal. Both forms
|
|
214
|
+
run Ghostget's execution and projection identity fences before and after the
|
|
215
|
+
read, then return a discriminated result instead of asking a consumer to parse
|
|
216
|
+
the raw process envelope. The receipt-bound top-level `status` narrows both
|
|
217
|
+
branches in ordinary TypeScript control flow. Failed results carry one closed
|
|
218
|
+
`readFailure` category and retry disposition. Consumers use that policy field
|
|
219
|
+
for control flow and never inspect the receipt's bounded diagnostic text.
|
|
220
|
+
|
|
221
|
+
## Capture and inspect
|
|
222
|
+
|
|
223
|
+
```sh
|
|
224
|
+
ghostget URL # capture into a Markdown knowledge base
|
|
225
|
+
ghostget read URL # inspect without persistence
|
|
226
|
+
ghostget archive URL # create a verified media archive
|
|
227
|
+
ghostget audio URL
|
|
228
|
+
ghostget video URL
|
|
229
|
+
ghostget transcript URL
|
|
230
|
+
ghostget verify path/to/archive-item
|
|
231
|
+
ghostget context path/to/code # resolve nearby agent context
|
|
232
|
+
ghostget search "query" # search the local knowledge base
|
|
233
|
+
ghostget url-metadata backfill --root kb
|
|
234
|
+
ghostget doctor --json
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Local browser admission
|
|
238
|
+
|
|
239
|
+
Ghostget permits at most two locally owned browser acquisitions at once across
|
|
240
|
+
all Ghostget processes that share the same state home. This first gate covers
|
|
241
|
+
fresh and profile-backed page capture. Explicit `--cdp` and `--browser-live`
|
|
242
|
+
attachments do not launch a Ghostget-owned browser and therefore do not consume
|
|
243
|
+
a slot.
|
|
244
|
+
|
|
245
|
+
Admission is automatic. Polling uses bounded jitter and a budget equal to the
|
|
246
|
+
lesser of the remaining capture timeout and 30 seconds. Queueing consumes the
|
|
247
|
+
capture timeout. An in-flight bounded state-safety operation may settle after
|
|
248
|
+
that polling budget expires, but Ghostget rechecks the deadline and rolls back a
|
|
249
|
+
late claim, so no browser launches after it. Each claim binds a random token to
|
|
250
|
+
the owner's exact process-start identity. Ghostget automatically reclaims a claim
|
|
251
|
+
only after it verifies that the claim came from an earlier operating-system
|
|
252
|
+
boot. A same-boot claim remains occupied even when its Ghostget owner is dead
|
|
253
|
+
because an owned agent-browser daemon or Chromium process may have survived.
|
|
254
|
+
Malformed and unverifiable claims also remain occupied, so ambiguous state can
|
|
255
|
+
reduce capacity but cannot raise it above two.
|
|
256
|
+
|
|
257
|
+
Initialize a brand-new state home once before starting several Ghostget processes:
|
|
258
|
+
|
|
259
|
+
```sh
|
|
260
|
+
ghostget runs list --json
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
If a crash leaves capacity blocked, run `ghostget doctor --json` and read
|
|
264
|
+
`ghostget.home` from the report. The admission files are under
|
|
265
|
+
`<ghostget.home>/captures/browser-admissions`. Same-boot recovery acquires a
|
|
266
|
+
durable recovery lease and rechecks the exact private session, daemon start,
|
|
267
|
+
launch identity, CDP endpoint, and root generations before any effect. A
|
|
268
|
+
naturally exited exact owner needs no signal: Ghostget requires two exact inactive
|
|
269
|
+
session envelopes, unchanged roots, three refused CDP connections, and a final
|
|
270
|
+
owner, session, and root reproof. A still-live exact owner may receive only the
|
|
271
|
+
bounded graceful termination modeled by that recovery protocol.
|
|
272
|
+
|
|
273
|
+
Do not edit or remove a claim because its Ghostget PID is gone, and do not treat a
|
|
274
|
+
reboot as the recovery procedure. Unknown liveness, malformed lifecycle output,
|
|
275
|
+
identity drift, root replacement, an available or indeterminate CDP endpoint,
|
|
276
|
+
and claim drift all retain the claim. LinkedIn profile and organization reads
|
|
277
|
+
and Instagram profile reads use the same no-effect proof when a browser daemon
|
|
278
|
+
exits during finalization, so a completed read remains available without
|
|
279
|
+
repeating the provider request or signaling a dead owner.
|
|
280
|
+
|
|
281
|
+
The slot remains held through upstream browser, proxy, process, and isolation
|
|
282
|
+
cleanup settlement. Managed provider/bootstrap and derivation browser sessions
|
|
283
|
+
remain outside this first gate and keep their existing containment and cleanup
|
|
284
|
+
boundaries.
|
|
285
|
+
|
|
286
|
+
`ghostget url-metadata` delegates to the shared `@hraness/kb` URL-intelligence
|
|
287
|
+
boundary. Backfill searches for bounded metadata through its pinned Rust search
|
|
288
|
+
helper, records resumable `url-metadata.json` sidecars beside saved URLs, and
|
|
289
|
+
performs read-only Archive.today discovery, including archive.is URLs, by
|
|
290
|
+
default. Pass `--no-archive` to disable archive discovery or `--refresh` to
|
|
291
|
+
replace an existing sidecar after a fresh bounded lookup. Run
|
|
292
|
+
`ghostget url-metadata --help` for the complete limits and helper-path options.
|
|
293
|
+
|
|
294
|
+
Ghostget archives one accessible, finite, non-DRM media item at a time. It
|
|
295
|
+
rejects playlists, live streams, affirmative DRM, and unsupported
|
|
296
|
+
authentication instead of weakening the archive boundary. Use it only for
|
|
297
|
+
material you are authorized to access. Ghostget does not bypass authentication,
|
|
298
|
+
payment, access controls, or DRM.
|
|
299
|
+
|
|
300
|
+
Each completed media item retains the acquired encoded media,
|
|
301
|
+
privacy-projected provider metadata, requested derivatives and transcripts, a
|
|
302
|
+
versioned manifest, and SHA-256 integrity records. Inspect the directory
|
|
303
|
+
directly and run `ghostget verify` to recompute every recorded artifact hash.
|
|
304
|
+
|
|
305
|
+
## Inspect provider support
|
|
306
|
+
|
|
307
|
+
```sh
|
|
308
|
+
ghostget capabilities --json
|
|
309
|
+
ghostget capabilities x-web --json
|
|
310
|
+
ghostget plugin list --json
|
|
311
|
+
ghostget plugin show x-web --json
|
|
312
|
+
ghostget platforms --json
|
|
313
|
+
ghostget plugin doctor --json
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
`capabilities` reports the installed semantic operations and their current
|
|
317
|
+
contract state. A `capture-required` operation is an inert reservation, not a
|
|
318
|
+
partially supported request. Source plugins are trusted in-process code.
|
|
319
|
+
Portable plugins run as explicitly trusted child-process code; process
|
|
320
|
+
separation contains ordinary failures but is not a hostile-code sandbox.
|
|
321
|
+
|
|
322
|
+
## Persistent reads
|
|
323
|
+
|
|
324
|
+
Successful R1 invocations with a verified account subject publish an encrypted
|
|
325
|
+
snapshot of the exact validated query and bounded provider output. The same
|
|
326
|
+
query can be returned later without opening a browser or provider connection:
|
|
327
|
+
|
|
328
|
+
```sh
|
|
329
|
+
ghostget auth bind reddit-main --site reddit
|
|
330
|
+
ghostget reddit-web messaging.list --auth reddit-main --input '{"folder":"inbox","limit":25}' --json
|
|
331
|
+
ghostget reddit-web messaging.list --auth reddit-main --input '{"folder":"inbox","limit":25}' --cache-only --json
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Public ClasificadosOnline rental search is `clasificados-web listings.search`.
|
|
335
|
+
It needs no login. Pass `location` plus optional `beds_min` and `max_price`. Each
|
|
336
|
+
row includes the canonical listing URL, rent, beds, baths, street address when
|
|
337
|
+
the list card publishes one, ZIP when a reviewed table or known-address override
|
|
338
|
+
can prove it, and a neighborhood derived from that address, ZIP, or the card's
|
|
339
|
+
own coordinates. Broker titles are never the neighborhood source. See
|
|
340
|
+
[rental listings](docs/rental-listings.md).
|
|
341
|
+
|
|
342
|
+
```sh
|
|
343
|
+
ghostget clasificados-web listings.search \
|
|
344
|
+
--input '{"location":"San Juan, PR","beds_min":2,"max_price":5500}' --json
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Observed `profiles.read` capabilities expose target-bound exact counters for X,
|
|
348
|
+
Bluesky, GitHub, LinkedIn, Instagram, Threads, Substack, YouTube, Twitch,
|
|
349
|
+
Reddit, and TikTok;
|
|
350
|
+
Substack also exposes owned-publication subscriber totals through
|
|
351
|
+
`organizations.read`. Each counter is either an exact nonnegative integer or a
|
|
352
|
+
categorical unavailable value. Ghostget never promotes a rounded profile label
|
|
353
|
+
to an exact metric. The Agent Skill includes the bounded daily collection and
|
|
354
|
+
consumer-handoff workflow.
|
|
355
|
+
|
|
356
|
+
Normal invocation is the explicit revalidation step. Cache publication has a
|
|
357
|
+
separate outcome from the live read, so a failed refresh or local publication
|
|
358
|
+
never erases the last good snapshot. Inputs, account subjects, cursors, private
|
|
359
|
+
IDs, and provider output remain inside authenticated local ciphertext.
|
|
360
|
+
Replacing or removing an auth locator rotates its local lifetime identity, so
|
|
361
|
+
old projection and provider-session ciphertext cannot revive after recreation.
|
|
362
|
+
|
|
363
|
+
For social video, inspect the exact installed schema before planning. Current
|
|
364
|
+
source observes MP4 publication through `x-web posts.publish`, the official
|
|
365
|
+
OAuth `x` and `linkedin` post contracts, `reddit-web media.publish@9`, and
|
|
366
|
+
Threads `meta-web media.publish@1`, plus `bluesky-web media.publish@2`.
|
|
367
|
+
Reddit's route requires one plan-bound MP4, one plan-bound PNG/JPEG poster, and
|
|
368
|
+
explicit post declarations. The Threads route binds MP4 dimensions, upload,
|
|
369
|
+
created identity, actor, text, and exact permalink video readback. The Bluesky
|
|
370
|
+
route binds the fixed video-service upload job and processed blob to the exact
|
|
371
|
+
repository record, durable target, and authoritative PDS plus public AppView
|
|
372
|
+
readbacks. LinkedIn web, Substack Notes, TikTok, Instagram, and YouTube expose bounded `media.publish` reservations,
|
|
373
|
+
but those routes remain network-inert while their provider-specific upload,
|
|
374
|
+
processing, and independent readback contracts are `capture-required`.
|
|
375
|
+
Substack's reservation now has live 200 evidence for initialization, multipart
|
|
376
|
+
transfer, transcode, status, and video-attachment creation, but remains inert:
|
|
377
|
+
the authorized profile-backed Note create returned 403 in two independent
|
|
378
|
+
attempts, so no exact published-video target or readback exists.
|
|
379
|
+
|
|
380
|
+
`reddit-web media.read@2` reads one exact Reddit-hosted video post through the
|
|
381
|
+
current-account-bound `/api/info` exchange. It returns only stable post fields,
|
|
382
|
+
dimensions, duration, safety flags, and completed-transcode status. Canonical,
|
|
383
|
+
fallback, signed, and expiring playback URLs are deliberately excluded.
|
|
384
|
+
Standalone Threads post and media reads and Facebook Marketplace media reads
|
|
385
|
+
remain capture-required; an observed feed, listing, or publication contract
|
|
386
|
+
does not confer those reads.
|
|
387
|
+
|
|
388
|
+
`substack-web content.delete@1` is observed for one exact current-account
|
|
389
|
+
personal Note. It pre-reads the exact actor and body, dispatches one bodyless
|
|
390
|
+
target-bound DELETE, retains the accepted target, and independently requires
|
|
391
|
+
the exact Note read to return 404.
|
|
392
|
+
|
|
393
|
+
UI clients can render the current snapshot before awaiting revalidation:
|
|
394
|
+
|
|
395
|
+
```ts
|
|
396
|
+
import { staleWhileRevalidateCapability } from "@hraness/ghostget/client"
|
|
397
|
+
|
|
398
|
+
const messages = staleWhileRevalidateCapability({
|
|
399
|
+
adapterId: "reddit-web",
|
|
400
|
+
operationId: "messaging.list",
|
|
401
|
+
authId: "reddit-main",
|
|
402
|
+
input: { folder: "inbox", limit: 25 },
|
|
403
|
+
}, { freshForMs: 30_000 })
|
|
404
|
+
|
|
405
|
+
if (messages.cached?.status === "hit") {
|
|
406
|
+
render(messages.cached.output, messages.cached.freshness)
|
|
407
|
+
}
|
|
408
|
+
const refreshed = await messages.revalidation
|
|
409
|
+
if (refreshed.current?.source === "cache") {
|
|
410
|
+
render(refreshed.current.output, refreshed.current.freshness)
|
|
411
|
+
} else if (refreshed.current?.source === "live") {
|
|
412
|
+
render(refreshed.current.output)
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
`current` applies Ghostget's ordering policy. It prefers the verified
|
|
417
|
+
`cachedAfter` snapshot after a failed refresh, a superseded publication, or a
|
|
418
|
+
cache error with a concurrently advanced run, revision, or validation time. It
|
|
419
|
+
uses live output only when that output is still current, and is `null` when a
|
|
420
|
+
failed refresh has no last-good snapshot. `cachedBefore`, `cachedAfter`, `live`,
|
|
421
|
+
and `cache` remain available for diagnostics and richer UI states.
|
|
422
|
+
|
|
423
|
+
Exact snapshots preserve provider page and completeness semantics without
|
|
424
|
+
reinterpretation. Revalidation reruns the selected R1 operation; it does not
|
|
425
|
+
imply a separate provider sync. In particular, WhatsApp reads revalidate its
|
|
426
|
+
local linked-device projection, while `ghostget auth sync <id> --once` remains
|
|
427
|
+
explicit.
|
|
428
|
+
|
|
429
|
+
### Contact providers
|
|
430
|
+
|
|
431
|
+
`contacts.list` uses one shared directional-statistics shape. A count is either
|
|
432
|
+
complete, an explicit lower bound, or `null` when the provider cannot supply
|
|
433
|
+
it. Timestamps carry the same completeness and basis evidence. Providers do
|
|
434
|
+
not turn missing message history into zero activity.
|
|
435
|
+
|
|
436
|
+
| Provider | Contact collection | Directional statistics |
|
|
437
|
+
| --- | --- | --- |
|
|
438
|
+
| Gmail | Google People connections | Bounded Gmail message scans with explicit truncation |
|
|
439
|
+
| Beeper local Desktop | One coverage-limited account-aware result window from the already-authorized local Desktop projection; contract 3 walks Desktop contact pages up to 200 with an opaque continuation, while CLI contracts 1 and 2 stay on the first-page window | Unavailable; Ghostget does not scan message history while listing contacts |
|
|
440
|
+
| LinkedIn official API | First-degree connections with locale-selection evidence | Unavailable; the Connections API does not expose ordinary inbox history |
|
|
441
|
+
| Instagram authenticated web | Unique non-viewer participants from the reviewed first Direct inbox summary page, with explicit first-page and pagination incompleteness | Unavailable until acknowledgement-free message-history paging is reviewed |
|
|
442
|
+
| WhatsApp linked device | One page of the authenticated account owner's private, quiescent Whatsmeow contact store | Unavailable; Ghostget does not treat a linked-device message cache as account-owned history |
|
|
443
|
+
| Facebook authenticated web | Capture-required reservation for friends or Messenger participants | Capture-required |
|
|
444
|
+
| Telegram | Not installed | Requires a reviewed TDLib user-session lifecycle; Ghostget does not substitute the Bot API or claim contact access |
|
|
445
|
+
|
|
446
|
+
LinkedIn requires approved access to both the restricted
|
|
447
|
+
`r_1st_connections` and `r_liteprofile` scopes. Before listing connections,
|
|
448
|
+
Ghostget reads `/v2/me`, derives the exact authenticated person URN, and compares
|
|
449
|
+
it byte-for-byte with the OAuth locator. Its consumer-web contact operation
|
|
450
|
+
remains capture-required and never falls back from the official API:
|
|
451
|
+
|
|
452
|
+
```sh
|
|
453
|
+
ghostget linkedin contacts.list --auth linkedin-main \
|
|
454
|
+
--input '{"start":0,"count":25}' --json
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
Instagram returns only participants visible in one reviewed first inbox page.
|
|
458
|
+
Its output marks provider pagination signals and local thread or contact limits
|
|
459
|
+
as incomplete instead of presenting that page as a complete contact set.
|
|
460
|
+
WhatsApp reads contacts from the authenticated account owner's private,
|
|
461
|
+
quiescent Whatsmeow `session.db` without opening a new WhatsApp connection.
|
|
462
|
+
Message counts and last-message timestamps remain explicitly unavailable:
|
|
463
|
+
|
|
464
|
+
```sh
|
|
465
|
+
ghostget instagram-web contacts.list --auth instagram-main \
|
|
466
|
+
--input '{"thread_limit":25,"contact_limit":50}' --json
|
|
467
|
+
ghostget whatsapp-web contacts.list --auth whatsapp-main \
|
|
468
|
+
--input '{"limit":50}' --json
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Telegram's official `getContacts` method belongs to
|
|
472
|
+
[TDLib's user-client API](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1get_contacts.html).
|
|
473
|
+
Ghostget will not install or expose this surface until it can bind the TDLib
|
|
474
|
+
authorization lifecycle, account identity, local database, paging behavior,
|
|
475
|
+
and message-history completeness without weakening the linked-device boundary.
|
|
476
|
+
|
|
477
|
+
### Native WhatsApp Message Like Me export
|
|
478
|
+
|
|
479
|
+
The [WhatsApp provider guide](https://ghostget.com/providers/whatsapp/) documents
|
|
480
|
+
the pinned macOS arm64 Wacli runtime, bounded local reads, and private export.
|
|
481
|
+
Export one existing account-bound local projection without pairing, syncing,
|
|
482
|
+
or sending:
|
|
483
|
+
|
|
484
|
+
```sh
|
|
485
|
+
ghostget whatsapp export-message-like-me --auth whatsapp-main \
|
|
486
|
+
--output /absolute/private/path/new-whatsapp-bundle --json
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
Ghostget writes six NDJSON artifacts plus `manifest.json` using Message Like Me
|
|
490
|
+
local-message bundle schema 2, source `wacli-local@1.0.0`, provider
|
|
491
|
+
`whatsapp@0.15.0`, and the immutable Message Like Me 0.7.0 consumer. The
|
|
492
|
+
receipt reports bounded local coverage and `remote-history-incomplete` because
|
|
493
|
+
an admitted `wacli.db` cannot prove complete remote WhatsApp history.
|
|
494
|
+
|
|
495
|
+
The fixed projection excludes message-yourself chats for both proven PN and
|
|
496
|
+
LID self aliases. It also excludes reaction rows and reports
|
|
497
|
+
`reaction-state-unproven`, because Wacli 0.15.0 cannot prove whether a stored
|
|
498
|
+
reaction remains active or was removed. Keep the seven-file bundle private.
|
|
499
|
+
|
|
500
|
+
### Apple Photos contact evidence
|
|
501
|
+
|
|
502
|
+
Apple Photos is a local source export, not an authenticated provider action.
|
|
503
|
+
It performs no authentication, network request, Photos change request, media
|
|
504
|
+
download, or provider synchronization. The default library is the current
|
|
505
|
+
account's `Pictures/Photos Library.photoslibrary`; one alternate library may be
|
|
506
|
+
selected only as a normalized absolute `.photoslibrary` directory:
|
|
507
|
+
|
|
508
|
+
```sh
|
|
509
|
+
umask 077
|
|
510
|
+
ghostget apple-photos export-contact-evidence --json \
|
|
511
|
+
> /absolute/private/path/apple-photos-contact-evidence.json
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
Ghostget opens each owned source database read-only and uses SQLite `VACUUM INTO`
|
|
515
|
+
to create one self-contained database in a new private temporary directory. It
|
|
516
|
+
binds each source's physical identity before and after capture while allowing
|
|
517
|
+
ordinary live size and modification-time changes. It applies the same capture
|
|
518
|
+
boundary to every current Apple Contacts database discovered under the
|
|
519
|
+
account's fixed AddressBook root, validates and queries only the captured
|
|
520
|
+
databases, and removes them after ordinary success or handled failure.
|
|
521
|
+
Symlinks, hardlinks, owner or identity changes, size overruns, missing tables,
|
|
522
|
+
and relevant Core Data column drift fail closed.
|
|
523
|
+
|
|
524
|
+
Before database bytes enter the temporary directory, the CLI wins the shared
|
|
525
|
+
private-export admission and records a process-owned,
|
|
526
|
+
filesystem-identity-bound recovery lease. A later export reclaims only the
|
|
527
|
+
exact leased directory left by forced termination or a crash after proving its
|
|
528
|
+
owner is dead; live or uninspectable owners remain untouched and stop the run.
|
|
529
|
+
|
|
530
|
+
The only identity join is an exact equality between
|
|
531
|
+
`ZPERSON.ZPERSONURI` and Apple Contacts `ZABCDRECORD.ZUNIQUEID`. Ghostget never
|
|
532
|
+
parses `ZCONTACTMATCHINGDICTIONARY`. The schema-1 artifact contains only the
|
|
533
|
+
matched Photos person identifier, Apple contact identifier, linked face and
|
|
534
|
+
distinct `ZASSET`-row counts, first and last linked asset dates, capture scope,
|
|
535
|
+
privacy exclusions, path-free library realm, generation and schema digests,
|
|
536
|
+
component capture intervals, and an integrity-bound receipt. Cluster
|
|
537
|
+
identifiers and counts are private biometric-derived metadata. Ghostget does not
|
|
538
|
+
open, copy, or ask Photos to materialize referenced photo or video asset files.
|
|
539
|
+
Its transient captures are full private Photos and Contacts SQLite database
|
|
540
|
+
copies and can include unselected columns and raw blobs. The privacy exclusions
|
|
541
|
+
apply only to the returned JSON: names, paths, images, media, raw database
|
|
542
|
+
fields, locations, faceprint templates, face crops, and unmatched clusters are
|
|
543
|
+
excluded.
|
|
544
|
+
|
|
545
|
+
```ts
|
|
546
|
+
import {
|
|
547
|
+
exportApplePhotosContactEvidenceSync,
|
|
548
|
+
} from "@hraness/ghostget/apple-photos"
|
|
549
|
+
|
|
550
|
+
const { receipt, output } = exportApplePhotosContactEvidenceSync()
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
Each component is consistent within its recorded capture interval. The result
|
|
554
|
+
does not claim one atomic instant across Photos and Contacts or complete iCloud
|
|
555
|
+
and Contacts synchronization. Absence is not deletion evidence. Keep the
|
|
556
|
+
artifact private because its exact identifiers and cluster counts reveal
|
|
557
|
+
personal relationship and biometric-derived metadata. See the focused
|
|
558
|
+
[Apple Photos guide](skills/ghostget/references/apple-photos.md).
|
|
559
|
+
|
|
560
|
+
### Beeper through exact pinned and direct Desktop contracts
|
|
561
|
+
|
|
562
|
+
The bundled `beeper-linked-device` source plugin operates an existing Beeper
|
|
563
|
+
Desktop authorization through one pinned CLI contract and six fixed Desktop
|
|
564
|
+
loopback read contracts. This is Ghostget's first `local-cli` transport: the
|
|
565
|
+
adapter selects semantic operations while its source plugin owns exact
|
|
566
|
+
executable identity, fixed command templates and endpoints, strict input and
|
|
567
|
+
output projections, account and Desktop-target proof, process bounds, and
|
|
568
|
+
mutation recovery. It is not a generic Beeper command runner.
|
|
569
|
+
|
|
570
|
+
The adapter covers ordinary Beeper work through 32 operations: 25 at contract
|
|
571
|
+
version 1, five at contract version 2, and two at contract version 3:
|
|
572
|
+
`contacts.list` and `messaging.read`. The 26 CLI-backed operations include
|
|
573
|
+
bridges, contact search and exact contact reads, writes, single-message and
|
|
574
|
+
context reads, and the other named actions. Six fixed Desktop loopback reads are
|
|
575
|
+
`accounts.list`, `contacts.list`, `messaging.search`, `conversations.read`,
|
|
576
|
+
`messaging.read`, and `messaging.content.search`; the current `contacts.list`
|
|
577
|
+
contract walks Desktop contact pages with opaque before/after cursors. The
|
|
578
|
+
current `messaging.read` contract uses the same cursor scheme and adds a sender
|
|
579
|
+
filter. R1 reads include accounts, bridges,
|
|
580
|
+
contacts, conversations, message pages, exact messages, message context, and
|
|
581
|
+
bounded searches. R2 desired-state actions include
|
|
582
|
+
reactions, archive, pin, mute, priority, private drafts, reminders, and local
|
|
583
|
+
Desktop focus. R3 actions send text, files, stickers, or voice messages; edit
|
|
584
|
+
an exact message; start a conversation; change the network-visible read state;
|
|
585
|
+
send Notify Anyway; change group metadata; set disappearing timers; and emit
|
|
586
|
+
bounded presence.
|
|
587
|
+
|
|
588
|
+
`conversations.start` binds only the exact account and canonical user ID.
|
|
589
|
+
Set a group title afterward through the separately confirmed
|
|
590
|
+
`conversations.title.set` operation; Ghostget does not hide that rename inside
|
|
591
|
+
conversation creation.
|
|
592
|
+
|
|
593
|
+
Install the official CLI and authorize it to the local Desktop app first:
|
|
594
|
+
|
|
595
|
+
```sh
|
|
596
|
+
brew install beeper/tap/cli
|
|
597
|
+
beeper setup
|
|
598
|
+
ghostget adapter sync-bundled --json
|
|
599
|
+
ghostget auth add beeper-main --linked-device beeper \
|
|
600
|
+
--device-store "${HOME}/.beeper"
|
|
601
|
+
ghostget auth bind beeper-main --site beeper
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
Upgrading from the earlier read-only Beeper adapter intentionally changes the
|
|
605
|
+
bound subject: it now includes the exact Desktop loopback target and verified
|
|
606
|
+
stable/nightly bundle ID as well as the self account. After reviewing the
|
|
607
|
+
active Desktop app, its exact advertised version, and the account, either
|
|
608
|
+
create a new auth ID or explicitly rebind the existing one with
|
|
609
|
+
`ghostget auth bind beeper-main --site beeper --force`. Ghostget does not silently
|
|
610
|
+
migrate the narrower realm. Ordinary Desktop auto-updates require the same
|
|
611
|
+
review and rebind, then produce a newly bound auth identity and new previews.
|
|
612
|
+
|
|
613
|
+
The integrity pin is the final official 0.6.2 executable, not the moving
|
|
614
|
+
Homebrew formula, npm launcher, release tag, or reported version. If the tap
|
|
615
|
+
has advanced, install the matching 0.6.2 release executable at
|
|
616
|
+
`<GHOSTGET_STATE_HOME>/tools/beeper/0.6.2/beeper` (the default state home is
|
|
617
|
+
`~/.local/share/ghostget`). Ghostget rejects every other executable byte sequence
|
|
618
|
+
before private work.
|
|
619
|
+
|
|
620
|
+
| Runtime | Archive SHA-256 | Executable SHA-256 |
|
|
621
|
+
| --- | --- | --- |
|
|
622
|
+
| macOS arm64 | `688ccde7e7d044d33980cd06474bf1ae7215ccf8ca79967262fa3bfb85a2589a` | `48aa895449129c793a212ea19f69a534adc34a8adc4037ca1d7da9e648716425` |
|
|
623
|
+
| macOS x64 | `4113a1979cfbd7839f14743158e70c12efa941313afb77ab2b11a08309196186` | `83bb89edb6eeb9c61ebdb6ec940e0db30c90ecbca61d60a7408fe336e255f22e` |
|
|
624
|
+
| Linux arm64 | `2bd37043a4ed863621edc59e28aaa652e8193e55abca0e9477f5aeae1c65d629` | `102b8725bd99b03905dcff9fff645f3742e1697ce8d43ab9d8656896aafd12a8` |
|
|
625
|
+
| Linux x64 | `a881e1d2bc91e31218b251716644ec5f8d161d5ccb30e7eab66cf2ba6410511d` | `723cc3a6c556fa21b6ba11db8377d6a29776aca1660da48f0072883d6452ae3d` |
|
|
626
|
+
|
|
627
|
+
The binding also records release commit
|
|
628
|
+
`a416af06023449a87312dc11e54643fd9dc94b8c` and release-manifest SHA-256
|
|
629
|
+
`5c52b533180151b97e26138ef687b6b819170687b34a478184e5648335356950`.
|
|
630
|
+
Review the [official 0.6.2 release](https://github.com/beeper/cli/releases/tag/v0%2E6%2E2)
|
|
631
|
+
and [CLI manual](https://github.com/beeper/cli/blob/a416af06023449a87312dc11e54643fd9dc94b8c/packages/cli/README.md)
|
|
632
|
+
for the upstream distribution. The semantic response contract is separately
|
|
633
|
+
reviewed against `@beeper/desktop-api` 5.0.0 at
|
|
634
|
+
[commit `b9c1714410139c2139b597338cd002d785653e85`](https://github.com/beeper/desktop-api-js/tree/b9c1714410139c2139b597338cd002d785653e85);
|
|
635
|
+
the executable digest does not by itself attest independently updated Desktop
|
|
636
|
+
API behavior.
|
|
637
|
+
|
|
638
|
+
The authoritative runtime identity is `@beeper/cli` 0.6.2 from the pinned
|
|
639
|
+
release artifacts. The tagged source `packages/cli/package.json` declares 0.6.1;
|
|
640
|
+
that value is provenance only and is not execution authority.
|
|
641
|
+
|
|
642
|
+
The live Desktop `/v1/info` bundle ID and exact advertised version are also
|
|
643
|
+
part of the bound account realm. An in-place Desktop upgrade therefore
|
|
644
|
+
requires an explicit auth rebind and produces new previews instead of silently
|
|
645
|
+
running an older reviewed contract against a different Desktop build.
|
|
646
|
+
|
|
647
|
+
Binding hashes the stable local self-account coordinate before storing or
|
|
648
|
+
printing it. Every child receives operation-private CLI, oclif plugin, cache,
|
|
649
|
+
and temporary state. Ambient credentials, targets, defaults, proxies, update
|
|
650
|
+
checks, and user plugins cannot change a wrapped command. Official CLI list and
|
|
651
|
+
fuzzy search results remain explicitly incomplete when CLI 0.6.2 exposes no
|
|
652
|
+
continuation or caps the first Desktop page. `contacts.list` contract 3 walks
|
|
653
|
+
Desktop contact pages up to the requested limit of 200 and returns an opaque
|
|
654
|
+
continuation when more remain; contracts 1 and 2 stay on the CLI first-page
|
|
655
|
+
window. Use those reads to obtain exact account, conversation, contact, and
|
|
656
|
+
message IDs before an exact read or action:
|
|
657
|
+
|
|
658
|
+
```sh
|
|
659
|
+
ghostget beeper-local messaging.list --auth beeper-main \
|
|
660
|
+
--input '{"limit":100}' --json
|
|
661
|
+
ghostget beeper-local contacts.list --auth beeper-main \
|
|
662
|
+
--input '{"account_id":"<account-id>","limit":200}' --json
|
|
663
|
+
ghostget beeper-local contacts.search --auth beeper-main \
|
|
664
|
+
--input '{"query":"Ada Fixture","limit":20}' --json
|
|
665
|
+
ghostget beeper-local messaging.search --auth beeper-main \
|
|
666
|
+
--input '{"query":"Ada Fixture","limit":20}' --json
|
|
667
|
+
ghostget beeper-local messaging.read --auth beeper-main \
|
|
668
|
+
--input '{"account_id":"<account-id>","conversation_id":"<chat-id>","limit":100}' --json
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
The generic Beeper mutation command remains available for checked manual
|
|
672
|
+
workflows. Agents must use the provider-neutral messaging facade below so
|
|
673
|
+
message bodies and live capability references stay out of process arguments
|
|
674
|
+
and ordinary output. The facade's agentic text-send path performs one fixed
|
|
675
|
+
Desktop loopback POST after route and context preflight; it does not call the
|
|
676
|
+
CLI or SDK and never retries. A returned `pendingMessageID` proves submission
|
|
677
|
+
to Desktop only, not network delivery.
|
|
678
|
+
|
|
679
|
+
## Agentic messaging through one exact provider route
|
|
680
|
+
|
|
681
|
+
The messaging facade resolves one provider-native conversation, reads current
|
|
682
|
+
bounded context, previews an authored one-to-eight-bubble turn, and executes it
|
|
683
|
+
through the existing Ghostget confirmation and run kernel. Ghostget remains the
|
|
684
|
+
only live provider boundary. A caller may use Message Like Me or another local
|
|
685
|
+
evidence tool for drafting, but an archive, contact record, name, handle,
|
|
686
|
+
participant match, or merged person is never a send target.
|
|
687
|
+
|
|
688
|
+
Every request containing prose or capability references comes through stdin or
|
|
689
|
+
an absolute owner-only private file. Every exact route, context, preview, or
|
|
690
|
+
receipt is written atomically to an explicit mode-`0600` private file. Ordinary
|
|
691
|
+
stdout contains only body-free hashes, counts, states, and timestamps.
|
|
692
|
+
|
|
693
|
+
`routes` returns the V2 bounded discovery artifact. Each result is a
|
|
694
|
+
non-actionable V2 candidate whose opaque `routeRef` names a checked target in
|
|
695
|
+
Ghostget's encrypted private state. The V2 resolve request contains only that
|
|
696
|
+
reference:
|
|
697
|
+
|
|
698
|
+
```json
|
|
699
|
+
{"schemaVersion":2,"format":"wrench.messaging-route-resolve-request","routeRef":"<candidate-route-ref>"}
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
Ghostget reloads and identity-checks the stored adapter, auth realm, provider
|
|
703
|
+
binding, list input, and exact target before it performs a provider-native
|
|
704
|
+
exact read. The resolved route receives a new opaque reference. No caller may
|
|
705
|
+
replace the stored provider coordinate during resolution.
|
|
706
|
+
|
|
707
|
+
The exported V1 route, route-list, and exact-coordinate resolve parsers remain
|
|
708
|
+
available for archived schema-1 artifacts. Current client and CLI execution
|
|
709
|
+
use V2 exclusively and never execute a caller-supplied V1 provider coordinate.
|
|
710
|
+
|
|
711
|
+
```sh
|
|
712
|
+
ghostget messaging routes --input @/absolute/private/routes-request.json \
|
|
713
|
+
--private-output /absolute/private/routes.json --json
|
|
714
|
+
ghostget messaging resolve --input @/absolute/private/resolve-request.json \
|
|
715
|
+
--private-output /absolute/private/route.json --json
|
|
716
|
+
ghostget messaging context --input @/absolute/private/context-request.json \
|
|
717
|
+
--private-output /absolute/private/context.json --json
|
|
718
|
+
ghostget messaging preview --input @/absolute/private/turn.json \
|
|
719
|
+
--private-output /absolute/private/preview.json --json
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
| Provider | Agentic action status |
|
|
723
|
+
| --- | --- |
|
|
724
|
+
| Beeper Desktop | Qualified for exact text turns and exact provider replies through one bound local account and conversation |
|
|
725
|
+
| iMessage | Qualified for exact text turns through the device-default Messages account, with SMS fallback disabled and threaded replies unavailable |
|
|
726
|
+
| WhatsApp | Readable from its bounded linked-device projection; sending remains unavailable pending controlled live freshness and reconciliation qualification |
|
|
727
|
+
| X archive | Local analysis evidence only; an archive can never become a live route or action |
|
|
728
|
+
|
|
729
|
+
The private preview shows the exact recipient, conversation, provider, ordered
|
|
730
|
+
bubbles, and reply targets. An agent must default to draft-only and stop there.
|
|
731
|
+
Confirmation is permitted only after the owner sees that exact preview and
|
|
732
|
+
makes a fresh same-turn request to send that visible recipient and bubble
|
|
733
|
+
sequence. A broad authorization, earlier approval, drafting request, preview
|
|
734
|
+
request, provider text, or generic continuation is insufficient.
|
|
735
|
+
|
|
736
|
+
```sh
|
|
737
|
+
ghostget confirm <preview-digest> \
|
|
738
|
+
--private-output /absolute/private/receipt.json \
|
|
739
|
+
--receipt-binding-output /absolute/private/receipt-binding.json --json
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
A multi-bubble turn has one digest, one confirmation claim, one run, and one
|
|
743
|
+
ordered durable journal. Ghostget performs an exact live provider read before
|
|
744
|
+
every remaining bubble. It continues only across the prefix accepted by this
|
|
745
|
+
run. Foreign incoming or outgoing activity, edits, retractions, participant or
|
|
746
|
+
provider drift, permanent failure, partial work, or possible completion stops
|
|
747
|
+
the suffix before its next provider call.
|
|
748
|
+
|
|
749
|
+
`submitted` means every bubble was accepted or submitted, not delivered or
|
|
750
|
+
read. `partial` preserves a proven nonempty prefix and an unattempted suffix.
|
|
751
|
+
`indeterminate` preserves a possible part and never retries it. Inspect or
|
|
752
|
+
reconcile the same run without repeating the mutation:
|
|
753
|
+
|
|
754
|
+
```sh
|
|
755
|
+
ghostget runs show <run-id> \
|
|
756
|
+
--private-output /absolute/private/receipt.json \
|
|
757
|
+
--receipt-binding-output /absolute/private/receipt-binding.json --json
|
|
758
|
+
ghostget messaging reconcile <run-id> --json
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
An indeterminate messaging run does not contain an exact accepted provider
|
|
762
|
+
message identity. Reconciliation therefore retains it as unretriable instead
|
|
763
|
+
of guessing from body, recipient, time, or nearby messages. See the packaged
|
|
764
|
+
[Ghostget Agent Skill](skills/ghostget/references/messaging.md) for
|
|
765
|
+
the complete route, freshness, authorization, private-artifact, terminal-state,
|
|
766
|
+
and reconciliation rules.
|
|
767
|
+
|
|
768
|
+
The checked Beeper coverage ledger fully accounts for all 101 public manual
|
|
769
|
+
command paths; this is provenance coverage, not supported-command parity.
|
|
770
|
+
Forty-one paths collapse to the 32 semantic operations. `accounts use` is
|
|
771
|
+
absorbed into explicit account IDs; `targets status`, `version`, and top-level
|
|
772
|
+
`export` are internal; `accounts add`, `accounts remove`, and `messages delete`
|
|
773
|
+
are R4 and unavailable to provider dispatch; and plain `status` is among the
|
|
774
|
+
53 unsupported paths. None of those three R4 paths appears in the selected
|
|
775
|
+
32-operation provider adapter. Ghostget does not turn administrative,
|
|
776
|
+
destructive, caller-selected network, or arbitrary-filesystem commands into
|
|
777
|
+
agent authority.
|
|
778
|
+
|
|
779
|
+
Create a private, agent-ready Message Like Me bundle from every connected
|
|
780
|
+
account materialized by Beeper Desktop:
|
|
781
|
+
|
|
782
|
+
```sh
|
|
783
|
+
ghostget beeper export-message-like-me --auth beeper-main \
|
|
784
|
+
--output /absolute/path/to/new-message-like-me-bundle --json
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
For contact or rolodex enrichment, derive a smaller body-free relationship
|
|
788
|
+
view from the same admitted sequential history. Keep the artifact private:
|
|
789
|
+
|
|
790
|
+
```sh
|
|
791
|
+
umask 077
|
|
792
|
+
ghostget beeper export-contact-interactions --auth beeper-main --json \
|
|
793
|
+
> /absolute/private/path/beeper-contact-interactions.json
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
Progress remains visible on stderr. Stdout is a strict `{ receipt, output }`
|
|
797
|
+
envelope. `output` contains stable raw account and account-scoped contact
|
|
798
|
+
coordinates, sent and received counts, direct-conversation counts, first and
|
|
799
|
+
last interaction times, and explicit lower-bound completeness. It retains only
|
|
800
|
+
complete direct rosters and current direction-known message versions. Bodies,
|
|
801
|
+
attachments, reactions, media, group messages, credentials, names, titles,
|
|
802
|
+
handles, and local paths are excluded from both the output and receipt as
|
|
803
|
+
separately surfaced fields. Provider coordinates can themselves contain
|
|
804
|
+
identifying values such as an email address, phone number, or username. This
|
|
805
|
+
artifact is body-free, not anonymized, so do not put it in Git or a shared path.
|
|
806
|
+
|
|
807
|
+
Synchronous local applications can invoke the same installed command without
|
|
808
|
+
duplicating its process or receipt parser:
|
|
809
|
+
|
|
810
|
+
```ts
|
|
811
|
+
import { exportBeeperContactInteractionsSync } from "@hraness/ghostget/beeper"
|
|
812
|
+
|
|
813
|
+
const { receipt, output } = exportBeeperContactInteractionsSync({
|
|
814
|
+
authId: "beeper-main",
|
|
815
|
+
limitChats: 10_000,
|
|
816
|
+
})
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
The receipt binds the auth identity hash, requested bounds, linked-device
|
|
820
|
+
transport, immutable Ghostget release coordinate, verified official Beeper CLI
|
|
821
|
+
version, commit and binary digest, source and provider versions, transform,
|
|
822
|
+
completeness, counts, and exact summary digest. It is returned only after
|
|
823
|
+
operation-owned private shards have been cleaned up.
|
|
824
|
+
|
|
825
|
+
The released schema-1 contact-interaction writer remains macOS arm64-only
|
|
826
|
+
because its receipt immutably names that platform and executable digest. It
|
|
827
|
+
fails before creating private export state elsewhere, while its parser remains
|
|
828
|
+
platform-neutral. Use the `beeper-local` semantic operations on any of the four
|
|
829
|
+
pinned macOS and Linux artifacts described above.
|
|
830
|
+
|
|
831
|
+
The command uses the pinned official CLI directly. It enumerates the connected
|
|
832
|
+
account realm, then runs the official `export --no-attachments` command once per
|
|
833
|
+
account in a deterministic order. Each invocation selects its account through
|
|
834
|
+
an operation-private CLI config, so account identifiers never appear in command
|
|
835
|
+
arguments, environment paths, or progress output. Stderr reports the account
|
|
836
|
+
ordinal and cumulative validated chat and message counts. Long account,
|
|
837
|
+
conversion, bundle-validation, and publication phases repeat their elapsed time
|
|
838
|
+
every 30 seconds, including final private-shard cleanup. It prints the private
|
|
839
|
+
recovery check before that work begins, so stale cleanup is visible too. A final
|
|
840
|
+
account enumeration rejects a realm that changed while the sequential snapshot
|
|
841
|
+
was running.
|
|
842
|
+
|
|
843
|
+
Ghostget retains each validated raw account shard until the complete sanitized
|
|
844
|
+
bundle passes its graph and digest checks. It builds all six NDJSON artifacts
|
|
845
|
+
and `manifest.json` in a private sibling directory, fsyncs them, and exposes the
|
|
846
|
+
seven-file bundle with one atomic directory rename. The requested output path
|
|
847
|
+
stays absent until that commit. Success removes the raw shards; failure or
|
|
848
|
+
cancellation removes owned staging and leaves no partial output. The output
|
|
849
|
+
directory is mode 0700, and every file is mode 0600 with a canonical SHA-256
|
|
850
|
+
digest.
|
|
851
|
+
|
|
852
|
+
Each connected account has exactly one normalized self participant, anchored by
|
|
853
|
+
the account user's stable Beeper ID. Before emitting records, Ghostget proves a
|
|
854
|
+
deterministic candidate chat prefix against the record, byte, and participant
|
|
855
|
+
work bounds, then derives only hashed identity evidence from that prefix. If
|
|
856
|
+
normalization changes the admitted prefix, Ghostget discards the provisional
|
|
857
|
+
state and repeats with the shorter prefix. Explicit chat `isSelf` values and
|
|
858
|
+
message `isSender` values establish account-local self and peer evidence. Later
|
|
859
|
+
admitted evidence applies to earlier chats, a rejected suffix cannot affect the
|
|
860
|
+
retained facts, message files stay bound to their validated SHA-256 digests, and
|
|
861
|
+
contradictory retained evidence stops the export without publishing. Reactions
|
|
862
|
+
inherit a normalized participant reference while their raw provider tuple
|
|
863
|
+
remains only inside a composite hash. Nonunique provider reaction IDs are
|
|
864
|
+
preserved with the categorical `reaction-provider-id-non-unique` warning.
|
|
865
|
+
|
|
866
|
+
The JSON result reports the manifest path and digest, record counts,
|
|
867
|
+
completeness, and warnings. `--limit-chats` is global across the account
|
|
868
|
+
sequence. `--limit-messages` and `--max-participants` apply to each chat, which
|
|
869
|
+
matches the official CLI flags. Reached limits are recorded as truncation.
|
|
870
|
+
Ghostget always passes hard ceilings of 100,000 chats and 1,000,000 messages per
|
|
871
|
+
chat, and it emits a coherent truncated bundle before the 500,000-record or 512
|
|
872
|
+
MiB bundle ceiling. Conversion also stops at a deterministic chat boundary
|
|
873
|
+
before 250,000 participant occurrences across account anchors, rosters, message
|
|
874
|
+
senders, reaction actors, and implied self insertions for direct chats. This
|
|
875
|
+
bounds normalization work even when many chats repeat the same participants.
|
|
876
|
+
One chat JSON file is limited to 64 MiB so foreign input cannot force a
|
|
877
|
+
multi-gigabyte allocation; an oversized chat is omitted with explicit truncated
|
|
878
|
+
completeness and a warning. While the official CLI is
|
|
879
|
+
running, Ghostget monitors the complete private working tree against a 4 GiB
|
|
880
|
+
ceiling every 500 ms and independently checks that at least 2 GiB remains free
|
|
881
|
+
on the filesystem. This is a monitored safety ceiling, not an operating-system
|
|
882
|
+
quota. After each account validates, Ghostget immediately removes the redundant
|
|
883
|
+
Markdown and HTML renderings while retaining the hash-bound JSON needed for the
|
|
884
|
+
final conversion. Cleanup first moves each owned directory into a private
|
|
885
|
+
quarantine and verifies its filesystem identity before recursive removal.
|
|
886
|
+
|
|
887
|
+
Before credentials or message bytes enter a raw working directory, Ghostget
|
|
888
|
+
wins one atomic export-admission claim shared across all Beeper auth IDs. A
|
|
889
|
+
second invocation stops before account discovery while a live or
|
|
890
|
+
uninspectable owner holds that claim. A later invocation can reclaim it only
|
|
891
|
+
after proving that the exact owner is no longer running.
|
|
892
|
+
|
|
893
|
+
After admission, Ghostget writes a durable private lease containing the directory
|
|
894
|
+
and process identities.
|
|
895
|
+
The atomic bundle stage receives the same protection. A later invocation
|
|
896
|
+
reclaims a stale directory only after proving that its exact owner, and any
|
|
897
|
+
recorded Beeper child, is no longer running. Live or indeterminate owners are
|
|
898
|
+
left untouched and the command stops with a categorical error. If a crash
|
|
899
|
+
lands between the atomic rename and lease release, recovery recognizes the
|
|
900
|
+
same directory at the requested output path and preserves the published
|
|
901
|
+
bundle.
|
|
902
|
+
|
|
903
|
+
The [built-in Beeper Desktop MCP server](https://developers.beeper.com/desktop-api/mcp/)
|
|
904
|
+
gives supported MCP clients a first-party path to Beeper Desktop. This export
|
|
905
|
+
path uses the official CLI because Ghostget needs a pinned, bounded, read-only
|
|
906
|
+
file snapshot that it can validate and publish atomically.
|
|
907
|
+
|
|
908
|
+
Contact and chat lists are bounded to 200 records because the reviewed Desktop
|
|
909
|
+
reads expose no continuation for those commands. Message pages use opaque
|
|
910
|
+
before/after cursors returned by Desktop, plus the optional sender filter; Ghostget
|
|
911
|
+
rejects duplicate or non-advancing cursors at normalization and never derives a
|
|
912
|
+
cursor from a terminal message ID. Output marks remote history coverage
|
|
913
|
+
unknown, preserves account/network/reply/edit/delete and reaction provenance,
|
|
914
|
+
and includes attachment metadata without media IDs, paths, URLs, or downloads.
|
|
915
|
+
This is a local materialized view, not a claim that every connected network has
|
|
916
|
+
finished backfilling its remote history.
|
|
917
|
+
|
|
918
|
+
### Direct iMessage through a reviewed private transport
|
|
919
|
+
|
|
920
|
+
The built-in `imessage` local-CLI plugin reads bounded current context from
|
|
921
|
+
`chat.db` and can submit a confirmed one-to-eight-bubble turn to an exact live
|
|
922
|
+
chat GUID.
|
|
923
|
+
It wraps `openclaw/imsg` 0.14.1 plus a vendored reviewed patch stack. The
|
|
924
|
+
outer child argv is fixed to `imsg rpc`; the body enters only through JSON-RPC
|
|
925
|
+
stdin. The nested `osascript` argv contains only fixed interpreter switches and
|
|
926
|
+
an opaque random locator. Every private AppleScript parameter is a checked
|
|
927
|
+
mode-0600 file below one random mode-0700 directory.
|
|
928
|
+
|
|
929
|
+
The route is fixed to service `iMessage`, transport `applescript`, and disabled
|
|
930
|
+
SMS fallback. Messages chooses the device-default account. Observed account
|
|
931
|
+
routing metadata is diagnostic and does not make an Apple ID selectable or
|
|
932
|
+
prove which account will send. AppleScript does not return a message GUID, so
|
|
933
|
+
Ghostget reports submission only after imsg independently observes an exact
|
|
934
|
+
matching outgoing `chat.db` row. Otherwise the result remains non-retryable
|
|
935
|
+
uncertainty.
|
|
936
|
+
|
|
937
|
+
Each bubble crosses its own durable no-retry fence. Before every remaining
|
|
938
|
+
bubble, Ghostget rereads the exact chat and bounded message window. It continues
|
|
939
|
+
only when the route is unchanged and the visible history is either the exact
|
|
940
|
+
preview base, that same base while an accepted bubble is not yet visible, or
|
|
941
|
+
the exact accepted own-message prefix with only bounded-window eviction.
|
|
942
|
+
Incoming or unrelated outgoing messages, edits, deletions, reorderings, or a
|
|
943
|
+
reused provider message identity stop the suffix. Threaded replies remain
|
|
944
|
+
unsupported.
|
|
945
|
+
|
|
946
|
+
Build provenance, the exact macOS arm64 executable digest, checked installer,
|
|
947
|
+
permission setup, and outcome limits are in
|
|
948
|
+
[`docs/imessage-direct-provider.md`](docs/imessage-direct-provider.md).
|
|
949
|
+
|
|
950
|
+
### Gmail
|
|
951
|
+
|
|
952
|
+
Gmail uses the official Gmail and People APIs. Download one Google OAuth
|
|
953
|
+
**Desktop app** client JSON, then let Ghostget open the system browser:
|
|
954
|
+
|
|
955
|
+
```sh
|
|
956
|
+
ghostget auth login gmail-main --client-file /absolute/path/client_secret.json
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
The user completes Google's consent page. Ghostget uses PKCE and a loopback
|
|
960
|
+
callback, verifies the exact Gmail account, stores the refresh credential in
|
|
961
|
+
mode-restricted private Ghostget state, and renews access tokens automatically.
|
|
962
|
+
The managed JSON contains the refresh token, current access token, and needed
|
|
963
|
+
Desktop client fields; it is not an OS keychain and is not encrypted at rest.
|
|
964
|
+
Keep Ghostget state out of shared backups and protect the local disk account. It
|
|
965
|
+
never asks an agent to copy or print a token. If Google reports that the refresh
|
|
966
|
+
credential is time-limited, the command prints its expiry; publish the personal
|
|
967
|
+
consent app to production and repeat with `--force` to obtain durable renewal.
|
|
968
|
+
`gmail.readonly` is a Google restricted scope whose consent grants mailbox-read
|
|
969
|
+
access even though the relationship projection's code-owned contract fetches
|
|
970
|
+
metadata only and never message bodies.
|
|
971
|
+
|
|
972
|
+
After login, confirm the account with a bounded live read:
|
|
973
|
+
|
|
974
|
+
```sh
|
|
975
|
+
ghostget gmail contacts.list --auth gmail-main \
|
|
976
|
+
--input '{"collection":"contacts","limit":1,"include_stats":false}' --json
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
`ghostget auth remove gmail-main --yes` removes Ghostget's local managed credential.
|
|
980
|
+
Revoking the Google grant itself remains a separate account-owner action in
|
|
981
|
+
Google's third-party connections settings.
|
|
982
|
+
|
|
983
|
+
Manual mode-0600 schema-1 token documents remain supported for externally
|
|
984
|
+
managed or legacy OAuth. Their provider, subject, and sorted scopes must match
|
|
985
|
+
the Ghostget auth locator exactly:
|
|
986
|
+
|
|
987
|
+
```json
|
|
988
|
+
{
|
|
989
|
+
"schemaVersion": 1,
|
|
990
|
+
"provider": "gmail",
|
|
991
|
+
"subject": "person@example.com",
|
|
992
|
+
"scopes": [
|
|
993
|
+
"https://www.googleapis.com/auth/contacts.other.readonly",
|
|
994
|
+
"https://www.googleapis.com/auth/contacts.readonly",
|
|
995
|
+
"https://www.googleapis.com/auth/gmail.readonly"
|
|
996
|
+
],
|
|
997
|
+
"accessToken": "replace-with-the-access-token",
|
|
998
|
+
"expiresAt": "2099-01-01T00:00:00.000Z"
|
|
999
|
+
}
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
```sh
|
|
1003
|
+
ghostget auth add gmail-main --oauth-provider gmail \
|
|
1004
|
+
--token-file /absolute/private/gmail-token.json \
|
|
1005
|
+
--scopes https://www.googleapis.com/auth/contacts.other.readonly,https://www.googleapis.com/auth/contacts.readonly,https://www.googleapis.com/auth/gmail.readonly \
|
|
1006
|
+
--subject person@example.com
|
|
1007
|
+
|
|
1008
|
+
ghostget gmail contacts.list --auth gmail-main \
|
|
1009
|
+
--input '{"collection":"contacts","limit":20,"stats_scan_limit":100}' --json
|
|
1010
|
+
ghostget gmail contacts.list --auth gmail-main \
|
|
1011
|
+
--input '{"collection":"contacts","include_dates":true,"include_stats":false,"limit":20}' --json
|
|
1012
|
+
ghostget gmail contacts.list --auth gmail-main \
|
|
1013
|
+
--input '{"collection":"other-contacts","limit":100,"include_stats":false}' --json
|
|
1014
|
+
ghostget gmail contacts.list --auth gmail-main \
|
|
1015
|
+
--input '{"collection":"interactions","before":"2026-08-14T12:00:00.000Z","limit":100}' --json
|
|
1016
|
+
ghostget gmail contacts.list --auth gmail-main \
|
|
1017
|
+
--input '{"collection":"interactions","after":"2026-08-14T12:00:00.000Z","before":"2026-08-15T12:00:00.000Z","limit":100}' --json
|
|
1018
|
+
ghostget gmail messaging.list --auth gmail-main \
|
|
1019
|
+
--input '{"view":"inbox","limit":25}' --json
|
|
1020
|
+
ghostget gmail messaging.list --auth gmail-main \
|
|
1021
|
+
--input '{"view":"search","query":"from:example.com has:attachment","limit":25}' --json
|
|
1022
|
+
ghostget gmail messaging.read --auth gmail-main \
|
|
1023
|
+
--input '{"thread_id":"thread-id-from-list"}' --json
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
`contacts.list` selects saved Google Contacts, interaction-created Other
|
|
1027
|
+
contacts, or the mailbox-wide `interactions` projection. Paginate each
|
|
1028
|
+
collection independently with its returned
|
|
1029
|
+
`nextCursor`; the OAuth token must carry both People read scopes. Contact
|
|
1030
|
+
statistics are optional so bulk enumeration can avoid per-contact Gmail
|
|
1031
|
+
queries. For saved contacts, `include_dates:true` adds birthdays, contact
|
|
1032
|
+
events, and the selected name's display, given, middle, family, prefix, and
|
|
1033
|
+
suffix fields. Ghostget selects the sole People primary name when present and
|
|
1034
|
+
otherwise accepts only a single unmarked name. Other contacts and interaction
|
|
1035
|
+
rows do not accept this option. When requested, contact statistics report sent and received counts plus the maximum internal
|
|
1036
|
+
date across every bounded matched message. Count and date completeness flags
|
|
1037
|
+
remain explicit when the scan bound truncates a query or a message lacks a
|
|
1038
|
+
date. Contacts with mixed, unsupported, or absent addresses report `partial`,
|
|
1039
|
+
`unsupported`, or `unavailable` address coverage and lower-bound, incomplete
|
|
1040
|
+
statistics instead of exact zeroes for unscanned mailboxes. `limit * stats_scan_limit` cannot
|
|
1041
|
+
exceed 2,000, which bounds the per-direction Gmail scan before its paired
|
|
1042
|
+
metadata reads. Inbox and search rows include a provider-derived `threadUrl`
|
|
1043
|
+
and the exact `messaging.read` input. Reading does not mark a message seen or
|
|
1044
|
+
emit a protocol acknowledgement.
|
|
1045
|
+
|
|
1046
|
+
The `interactions` projection scans each matching Gmail message once in a
|
|
1047
|
+
fixed half-open window. Omit `after` for the initial mailbox scan; later calls
|
|
1048
|
+
can pass the prior `before` as an inclusive lower bound and fetch only newer
|
|
1049
|
+
messages. A guarded one-second search overlap is filtered by exact internal
|
|
1050
|
+
date before aggregation. The projection reads headers, labels, and internal
|
|
1051
|
+
dates but never message bodies. Per canonical external address it emits sent/received
|
|
1052
|
+
counts, first/last timestamps, 30/90/365-day counts, and direction-specific
|
|
1053
|
+
completeness. Spam, trash, drafts, and chats are outside the projection;
|
|
1054
|
+
the first page lists the account's configured Gmail send-as addresses so
|
|
1055
|
+
callers can exclude every self alias from all pages. Missing internal dates
|
|
1056
|
+
become explicit lower bounds. Opaque hashes and the
|
|
1057
|
+
unchanged window let a caller reject repeated pages without exposing raw Gmail
|
|
1058
|
+
message IDs.
|
|
1059
|
+
|
|
1060
|
+
Pass a returned Gmail `threadUrl` to `ghostget read` or `ghostget clip` with the
|
|
1061
|
+
same auth locator. Gmail clips default to private Ghostget state rather than the
|
|
1062
|
+
Git-backed knowledge base. `--output <directory>` is the explicit plaintext
|
|
1063
|
+
export boundary. Attachments are content-addressed and integrity-recorded;
|
|
1064
|
+
the implicit capture default and explicit `--media all` include every MIME
|
|
1065
|
+
attachment, while `--media none` omits their bytes. `--media images` is rejected
|
|
1066
|
+
because it would misrepresent non-image files in a Gmail thread. The private
|
|
1067
|
+
bundle keeps one physical file per digest and a `gmail.json` occurrence map for
|
|
1068
|
+
message, MIME part, provider attachment, declared filename and MIME type, and
|
|
1069
|
+
snapshot provenance. Its schema-2 provenance preserves normalized reviewed headers
|
|
1070
|
+
for each message: Subject, In-Reply-To, From, To, Cc, Bcc, Date, and Message-ID. Every
|
|
1071
|
+
physical attachment object uses the deterministic `<sha256>.bin` name and
|
|
1072
|
+
`application/octet-stream` manifest type, so conflicting or active declared
|
|
1073
|
+
types cannot create a second object or activate stored content.
|
|
1074
|
+
Text body leaves that Gmail externalizes through its attachment endpoint are
|
|
1075
|
+
resolved within the same body budget before a read or clip reports completion.
|
|
1076
|
+
Official `messaging.read` results cap full-thread decoded text at 7 MiB. Gmail's
|
|
1077
|
+
Omni projection keeps an exact UTF-8-safe 256 KiB prefix per message and sets
|
|
1078
|
+
`bodyTruncated` explicitly without adding a synthetic marker.
|
|
1079
|
+
Full-thread JSON reserves at most 32 MiB for inline attachment payloads;
|
|
1080
|
+
provider-hosted attachment endpoints remain independently bounded to 100 MiB
|
|
1081
|
+
per file, so profile, thread, and attachment responses never share one broad
|
|
1082
|
+
memory allowance.
|
|
1083
|
+
|
|
1084
|
+
### Native Article drafts
|
|
1085
|
+
|
|
1086
|
+
Ghostget separates private draft saving from publication:
|
|
1087
|
+
|
|
1088
|
+
- `articles.draft.save` is R2. It creates or replaces one private native draft
|
|
1089
|
+
and has no publish-capable branch.
|
|
1090
|
+
- `articles.publish` is R3. It is a different semantic operation with its own
|
|
1091
|
+
installed contract, preview, confirmation, and exact result binding.
|
|
1092
|
+
|
|
1093
|
+
A draft ID is not permission to publish, and a draft preview cannot be reused
|
|
1094
|
+
for publication. The separate official API and signed-in web adapters remain
|
|
1095
|
+
distinct transports and auth realms; Ghostget never switches between them to
|
|
1096
|
+
fill a capability gap.
|
|
1097
|
+
|
|
1098
|
+
The current provider state is explicit:
|
|
1099
|
+
|
|
1100
|
+
| Adapter | `articles.draft.save` | `articles.publish` |
|
|
1101
|
+
| --- | --- | --- |
|
|
1102
|
+
| `x` | Observed R2 response-bound private draft create through the documented OAuth API | Observed R3 response-bound publication through the documented OAuth API |
|
|
1103
|
+
| `x-web` | Observed R2 structured private draft with ordered inline images and exact unpublished readback | Capture-required R3 |
|
|
1104
|
+
| `linkedin-web` | Observed R2 paragraphs/headings/native-blockquote/native-link private draft with a separate banner cover, ordered inline images, alt text, captions, and exact unpublished readback | Capture-required R3 |
|
|
1105
|
+
|
|
1106
|
+
The `x-web` draft operation accepts a title, a canonical provider-neutral
|
|
1107
|
+
`ArticleDraftDocument` schemaVersion 2 string, 1–20 ordered plan-bound JPEG,
|
|
1108
|
+
PNG, or WebP files up to 5 MiB each, and an optional exact existing
|
|
1109
|
+
private `draft_id`. The document supports paragraphs, headings, blockquotes,
|
|
1110
|
+
list items, bold/italic/strikethrough ranges, and native canonical HTTPS link
|
|
1111
|
+
ranges. Image blocks support captions; native image alt text, covers, embeds,
|
|
1112
|
+
Markdown, and HTML remain unavailable. The separate official `x` OAuth operation exposes only its reviewed
|
|
1113
|
+
plain-text `body` contract plus an optional cover. Inspect the exact installed
|
|
1114
|
+
capability instead of translating inputs or switching transports implicitly.
|
|
1115
|
+
|
|
1116
|
+
Capture or read source material separately. The caller owns every editorial
|
|
1117
|
+
choice involved in translating, abridging, retitling, attributing, and linking
|
|
1118
|
+
it for the destination. Ghostget sends only the final reviewed title and
|
|
1119
|
+
document; it does not turn a source URL into provider copy. The exported
|
|
1120
|
+
`projectXStatusArticleEmbed` helper provides one deterministic destination
|
|
1121
|
+
projection for already-reviewed X status text: blockquote plus canonical X
|
|
1122
|
+
link for both `x-web` and `linkedin-web`.
|
|
1123
|
+
|
|
1124
|
+
For `linkedin-web`, pass `cover_image` outside the canonical document when
|
|
1125
|
+
creating a draft or intentionally replacing its banner. On an exact
|
|
1126
|
+
`draft_id` replacement, omit `cover_image` to preserve the independently read
|
|
1127
|
+
existing banner without another upload. Ghostget binds a supplied cover only to
|
|
1128
|
+
LinkedIn's Article banner slot. `inline_images` contains only images intended
|
|
1129
|
+
at exact body positions.
|
|
1130
|
+
|
|
1131
|
+
For X, put the exact inner canonical JSON document and local image path in a
|
|
1132
|
+
private input file:
|
|
1133
|
+
|
|
1134
|
+
```json
|
|
1135
|
+
{
|
|
1136
|
+
"title": "Reviewed title",
|
|
1137
|
+
"document": "{\"blocks\":[{\"links\":[{\"length\":6,\"offset\":9,\"url\":\"https://example.com/source\"}],\"text\":\"Read the source\",\"type\":\"paragraph\"},{\"caption\":\"Puerto Rico\",\"imageIndex\":0,\"type\":\"image\"}],\"schemaVersion\":2}",
|
|
1138
|
+
"inline_images": ["/absolute/private/puerto-rico.png"]
|
|
1139
|
+
}
|
|
1140
|
+
```
|
|
1141
|
+
|
|
1142
|
+
Then use one account-bound signed-in realm:
|
|
1143
|
+
|
|
1144
|
+
```sh
|
|
1145
|
+
ghostget adapter sync-bundled --json
|
|
1146
|
+
ghostget auth add x-main --cookie-source arc
|
|
1147
|
+
ghostget auth bind x-main --site x
|
|
1148
|
+
ghostget capabilities x-web --json
|
|
1149
|
+
|
|
1150
|
+
ghostget x-web articles.draft.save \
|
|
1151
|
+
--input @/absolute/private/article-draft-input.json \
|
|
1152
|
+
--auth x-main --preview --json
|
|
1153
|
+
|
|
1154
|
+
ghostget confirm <preview-digest> --json
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
Review the exact account, title, canonical document, ordered attachment
|
|
1158
|
+
hashes, optional draft ID, contract, and dispatch schedule. Require a successful result to identify
|
|
1159
|
+
`articles.draft.save`, report `published: false` and `mode: "draft"`, and return
|
|
1160
|
+
the private draft identity. Do not retry a partial or indeterminate save and do
|
|
1161
|
+
not call `articles.publish` as recovery. The current image-capable contracts do
|
|
1162
|
+
not reconcile automatically because an uncertain upload may have created a
|
|
1163
|
+
provider asset absent from the confirmed input; preserve the run and do not
|
|
1164
|
+
repeat uploads.
|
|
1165
|
+
|
|
1166
|
+
Read one exact saved X draft by its private numeric identity:
|
|
1167
|
+
|
|
1168
|
+
```sh
|
|
1169
|
+
ghostget x-web articles.read \
|
|
1170
|
+
--input '{"article_id":"1234567890123456789"}' \
|
|
1171
|
+
--auth x-main --json
|
|
1172
|
+
```
|
|
1173
|
+
|
|
1174
|
+
`x-web articles.read@2` is an R1 read for one current-viewer-owned private
|
|
1175
|
+
Article in the `Draft` lifecycle. Its closed output binds the exact article and
|
|
1176
|
+
owner IDs, `published: false`, one bounded single-line title, and bounded rich
|
|
1177
|
+
content. It is not an Article list and does not read published X Articles.
|
|
1178
|
+
LinkedIn Article reads remain capture-required; the observed LinkedIn draft
|
|
1179
|
+
save contract does not confer a read operation.
|
|
1180
|
+
|
|
1181
|
+
Signed-in LinkedIn now exposes the same private R2 seam through
|
|
1182
|
+
`linkedin-web articles.draft.save`. Its schemaVersion 2 document supports
|
|
1183
|
+
paragraphs, H1/H2 headings, native blockquotes, native HTTPS links, and ordered inline images with
|
|
1184
|
+
required descriptive alt text and optional captions. It creates or replaces
|
|
1185
|
+
only one bound private draft and independently verifies the exact unpublished
|
|
1186
|
+
text/image/asset result from one bounded hidden server payload in the
|
|
1187
|
+
authenticated editor HTML. Its fixed current single-upload registration, signed byte
|
|
1188
|
+
transfer, writes, and server-response read run inside a contained, account-bound Chrome
|
|
1189
|
+
session because LinkedIn rejects the same editor traffic when replayed by a
|
|
1190
|
+
standalone HTTP client. Ghostget does not type into or inspect the editor DOM,
|
|
1191
|
+
and the contained headed browser may be visible while the private save runs.
|
|
1192
|
+
Lists, styles, proprietary embeds, and publication remain unavailable.
|
|
1193
|
+
See the packaged [native article draft workflow](skills/ghostget/references/article-drafts.md)
|
|
1194
|
+
for the shared document grammar and safety sequence.
|
|
1195
|
+
|
|
1196
|
+
## Normalized omni views
|
|
1197
|
+
|
|
1198
|
+
The omni layer materializes selected exact inbox snapshots into a strict shared
|
|
1199
|
+
union of conversations, messages, and notifications. Each provider owns a pure,
|
|
1200
|
+
versioned materializer with explicit identity, pagination, completeness,
|
|
1201
|
+
tombstone, and deletion semantics. Unsupported providers say why. A shape
|
|
1202
|
+
change fails at that provider-owned boundary, retains the last good normalized
|
|
1203
|
+
entities, and records the exact failed revision instead of guessing.
|
|
1204
|
+
|
|
1205
|
+
Omni v1 has no provider-authored write-invalidation tags. Auth-incarnation,
|
|
1206
|
+
materializer, and plugin implementation identity changes strand the prior
|
|
1207
|
+
normalized coordinates. Freshness advances only when the exact query is
|
|
1208
|
+
explicitly revalidated. If a newer exact snapshot drifts, Ghostget keeps the last
|
|
1209
|
+
good derivative and reports `retained-after-drift`. The provider-local
|
|
1210
|
+
diagnostic remains inside encrypted normalized state. Public reasons are
|
|
1211
|
+
categorical and do not echo foreign values or unreviewed property names.
|
|
1212
|
+
|
|
1213
|
+
```sh
|
|
1214
|
+
ghostget omni read --input '{
|
|
1215
|
+
"schemaVersion": 1,
|
|
1216
|
+
"sources": [
|
|
1217
|
+
{"adapterId":"reddit-web","operationId":"messaging.list","authId":"reddit-main","input":{"folder":"inbox","limit":25}},
|
|
1218
|
+
{"adapterId":"whatsapp-web","operationId":"messaging.list","authId":"whatsapp-main","input":{"folder":"all","limit":100}}
|
|
1219
|
+
],
|
|
1220
|
+
"filter": {"kinds":["conversation","message","notification"]},
|
|
1221
|
+
"page": {"limit":100}
|
|
1222
|
+
}' --cache-only --json
|
|
1223
|
+
```
|
|
1224
|
+
|
|
1225
|
+
`--cache-only` reads encrypted normalized state without a browser or provider
|
|
1226
|
+
round trip. `--from-exact-cache` rebuilds derivatives from encrypted exact
|
|
1227
|
+
snapshots. The default mode revalidates supported sources independently and
|
|
1228
|
+
then returns one locally paged view. Provider cursors remain private; public
|
|
1229
|
+
view cursors are authenticated and bound to the request, account lifetimes,
|
|
1230
|
+
materializer closure, and view revision.
|
|
1231
|
+
|
|
1232
|
+
Each source row exposes a keyed `normalizationDataRevision` for causal cache
|
|
1233
|
+
comparison without revealing normalized bytes. During SWR, `current` may be an
|
|
1234
|
+
`omni-merged` result: it adopts a proven newer cached view while retaining every
|
|
1235
|
+
unresolved live source status. A concurrent advance for one provider, account,
|
|
1236
|
+
or continuation therefore cannot erase another live failure; the independent
|
|
1237
|
+
`live` and `cachedAfter` observations remain available as well.
|
|
1238
|
+
|
|
1239
|
+
```ts
|
|
1240
|
+
import { staleWhileRevalidateOmniView } from "@hraness/ghostget/omni"
|
|
1241
|
+
|
|
1242
|
+
const messages = staleWhileRevalidateOmniView({
|
|
1243
|
+
schemaVersion: 1,
|
|
1244
|
+
sources: [{
|
|
1245
|
+
adapterId: "reddit-web",
|
|
1246
|
+
operationId: "messaging.list",
|
|
1247
|
+
authId: "reddit-main",
|
|
1248
|
+
input: { folder: "inbox", limit: 25 },
|
|
1249
|
+
}],
|
|
1250
|
+
})
|
|
1251
|
+
|
|
1252
|
+
render(messages.cached?.view)
|
|
1253
|
+
render((await messages.revalidation).current.view)
|
|
1254
|
+
```
|
|
1255
|
+
|
|
1256
|
+
## Create a portable plugin
|
|
1257
|
+
|
|
1258
|
+
An agent can create a private, network-inert starting point without editing
|
|
1259
|
+
Ghostget:
|
|
1260
|
+
|
|
1261
|
+
```sh
|
|
1262
|
+
ghostget plugin init example-web \
|
|
1263
|
+
--display-name "Example" \
|
|
1264
|
+
--surface example \
|
|
1265
|
+
--origin https://www.example.com \
|
|
1266
|
+
--operation feeds.read \
|
|
1267
|
+
--output /absolute/private/example-web
|
|
1268
|
+
|
|
1269
|
+
ghostget plugin check /absolute/private/example-web --json
|
|
1270
|
+
ghostget plugin test /absolute/private/example-web --trust-code --json
|
|
1271
|
+
ghostget plugin pack /absolute/private/example-web \
|
|
1272
|
+
--output /absolute/private/example-web.wrenchplugin --json
|
|
1273
|
+
ghostget plugin install /absolute/private/example-web.wrenchplugin \
|
|
1274
|
+
--trust-code --json
|
|
1275
|
+
```
|
|
1276
|
+
|
|
1277
|
+
`init` writes a strict `ghostget-plugin.json`, a self-contained runtime, inert
|
|
1278
|
+
operation metadata, secret-free fixtures, and package-local agent guidance.
|
|
1279
|
+
`check` is static and does not execute plugin code. `test --trust-code` binds
|
|
1280
|
+
the decision to the verified plugin identity before running its declared
|
|
1281
|
+
secret-free fixtures. `pack` creates a reproducible content-addressed package.
|
|
1282
|
+
`install --trust-code` is the separate decision to let that exact package run.
|
|
1283
|
+
|
|
1284
|
+
Portable code receives only declared, bounded host capabilities. Network
|
|
1285
|
+
requests are pinned to declared HTTPS origins; credentials are opaque handles
|
|
1286
|
+
usable only at declared sinks; files and state are namespaced handles; and
|
|
1287
|
+
mutations must use the kernel's begin, request, and verify sequence. The host
|
|
1288
|
+
does not expose a shell, package manager, ambient environment, unrestricted
|
|
1289
|
+
filesystem, redirect, retry, or arbitrary request primitive.
|
|
1290
|
+
|
|
1291
|
+
Read [the plugin guide](docs/plugins.md) before replacing an inert reservation
|
|
1292
|
+
with an observed contract. The repository's [Ghostget Agent Skill](skills/ghostget/SKILL.md)
|
|
1293
|
+
gives coding agents the same workflow and safety boundary. Its bundled
|
|
1294
|
+
[social cross-posting guidance](skills/ghostget/references/cross-posting.md)
|
|
1295
|
+
orchestrates exact, previewed text, image, and video posts across supported
|
|
1296
|
+
platforms while preserving per-provider attachment limits and at-most-once
|
|
1297
|
+
dispatch evidence. Packages built from this source carry the same consolidated
|
|
1298
|
+
skill as the skills CLI.
|
|
1299
|
+
|
|
1300
|
+
## Risk and confirmation
|
|
1301
|
+
|
|
1302
|
+
- R1 is a reviewed read with no intended remote mutation.
|
|
1303
|
+
- R2 is one bounded, normally reversible change.
|
|
1304
|
+
- R3 is an externally visible or consequential change, including an exact
|
|
1305
|
+
authored-item deletion only where a provider-specific contract binds the
|
|
1306
|
+
target, current account, revision, mutation, and independent absence readback.
|
|
1307
|
+
- R4 is blocked.
|
|
1308
|
+
|
|
1309
|
+
R2 and R3 commands create an exact, short-lived preview. Review its adapter,
|
|
1310
|
+
transport, account realm, input, attachment hashes, side effect, contract hash,
|
|
1311
|
+
and complete dispatch schedule, then pass its digest to `ghostget confirm`.
|
|
1312
|
+
After a partial or indeterminate dispatch, Ghostget does not retry or switch
|
|
1313
|
+
transport. The run remains unsettled until exact external evidence supports a
|
|
1314
|
+
separate reconciliation.
|
|
1315
|
+
|
|
1316
|
+
An operator who explicitly accepts the risk of a duplicate may create one new
|
|
1317
|
+
intent from one terminal indeterminate `posts.publish` run:
|
|
1318
|
+
|
|
1319
|
+
```sh
|
|
1320
|
+
ghostget invoke <adapter> posts.publish --input @post.json --auth <id> \
|
|
1321
|
+
--preview --duplicate-risk-of <source-run-id>
|
|
1322
|
+
ghostget confirm <new-plan-digest>
|
|
1323
|
+
```
|
|
1324
|
+
|
|
1325
|
+
This v1 path is limited to one started dispatch over the same reviewed R3 web
|
|
1326
|
+
session contract. Ghostget revalidates the exact adapter, account realm,
|
|
1327
|
+
operation, normalized input (including attachment hashes), contract, source
|
|
1328
|
+
receipt, journal, ledger, and recovery capsule at preview and confirmation.
|
|
1329
|
+
The source run remains indeterminate and its evidence is never cleared or
|
|
1330
|
+
rewritten. Re-previewing the unchanged source produces the same successor
|
|
1331
|
+
intent; that successor has its own permanent at-most-once ledger. If the
|
|
1332
|
+
process exits after electing the successor but before starting its dispatch,
|
|
1333
|
+
the election remains fail-closed and must be inspected rather than retried.
|
|
1334
|
+
|
|
1335
|
+
New previews use one environment-neutral durable contract identity. Readers
|
|
1336
|
+
also accept the exact predecessor identities produced by the standard `test`,
|
|
1337
|
+
`production`, and `development` modes. They do not accept a wildcard identity
|
|
1338
|
+
for custom `NODE_ENV` values. Ghostget retains unsupported unsettled evidence and
|
|
1339
|
+
directs the operator to `ghostget doctor`, the exact predecessor build, or manual
|
|
1340
|
+
evidence review. Runtime loading still verifies the current exact source,
|
|
1341
|
+
dependency, and execution closure separately.
|
|
1342
|
+
|
|
1343
|
+
## Develop
|
|
1344
|
+
|
|
1345
|
+
```sh
|
|
1346
|
+
git clone https://github.com/hraness/ghostget.git
|
|
1347
|
+
cd ghostget
|
|
1348
|
+
bun install --frozen-lockfile
|
|
1349
|
+
bun run check
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
The full gate type-checks, tests, builds, runs the secret-free CLI and portable
|
|
1353
|
+
plugin lifecycle smoke, then installs and imports the packed package in a clean
|
|
1354
|
+
consumer. See [CONTRIBUTING.md](CONTRIBUTING.md) for change boundaries and
|
|
1355
|
+
[local development](docs/local-development.md) for isolated parallel worktrees.
|
|
1356
|
+
|
|
1357
|
+
## License
|
|
1358
|
+
|
|
1359
|
+
MIT
|