@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,439 @@
|
|
|
1
|
+
import { isLiteralLanguageTag, isValidBrowserSpec, type CaptureMode } from "./args";
|
|
2
|
+
import {
|
|
3
|
+
isWellFormedIdentity,
|
|
4
|
+
parseProbeMetadata,
|
|
5
|
+
type YtDlpAuthorizationIdentityInput,
|
|
6
|
+
type CaptionSelection,
|
|
7
|
+
type ParseProbeResult,
|
|
8
|
+
type ProbeMetadata,
|
|
9
|
+
} from "./metadata";
|
|
10
|
+
import {
|
|
11
|
+
runProcess,
|
|
12
|
+
urlDerivedRedactions,
|
|
13
|
+
type CommandArgv,
|
|
14
|
+
type ProcessFailureReason,
|
|
15
|
+
type ProcessResult,
|
|
16
|
+
type RunProcessOptions,
|
|
17
|
+
} from "./process";
|
|
18
|
+
|
|
19
|
+
const PROBE_TIMEOUT_MS = 2 * 60 * 1_000;
|
|
20
|
+
const CAPTURE_TIMEOUT_MS = 24 * 60 * 60 * 1_000;
|
|
21
|
+
const PROBE_STDOUT_LIMIT_BYTES = 32 * 1024 * 1024;
|
|
22
|
+
const CAPTURE_STDOUT_LIMIT_BYTES = 16 * 1024;
|
|
23
|
+
const TOOL_STDERR_LIMIT_BYTES = 2 * 1024 * 1024;
|
|
24
|
+
const CAPTURE_IDENTITY_PREFIX = "WRENCH_MEDIA_CAPTURE_IDENTITY_V1\t";
|
|
25
|
+
const CAPTURE_IDENTITY_MAX_CODE_UNITS = 512;
|
|
26
|
+
const CAPTURE_EXTENSION_MAX_CODE_UNITS = 16;
|
|
27
|
+
const RESERVED_CAPTURE_EXTENSIONS: ReadonlySet<string> = new Set([
|
|
28
|
+
"ass",
|
|
29
|
+
"avif",
|
|
30
|
+
"bmp",
|
|
31
|
+
"description",
|
|
32
|
+
"dfxp",
|
|
33
|
+
"gif",
|
|
34
|
+
"ico",
|
|
35
|
+
"jpeg",
|
|
36
|
+
"jfif",
|
|
37
|
+
"jpg",
|
|
38
|
+
"json",
|
|
39
|
+
"json3",
|
|
40
|
+
"jxl",
|
|
41
|
+
"lrc",
|
|
42
|
+
"part",
|
|
43
|
+
"png",
|
|
44
|
+
"sami",
|
|
45
|
+
"scc",
|
|
46
|
+
"srv1",
|
|
47
|
+
"srv2",
|
|
48
|
+
"srv3",
|
|
49
|
+
"srt",
|
|
50
|
+
"svg",
|
|
51
|
+
"tif",
|
|
52
|
+
"tiff",
|
|
53
|
+
"txt",
|
|
54
|
+
"ttml",
|
|
55
|
+
"vtt",
|
|
56
|
+
"webp",
|
|
57
|
+
"xml",
|
|
58
|
+
"ytdl",
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
// yt-dlp loads ambient configuration before command-line arguments. These
|
|
62
|
+
// Ghostget media-owned negatives are therefore always emitted on the CLI, including
|
|
63
|
+
// when the user explicitly inherits config, so config cannot weaken them.
|
|
64
|
+
const OWNED_PROBE_BOUNDARY_ARGS = [
|
|
65
|
+
"--no-remote-components",
|
|
66
|
+
"--ignore-dynamic-mpd",
|
|
67
|
+
"--no-wait-for-video",
|
|
68
|
+
"--no-live-from-start",
|
|
69
|
+
"--no-allow-unplayable-formats",
|
|
70
|
+
] as const;
|
|
71
|
+
|
|
72
|
+
const OWNED_CAPTURE_BOUNDARY_ARGS = [
|
|
73
|
+
...OWNED_PROBE_BOUNDARY_ARGS,
|
|
74
|
+
"--no-keep-fragments",
|
|
75
|
+
"--no-hls-split-discontinuity",
|
|
76
|
+
] as const;
|
|
77
|
+
|
|
78
|
+
// `after_move` is the first point where a media capture's final file exists.
|
|
79
|
+
// Transcript-only runs do not execute that stage, so they report at
|
|
80
|
+
// `after_video`, which is the corresponding once-per-item completion boundary.
|
|
81
|
+
const CAPTURE_IDENTITY_TEMPLATE = `${CAPTURE_IDENTITY_PREFIX}{"extractor":%(extractor_key,extractor|)j,"id":%(id|)j,"ext":%(ext|)j}`;
|
|
82
|
+
|
|
83
|
+
export interface YtDlpAuthOptions {
|
|
84
|
+
readonly browser?: string;
|
|
85
|
+
readonly inheritConfig: boolean;
|
|
86
|
+
readonly authContextSha256?: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface ProbeYtDlpOptions extends YtDlpAuthOptions {
|
|
90
|
+
readonly executable: string;
|
|
91
|
+
readonly url: string;
|
|
92
|
+
readonly signal?: AbortSignal;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface CaptureYtDlpOptions extends YtDlpAuthOptions {
|
|
96
|
+
readonly executable: string;
|
|
97
|
+
readonly url: string;
|
|
98
|
+
readonly mode: CaptureMode;
|
|
99
|
+
readonly captureDirectory: string;
|
|
100
|
+
readonly temporaryDirectory: string;
|
|
101
|
+
readonly caption: CaptionSelection | null;
|
|
102
|
+
readonly persistDescriptiveMetadata: boolean;
|
|
103
|
+
readonly privateRedactions?: readonly string[];
|
|
104
|
+
readonly signal?: AbortSignal;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface YtDlpProcessFailure {
|
|
108
|
+
readonly ok: false;
|
|
109
|
+
readonly diagnostic: string;
|
|
110
|
+
/** Exact process-layer reason; archive maps `aborted` to cancellation. */
|
|
111
|
+
readonly processReason: ProcessFailureReason;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface YtDlpOutputFailure {
|
|
115
|
+
readonly ok: false;
|
|
116
|
+
readonly diagnostic: string;
|
|
117
|
+
readonly processReason?: never;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export type YtDlpProbeResult =
|
|
121
|
+
| { readonly ok: true; readonly metadata: ProbeMetadata }
|
|
122
|
+
| Extract<ParseProbeResult, { readonly kind: "unsupported" }>
|
|
123
|
+
| YtDlpProcessFailure
|
|
124
|
+
| YtDlpOutputFailure;
|
|
125
|
+
|
|
126
|
+
export interface YtDlpCaptureIdentity {
|
|
127
|
+
readonly extractor: string;
|
|
128
|
+
readonly id: string;
|
|
129
|
+
readonly ext: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type YtDlpCaptureResult =
|
|
133
|
+
| { readonly ok: true; readonly identity: YtDlpCaptureIdentity }
|
|
134
|
+
| YtDlpProcessFailure
|
|
135
|
+
| YtDlpOutputFailure;
|
|
136
|
+
|
|
137
|
+
export type ParseYtDlpCaptureIdentityResult = YtDlpCaptureResult;
|
|
138
|
+
|
|
139
|
+
export interface YtDlpDependencies {
|
|
140
|
+
readonly runProcess: (argv: CommandArgv, options: RunProcessOptions) => Promise<ProcessResult>;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const defaultDependencies: YtDlpDependencies = { runProcess: (argv, options) => runProcess(argv, options) };
|
|
144
|
+
const SHA256_PATTERN = /^[0-9a-f]{64}$/u;
|
|
145
|
+
|
|
146
|
+
function authorizationIdentity(
|
|
147
|
+
options: YtDlpAuthOptions,
|
|
148
|
+
): YtDlpAuthorizationIdentityInput | undefined {
|
|
149
|
+
if (options.browser !== undefined && !isValidBrowserSpec(options.browser)) {
|
|
150
|
+
throw new TypeError("yt-dlp browser selector is malformed");
|
|
151
|
+
}
|
|
152
|
+
if (options.browser !== undefined && options.inheritConfig) {
|
|
153
|
+
throw new TypeError("yt-dlp browser and ambient configuration are mutually exclusive");
|
|
154
|
+
}
|
|
155
|
+
const hasPrivateAccess = options.browser !== undefined || options.inheritConfig;
|
|
156
|
+
if (hasPrivateAccess !== (options.authContextSha256 !== undefined)) {
|
|
157
|
+
throw new TypeError("yt-dlp private access requires exactly one authorization context");
|
|
158
|
+
}
|
|
159
|
+
if (options.authContextSha256 === undefined) return undefined;
|
|
160
|
+
if (!SHA256_PATTERN.test(options.authContextSha256)) {
|
|
161
|
+
throw new TypeError("yt-dlp authorization context digest is malformed");
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
mode: options.browser === undefined ? "ambient_config" : "browser",
|
|
165
|
+
contextSha256: options.authContextSha256,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function configurationArgs(options: YtDlpAuthOptions): string[] {
|
|
170
|
+
return options.inheritConfig ? [] : ["--ignore-config", "--no-plugin-dirs"];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function authenticationArgs(options: YtDlpAuthOptions): string[] {
|
|
174
|
+
return options.browser === undefined ? [] : ["--cookies-from-browser", options.browser];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function buildYtDlpProbeArgv(options: ProbeYtDlpOptions): CommandArgv {
|
|
178
|
+
authorizationIdentity(options);
|
|
179
|
+
return [
|
|
180
|
+
options.executable,
|
|
181
|
+
...configurationArgs(options),
|
|
182
|
+
...OWNED_PROBE_BOUNDARY_ARGS,
|
|
183
|
+
"--no-playlist",
|
|
184
|
+
"--skip-download",
|
|
185
|
+
"--dump-single-json",
|
|
186
|
+
"--no-warnings",
|
|
187
|
+
...authenticationArgs(options),
|
|
188
|
+
"--",
|
|
189
|
+
options.url,
|
|
190
|
+
];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function modeArgs(mode: CaptureMode): string[] {
|
|
194
|
+
switch (mode) {
|
|
195
|
+
case "archive":
|
|
196
|
+
return ["--format", "bv*+ba/b", "--merge-output-format", "mkv"];
|
|
197
|
+
case "audio":
|
|
198
|
+
return ["--format", "bestaudio/best"];
|
|
199
|
+
case "video":
|
|
200
|
+
return ["--format", "bestvideo/best"];
|
|
201
|
+
case "transcript":
|
|
202
|
+
return ["--skip-download"];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function captionArgs(caption: CaptionSelection | null): string[] {
|
|
207
|
+
if (caption === null) return [];
|
|
208
|
+
if (!isLiteralLanguageTag(caption.language)) {
|
|
209
|
+
throw new TypeError("caption language must be a literal BCP-47-style tag");
|
|
210
|
+
}
|
|
211
|
+
return [
|
|
212
|
+
caption.source === "manual" ? "--write-subs" : "--write-auto-subs",
|
|
213
|
+
"--sub-langs",
|
|
214
|
+
caption.language,
|
|
215
|
+
"--sub-format",
|
|
216
|
+
"vtt/best",
|
|
217
|
+
"--convert-subs",
|
|
218
|
+
"vtt",
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function captureIdentityPrintArg(mode: CaptureMode): string {
|
|
223
|
+
const when = mode === "transcript" ? "after_video" : "after_move";
|
|
224
|
+
return `${when}:${CAPTURE_IDENTITY_TEMPLATE}`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function buildYtDlpCaptureArgv(options: CaptureYtDlpOptions): CommandArgv {
|
|
228
|
+
authorizationIdentity(options);
|
|
229
|
+
return [
|
|
230
|
+
options.executable,
|
|
231
|
+
...configurationArgs(options),
|
|
232
|
+
...OWNED_CAPTURE_BOUNDARY_ARGS,
|
|
233
|
+
"--no-playlist",
|
|
234
|
+
"--abort-on-error",
|
|
235
|
+
"--abort-on-unavailable-fragments",
|
|
236
|
+
"--check-formats",
|
|
237
|
+
"--quiet",
|
|
238
|
+
"--no-progress",
|
|
239
|
+
"--no-simulate",
|
|
240
|
+
"--continue",
|
|
241
|
+
"--part",
|
|
242
|
+
"--no-overwrites",
|
|
243
|
+
"--no-mtime",
|
|
244
|
+
"--retries",
|
|
245
|
+
"10",
|
|
246
|
+
"--fragment-retries",
|
|
247
|
+
"10",
|
|
248
|
+
"--retry-sleep",
|
|
249
|
+
"http:exp=1:20",
|
|
250
|
+
"--retry-sleep",
|
|
251
|
+
"fragment:exp=1:20",
|
|
252
|
+
// Probe JSON is parsed into Ghostget media's owned metadata model. Explicitly disable
|
|
253
|
+
// raw info sidecars even when an inherited yt-dlp config enables them.
|
|
254
|
+
"--no-write-info-json",
|
|
255
|
+
...(options.persistDescriptiveMetadata
|
|
256
|
+
? ["--write-description", "--write-thumbnail"]
|
|
257
|
+
: ["--no-write-description", "--no-write-thumbnail"]),
|
|
258
|
+
...captionArgs(options.caption),
|
|
259
|
+
...modeArgs(options.mode),
|
|
260
|
+
"--paths",
|
|
261
|
+
`home:${options.captureDirectory}`,
|
|
262
|
+
"--paths",
|
|
263
|
+
`temp:${options.temporaryDirectory}`,
|
|
264
|
+
"--output",
|
|
265
|
+
"media.%(ext)s",
|
|
266
|
+
// `--print` is a machine-readable provenance channel, not user-facing
|
|
267
|
+
// progress. JSON conversion safely quotes arbitrary extractor identities.
|
|
268
|
+
"--print",
|
|
269
|
+
captureIdentityPrintArg(options.mode),
|
|
270
|
+
...authenticationArgs(options),
|
|
271
|
+
"--",
|
|
272
|
+
options.url,
|
|
273
|
+
];
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
|
|
277
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function hasIdentityControlCharacter(value: string): boolean {
|
|
281
|
+
for (const character of value) {
|
|
282
|
+
const codePoint = character.codePointAt(0);
|
|
283
|
+
if (
|
|
284
|
+
codePoint !== undefined
|
|
285
|
+
&& (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f))
|
|
286
|
+
) {
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function boundedIdentityString(value: unknown): string | null {
|
|
294
|
+
if (typeof value !== "string") return null;
|
|
295
|
+
// Provider identity is opaque. Normalizing or trimming here could make two
|
|
296
|
+
// distinct post-capture identities compare equal across the probe boundary.
|
|
297
|
+
return value.length > 0
|
|
298
|
+
&& value.length <= CAPTURE_IDENTITY_MAX_CODE_UNITS
|
|
299
|
+
&& !hasIdentityControlCharacter(value)
|
|
300
|
+
&& isWellFormedIdentity(value)
|
|
301
|
+
? value
|
|
302
|
+
: null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Revalidates completion extensions at every injected adapter boundary. */
|
|
306
|
+
export function isSafeYtDlpCaptureExtension(value: unknown): value is string {
|
|
307
|
+
return typeof value === "string"
|
|
308
|
+
&& value.length > 0
|
|
309
|
+
&& value.length <= CAPTURE_EXTENSION_MAX_CODE_UNITS
|
|
310
|
+
&& /^[a-z0-9]+$/u.test(value)
|
|
311
|
+
&& !RESERVED_CAPTURE_EXTENSIONS.has(value);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Parses only Ghostget media's prefixed, allowlisted completion record. Other stdout can
|
|
316
|
+
* be produced by an explicitly inherited yt-dlp config, but never crosses this
|
|
317
|
+
* boundary. Exactly one Ghostget media record is required.
|
|
318
|
+
*/
|
|
319
|
+
export function parseYtDlpCaptureIdentity(
|
|
320
|
+
stdout: unknown,
|
|
321
|
+
): ParseYtDlpCaptureIdentityResult {
|
|
322
|
+
if (typeof stdout !== "string") {
|
|
323
|
+
return { ok: false, diagnostic: "yt-dlp capture did not return an identity record" };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const records = stdout
|
|
327
|
+
.split("\n")
|
|
328
|
+
.filter((line) => line.startsWith(CAPTURE_IDENTITY_PREFIX));
|
|
329
|
+
if (records.length === 0) {
|
|
330
|
+
return { ok: false, diagnostic: "yt-dlp capture did not return an identity record" };
|
|
331
|
+
}
|
|
332
|
+
if (records.length !== 1) {
|
|
333
|
+
return { ok: false, diagnostic: "yt-dlp capture returned multiple identity records" };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const record = records[0];
|
|
337
|
+
if (record === undefined) {
|
|
338
|
+
return { ok: false, diagnostic: "yt-dlp capture did not return an identity record" };
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
let value: unknown;
|
|
342
|
+
try {
|
|
343
|
+
value = JSON.parse(record.slice(CAPTURE_IDENTITY_PREFIX.length));
|
|
344
|
+
} catch {
|
|
345
|
+
return { ok: false, diagnostic: "yt-dlp capture returned a malformed identity record" };
|
|
346
|
+
}
|
|
347
|
+
if (!isRecord(value)) {
|
|
348
|
+
return { ok: false, diagnostic: "yt-dlp capture returned a malformed identity record" };
|
|
349
|
+
}
|
|
350
|
+
const keys = Object.keys(value).toSorted();
|
|
351
|
+
if (
|
|
352
|
+
keys.length !== 3
|
|
353
|
+
|| keys[0] !== "ext"
|
|
354
|
+
|| keys[1] !== "extractor"
|
|
355
|
+
|| keys[2] !== "id"
|
|
356
|
+
) {
|
|
357
|
+
return { ok: false, diagnostic: "yt-dlp capture returned a malformed identity record" };
|
|
358
|
+
}
|
|
359
|
+
const extractor = boundedIdentityString(value["extractor"]);
|
|
360
|
+
const id = boundedIdentityString(value["id"]);
|
|
361
|
+
const ext = value["ext"];
|
|
362
|
+
if (extractor === null || id === null || !isSafeYtDlpCaptureExtension(ext)) {
|
|
363
|
+
return { ok: false, diagnostic: "yt-dlp capture returned a malformed identity record" };
|
|
364
|
+
}
|
|
365
|
+
return { ok: true, identity: { extractor, id, ext } };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export async function probeWithYtDlp(
|
|
369
|
+
options: ProbeYtDlpOptions,
|
|
370
|
+
dependencies: YtDlpDependencies = defaultDependencies,
|
|
371
|
+
): Promise<YtDlpProbeResult> {
|
|
372
|
+
const result = await dependencies.runProcess(buildYtDlpProbeArgv(options), {
|
|
373
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
374
|
+
timeoutMs: PROBE_TIMEOUT_MS,
|
|
375
|
+
maxStdoutBytes: PROBE_STDOUT_LIMIT_BYTES,
|
|
376
|
+
maxStderrBytes: TOOL_STDERR_LIMIT_BYTES,
|
|
377
|
+
redactions: [
|
|
378
|
+
options.url,
|
|
379
|
+
...urlDerivedRedactions(options.url),
|
|
380
|
+
...(options.browser === undefined ? [] : [options.browser]),
|
|
381
|
+
],
|
|
382
|
+
});
|
|
383
|
+
if (!result.ok) {
|
|
384
|
+
return {
|
|
385
|
+
ok: false,
|
|
386
|
+
diagnostic: result.diagnostic,
|
|
387
|
+
processReason: result.reason,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
if (result.stdoutTruncated) return { ok: false, diagnostic: "yt-dlp probe exceeded Ghostget media's metadata limit" };
|
|
391
|
+
let value: unknown;
|
|
392
|
+
try { value = JSON.parse(result.stdout); } catch { return { ok: false, diagnostic: "yt-dlp probe did not return valid JSON" }; }
|
|
393
|
+
const parsed = parseProbeMetadata(value, options.url, authorizationIdentity(options));
|
|
394
|
+
if (parsed.ok || parsed.kind === "unsupported") return parsed;
|
|
395
|
+
return { ok: false, diagnostic: parsed.message };
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export async function captureWithYtDlp(
|
|
399
|
+
options: CaptureYtDlpOptions,
|
|
400
|
+
dependencies: YtDlpDependencies = defaultDependencies,
|
|
401
|
+
): Promise<YtDlpCaptureResult> {
|
|
402
|
+
const result = await dependencies.runProcess(buildYtDlpCaptureArgv(options), {
|
|
403
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
404
|
+
timeoutMs: CAPTURE_TIMEOUT_MS,
|
|
405
|
+
maxStdoutBytes: CAPTURE_STDOUT_LIMIT_BYTES,
|
|
406
|
+
maxStderrBytes: TOOL_STDERR_LIMIT_BYTES,
|
|
407
|
+
redactions: [
|
|
408
|
+
options.url,
|
|
409
|
+
...urlDerivedRedactions(options.url),
|
|
410
|
+
...(options.privateRedactions ?? []),
|
|
411
|
+
...(options.browser === undefined ? [] : [options.browser]),
|
|
412
|
+
],
|
|
413
|
+
});
|
|
414
|
+
if (!result.ok) {
|
|
415
|
+
return {
|
|
416
|
+
ok: false,
|
|
417
|
+
diagnostic: result.diagnostic,
|
|
418
|
+
processReason: result.reason,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
if (result.stdoutTruncated) {
|
|
422
|
+
return { ok: false, diagnostic: "yt-dlp capture exceeded Ghostget media's identity output limit" };
|
|
423
|
+
}
|
|
424
|
+
return parseYtDlpCaptureIdentity(result.stdout);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export async function ytDlpVersion(
|
|
428
|
+
executable: string,
|
|
429
|
+
dependencies: YtDlpDependencies = defaultDependencies,
|
|
430
|
+
): Promise<string> {
|
|
431
|
+
const result = await dependencies.runProcess([executable, "--version"], {
|
|
432
|
+
timeoutMs: 15_000,
|
|
433
|
+
maxStdoutBytes: 32 * 1024,
|
|
434
|
+
maxStderrBytes: 32 * 1024,
|
|
435
|
+
});
|
|
436
|
+
if (!result.ok) return "unknown";
|
|
437
|
+
const firstLine = result.stdout.trim().split(/\s+/u)[0];
|
|
438
|
+
return firstLine === undefined || firstLine.length === 0 || firstLine.length > 128 ? "unknown" : firstLine;
|
|
439
|
+
}
|