@hraness/ghostget 0.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +458 -0
- package/LICENSE +21 -0
- package/README.md +1359 -0
- package/bunfig.toml +2 -0
- package/dist/apple-photos-client.js +704 -0
- package/dist/beeper-client.js +4602 -0
- package/dist/client.js +1605 -0
- package/dist/index-26yq8q16.js +31 -0
- package/dist/index-9t13b4k7.js +508 -0
- package/dist/index-axwm9b3v.js +5 -0
- package/dist/index-gwk7rbyj.js +30 -0
- package/dist/index.js +701 -0
- package/dist/messaging.js +1130 -0
- package/dist/omni-client.js +1406 -0
- package/dist/whatsapp-client.js +355 -0
- package/docs/imessage-direct-provider.md +106 -0
- package/docs/rental-listings.md +94 -0
- package/package.json +600 -0
- package/skills/ghostget/SKILL.md +251 -0
- package/skills/ghostget/agents/openai.yaml +4 -0
- package/skills/ghostget/references/adapter-contract.md +181 -0
- package/skills/ghostget/references/apple-photos.md +115 -0
- package/skills/ghostget/references/article-drafts.md +235 -0
- package/skills/ghostget/references/code-owned-provider-scaffold.md +134 -0
- package/skills/ghostget/references/cross-posting-video.md +139 -0
- package/skills/ghostget/references/cross-posting.md +82 -0
- package/skills/ghostget/references/derivation.md +390 -0
- package/skills/ghostget/references/hraness-social-profile-stats.json +269 -0
- package/skills/ghostget/references/install.md +66 -0
- package/skills/ghostget/references/linkedin-adapter.md +442 -0
- package/skills/ghostget/references/messaging.md +168 -0
- package/skills/ghostget/references/meta-comet-contract.md +153 -0
- package/skills/ghostget/references/platform-patterns.md +122 -0
- package/skills/ghostget/references/provider-plugins.md +496 -0
- package/skills/ghostget/references/reddit-flair.md +78 -0
- package/skills/ghostget/references/safety-and-state.md +214 -0
- package/skills/ghostget/references/settlement-and-duplicate-cleanup.md +79 -0
- package/skills/ghostget/references/social-platform-routing.md +66 -0
- package/skills/ghostget/references/social-profile-stats.md +195 -0
- package/skills/ghostget/references/social-video-platform-routing.md +89 -0
- package/skills/ghostget/references/whatsapp-adapter.md +101 -0
- package/skills/ghostget/references/x-adapter.md +332 -0
- package/skills/ghostget/references/x-ai-disclosure.md +71 -0
- package/src/apple-photos-cli.ts +639 -0
- package/src/apple-photos-client-types.ts +168 -0
- package/src/apple-photos-client.ts +201 -0
- package/src/apple-photos-contact-evidence.ts +838 -0
- package/src/apple-photos-local-source.ts +1131 -0
- package/src/args.ts +2053 -0
- package/src/article-draft-document.ts +417 -0
- package/src/article-draft-embeds.ts +123 -0
- package/src/article-draft-images.ts +214 -0
- package/src/assets/adapter-template/wrench-adapter.json +40 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.json +1500 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v1.0.0.json +129 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v1.1.0.json +207 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.0.0.json +1463 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.1.0.json +1463 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.2.0.json +1475 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.3.0.json +1488 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.0.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.1.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.2.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.3.0.json +615 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.4.0.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.5.0.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.6.0.json +684 -0
- package/src/assets/adapters/clasificados/wrench-web-adapter.json +54 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.json +750 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.0.0.json +706 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.1.0.json +703 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.2.0.json +750 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.json +682 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.0.0.json +583 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.1.0.json +682 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.json +437 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.0.0.json +383 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.0.json +437 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.1.json +437 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.json +880 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.0.0.json +740 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.1.0.json +880 -0
- package/src/assets/adapters/github/wrench-web-adapter.json +69 -0
- package/src/assets/adapters/github/wrench-web-adapter.v1.0.0.json +42 -0
- package/src/assets/adapters/github/wrench-web-adapter.v1.1.0.json +69 -0
- package/src/assets/adapters/gmail/wrench-adapter.json +161 -0
- package/src/assets/adapters/gmail/wrench-adapter.v1.2.0.json +157 -0
- package/src/assets/adapters/hacker-news/wrench-web-adapter.json +315 -0
- package/src/assets/adapters/hacker-news/wrench-web-adapter.v1.0.0.json +315 -0
- package/src/assets/adapters/imessage/wrench-web-adapter.json +236 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.json +719 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.0.0.json +627 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.1.0.json +609 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.2.0.json +640 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.3.0.json +671 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.4.0.json +710 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.5.0.json +710 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.6.0.json +719 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.7.0.json +719 -0
- package/src/assets/adapters/linkedin/wrench-adapter.json +442 -0
- package/src/assets/adapters/linkedin/wrench-adapter.v0.4.0.json +124 -0
- package/src/assets/adapters/linkedin/wrench-adapter.v1.0.0.json +411 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.json +914 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.0.0.json +604 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.1.0.json +604 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.10.0.json +809 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.11.0.json +809 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.12.0.json +820 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.13.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.14.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.15.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.16.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.17.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.18.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.19.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.2.0.json +731 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.3.0.json +768 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.4.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.5.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.6.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.7.0.json +825 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.8.0.json +792 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.9.0.json +809 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.json +855 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.0.0.json +577 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.1.0.json +604 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.10.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.11.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.12.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.2.0.json +653 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.3.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.4.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.5.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.6.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.7.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.8.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.9.0.json +713 -0
- package/src/assets/adapters/substack/wrench-web-adapter.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.0.0.json +817 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.1.0.json +812 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.2.0.json +812 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.3.0.json +902 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.4.0.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.5.0.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.6.0.json +936 -0
- package/src/assets/adapters/threads/wrench-web-adapter.json +692 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.0.0.json +629 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.1.0.json +623 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.2.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.3.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.4.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.5.0.json +699 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.6.0.json +693 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.7.0.json +692 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.json +762 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.0.0.json +681 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.1.0.json +708 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.2.0.json +762 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.3.0.json +762 -0
- package/src/assets/adapters/twitch/wrench-web-adapter.json +42 -0
- package/src/assets/adapters/twitch/wrench-web-adapter.v1.0.0.json +42 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.json +400 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.0.0.json +355 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.1.0.json +386 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.2.0.json +386 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.3.0.json +400 -0
- package/src/assets/adapters/x/wrench-adapter.json +749 -0
- package/src/assets/adapters/x/wrench-adapter.v1.0.0.json +700 -0
- package/src/assets/adapters/x/wrench-adapter.v1.1.0.json +707 -0
- package/src/assets/adapters/x/wrench-web-adapter.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.1.0.json +579 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.10.0.json +685 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.11.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.12.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.13.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.2.0.json +578 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.3.0.json +610 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.4.0.json +617 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.5.0.json +634 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.6.0.json +632 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.7.0.json +649 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.8.0.json +649 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.9.0.json +683 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.json +561 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.0.0.json +465 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.1.0.json +519 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.2.0.json +561 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.3.0.json +561 -0
- package/src/assets/code-owned-provider-template/plugin.test.ts.template +54 -0
- package/src/assets/code-owned-provider-template/plugin.ts.template +102 -0
- package/src/assets/code-owned-provider-template/runtime.internal.test.ts.template +44 -0
- package/src/assets/code-owned-provider-template/runtime.ts.template +59 -0
- package/src/assets/code-owned-provider-template/wrench-adapter.json.template +33 -0
- package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/PROVENANCE.md +14 -0
- package/src/assets.d.ts +4 -0
- package/src/auth.ts +1157 -0
- package/src/beeper-client-types.ts +169 -0
- package/src/beeper-client.ts +250 -0
- package/src/beeper-contact-interactions-cli.ts +180 -0
- package/src/beeper-contact-interactions.ts +1612 -0
- package/src/beeper-message-bundle-v1.ts +257 -0
- package/src/beeper-message-like-me-cli.ts +84 -0
- package/src/beeper-message-like-me-export.ts +2274 -0
- package/src/beeper-message-like-me-recovery.ts +931 -0
- package/src/beeper-message-like-me-source.ts +4034 -0
- package/src/browser-admission.ts +928 -0
- package/src/browser-snapshots.ts +260 -0
- package/src/browser.ts +3873 -0
- package/src/canonical-json.ts +60 -0
- package/src/catalog-cli.ts +638 -0
- package/src/cli.ts +208 -0
- package/src/client-types.ts +301 -0
- package/src/client.ts +2726 -0
- package/src/confirmed-write-failure.ts +44 -0
- package/src/confirmed-write-model.ts +72 -0
- package/src/confirmed-write-platform.ts +1055 -0
- package/src/confirmed-write-program.ts +224 -0
- package/src/confirmed-write-runtime.ts +16 -0
- package/src/cursor-token.ts +228 -0
- package/src/derivation-dnr-readiness.ts +367 -0
- package/src/derivation-file-chooser.ts +384 -0
- package/src/derivation-network-boundary.ts +835 -0
- package/src/derivation-network-guard.ts +782 -0
- package/src/derivation-network-proxy-helper.ts +440 -0
- package/src/derivation-network-proxy.ts +385 -0
- package/src/derive-command-helper.ts +693 -0
- package/src/derive-fixtures.ts +343 -0
- package/src/derive-review.ts +980 -0
- package/src/derive.ts +4754 -0
- package/src/fixtures/beeper-message-like-me-v1/accounts.ndjson +2 -0
- package/src/fixtures/beeper-message-like-me-v1/conversations.ndjson +1 -0
- package/src/fixtures/beeper-message-like-me-v1/manifest.json +1 -0
- package/src/fixtures/beeper-message-like-me-v1/messages.ndjson +2 -0
- package/src/fixtures/beeper-message-like-me-v1/participants.ndjson +3 -0
- package/src/fixtures/beeper-message-like-me-v1/reactions.ndjson +1 -0
- package/src/fixtures/beeper-message-like-me-v1/tombstones.ndjson +1 -0
- package/src/ghostget.ts +3672 -0
- package/src/gmail-capture.ts +1244 -0
- package/src/har-internal.ts +1242 -0
- package/src/har.ts +743 -0
- package/src/index.ts +218 -0
- package/src/invocation-read-platform.ts +55 -0
- package/src/invocation-read-program.ts +93 -0
- package/src/invocation-read-runtime.ts +11 -0
- package/src/linked-device-lifecycle-admission.ts +450 -0
- package/src/linked-device-lifecycle-journal.ts +1560 -0
- package/src/linked-device-lifecycle-runtime.ts +1602 -0
- package/src/local-cli-admission.ts +135 -0
- package/src/local-cli-contracts.ts +271 -0
- package/src/local-cli-execution.ts +98 -0
- package/src/local-cli-surface-contract.ts +1510 -0
- package/src/local-cli-tool-identity.ts +336 -0
- package/src/media/archive.ts +2325 -0
- package/src/media/args.ts +303 -0
- package/src/media/cli.ts +287 -0
- package/src/media/doctor.ts +475 -0
- package/src/media/ffmpeg.ts +1330 -0
- package/src/media/http-capture.ts +711 -0
- package/src/media/http-probe.ts +593 -0
- package/src/media/http.ts +387 -0
- package/src/media/index.ts +10 -0
- package/src/media/local-transcription.ts +613 -0
- package/src/media/lock.ts +321 -0
- package/src/media/manifest.ts +1712 -0
- package/src/media/metadata.ts +906 -0
- package/src/media/process.ts +723 -0
- package/src/media/revision.ts +166 -0
- package/src/media/runtime-closure.ts +1063 -0
- package/src/media/source-router.ts +187 -0
- package/src/media/transcriber-config.ts +1122 -0
- package/src/media/transcript-persistence-model.ts +15 -0
- package/src/media/transcript-persistence-platform.ts +89 -0
- package/src/media/transcript-persistence-program.ts +39 -0
- package/src/media/transcript-persistence-runtime.ts +24 -0
- package/src/media/transcript.ts +616 -0
- package/src/media/utf8-order.ts +30 -0
- package/src/media/whisper-cpp.ts +464 -0
- package/src/media/whisper-language.ts +58 -0
- package/src/media/yt-dlp.ts +439 -0
- package/src/message-like-me-agentic-messaging.ts +882 -0
- package/src/messaging-action-store.ts +793 -0
- package/src/messaging-runtime.ts +2136 -0
- package/src/messaging-store.ts +539 -0
- package/src/messaging-types.ts +1663 -0
- package/src/messaging.ts +334 -0
- package/src/model.ts +2671 -0
- package/src/oauth-google.ts +888 -0
- package/src/omni-client-types.ts +306 -0
- package/src/omni-client.ts +2225 -0
- package/src/omni-limits.ts +34 -0
- package/src/omni-model.ts +1614 -0
- package/src/omni-request.ts +379 -0
- package/src/omni-runtime.ts +1632 -0
- package/src/operation-deadline.ts +189 -0
- package/src/path-helper.ts +1656 -0
- package/src/pinned-https.ts +194 -0
- package/src/plan-assets.ts +832 -0
- package/src/platform-catalog.ts +1832 -0
- package/src/plugins/beeper-linked-device/plugin.ts +310 -0
- package/src/plugins/bluesky-web/plugin.ts +178 -0
- package/src/plugins/clasificados-web/plugin.ts +66 -0
- package/src/plugins/github-web/plugin.ts +80 -0
- package/src/plugins/gmail-official/plugin.ts +110 -0
- package/src/plugins/hacker-news-web/plugin.ts +52 -0
- package/src/plugins/imessage-direct/plugin.ts +195 -0
- package/src/plugins/imessage-direct/vendor/0001-fix-keep-AppleScript-send-payloads-out-of-child-argv.patch +1430 -0
- package/src/plugins/imessage-direct/vendor/0002-feat-rpc-add-exact-chat-lookup.patch +225 -0
- package/src/plugins/imessage-direct/vendor/provenance.json +80 -0
- package/src/plugins/linkedin-official/plugin.ts +84 -0
- package/src/plugins/linkedin-web/plugin.ts +412 -0
- package/src/plugins/meta-web/plugin.ts +317 -0
- package/src/plugins/reddit-web/flair.ts +492 -0
- package/src/plugins/reddit-web/plugin.ts +477 -0
- package/src/plugins/substack-web/plugin.ts +124 -0
- package/src/plugins/tiktok-web/plugin.ts +67 -0
- package/src/plugins/twitch-web/plugin.ts +54 -0
- package/src/plugins/whatsapp-linked-device/plugin.ts +105 -0
- package/src/plugins/x-official/plugin.ts +64 -0
- package/src/plugins/x-web/plugin.ts +488 -0
- package/src/plugins/youtube-web/plugin.ts +101 -0
- package/src/portable-run-recovery.ts +466 -0
- package/src/process-identity.ts +501 -0
- package/src/profile-clone-helper.ts +104 -0
- package/src/provider-catalog-views.ts +51 -0
- package/src/provider-context.ts +69 -0
- package/src/provider-contract-definitions-x.ts +45 -0
- package/src/provider-contract-definitions.ts +948 -0
- package/src/provider-contract-input-gmail.ts +176 -0
- package/src/provider-contract-input-linkedin.ts +77 -0
- package/src/provider-contract-input-x.ts +304 -0
- package/src/provider-contract-planning.ts +23 -0
- package/src/provider-contract-semantic-identity.ts +54 -0
- package/src/provider-contracts.ts +251 -0
- package/src/provider-http.ts +723 -0
- package/src/provider-plugin-auth.ts +36 -0
- package/src/provider-plugin-builtins.ts +229 -0
- package/src/provider-plugin-cleanup-barrier.ts +229 -0
- package/src/provider-plugin-cleanup-execution.ts +79 -0
- package/src/provider-plugin-cleanup-resource.ts +470 -0
- package/src/provider-plugin-contract-identity.ts +707 -0
- package/src/provider-plugin-host.ts +1972 -0
- package/src/provider-plugin-identifiers.ts +60 -0
- package/src/provider-plugin-invocation-lease.ts +889 -0
- package/src/provider-plugin-lifecycle-kernel.ts +566 -0
- package/src/provider-plugin-lifecycle.ts +1455 -0
- package/src/provider-plugin-module-analysis.ts +336 -0
- package/src/provider-plugin-package.ts +2530 -0
- package/src/provider-plugin-portable-authority.ts +106 -0
- package/src/provider-plugin-portable-catalog.ts +94 -0
- package/src/provider-plugin-portable-identity.ts +315 -0
- package/src/provider-plugin-portable-runtime.ts +2057 -0
- package/src/provider-plugin-protocol.ts +1625 -0
- package/src/provider-plugin-registry.ts +3342 -0
- package/src/provider-plugin-store.ts +2155 -0
- package/src/provider-plugin-test-fixture.ts +5 -0
- package/src/provider-plugin.ts +4907 -0
- package/src/provider-plugins.generated.ts +43 -0
- package/src/provider-plugins.ts +8 -0
- package/src/provider-subject.ts +41 -0
- package/src/provider.ts +315 -0
- package/src/providers/beeper-local-runtime.ts +5426 -0
- package/src/providers/beeper-local.ts +2739 -0
- package/src/providers/beeper-messaging.ts +462 -0
- package/src/providers/beeper-omni.ts +1159 -0
- package/src/providers/bluesky-web-runtime.ts +2895 -0
- package/src/providers/bluesky-web.ts +1614 -0
- package/src/providers/clasificados-web-runtime.ts +255 -0
- package/src/providers/clasificados-web.ts +503 -0
- package/src/providers/contact-projection.ts +301 -0
- package/src/providers/github-read-model.ts +94 -0
- package/src/providers/github-read-platform.ts +165 -0
- package/src/providers/github-read-program.ts +97 -0
- package/src/providers/github-web-runtime.ts +320 -0
- package/src/providers/github-web.ts +403 -0
- package/src/providers/gmail-api.ts +2834 -0
- package/src/providers/gmail-omni.ts +830 -0
- package/src/providers/gmail.ts +1130 -0
- package/src/providers/hacker-news-web-runtime.ts +220 -0
- package/src/providers/hacker-news-web.ts +809 -0
- package/src/providers/imessage-direct-install.ts +354 -0
- package/src/providers/imessage-direct-messaging.ts +532 -0
- package/src/providers/imessage-direct-omni.ts +400 -0
- package/src/providers/imessage-direct-runtime.ts +1498 -0
- package/src/providers/imessage-direct.ts +351 -0
- package/src/providers/instagram-video-foundations.ts +913 -0
- package/src/providers/instagram-web-profile-browser.ts +600 -0
- package/src/providers/iso-bmff.ts +338 -0
- package/src/providers/linkedin-company-platform.ts +42 -0
- package/src/providers/linkedin-company-program.ts +88 -0
- package/src/providers/linkedin-contact-failure.ts +84 -0
- package/src/providers/linkedin-contact-platform.ts +45 -0
- package/src/providers/linkedin-contact-program.ts +91 -0
- package/src/providers/linkedin-profile-activity-failure.ts +76 -0
- package/src/providers/linkedin-profile-activity-platform.ts +41 -0
- package/src/providers/linkedin-profile-activity-program.ts +67 -0
- package/src/providers/linkedin-read-failure.ts +52 -0
- package/src/providers/linkedin-self-platform.ts +53 -0
- package/src/providers/linkedin-self-program.ts +122 -0
- package/src/providers/linkedin-web-article-browser.ts +794 -0
- package/src/providers/linkedin-web-bootstrap.ts +146 -0
- package/src/providers/linkedin-web-contact.ts +830 -0
- package/src/providers/linkedin-web-feed-browser.ts +832 -0
- package/src/providers/linkedin-web-feed.ts +864 -0
- package/src/providers/linkedin-web-post-browser.ts +653 -0
- package/src/providers/linkedin-web-profile-browser.ts +683 -0
- package/src/providers/linkedin-web-runtime.ts +2388 -0
- package/src/providers/linkedin-web.ts +3305 -0
- package/src/providers/linkedin.ts +1885 -0
- package/src/providers/meta-bootstrap.ts +1043 -0
- package/src/providers/meta-facebook-group.ts +690 -0
- package/src/providers/meta-marketplace-relay.ts +747 -0
- package/src/providers/meta-omni.ts +373 -0
- package/src/providers/meta-relay-bundle.ts +1049 -0
- package/src/providers/meta-web-descriptors.ts +1740 -0
- package/src/providers/meta-web-runtime.ts +3388 -0
- package/src/providers/meta-web.ts +2931 -0
- package/src/providers/read-failure.ts +139 -0
- package/src/providers/reddit-omni.ts +462 -0
- package/src/providers/reddit-web-runtime.ts +1929 -0
- package/src/providers/reddit-web.ts +1611 -0
- package/src/providers/rental-listings.ts +460 -0
- package/src/providers/substack-omni.ts +406 -0
- package/src/providers/substack-video-mp4.ts +33 -0
- package/src/providers/substack-web-runtime.ts +2812 -0
- package/src/providers/substack-web.ts +1502 -0
- package/src/providers/tiktok-video-mp4.ts +33 -0
- package/src/providers/tiktok-web-runtime.ts +770 -0
- package/src/providers/tiktok-web.ts +2786 -0
- package/src/providers/twitch-web-runtime.ts +226 -0
- package/src/providers/twitch-web.ts +274 -0
- package/src/providers/whatsapp-account-identity.ts +55 -0
- package/src/providers/whatsapp-contact-projection-helper.ts +874 -0
- package/src/providers/whatsapp-contact-projection-protocol.ts +403 -0
- package/src/providers/whatsapp-interaction-projection-helper.ts +1452 -0
- package/src/providers/whatsapp-interaction-projection-protocol.ts +462 -0
- package/src/providers/whatsapp-message-export-projection-protocol.ts +532 -0
- package/src/providers/whatsapp-messaging.ts +132 -0
- package/src/providers/whatsapp-omni.ts +586 -0
- package/src/providers/whatsapp-web-runtime.ts +3108 -0
- package/src/providers/whatsapp-web.ts +1037 -0
- package/src/providers/x-image-provenance.ts +523 -0
- package/src/providers/x-made-with-ai.ts +106 -0
- package/src/providers/x-transaction-id.ts +413 -0
- package/src/providers/x-web-runtime.ts +3485 -0
- package/src/providers/x-web.ts +2332 -0
- package/src/providers/x.ts +1889 -0
- package/src/providers/youtube-web-runtime.ts +1400 -0
- package/src/providers/youtube-web.ts +1221 -0
- package/src/read-admission-runtime.ts +91 -0
- package/src/read-client.ts +521 -0
- package/src/read-effect-platform.ts +7 -0
- package/src/read-effect-runtime.ts +14 -0
- package/src/read-effect.ts +52 -0
- package/src/read-projection-admission.ts +866 -0
- package/src/read-projections.ts +3151 -0
- package/src/recovery.ts +1492 -0
- package/src/reviewed-template.ts +146 -0
- package/src/run-journal.ts +1272 -0
- package/src/runtime.ts +5116 -0
- package/src/scripts/generate-provider-plugin-catalog.ts +121 -0
- package/src/scripts/install-whatsapp-protocol.sh +232 -0
- package/src/scripts/recover-install-stages.ts +1491 -0
- package/src/scripts/resolve-state-home.ts +30 -0
- package/src/scripts/scaffold-web-provider.ts +973 -0
- package/src/scripts/sync-bundled-adapters.ts +695 -0
- package/src/session-secrets.ts +1183 -0
- package/src/state-helper.bunfig.toml +2 -0
- package/src/state-helper.ts +2914 -0
- package/src/storage.ts +3842 -0
- package/src/transport-policy.ts +2 -0
- package/src/usage.ts +182 -0
- package/src/version.ts +2 -0
- package/src/web-session-authentication-policy.ts +136 -0
- package/src/web-session-cleanup-admission.ts +2344 -0
- package/src/web-session-cleanup-join.ts +99 -0
- package/src/web-session-client.ts +996 -0
- package/src/web-session-contract-definitions.ts +820 -0
- package/src/web-session-contract-planning.ts +42 -0
- package/src/web-session-contracts.ts +228 -0
- package/src/web-session-cookies.ts +500 -0
- package/src/web-session-execution.ts +385 -0
- package/src/web-session-http.ts +373 -0
- package/src/web-session-read-errors.ts +19 -0
- package/src/web-session-read-runtime.ts +124 -0
- package/src/web-session-recovery.ts +818 -0
- package/src/web-session-sites.ts +58 -0
- package/src/web-session-template.ts +994 -0
- package/src/web-session.ts +93 -0
- package/src/whatsapp-client-binding.ts +21 -0
- package/src/whatsapp-client-types.ts +75 -0
- package/src/whatsapp-client.ts +395 -0
- package/src/whatsapp-export-coordinate.ts +17 -0
- package/src/whatsapp-message-bundle-v2.ts +195 -0
- package/src/whatsapp-message-like-me-cli.ts +195 -0
- package/src/whatsapp-message-like-me-export.ts +1072 -0
- package/src/whatsapp-message-like-me-source.ts +1012 -0
- package/tsconfig.json +67 -0
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
import { deflateSync, inflateSync } from "node:zlib";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pixel-only X upload bytes. Re-encode PNG/JPEG so C2PA, XMP, Exif, IPTC, and
|
|
5
|
+
* Adobe provenance cannot ride along in ancillary chunks or APP segments.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const IMAGE_PROVENANCE_MARKER = /c2pa|cabx|trainedalgorithmic|digitalsourcetype/iu;
|
|
9
|
+
|
|
10
|
+
const PNG_SIGNATURE = Uint8Array.of(0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a);
|
|
11
|
+
const MAX_PNG_DIMENSION = 16_384;
|
|
12
|
+
const MAX_PNG_PIXELS = 16_384 * 16_384;
|
|
13
|
+
|
|
14
|
+
const CRC_TABLE = (() => {
|
|
15
|
+
const table = new Uint32Array(256);
|
|
16
|
+
for (let index = 0; index < 256; index += 1) {
|
|
17
|
+
let value = index;
|
|
18
|
+
for (let bit = 0; bit < 8; bit += 1) {
|
|
19
|
+
value = (value & 1) === 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;
|
|
20
|
+
}
|
|
21
|
+
table[index] = value >>> 0;
|
|
22
|
+
}
|
|
23
|
+
return table;
|
|
24
|
+
})();
|
|
25
|
+
|
|
26
|
+
function pngCrc(bytes: Uint8Array): number {
|
|
27
|
+
let crc = 0xffffffff;
|
|
28
|
+
for (const byte of bytes) {
|
|
29
|
+
crc = CRC_TABLE[(crc ^ byte) & 0xff]! ^ (crc >>> 8);
|
|
30
|
+
}
|
|
31
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function readUint32(bytes: Uint8Array, offset: number): number {
|
|
35
|
+
return (
|
|
36
|
+
((bytes[offset] ?? 0) << 24)
|
|
37
|
+
| ((bytes[offset + 1] ?? 0) << 16)
|
|
38
|
+
| ((bytes[offset + 2] ?? 0) << 8)
|
|
39
|
+
| (bytes[offset + 3] ?? 0)
|
|
40
|
+
) >>> 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function writeUint32(bytes: Uint8Array, offset: number, value: number): void {
|
|
44
|
+
bytes[offset] = (value >>> 24) & 0xff;
|
|
45
|
+
bytes[offset + 1] = (value >>> 16) & 0xff;
|
|
46
|
+
bytes[offset + 2] = (value >>> 8) & 0xff;
|
|
47
|
+
bytes[offset + 3] = value & 0xff;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function concatBytes(parts: readonly Uint8Array[]): Uint8Array {
|
|
51
|
+
const output = new Uint8Array(parts.reduce((sum, part) => sum + part.byteLength, 0));
|
|
52
|
+
let offset = 0;
|
|
53
|
+
for (const part of parts) {
|
|
54
|
+
output.set(part, offset);
|
|
55
|
+
offset += part.byteLength;
|
|
56
|
+
}
|
|
57
|
+
return output;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function asciiBytes(value: string): Uint8Array {
|
|
61
|
+
const output = new Uint8Array(value.length);
|
|
62
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
63
|
+
output[index] = value.charCodeAt(index) & 0xff;
|
|
64
|
+
}
|
|
65
|
+
return output;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
type PngChunk = {
|
|
69
|
+
readonly type: string;
|
|
70
|
+
readonly data: Uint8Array;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
function parsePngChunks(bytes: Uint8Array): readonly PngChunk[] {
|
|
74
|
+
if (bytes.byteLength < PNG_SIGNATURE.byteLength + 12) {
|
|
75
|
+
throw new Error("X PNG attachment is not a complete PNG");
|
|
76
|
+
}
|
|
77
|
+
for (const [index, expected] of PNG_SIGNATURE.entries()) {
|
|
78
|
+
if (bytes[index] !== expected) throw new Error("X PNG attachment is not a complete PNG");
|
|
79
|
+
}
|
|
80
|
+
const chunks: PngChunk[] = [];
|
|
81
|
+
let offset = PNG_SIGNATURE.byteLength;
|
|
82
|
+
let sawIend = false;
|
|
83
|
+
while (offset + 12 <= bytes.byteLength) {
|
|
84
|
+
const length = readUint32(bytes, offset);
|
|
85
|
+
if (length > bytes.byteLength - offset - 12) {
|
|
86
|
+
throw new Error("X PNG attachment contained a truncated chunk");
|
|
87
|
+
}
|
|
88
|
+
const typeBytes = bytes.subarray(offset + 4, offset + 8);
|
|
89
|
+
const type = String.fromCharCode(...typeBytes);
|
|
90
|
+
if (!/^[A-Za-z]{4}$/u.test(type)) throw new Error("X PNG attachment contained an invalid chunk type");
|
|
91
|
+
const data = bytes.subarray(offset + 8, offset + 8 + length);
|
|
92
|
+
const expectedCrc = readUint32(bytes, offset + 8 + length);
|
|
93
|
+
if (pngCrc(concatBytes([typeBytes, data])) !== expectedCrc) {
|
|
94
|
+
throw new Error("X PNG attachment failed its chunk checksum");
|
|
95
|
+
}
|
|
96
|
+
chunks.push({ type, data: new Uint8Array(data) });
|
|
97
|
+
offset += 12 + length;
|
|
98
|
+
if (type === "IEND") {
|
|
99
|
+
sawIend = true;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!sawIend) throw new Error("X PNG attachment omitted IEND");
|
|
104
|
+
if (chunks[0]?.type !== "IHDR") throw new Error("X PNG attachment omitted IHDR");
|
|
105
|
+
return Object.freeze(chunks);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function writePngChunk(type: string, data: Uint8Array): Uint8Array {
|
|
109
|
+
const typeBytes = asciiBytes(type);
|
|
110
|
+
const output = new Uint8Array(12 + data.byteLength);
|
|
111
|
+
writeUint32(output, 0, data.byteLength);
|
|
112
|
+
output.set(typeBytes, 4);
|
|
113
|
+
output.set(data, 8);
|
|
114
|
+
writeUint32(output, 8 + data.byteLength, pngCrc(concatBytes([typeBytes, data])));
|
|
115
|
+
return output;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function paethPredictor(left: number, up: number, upLeft: number): number {
|
|
119
|
+
const estimate = left + up - upLeft;
|
|
120
|
+
const distanceLeft = Math.abs(estimate - left);
|
|
121
|
+
const distanceUp = Math.abs(estimate - up);
|
|
122
|
+
const distanceUpLeft = Math.abs(estimate - upLeft);
|
|
123
|
+
if (distanceLeft <= distanceUp && distanceLeft <= distanceUpLeft) return left;
|
|
124
|
+
if (distanceUp <= distanceUpLeft) return up;
|
|
125
|
+
return upLeft;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function bytesPerPixel(bitDepth: number, colorType: number): number {
|
|
129
|
+
const samples = colorType === 0 || colorType === 3
|
|
130
|
+
? 1
|
|
131
|
+
: colorType === 2
|
|
132
|
+
? 3
|
|
133
|
+
: colorType === 4
|
|
134
|
+
? 2
|
|
135
|
+
: 4;
|
|
136
|
+
return Math.max(1, Math.ceil((samples * bitDepth) / 8));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function scanlineWidth(width: number, bitDepth: number, colorType: number): number {
|
|
140
|
+
const samples = colorType === 0 || colorType === 3
|
|
141
|
+
? 1
|
|
142
|
+
: colorType === 2
|
|
143
|
+
? 3
|
|
144
|
+
: colorType === 4
|
|
145
|
+
? 2
|
|
146
|
+
: 4;
|
|
147
|
+
return Math.ceil((width * samples * bitDepth) / 8);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function unfilter(
|
|
151
|
+
filtered: Uint8Array,
|
|
152
|
+
width: number,
|
|
153
|
+
height: number,
|
|
154
|
+
bitDepth: number,
|
|
155
|
+
colorType: number,
|
|
156
|
+
): Uint8Array {
|
|
157
|
+
const bpp = bytesPerPixel(bitDepth, colorType);
|
|
158
|
+
const stride = scanlineWidth(width, bitDepth, colorType);
|
|
159
|
+
const expected = height * (stride + 1);
|
|
160
|
+
if (filtered.byteLength !== expected) {
|
|
161
|
+
throw new Error("X PNG attachment scanlines did not match IHDR");
|
|
162
|
+
}
|
|
163
|
+
const raw = new Uint8Array(height * stride);
|
|
164
|
+
for (let row = 0; row < height; row += 1) {
|
|
165
|
+
const filter = filtered[row * (stride + 1)] ?? 0;
|
|
166
|
+
const source = filtered.subarray(row * (stride + 1) + 1, row * (stride + 1) + 1 + stride);
|
|
167
|
+
const destination = raw.subarray(row * stride, row * stride + stride);
|
|
168
|
+
const prior = row === 0 ? null : raw.subarray((row - 1) * stride, row * stride);
|
|
169
|
+
for (let column = 0; column < stride; column += 1) {
|
|
170
|
+
const sample = source[column] ?? 0;
|
|
171
|
+
const left = column >= bpp ? destination[column - bpp] ?? 0 : 0;
|
|
172
|
+
const up = prior?.[column] ?? 0;
|
|
173
|
+
const upLeft = prior !== null && column >= bpp ? prior[column - bpp] ?? 0 : 0;
|
|
174
|
+
const reconstructed = filter === 0
|
|
175
|
+
? sample
|
|
176
|
+
: filter === 1
|
|
177
|
+
? (sample + left) & 0xff
|
|
178
|
+
: filter === 2
|
|
179
|
+
? (sample + up) & 0xff
|
|
180
|
+
: filter === 3
|
|
181
|
+
? (sample + Math.floor((left + up) / 2)) & 0xff
|
|
182
|
+
: filter === 4
|
|
183
|
+
? (sample + paethPredictor(left, up, upLeft)) & 0xff
|
|
184
|
+
: null;
|
|
185
|
+
if (reconstructed === null) throw new Error("X PNG attachment used an unsupported filter");
|
|
186
|
+
destination[column] = reconstructed;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return raw;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function sampleChannel(
|
|
193
|
+
packed: Uint8Array,
|
|
194
|
+
pixelIndex: number,
|
|
195
|
+
channel: number,
|
|
196
|
+
channels: number,
|
|
197
|
+
bitDepth: number,
|
|
198
|
+
): number {
|
|
199
|
+
if (bitDepth === 8) return packed[pixelIndex * channels + channel] ?? 0;
|
|
200
|
+
if (bitDepth === 16) {
|
|
201
|
+
const offset = (pixelIndex * channels + channel) * 2;
|
|
202
|
+
return packed[offset] ?? 0;
|
|
203
|
+
}
|
|
204
|
+
const bitsPerPixel = channels * bitDepth;
|
|
205
|
+
const bitOffset = pixelIndex * bitsPerPixel + channel * bitDepth;
|
|
206
|
+
const byteIndex = Math.floor(bitOffset / 8);
|
|
207
|
+
const shift = 8 - bitDepth - (bitOffset % 8);
|
|
208
|
+
const mask = (1 << bitDepth) - 1;
|
|
209
|
+
const value = ((packed[byteIndex] ?? 0) >> shift) & mask;
|
|
210
|
+
return bitDepth === 1 ? value * 255 : Math.round((value * 255) / mask);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function expandToRgba(
|
|
214
|
+
packed: Uint8Array,
|
|
215
|
+
width: number,
|
|
216
|
+
height: number,
|
|
217
|
+
bitDepth: number,
|
|
218
|
+
colorType: number,
|
|
219
|
+
palette: Uint8Array | null,
|
|
220
|
+
transparency: Uint8Array | null,
|
|
221
|
+
): Uint8Array {
|
|
222
|
+
const rgba = new Uint8Array(width * height * 4);
|
|
223
|
+
const channels = colorType === 0
|
|
224
|
+
? 1
|
|
225
|
+
: colorType === 2
|
|
226
|
+
? 3
|
|
227
|
+
: colorType === 3
|
|
228
|
+
? 1
|
|
229
|
+
: colorType === 4
|
|
230
|
+
? 2
|
|
231
|
+
: 4;
|
|
232
|
+
for (let pixel = 0; pixel < width * height; pixel += 1) {
|
|
233
|
+
const dest = pixel * 4;
|
|
234
|
+
if (colorType === 3) {
|
|
235
|
+
const index = sampleChannel(packed, pixel, 0, 1, bitDepth);
|
|
236
|
+
rgba[dest] = palette?.[index * 3] ?? 0;
|
|
237
|
+
rgba[dest + 1] = palette?.[index * 3 + 1] ?? 0;
|
|
238
|
+
rgba[dest + 2] = palette?.[index * 3 + 2] ?? 0;
|
|
239
|
+
rgba[dest + 3] = transparency?.[index] ?? 255;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
const red = sampleChannel(packed, pixel, 0, channels, bitDepth);
|
|
243
|
+
const green = colorType === 0 || colorType === 4
|
|
244
|
+
? red
|
|
245
|
+
: sampleChannel(packed, pixel, 1, channels, bitDepth);
|
|
246
|
+
const blue = colorType === 0 || colorType === 4
|
|
247
|
+
? red
|
|
248
|
+
: sampleChannel(packed, pixel, 2, channels, bitDepth);
|
|
249
|
+
const alpha = colorType === 4
|
|
250
|
+
? sampleChannel(packed, pixel, 1, channels, bitDepth)
|
|
251
|
+
: colorType === 6
|
|
252
|
+
? sampleChannel(packed, pixel, 3, channels, bitDepth)
|
|
253
|
+
: transparency === null
|
|
254
|
+
? 255
|
|
255
|
+
: colorType === 0
|
|
256
|
+
? (red === (transparency[0] ?? -1) ? 0 : 255)
|
|
257
|
+
: (
|
|
258
|
+
red === (transparency[1] ?? -1)
|
|
259
|
+
&& green === (transparency[3] ?? -1)
|
|
260
|
+
&& blue === (transparency[5] ?? -1)
|
|
261
|
+
) ? 0 : 255;
|
|
262
|
+
rgba[dest] = red;
|
|
263
|
+
rgba[dest + 1] = green;
|
|
264
|
+
rgba[dest + 2] = blue;
|
|
265
|
+
rgba[dest + 3] = alpha;
|
|
266
|
+
}
|
|
267
|
+
return rgba;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const ADAM7 = [
|
|
271
|
+
{ x: 0, y: 0, dx: 8, dy: 8 },
|
|
272
|
+
{ x: 4, y: 0, dx: 8, dy: 8 },
|
|
273
|
+
{ x: 0, y: 4, dx: 4, dy: 8 },
|
|
274
|
+
{ x: 2, y: 0, dx: 4, dy: 4 },
|
|
275
|
+
{ x: 0, y: 2, dx: 2, dy: 4 },
|
|
276
|
+
{ x: 1, y: 0, dx: 2, dy: 2 },
|
|
277
|
+
{ x: 0, y: 1, dx: 1, dy: 2 },
|
|
278
|
+
] as const;
|
|
279
|
+
|
|
280
|
+
function passSize(width: number, height: number, pass: (typeof ADAM7)[number]): {
|
|
281
|
+
readonly width: number;
|
|
282
|
+
readonly height: number;
|
|
283
|
+
} {
|
|
284
|
+
return {
|
|
285
|
+
width: pass.x >= width ? 0 : Math.floor((width - pass.x - 1) / pass.dx) + 1,
|
|
286
|
+
height: pass.y >= height ? 0 : Math.floor((height - pass.y - 1) / pass.dy) + 1,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function decodePngToRgba(bytes: Uint8Array): {
|
|
291
|
+
readonly width: number;
|
|
292
|
+
readonly height: number;
|
|
293
|
+
readonly rgba: Uint8Array;
|
|
294
|
+
} {
|
|
295
|
+
const chunks = parsePngChunks(bytes);
|
|
296
|
+
const ihdr = chunks[0]!.data;
|
|
297
|
+
if (ihdr.byteLength !== 13) throw new Error("X PNG attachment IHDR was invalid");
|
|
298
|
+
const width = readUint32(ihdr, 0);
|
|
299
|
+
const height = readUint32(ihdr, 4);
|
|
300
|
+
const bitDepth = ihdr[8] ?? 0;
|
|
301
|
+
const colorType = ihdr[9] ?? 0;
|
|
302
|
+
const compression = ihdr[10] ?? 1;
|
|
303
|
+
const filter = ihdr[11] ?? 1;
|
|
304
|
+
const interlace = ihdr[12] ?? 1;
|
|
305
|
+
if (
|
|
306
|
+
width < 1
|
|
307
|
+
|| height < 1
|
|
308
|
+
|| width > MAX_PNG_DIMENSION
|
|
309
|
+
|| height > MAX_PNG_DIMENSION
|
|
310
|
+
|| width * height > MAX_PNG_PIXELS
|
|
311
|
+
) {
|
|
312
|
+
throw new Error("X PNG attachment dimensions escaped the reviewed bound");
|
|
313
|
+
}
|
|
314
|
+
if (compression !== 0 || filter !== 0 || (interlace !== 0 && interlace !== 1)) {
|
|
315
|
+
throw new Error("X PNG attachment used an unsupported IHDR method");
|
|
316
|
+
}
|
|
317
|
+
if (![0, 2, 3, 4, 6].includes(colorType)) {
|
|
318
|
+
throw new Error("X PNG attachment used an unsupported color type");
|
|
319
|
+
}
|
|
320
|
+
if (
|
|
321
|
+
(colorType === 3 && ![1, 2, 4, 8].includes(bitDepth))
|
|
322
|
+
|| (colorType !== 3 && ![8, 16].includes(bitDepth) && !(colorType === 0 && [1, 2, 4, 8, 16].includes(bitDepth)))
|
|
323
|
+
) {
|
|
324
|
+
throw new Error("X PNG attachment used an unsupported bit depth");
|
|
325
|
+
}
|
|
326
|
+
let palette: Uint8Array | null = null;
|
|
327
|
+
let transparency: Uint8Array | null = null;
|
|
328
|
+
const idat: Uint8Array[] = [];
|
|
329
|
+
for (const chunk of chunks.slice(1)) {
|
|
330
|
+
if (chunk.type === "PLTE") palette = chunk.data;
|
|
331
|
+
else if (chunk.type === "tRNS") transparency = chunk.data;
|
|
332
|
+
else if (chunk.type === "IDAT") idat.push(chunk.data);
|
|
333
|
+
}
|
|
334
|
+
if (colorType === 3 && palette === null) throw new Error("X PNG attachment omitted PLTE");
|
|
335
|
+
if (idat.length === 0) throw new Error("X PNG attachment omitted IDAT");
|
|
336
|
+
let inflated: Uint8Array;
|
|
337
|
+
try {
|
|
338
|
+
inflated = new Uint8Array(inflateSync(Buffer.concat(idat.map((part) => Buffer.from(part)))));
|
|
339
|
+
} catch {
|
|
340
|
+
throw new Error("X PNG attachment IDAT could not be inflated");
|
|
341
|
+
}
|
|
342
|
+
if (interlace === 0) {
|
|
343
|
+
const raw = unfilter(inflated, width, height, bitDepth, colorType);
|
|
344
|
+
return {
|
|
345
|
+
width,
|
|
346
|
+
height,
|
|
347
|
+
rgba: expandToRgba(raw, width, height, bitDepth, colorType, palette, transparency),
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
const rgba = new Uint8Array(width * height * 4);
|
|
351
|
+
let offset = 0;
|
|
352
|
+
for (const pass of ADAM7) {
|
|
353
|
+
const size = passSize(width, height, pass);
|
|
354
|
+
if (size.width === 0 || size.height === 0) continue;
|
|
355
|
+
const expected = size.height * (scanlineWidth(size.width, bitDepth, colorType) + 1);
|
|
356
|
+
const passBytes = inflated.subarray(offset, offset + expected);
|
|
357
|
+
if (passBytes.byteLength !== expected) {
|
|
358
|
+
throw new Error("X PNG attachment interlacing did not match IHDR");
|
|
359
|
+
}
|
|
360
|
+
const raw = unfilter(passBytes, size.width, size.height, bitDepth, colorType);
|
|
361
|
+
const passRgba = expandToRgba(raw, size.width, size.height, bitDepth, colorType, palette, transparency);
|
|
362
|
+
for (let row = 0; row < size.height; row += 1) {
|
|
363
|
+
for (let column = 0; column < size.width; column += 1) {
|
|
364
|
+
const source = (row * size.width + column) * 4;
|
|
365
|
+
const destX = pass.x + column * pass.dx;
|
|
366
|
+
const destY = pass.y + row * pass.dy;
|
|
367
|
+
rgba.set(passRgba.subarray(source, source + 4), (destY * width + destX) * 4);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
offset += expected;
|
|
371
|
+
}
|
|
372
|
+
if (offset !== inflated.byteLength) {
|
|
373
|
+
throw new Error("X PNG attachment interlacing left unread IDAT");
|
|
374
|
+
}
|
|
375
|
+
return { width, height, rgba };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export function encodePixelsOnlyPng(input: {
|
|
379
|
+
readonly width: number;
|
|
380
|
+
readonly height: number;
|
|
381
|
+
readonly rgba: Uint8Array;
|
|
382
|
+
}): Uint8Array {
|
|
383
|
+
if (
|
|
384
|
+
input.width < 1
|
|
385
|
+
|| input.height < 1
|
|
386
|
+
|| input.width > MAX_PNG_DIMENSION
|
|
387
|
+
|| input.height > MAX_PNG_DIMENSION
|
|
388
|
+
|| input.rgba.byteLength !== input.width * input.height * 4
|
|
389
|
+
) {
|
|
390
|
+
throw new Error("X PNG encoder received an invalid pixel buffer");
|
|
391
|
+
}
|
|
392
|
+
const ihdr = new Uint8Array(13);
|
|
393
|
+
writeUint32(ihdr, 0, input.width);
|
|
394
|
+
writeUint32(ihdr, 4, input.height);
|
|
395
|
+
ihdr[8] = 8;
|
|
396
|
+
ihdr[9] = 6;
|
|
397
|
+
const stride = input.width * 4;
|
|
398
|
+
const filtered = new Uint8Array(input.height * (stride + 1));
|
|
399
|
+
for (let row = 0; row < input.height; row += 1) {
|
|
400
|
+
filtered[row * (stride + 1)] = 0;
|
|
401
|
+
filtered.set(
|
|
402
|
+
input.rgba.subarray(row * stride, row * stride + stride),
|
|
403
|
+
row * (stride + 1) + 1,
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
const idat = new Uint8Array(deflateSync(Buffer.from(filtered), { level: 9 }));
|
|
407
|
+
return concatBytes([
|
|
408
|
+
PNG_SIGNATURE,
|
|
409
|
+
writePngChunk("IHDR", ihdr),
|
|
410
|
+
writePngChunk("IDAT", idat),
|
|
411
|
+
writePngChunk("IEND", new Uint8Array()),
|
|
412
|
+
]);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export function embedPngChunk(
|
|
416
|
+
bytes: Uint8Array,
|
|
417
|
+
type: string,
|
|
418
|
+
data: Uint8Array,
|
|
419
|
+
): Uint8Array {
|
|
420
|
+
if (!/^[A-Za-z]{4}$/u.test(type)) throw new Error("PNG test chunk type must be four letters");
|
|
421
|
+
const chunks = [...parsePngChunks(bytes)];
|
|
422
|
+
const iend = chunks.findIndex((chunk) => chunk.type === "IEND");
|
|
423
|
+
if (iend < 0) throw new Error("PNG fixture omitted IEND");
|
|
424
|
+
chunks.splice(iend, 0, { type, data: new Uint8Array(data) });
|
|
425
|
+
return concatBytes([
|
|
426
|
+
PNG_SIGNATURE,
|
|
427
|
+
...chunks.map((chunk) => writePngChunk(chunk.type, chunk.data)),
|
|
428
|
+
]);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function pngChunkTypes(bytes: Uint8Array): readonly string[] {
|
|
432
|
+
return parsePngChunks(bytes).map((chunk) => chunk.type);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export function reencodePixelsOnlyPng(bytes: Uint8Array): Uint8Array {
|
|
436
|
+
const decoded = decodePngToRgba(bytes);
|
|
437
|
+
return encodePixelsOnlyPng(decoded);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function stripJpegProvenance(bytes: Uint8Array): Uint8Array {
|
|
441
|
+
if (bytes.byteLength < 4 || bytes[0] !== 0xff || bytes[1] !== 0xd8) {
|
|
442
|
+
throw new Error("X JPEG attachment is not a complete JPEG");
|
|
443
|
+
}
|
|
444
|
+
const kept: number[] = [0xff, 0xd8];
|
|
445
|
+
let offset = 2;
|
|
446
|
+
while (offset + 1 < bytes.byteLength) {
|
|
447
|
+
if (bytes[offset] !== 0xff) {
|
|
448
|
+
throw new Error("X JPEG attachment left its marker structure");
|
|
449
|
+
}
|
|
450
|
+
let marker = bytes[offset + 1] ?? 0;
|
|
451
|
+
while (marker === 0xff && offset + 2 < bytes.byteLength) {
|
|
452
|
+
offset += 1;
|
|
453
|
+
marker = bytes[offset + 1] ?? 0;
|
|
454
|
+
}
|
|
455
|
+
if (marker === 0xd9) {
|
|
456
|
+
kept.push(0xff, 0xd9);
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
if (marker === 0xda) {
|
|
460
|
+
kept.push(0xff, 0xda);
|
|
461
|
+
offset += 2;
|
|
462
|
+
const remaining = bytes.subarray(offset);
|
|
463
|
+
const eoi = remaining.findIndex((value, index) =>
|
|
464
|
+
value === 0xff && remaining[index + 1] === 0xd9 && remaining[index - 1] !== 0x00);
|
|
465
|
+
if (eoi < 0) throw new Error("X JPEG attachment omitted EOI");
|
|
466
|
+
kept.push(...remaining.subarray(0, eoi), 0xff, 0xd9);
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
if (offset + 4 > bytes.byteLength) throw new Error("X JPEG attachment contained a truncated marker");
|
|
470
|
+
const length = ((bytes[offset + 2] ?? 0) << 8) | (bytes[offset + 3] ?? 0);
|
|
471
|
+
if (length < 2 || offset + 2 + length > bytes.byteLength) {
|
|
472
|
+
throw new Error("X JPEG attachment contained a truncated marker");
|
|
473
|
+
}
|
|
474
|
+
const drop = marker >= 0xe0 && marker <= 0xef || marker === 0xfe;
|
|
475
|
+
if (!drop) kept.push(...bytes.subarray(offset, offset + 2 + length));
|
|
476
|
+
offset += 2 + length;
|
|
477
|
+
}
|
|
478
|
+
if (kept.length < 4 || kept[kept.length - 2] !== 0xff || kept[kept.length - 1] !== 0xd9) {
|
|
479
|
+
throw new Error("X JPEG attachment omitted EOI");
|
|
480
|
+
}
|
|
481
|
+
return Uint8Array.from(kept);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export function imageBytesContainProvenance(bytes: Uint8Array): boolean {
|
|
485
|
+
return IMAGE_PROVENANCE_MARKER.test(Buffer.from(bytes).toString("latin1"));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export function rejectImageProvenanceMarkers(bytes: Uint8Array, label: string): void {
|
|
489
|
+
if (imageBytesContainProvenance(bytes)) {
|
|
490
|
+
throw new Error(`${label} still contained provenance after pixel-only re-encoding`);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
export function scrubXUploadImage(
|
|
495
|
+
bytes: Uint8Array,
|
|
496
|
+
mediaType: "image/png" | "image/jpeg",
|
|
497
|
+
): Uint8Array {
|
|
498
|
+
const scrubbed = mediaType === "image/png"
|
|
499
|
+
? reencodePixelsOnlyPng(bytes)
|
|
500
|
+
: stripJpegProvenance(bytes);
|
|
501
|
+
rejectImageProvenanceMarkers(scrubbed, "X upload image");
|
|
502
|
+
if (mediaType === "image/png") {
|
|
503
|
+
const types = pngChunkTypes(scrubbed);
|
|
504
|
+
if (types.some((type) => type !== "IHDR" && type !== "IDAT" && type !== "IEND")) {
|
|
505
|
+
throw new Error("X upload image retained ancillary PNG chunks");
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return scrubbed;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export function rejectGifProvenanceMarkers(bytes: Uint8Array): void {
|
|
512
|
+
if (imageBytesContainProvenance(bytes)) {
|
|
513
|
+
throw new Error("X GIF attachment contained provenance markers");
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export function minimalPngBytes(): Uint8Array {
|
|
518
|
+
return encodePixelsOnlyPng({
|
|
519
|
+
width: 1,
|
|
520
|
+
height: 1,
|
|
521
|
+
rgba: Uint8Array.of(200, 40, 40, 255),
|
|
522
|
+
});
|
|
523
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed detection of X's Made with AI / sparkle disclosure.
|
|
3
|
+
*
|
|
4
|
+
* A live label is a terminal unlabeled-copy failure. The post may already
|
|
5
|
+
* exist; do not delete it. The run journal forbids `failed` after dispatch
|
|
6
|
+
* starts, so x-web reports `indeterminate` with this error.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const X_UNLABELED_COPY_POLICY_ERROR =
|
|
10
|
+
"X applied Made with AI label; publish failed for unlabeled-copy policy";
|
|
11
|
+
|
|
12
|
+
const DISCLOSURE_KEY = /^(made_with_ai|content_disclosure|ai_generated_disclosure|ai_generated|is_ai_generated|has_ai_generated_media|ai_highlight(?:_label|_info)?|grok_generated|trained_algorithmic_media|digital_source_type)$/iu;
|
|
13
|
+
const DISCLOSURE_TEXT = /made with (?:ai|grok)|ai-generated content|trainedalgorithmicmedia|digitalsourcetype/iu;
|
|
14
|
+
const AUTHOR_TEXT_KEYS = new Set([
|
|
15
|
+
"full_text",
|
|
16
|
+
"description",
|
|
17
|
+
"screen_name",
|
|
18
|
+
"location",
|
|
19
|
+
"username",
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
export class XUnlabeledCopyPolicyError extends Error {
|
|
23
|
+
readonly post: { readonly id: string; readonly url: string } | undefined;
|
|
24
|
+
|
|
25
|
+
constructor(post?: { readonly id: string; readonly url: string }) {
|
|
26
|
+
super(X_UNLABELED_COPY_POLICY_ERROR);
|
|
27
|
+
this.name = "XUnlabeledCopyPolicyError";
|
|
28
|
+
this.post = post;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
|
|
33
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function skipAuthorText(key: string, ancestors: readonly string[]): boolean {
|
|
37
|
+
if (AUTHOR_TEXT_KEYS.has(key)) return true;
|
|
38
|
+
if (key === "name") {
|
|
39
|
+
return ancestors.includes("user_results") || ancestors.includes("core") || ancestors.includes("user");
|
|
40
|
+
}
|
|
41
|
+
if (key !== "text") return false;
|
|
42
|
+
return ancestors.includes("legacy") || ancestors.includes("note_tweet");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function annotationLooksLikeAi(value: unknown): boolean {
|
|
46
|
+
if (typeof value === "string") return DISCLOSURE_TEXT.test(value) || DISCLOSURE_KEY.test(value);
|
|
47
|
+
if (!isRecord(value)) return false;
|
|
48
|
+
for (const [key, item] of Object.entries(value)) {
|
|
49
|
+
if (DISCLOSURE_KEY.test(key)) return true;
|
|
50
|
+
if (typeof item === "string" && (DISCLOSURE_TEXT.test(item) || DISCLOSURE_KEY.test(item))) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function walkTweet(
|
|
58
|
+
value: unknown,
|
|
59
|
+
ancestors: readonly string[],
|
|
60
|
+
): boolean {
|
|
61
|
+
if (typeof value === "string") {
|
|
62
|
+
const key = ancestors.at(-1) ?? "";
|
|
63
|
+
if (skipAuthorText(key, ancestors.slice(0, -1))) return false;
|
|
64
|
+
return DISCLOSURE_TEXT.test(value) || DISCLOSURE_KEY.test(value);
|
|
65
|
+
}
|
|
66
|
+
if (typeof value === "boolean") {
|
|
67
|
+
const key = ancestors.at(-1) ?? "";
|
|
68
|
+
return value === true && DISCLOSURE_KEY.test(key);
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
const key = ancestors.at(-1) ?? "";
|
|
72
|
+
if (
|
|
73
|
+
(key === "semantic_annotations" || key === "semantic_annotation_ids")
|
|
74
|
+
&& value.some(annotationLooksLikeAi)
|
|
75
|
+
) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return value.some((item) => walkTweet(item, ancestors));
|
|
79
|
+
}
|
|
80
|
+
if (!isRecord(value)) return false;
|
|
81
|
+
for (const [key, item] of Object.entries(value)) {
|
|
82
|
+
if (DISCLOSURE_KEY.test(key)) {
|
|
83
|
+
if (item === true) return true;
|
|
84
|
+
if (typeof item === "string" && item.length > 0 && item !== "false") return true;
|
|
85
|
+
if (isRecord(item) || Array.isArray(item)) {
|
|
86
|
+
if (walkTweet(item, [...ancestors, key])) return true;
|
|
87
|
+
}
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (walkTweet(item, [...ancestors, key])) return true;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function xTweetHasMadeWithAiLabel(value: unknown): boolean {
|
|
96
|
+
return walkTweet(value, []);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function rejectXTweetMadeWithAiLabel(
|
|
100
|
+
value: unknown,
|
|
101
|
+
post?: { readonly id: string; readonly url: string },
|
|
102
|
+
): void {
|
|
103
|
+
if (xTweetHasMadeWithAiLabel(value)) {
|
|
104
|
+
throw new XUnlabeledCopyPolicyError(post);
|
|
105
|
+
}
|
|
106
|
+
}
|