@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,889 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { canonicalJson, sha256 } from "./canonical-json";
|
|
5
|
+
import {
|
|
6
|
+
parsePortableOperationIdentityV1,
|
|
7
|
+
type PortableOperationIdentityV1,
|
|
8
|
+
} from "./provider-plugin-portable-identity";
|
|
9
|
+
import {
|
|
10
|
+
loadInstalledPortableProviderPlugin,
|
|
11
|
+
withPortableProviderPluginCatalogLock,
|
|
12
|
+
} from "./provider-plugin-store";
|
|
13
|
+
import {
|
|
14
|
+
currentProcessStartIdentity,
|
|
15
|
+
processOwnerStatus,
|
|
16
|
+
type ProcessOwnerIdentity,
|
|
17
|
+
type ProcessOwnerStatus,
|
|
18
|
+
} from "./process-identity";
|
|
19
|
+
import type {
|
|
20
|
+
PortableProviderPluginCleanupContainment,
|
|
21
|
+
} from "./provider-plugin-cleanup-barrier";
|
|
22
|
+
import {
|
|
23
|
+
createPrivateJsonIfAbsent,
|
|
24
|
+
ensurePrivateStateDirectory,
|
|
25
|
+
ghostgetStateHome,
|
|
26
|
+
readPrivateStateFilesBatched,
|
|
27
|
+
removePrivateStateFileIfUnchanged,
|
|
28
|
+
snapshotPrivateStateDirectory,
|
|
29
|
+
writePrivateJsonIfUnchanged,
|
|
30
|
+
} from "./storage";
|
|
31
|
+
|
|
32
|
+
type Environment = Readonly<Record<string, string | undefined>>;
|
|
33
|
+
type JsonRecord = Record<string, unknown>;
|
|
34
|
+
|
|
35
|
+
const LEASE_SCHEMA_VERSION = 2 as const;
|
|
36
|
+
const LEGACY_LEASE_SCHEMA_VERSION = 1 as const;
|
|
37
|
+
const MAX_LEASE_BYTES = 32 * 1024;
|
|
38
|
+
const uuidPattern =
|
|
39
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
|
|
40
|
+
|
|
41
|
+
type PortableProviderPluginInvocationLeaseBase = {
|
|
42
|
+
readonly leaseId: string;
|
|
43
|
+
readonly runId: string;
|
|
44
|
+
readonly identity: PortableOperationIdentityV1;
|
|
45
|
+
readonly owner: ProcessOwnerIdentity & {
|
|
46
|
+
readonly token: string;
|
|
47
|
+
};
|
|
48
|
+
readonly acquiredAt: string;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type PortableProviderPluginInvocationLeaseContainment =
|
|
52
|
+
| {
|
|
53
|
+
readonly status: "parent-owned";
|
|
54
|
+
}
|
|
55
|
+
| {
|
|
56
|
+
readonly status: "host-starting" | "host-active";
|
|
57
|
+
readonly host: ProcessOwnerIdentity;
|
|
58
|
+
}
|
|
59
|
+
| {
|
|
60
|
+
readonly status: "cleanup-complete" | "cleanup-unsafe";
|
|
61
|
+
readonly host: ProcessOwnerIdentity | null;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type PortableProviderPluginInvocationLease =
|
|
65
|
+
| PortableProviderPluginInvocationLeaseBase & {
|
|
66
|
+
readonly schemaVersion: typeof LEGACY_LEASE_SCHEMA_VERSION;
|
|
67
|
+
}
|
|
68
|
+
| PortableProviderPluginInvocationLeaseBase & {
|
|
69
|
+
readonly schemaVersion: typeof LEASE_SCHEMA_VERSION;
|
|
70
|
+
readonly containment: PortableProviderPluginInvocationLeaseContainment;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type PortableProviderPluginInvocationLeaseSnapshot = {
|
|
74
|
+
readonly lease: PortableProviderPluginInvocationLease;
|
|
75
|
+
readonly contentSha256: string;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export type PortableProviderPluginInvocationLeaseListEntry =
|
|
79
|
+
| PortableProviderPluginInvocationLeaseSnapshot
|
|
80
|
+
| {
|
|
81
|
+
readonly leaseId: string;
|
|
82
|
+
readonly invalid: true;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function record(value: unknown, label: string): JsonRecord {
|
|
86
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
87
|
+
throw new Error(`${label} must be an object`);
|
|
88
|
+
}
|
|
89
|
+
const prototype: unknown = Object.getPrototypeOf(value) as unknown;
|
|
90
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
91
|
+
throw new Error(`${label} has an unsupported prototype`);
|
|
92
|
+
}
|
|
93
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
94
|
+
const result: JsonRecord = {};
|
|
95
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
96
|
+
if (typeof key !== "string") {
|
|
97
|
+
throw new Error(`${label} has unsupported symbol fields`);
|
|
98
|
+
}
|
|
99
|
+
const descriptor = descriptors[key];
|
|
100
|
+
if (
|
|
101
|
+
descriptor === undefined
|
|
102
|
+
|| !descriptor.enumerable
|
|
103
|
+
|| !("value" in descriptor)
|
|
104
|
+
) {
|
|
105
|
+
throw new Error(`${label} has unsupported accessor fields`);
|
|
106
|
+
}
|
|
107
|
+
Object.defineProperty(result, key, {
|
|
108
|
+
value: descriptor.value,
|
|
109
|
+
enumerable: true,
|
|
110
|
+
configurable: false,
|
|
111
|
+
writable: false,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function exactKeys(
|
|
118
|
+
value: JsonRecord,
|
|
119
|
+
expectedKeys: readonly string[],
|
|
120
|
+
label: string,
|
|
121
|
+
): void {
|
|
122
|
+
const actual = Object.keys(value).sort();
|
|
123
|
+
const expected = [...expectedKeys].sort();
|
|
124
|
+
if (
|
|
125
|
+
actual.length !== expected.length
|
|
126
|
+
|| actual.some((key, index) => key !== expected[index])
|
|
127
|
+
) {
|
|
128
|
+
throw new Error(`${label} has unsupported fields`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function uuid(value: unknown, label: string): string {
|
|
133
|
+
if (typeof value !== "string" || !uuidPattern.test(value)) {
|
|
134
|
+
throw new Error(`${label} is malformed`);
|
|
135
|
+
}
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function timestamp(value: unknown, label: string): string {
|
|
140
|
+
if (
|
|
141
|
+
typeof value !== "string"
|
|
142
|
+
|| !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value)
|
|
143
|
+
|| new Date(value).toISOString() !== value
|
|
144
|
+
) {
|
|
145
|
+
throw new Error(`${label} is malformed`);
|
|
146
|
+
}
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function parseProcessOwner(
|
|
151
|
+
value: unknown,
|
|
152
|
+
label: string,
|
|
153
|
+
): ProcessOwnerIdentity {
|
|
154
|
+
const owner = record(value, label);
|
|
155
|
+
exactKeys(
|
|
156
|
+
owner,
|
|
157
|
+
["pid", "bootId", "processStartId"],
|
|
158
|
+
label,
|
|
159
|
+
);
|
|
160
|
+
if (
|
|
161
|
+
typeof owner.pid !== "number"
|
|
162
|
+
|| !Number.isSafeInteger(owner.pid)
|
|
163
|
+
|| owner.pid < 1
|
|
164
|
+
|| typeof owner.bootId !== "string"
|
|
165
|
+
|| !/^[a-f0-9]{64}$/u.test(owner.bootId)
|
|
166
|
+
|| typeof owner.processStartId !== "string"
|
|
167
|
+
|| !/^[a-f0-9]{64}$/u.test(owner.processStartId)
|
|
168
|
+
) {
|
|
169
|
+
throw new Error(`${label} is malformed`);
|
|
170
|
+
}
|
|
171
|
+
return Object.freeze({
|
|
172
|
+
pid: owner.pid,
|
|
173
|
+
bootId: owner.bootId,
|
|
174
|
+
processStartId: owner.processStartId,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function parseOwner(value: unknown): PortableProviderPluginInvocationLease["owner"] {
|
|
179
|
+
const owner = record(value, "portable invocation lease owner");
|
|
180
|
+
exactKeys(
|
|
181
|
+
owner,
|
|
182
|
+
["pid", "token", "bootId", "processStartId"],
|
|
183
|
+
"portable invocation lease owner",
|
|
184
|
+
);
|
|
185
|
+
if (
|
|
186
|
+
typeof owner.token !== "string"
|
|
187
|
+
|| !uuidPattern.test(owner.token)
|
|
188
|
+
) {
|
|
189
|
+
throw new Error("portable invocation lease owner is malformed");
|
|
190
|
+
}
|
|
191
|
+
return Object.freeze({
|
|
192
|
+
...parseProcessOwner(
|
|
193
|
+
{
|
|
194
|
+
pid: owner.pid,
|
|
195
|
+
bootId: owner.bootId,
|
|
196
|
+
processStartId: owner.processStartId,
|
|
197
|
+
},
|
|
198
|
+
"portable invocation lease owner",
|
|
199
|
+
),
|
|
200
|
+
token: owner.token,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function parseContainment(
|
|
205
|
+
value: unknown,
|
|
206
|
+
): PortableProviderPluginInvocationLeaseContainment {
|
|
207
|
+
const containment = record(value, "portable invocation lease containment");
|
|
208
|
+
if (containment.status === "parent-owned") {
|
|
209
|
+
exactKeys(
|
|
210
|
+
containment,
|
|
211
|
+
["status"],
|
|
212
|
+
"portable invocation lease containment",
|
|
213
|
+
);
|
|
214
|
+
return Object.freeze({ status: "parent-owned" });
|
|
215
|
+
}
|
|
216
|
+
if (
|
|
217
|
+
containment.status === "host-starting"
|
|
218
|
+
|| containment.status === "host-active"
|
|
219
|
+
) {
|
|
220
|
+
exactKeys(
|
|
221
|
+
containment,
|
|
222
|
+
["status", "host"],
|
|
223
|
+
"portable invocation lease containment",
|
|
224
|
+
);
|
|
225
|
+
return Object.freeze({
|
|
226
|
+
status: containment.status,
|
|
227
|
+
host: parseProcessOwner(
|
|
228
|
+
containment.host,
|
|
229
|
+
"portable invocation lease host",
|
|
230
|
+
),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if (
|
|
234
|
+
containment.status === "cleanup-complete"
|
|
235
|
+
|| containment.status === "cleanup-unsafe"
|
|
236
|
+
) {
|
|
237
|
+
exactKeys(
|
|
238
|
+
containment,
|
|
239
|
+
["status", "host"],
|
|
240
|
+
"portable invocation lease containment",
|
|
241
|
+
);
|
|
242
|
+
return Object.freeze({
|
|
243
|
+
status: containment.status,
|
|
244
|
+
host: containment.host === null
|
|
245
|
+
? null
|
|
246
|
+
: parseProcessOwner(
|
|
247
|
+
containment.host,
|
|
248
|
+
"portable invocation lease host",
|
|
249
|
+
),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
throw new Error("portable invocation lease containment is malformed");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function parsePortableProviderPluginInvocationLease(
|
|
256
|
+
value: unknown,
|
|
257
|
+
): PortableProviderPluginInvocationLease {
|
|
258
|
+
const lease = record(value, "portable invocation lease");
|
|
259
|
+
if (lease.schemaVersion === LEGACY_LEASE_SCHEMA_VERSION) {
|
|
260
|
+
exactKeys(
|
|
261
|
+
lease,
|
|
262
|
+
[
|
|
263
|
+
"schemaVersion",
|
|
264
|
+
"leaseId",
|
|
265
|
+
"runId",
|
|
266
|
+
"identity",
|
|
267
|
+
"owner",
|
|
268
|
+
"acquiredAt",
|
|
269
|
+
],
|
|
270
|
+
"portable invocation lease",
|
|
271
|
+
);
|
|
272
|
+
return Object.freeze({
|
|
273
|
+
schemaVersion: LEGACY_LEASE_SCHEMA_VERSION,
|
|
274
|
+
leaseId: uuid(lease.leaseId, "portable invocation lease ID"),
|
|
275
|
+
runId: uuid(lease.runId, "portable invocation run ID"),
|
|
276
|
+
identity: parsePortableOperationIdentityV1(lease.identity),
|
|
277
|
+
owner: parseOwner(lease.owner),
|
|
278
|
+
acquiredAt: timestamp(
|
|
279
|
+
lease.acquiredAt,
|
|
280
|
+
"portable invocation lease acquisition time",
|
|
281
|
+
),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
exactKeys(
|
|
285
|
+
lease,
|
|
286
|
+
[
|
|
287
|
+
"schemaVersion",
|
|
288
|
+
"leaseId",
|
|
289
|
+
"runId",
|
|
290
|
+
"identity",
|
|
291
|
+
"owner",
|
|
292
|
+
"acquiredAt",
|
|
293
|
+
"containment",
|
|
294
|
+
],
|
|
295
|
+
"portable invocation lease",
|
|
296
|
+
);
|
|
297
|
+
if (lease.schemaVersion !== LEASE_SCHEMA_VERSION) {
|
|
298
|
+
throw new Error("portable invocation lease schema is unsupported");
|
|
299
|
+
}
|
|
300
|
+
return Object.freeze({
|
|
301
|
+
schemaVersion: LEASE_SCHEMA_VERSION,
|
|
302
|
+
leaseId: uuid(lease.leaseId, "portable invocation lease ID"),
|
|
303
|
+
runId: uuid(lease.runId, "portable invocation run ID"),
|
|
304
|
+
identity: parsePortableOperationIdentityV1(lease.identity),
|
|
305
|
+
owner: parseOwner(lease.owner),
|
|
306
|
+
acquiredAt: timestamp(
|
|
307
|
+
lease.acquiredAt,
|
|
308
|
+
"portable invocation lease acquisition time",
|
|
309
|
+
),
|
|
310
|
+
containment: parseContainment(lease.containment),
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function leaseDirectory(environment: Environment): string {
|
|
315
|
+
return join(ghostgetStateHome(environment), "provider-plugin-state", ".invocations");
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function leasePath(
|
|
319
|
+
leaseId: string,
|
|
320
|
+
environment: Environment,
|
|
321
|
+
): string {
|
|
322
|
+
return join(
|
|
323
|
+
leaseDirectory(environment),
|
|
324
|
+
`${uuid(leaseId, "portable invocation lease ID")}.json`,
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function storeRoot(environment: Environment): string {
|
|
329
|
+
return join(ghostgetStateHome(environment), "provider-plugins");
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function snapshot(
|
|
333
|
+
leaseValue: PortableProviderPluginInvocationLease,
|
|
334
|
+
): PortableProviderPluginInvocationLeaseSnapshot {
|
|
335
|
+
const lease = parsePortableProviderPluginInvocationLease(leaseValue);
|
|
336
|
+
return Object.freeze({
|
|
337
|
+
lease,
|
|
338
|
+
contentSha256: sha256(`${canonicalJson(lease)}\n`),
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function assertExactIdentityIsActive(
|
|
343
|
+
identity: PortableOperationIdentityV1,
|
|
344
|
+
environment: Environment,
|
|
345
|
+
): void {
|
|
346
|
+
const installed = loadInstalledPortableProviderPlugin(
|
|
347
|
+
storeRoot(environment),
|
|
348
|
+
identity.pluginId,
|
|
349
|
+
);
|
|
350
|
+
if (
|
|
351
|
+
installed === null
|
|
352
|
+
|| installed.package.manifest.id !== identity.pluginId
|
|
353
|
+
|| installed.package.manifest.version !== identity.pluginVersion
|
|
354
|
+
|| installed.package.manifest.hostApiVersion !== identity.hostApiVersion
|
|
355
|
+
|| installed.package.bundleSha256 !== identity.bundleSha256
|
|
356
|
+
|| installed.package.manifestSha256 !== identity.manifestSha256
|
|
357
|
+
) {
|
|
358
|
+
throw new Error(
|
|
359
|
+
`portable provider plugin ${identity.pluginId} changed or was disabled before execution; retry from current capabilities`,
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function assertPortableProviderPluginInvocationAdmitted(
|
|
365
|
+
identity: PortableOperationIdentityV1,
|
|
366
|
+
environment: Environment,
|
|
367
|
+
): void {
|
|
368
|
+
for (const entry of listPortableProviderPluginInvocationLeases(environment)) {
|
|
369
|
+
if ("invalid" in entry) {
|
|
370
|
+
throw new Error(
|
|
371
|
+
`portable provider plugin invocation is blocked by malformed lease ${entry.leaseId}; run ghostget plugin doctor before retrying`,
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
const existing = entry.lease;
|
|
375
|
+
if (existing.identity.pluginId !== identity.pluginId) continue;
|
|
376
|
+
if (
|
|
377
|
+
existing.schemaVersion === LEASE_SCHEMA_VERSION
|
|
378
|
+
&& existing.containment.status === "cleanup-complete"
|
|
379
|
+
) {
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (
|
|
383
|
+
existing.schemaVersion === LEASE_SCHEMA_VERSION
|
|
384
|
+
&& existing.containment.status === "cleanup-unsafe"
|
|
385
|
+
) {
|
|
386
|
+
throw new Error(
|
|
387
|
+
`portable provider plugin ${identity.pluginId} invocation is blocked by cleanup-unsafe lease ${existing.leaseId}; run ghostget plugin doctor before retrying`,
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
if (
|
|
391
|
+
portableProviderPluginInvocationLeaseOwnerStatus(entry)
|
|
392
|
+
!== "exact-live-owner"
|
|
393
|
+
) {
|
|
394
|
+
throw new Error(
|
|
395
|
+
`portable provider plugin ${identity.pluginId} invocation is blocked by unverifiable non-complete lease ${existing.leaseId}; run ghostget plugin doctor before retrying`,
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function acquirePortableProviderPluginInvocationLease(
|
|
402
|
+
identityValue: PortableOperationIdentityV1,
|
|
403
|
+
runIdValue: string,
|
|
404
|
+
environment: Environment = process.env,
|
|
405
|
+
now = new Date(),
|
|
406
|
+
): PortableProviderPluginInvocationLeaseSnapshot {
|
|
407
|
+
const identity = parsePortableOperationIdentityV1(identityValue);
|
|
408
|
+
const runId = uuid(runIdValue, "portable invocation run ID");
|
|
409
|
+
return withPortableProviderPluginCatalogLock(
|
|
410
|
+
storeRoot(environment),
|
|
411
|
+
now,
|
|
412
|
+
() => {
|
|
413
|
+
assertExactIdentityIsActive(identity, environment);
|
|
414
|
+
ensurePrivateStateDirectory(leaseDirectory(environment), environment);
|
|
415
|
+
assertPortableProviderPluginInvocationAdmitted(identity, environment);
|
|
416
|
+
const lease: PortableProviderPluginInvocationLease =
|
|
417
|
+
Object.freeze({
|
|
418
|
+
schemaVersion: LEASE_SCHEMA_VERSION,
|
|
419
|
+
leaseId: randomUUID(),
|
|
420
|
+
runId,
|
|
421
|
+
identity,
|
|
422
|
+
owner: Object.freeze({
|
|
423
|
+
pid: process.pid,
|
|
424
|
+
token: randomUUID(),
|
|
425
|
+
...currentProcessStartIdentity(),
|
|
426
|
+
}),
|
|
427
|
+
acquiredAt: now.toISOString(),
|
|
428
|
+
containment: Object.freeze({
|
|
429
|
+
status: "parent-owned",
|
|
430
|
+
}),
|
|
431
|
+
});
|
|
432
|
+
const created = createPrivateJsonIfAbsent(
|
|
433
|
+
leasePath(lease.leaseId, environment),
|
|
434
|
+
lease,
|
|
435
|
+
{ environment },
|
|
436
|
+
);
|
|
437
|
+
if (!created.created) {
|
|
438
|
+
throw new Error("portable invocation lease ID collided");
|
|
439
|
+
}
|
|
440
|
+
return snapshot(lease);
|
|
441
|
+
},
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function sameProcessOwner(
|
|
446
|
+
left: ProcessOwnerIdentity,
|
|
447
|
+
right: ProcessOwnerIdentity,
|
|
448
|
+
): boolean {
|
|
449
|
+
return (
|
|
450
|
+
left.pid === right.pid
|
|
451
|
+
&& left.bootId === right.bootId
|
|
452
|
+
&& left.processStartId === right.processStartId
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function replacePortableProviderPluginInvocationLease(
|
|
457
|
+
current: PortableProviderPluginInvocationLeaseSnapshot,
|
|
458
|
+
containment: PortableProviderPluginInvocationLeaseContainment,
|
|
459
|
+
environment: Environment,
|
|
460
|
+
now = new Date(),
|
|
461
|
+
): PortableProviderPluginInvocationLeaseSnapshot {
|
|
462
|
+
const checked = snapshot(current.lease);
|
|
463
|
+
if (checked.contentSha256 !== current.contentSha256) {
|
|
464
|
+
throw new Error("portable invocation lease snapshot is not content-bound");
|
|
465
|
+
}
|
|
466
|
+
if (checked.lease.schemaVersion !== LEASE_SCHEMA_VERSION) {
|
|
467
|
+
throw new Error("legacy portable invocation leases cannot bind containment");
|
|
468
|
+
}
|
|
469
|
+
const next = snapshot(Object.freeze({
|
|
470
|
+
...checked.lease,
|
|
471
|
+
containment,
|
|
472
|
+
}));
|
|
473
|
+
withPortableProviderPluginCatalogLock(
|
|
474
|
+
storeRoot(environment),
|
|
475
|
+
now,
|
|
476
|
+
() => {
|
|
477
|
+
if (!writePrivateJsonIfUnchanged(
|
|
478
|
+
leasePath(checked.lease.leaseId, environment),
|
|
479
|
+
next.lease,
|
|
480
|
+
{ expectedCurrentContentSha256: checked.contentSha256 },
|
|
481
|
+
)) {
|
|
482
|
+
throw new Error(
|
|
483
|
+
"portable invocation lease changed or disappeared before containment update",
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
);
|
|
488
|
+
return next;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
export type PortableProviderPluginInvocationLeaseContainmentController =
|
|
492
|
+
PortableProviderPluginCleanupContainment & {
|
|
493
|
+
readonly current: PortableProviderPluginInvocationLeaseSnapshot;
|
|
494
|
+
readonly cleanupComplete: () => void;
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Bind host containment transitions to one exact durable lease. Every update
|
|
499
|
+
* is a content-addressed CAS under the catalog lock.
|
|
500
|
+
*/
|
|
501
|
+
export function createPortableProviderPluginInvocationLeaseContainmentController(
|
|
502
|
+
initial: PortableProviderPluginInvocationLeaseSnapshot,
|
|
503
|
+
environment: Environment = process.env,
|
|
504
|
+
): PortableProviderPluginInvocationLeaseContainmentController {
|
|
505
|
+
let current = snapshot(initial.lease);
|
|
506
|
+
if (current.contentSha256 !== initial.contentSha256) {
|
|
507
|
+
throw new Error("portable invocation lease snapshot is not content-bound");
|
|
508
|
+
}
|
|
509
|
+
const controller: PortableProviderPluginInvocationLeaseContainmentController = {
|
|
510
|
+
get current() {
|
|
511
|
+
return current;
|
|
512
|
+
},
|
|
513
|
+
hostStarting: (host) => {
|
|
514
|
+
const lease = current.lease;
|
|
515
|
+
if (lease.schemaVersion !== LEASE_SCHEMA_VERSION) {
|
|
516
|
+
throw new Error("legacy portable invocation leases cannot start a host");
|
|
517
|
+
}
|
|
518
|
+
if (lease.containment.status !== "parent-owned") {
|
|
519
|
+
throw new Error("portable invocation lease host started out of order");
|
|
520
|
+
}
|
|
521
|
+
current = replacePortableProviderPluginInvocationLease(
|
|
522
|
+
current,
|
|
523
|
+
Object.freeze({
|
|
524
|
+
status: "host-starting",
|
|
525
|
+
host: Object.freeze({ ...host }),
|
|
526
|
+
}),
|
|
527
|
+
environment,
|
|
528
|
+
);
|
|
529
|
+
},
|
|
530
|
+
hostStarted: (host) => {
|
|
531
|
+
const lease = current.lease;
|
|
532
|
+
if (
|
|
533
|
+
lease.schemaVersion !== LEASE_SCHEMA_VERSION
|
|
534
|
+
|| lease.containment.status !== "host-starting"
|
|
535
|
+
|| !sameProcessOwner(lease.containment.host, host)
|
|
536
|
+
) {
|
|
537
|
+
throw new Error("portable invocation lease host admission changed");
|
|
538
|
+
}
|
|
539
|
+
current = replacePortableProviderPluginInvocationLease(
|
|
540
|
+
current,
|
|
541
|
+
Object.freeze({
|
|
542
|
+
status: "host-active",
|
|
543
|
+
host: Object.freeze({ ...host }),
|
|
544
|
+
}),
|
|
545
|
+
environment,
|
|
546
|
+
);
|
|
547
|
+
},
|
|
548
|
+
cleanupComplete: () => {
|
|
549
|
+
const lease = current.lease;
|
|
550
|
+
if (lease.schemaVersion !== LEASE_SCHEMA_VERSION) {
|
|
551
|
+
throw new Error("legacy portable invocation lease cleanup is unverifiable");
|
|
552
|
+
}
|
|
553
|
+
if (lease.containment.status === "cleanup-complete") return;
|
|
554
|
+
if (lease.containment.status === "cleanup-unsafe") {
|
|
555
|
+
throw new Error(
|
|
556
|
+
"portable invocation lease cleanup cannot complete after becoming unsafe",
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
const host = lease.containment.status === "parent-owned"
|
|
560
|
+
? null
|
|
561
|
+
: lease.containment.host;
|
|
562
|
+
current = replacePortableProviderPluginInvocationLease(
|
|
563
|
+
current,
|
|
564
|
+
Object.freeze({
|
|
565
|
+
status: "cleanup-complete",
|
|
566
|
+
host,
|
|
567
|
+
}),
|
|
568
|
+
environment,
|
|
569
|
+
);
|
|
570
|
+
},
|
|
571
|
+
cleanupUnsafe: () => {
|
|
572
|
+
const lease = current.lease;
|
|
573
|
+
if (lease.schemaVersion !== LEASE_SCHEMA_VERSION) {
|
|
574
|
+
throw new Error("legacy portable invocation lease cleanup is unverifiable");
|
|
575
|
+
}
|
|
576
|
+
if (lease.containment.status === "cleanup-unsafe") return;
|
|
577
|
+
if (lease.containment.status === "cleanup-complete") {
|
|
578
|
+
throw new Error(
|
|
579
|
+
"portable invocation lease cleanup became unsafe after completion",
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
const host = lease.containment.status === "parent-owned"
|
|
583
|
+
? null
|
|
584
|
+
: lease.containment.host;
|
|
585
|
+
current = replacePortableProviderPluginInvocationLease(
|
|
586
|
+
current,
|
|
587
|
+
Object.freeze({
|
|
588
|
+
status: "cleanup-unsafe",
|
|
589
|
+
host,
|
|
590
|
+
}),
|
|
591
|
+
environment,
|
|
592
|
+
);
|
|
593
|
+
},
|
|
594
|
+
};
|
|
595
|
+
return Object.freeze(controller);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
export function releasePortableProviderPluginInvocationLease(
|
|
599
|
+
current: PortableProviderPluginInvocationLeaseSnapshot,
|
|
600
|
+
environment: Environment = process.env,
|
|
601
|
+
now = new Date(),
|
|
602
|
+
): void {
|
|
603
|
+
const checked = snapshot(current.lease);
|
|
604
|
+
if (checked.contentSha256 !== current.contentSha256) {
|
|
605
|
+
throw new Error("portable invocation lease snapshot is not content-bound");
|
|
606
|
+
}
|
|
607
|
+
if (
|
|
608
|
+
checked.lease.schemaVersion !== LEASE_SCHEMA_VERSION
|
|
609
|
+
|| checked.lease.containment.status !== "cleanup-complete"
|
|
610
|
+
) {
|
|
611
|
+
throw new Error(
|
|
612
|
+
"portable invocation lease cannot be released before durable cleanup completion",
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
withPortableProviderPluginCatalogLock(
|
|
616
|
+
storeRoot(environment),
|
|
617
|
+
now,
|
|
618
|
+
() => {
|
|
619
|
+
if (!removePrivateStateFileIfUnchanged(
|
|
620
|
+
leasePath(checked.lease.leaseId, environment),
|
|
621
|
+
{ expectedCurrentContentSha256: checked.contentSha256 },
|
|
622
|
+
environment,
|
|
623
|
+
)) {
|
|
624
|
+
throw new Error(
|
|
625
|
+
"portable invocation lease changed or disappeared before release",
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
},
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export function listPortableProviderPluginInvocationLeases(
|
|
633
|
+
environment: Environment = process.env,
|
|
634
|
+
): readonly PortableProviderPluginInvocationLeaseListEntry[] {
|
|
635
|
+
const directory = leaseDirectory(environment);
|
|
636
|
+
const directorySnapshot = snapshotPrivateStateDirectory(
|
|
637
|
+
directory,
|
|
638
|
+
environment,
|
|
639
|
+
);
|
|
640
|
+
if (directorySnapshot.identity === null) return Object.freeze([]);
|
|
641
|
+
const candidates = directorySnapshot.entries.filter((entry) =>
|
|
642
|
+
uuidPattern.test(entry.name.slice(0, -5))
|
|
643
|
+
&& entry.name.endsWith(".json")
|
|
644
|
+
);
|
|
645
|
+
const files = readPrivateStateFilesBatched(
|
|
646
|
+
directory,
|
|
647
|
+
candidates
|
|
648
|
+
.filter((entry) => entry.kind === "file")
|
|
649
|
+
.map((entry) => entry.name),
|
|
650
|
+
{
|
|
651
|
+
maximumBytesPerFile: MAX_LEASE_BYTES,
|
|
652
|
+
environment,
|
|
653
|
+
expectedDirectoryIdentity: directorySnapshot.identity,
|
|
654
|
+
},
|
|
655
|
+
);
|
|
656
|
+
const byName = new Map(files.map((file) => [file.name, file] as const));
|
|
657
|
+
return Object.freeze(candidates.map((entry) => {
|
|
658
|
+
const leaseId = entry.name.slice(0, -5);
|
|
659
|
+
if (entry.kind !== "file") return { leaseId, invalid: true as const };
|
|
660
|
+
const file = byName.get(entry.name);
|
|
661
|
+
if (file?.status !== "present") {
|
|
662
|
+
return { leaseId, invalid: true as const };
|
|
663
|
+
}
|
|
664
|
+
try {
|
|
665
|
+
const lease = parsePortableProviderPluginInvocationLease(
|
|
666
|
+
JSON.parse(file.content) as unknown,
|
|
667
|
+
);
|
|
668
|
+
if (
|
|
669
|
+
lease.leaseId !== leaseId
|
|
670
|
+
|| file.content !== `${canonicalJson(lease)}\n`
|
|
671
|
+
) {
|
|
672
|
+
return { leaseId, invalid: true as const };
|
|
673
|
+
}
|
|
674
|
+
return Object.freeze({
|
|
675
|
+
lease,
|
|
676
|
+
contentSha256: sha256(file.content),
|
|
677
|
+
});
|
|
678
|
+
} catch {
|
|
679
|
+
return { leaseId, invalid: true as const };
|
|
680
|
+
}
|
|
681
|
+
}));
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function combinedOwnerStatus(
|
|
685
|
+
lease: PortableProviderPluginInvocationLease,
|
|
686
|
+
inspectOwner: (
|
|
687
|
+
owner: ProcessOwnerIdentity,
|
|
688
|
+
) => ProcessOwnerStatus,
|
|
689
|
+
): ProcessOwnerStatus {
|
|
690
|
+
const parent = inspectOwner(lease.owner);
|
|
691
|
+
if (
|
|
692
|
+
lease.schemaVersion === LEGACY_LEASE_SCHEMA_VERSION
|
|
693
|
+
|| lease.containment.status === "parent-owned"
|
|
694
|
+
) {
|
|
695
|
+
return parent;
|
|
696
|
+
}
|
|
697
|
+
const host = lease.containment.host;
|
|
698
|
+
if (host === null) return parent;
|
|
699
|
+
const child = inspectOwner(host);
|
|
700
|
+
if (
|
|
701
|
+
parent === "exact-live-owner"
|
|
702
|
+
|| child === "exact-live-owner"
|
|
703
|
+
) return "exact-live-owner";
|
|
704
|
+
if (parent === "unknown" || child === "unknown") return "unknown";
|
|
705
|
+
return "different-or-dead";
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
export function portableProviderPluginInvocationLeaseOwnerStatus(
|
|
709
|
+
snapshotValue: PortableProviderPluginInvocationLeaseSnapshot,
|
|
710
|
+
inspectOwner: (
|
|
711
|
+
owner: ProcessOwnerIdentity,
|
|
712
|
+
) => ProcessOwnerStatus = processOwnerStatus,
|
|
713
|
+
): ProcessOwnerStatus {
|
|
714
|
+
const checked = snapshot(snapshotValue.lease);
|
|
715
|
+
if (checked.contentSha256 !== snapshotValue.contentSha256) {
|
|
716
|
+
throw new Error("portable invocation lease snapshot is not content-bound");
|
|
717
|
+
}
|
|
718
|
+
const status = combinedOwnerStatus(checked.lease, inspectOwner);
|
|
719
|
+
if (checked.lease.schemaVersion === LEGACY_LEASE_SCHEMA_VERSION) {
|
|
720
|
+
// Schema v1 did not persist the detached host identity. A dead parent on
|
|
721
|
+
// the same boot is therefore not proof that its old host is gone.
|
|
722
|
+
return status === "exact-live-owner" ? status : "unknown";
|
|
723
|
+
}
|
|
724
|
+
if (
|
|
725
|
+
checked.lease.containment.status === "parent-owned"
|
|
726
|
+
|| checked.lease.containment.status === "host-starting"
|
|
727
|
+
|| checked.lease.containment.status === "host-active"
|
|
728
|
+
|| checked.lease.containment.status === "cleanup-unsafe"
|
|
729
|
+
) {
|
|
730
|
+
// Every pre-completion state is a tombstone. Parent/host death alone does
|
|
731
|
+
// not prove that detached descendants or parent-side helpers are gone.
|
|
732
|
+
return status === "exact-live-owner" ? status : "unknown";
|
|
733
|
+
}
|
|
734
|
+
return status;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
export type PortableProviderPluginInvocationLeaseRepairReport = {
|
|
738
|
+
readonly inspected: number;
|
|
739
|
+
readonly removed: number;
|
|
740
|
+
readonly active: number;
|
|
741
|
+
readonly unknown: number;
|
|
742
|
+
readonly invalid: number;
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
type PortableProviderPluginInvocationLeaseRepairOptions = {
|
|
746
|
+
readonly recoverContainmentTombstones?: boolean;
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Remove only leases whose exact process identity is definitely gone. Unknown
|
|
751
|
+
* ownership is retained and reported; wall-clock age is never used as proof.
|
|
752
|
+
*/
|
|
753
|
+
export function repairPortableProviderPluginInvocationLeases(
|
|
754
|
+
environment: Environment = process.env,
|
|
755
|
+
now = new Date(),
|
|
756
|
+
inspectOwner: (
|
|
757
|
+
owner: ProcessOwnerIdentity,
|
|
758
|
+
) => ProcessOwnerStatus = processOwnerStatus,
|
|
759
|
+
options: PortableProviderPluginInvocationLeaseRepairOptions = {},
|
|
760
|
+
): PortableProviderPluginInvocationLeaseRepairReport {
|
|
761
|
+
return withPortableProviderPluginCatalogLock(
|
|
762
|
+
storeRoot(environment),
|
|
763
|
+
now,
|
|
764
|
+
() => {
|
|
765
|
+
const entries = listPortableProviderPluginInvocationLeases(environment);
|
|
766
|
+
let removed = 0;
|
|
767
|
+
let active = 0;
|
|
768
|
+
let unknown = 0;
|
|
769
|
+
let invalid = 0;
|
|
770
|
+
for (const entry of entries) {
|
|
771
|
+
if ("invalid" in entry) {
|
|
772
|
+
invalid += 1;
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
let ownerStatus =
|
|
776
|
+
portableProviderPluginInvocationLeaseOwnerStatus(entry, inspectOwner);
|
|
777
|
+
const containmentTombstone = (
|
|
778
|
+
entry.lease.schemaVersion === LEGACY_LEASE_SCHEMA_VERSION
|
|
779
|
+
|| (
|
|
780
|
+
entry.lease.containment.status === "parent-owned"
|
|
781
|
+
|| entry.lease.containment.status === "host-starting"
|
|
782
|
+
|| entry.lease.containment.status === "host-active"
|
|
783
|
+
|| entry.lease.containment.status === "cleanup-unsafe"
|
|
784
|
+
)
|
|
785
|
+
);
|
|
786
|
+
if (
|
|
787
|
+
containmentTombstone
|
|
788
|
+
&& options.recoverContainmentTombstones === true
|
|
789
|
+
&& entry.lease.schemaVersion === LEASE_SCHEMA_VERSION
|
|
790
|
+
) {
|
|
791
|
+
if (
|
|
792
|
+
entry.lease.containment.status === "parent-owned"
|
|
793
|
+
|| entry.lease.containment.status === "host-starting"
|
|
794
|
+
) {
|
|
795
|
+
ownerStatus = combinedOwnerStatus(entry.lease, inspectOwner);
|
|
796
|
+
} else if (
|
|
797
|
+
entry.lease.containment.status === "host-active"
|
|
798
|
+
|| entry.lease.containment.status === "cleanup-unsafe"
|
|
799
|
+
) {
|
|
800
|
+
try {
|
|
801
|
+
const currentBootId = currentProcessStartIdentity().bootId;
|
|
802
|
+
const hostBootId = entry.lease.containment.host?.bootId;
|
|
803
|
+
// Once plugin code crossed admission, cleanup uncertainty can
|
|
804
|
+
// include detached descendants or parent-side helpers. Only a
|
|
805
|
+
// reboot proves every process from that boundary is gone.
|
|
806
|
+
ownerStatus = (
|
|
807
|
+
entry.lease.owner.bootId !== currentBootId
|
|
808
|
+
&& (hostBootId === undefined || hostBootId !== currentBootId)
|
|
809
|
+
)
|
|
810
|
+
? "different-or-dead"
|
|
811
|
+
: "unknown";
|
|
812
|
+
} catch {
|
|
813
|
+
ownerStatus = "unknown";
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
if (
|
|
818
|
+
entry.lease.schemaVersion === LEGACY_LEASE_SCHEMA_VERSION
|
|
819
|
+
&& options.recoverContainmentTombstones === true
|
|
820
|
+
) {
|
|
821
|
+
try {
|
|
822
|
+
// A different boot is OS-level proof that no process from the
|
|
823
|
+
// legacy lease can remain. Same-boot v1 state has no safe automatic
|
|
824
|
+
// recovery because it never recorded the detached child.
|
|
825
|
+
if (
|
|
826
|
+
entry.lease.owner.bootId
|
|
827
|
+
!== currentProcessStartIdentity().bootId
|
|
828
|
+
) {
|
|
829
|
+
ownerStatus = "different-or-dead";
|
|
830
|
+
}
|
|
831
|
+
} catch {
|
|
832
|
+
ownerStatus = "unknown";
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
if (ownerStatus === "exact-live-owner") {
|
|
836
|
+
active += 1;
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
if (
|
|
840
|
+
ownerStatus === "unknown"
|
|
841
|
+
|| (
|
|
842
|
+
containmentTombstone
|
|
843
|
+
&& options.recoverContainmentTombstones !== true
|
|
844
|
+
)
|
|
845
|
+
) {
|
|
846
|
+
unknown += 1;
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
if (removePrivateStateFileIfUnchanged(
|
|
850
|
+
leasePath(entry.lease.leaseId, environment),
|
|
851
|
+
{ expectedCurrentContentSha256: entry.contentSha256 },
|
|
852
|
+
environment,
|
|
853
|
+
)) {
|
|
854
|
+
removed += 1;
|
|
855
|
+
} else {
|
|
856
|
+
invalid += 1;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return Object.freeze({
|
|
860
|
+
inspected: entries.length,
|
|
861
|
+
removed,
|
|
862
|
+
active,
|
|
863
|
+
unknown,
|
|
864
|
+
invalid,
|
|
865
|
+
});
|
|
866
|
+
},
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* Explicit operator recovery used by plugin doctor. A pre-admission tombstone
|
|
872
|
+
* is removed only after exact parent-and-host death. Active or unsafe
|
|
873
|
+
* containment is removed only after reboot proves every potentially escaped
|
|
874
|
+
* descendant is gone.
|
|
875
|
+
*/
|
|
876
|
+
export function recoverPortableProviderPluginInvocationLeaseTombstones(
|
|
877
|
+
environment: Environment = process.env,
|
|
878
|
+
now = new Date(),
|
|
879
|
+
inspectOwner: (
|
|
880
|
+
owner: ProcessOwnerIdentity,
|
|
881
|
+
) => ProcessOwnerStatus = processOwnerStatus,
|
|
882
|
+
): PortableProviderPluginInvocationLeaseRepairReport {
|
|
883
|
+
return repairPortableProviderPluginInvocationLeases(
|
|
884
|
+
environment,
|
|
885
|
+
now,
|
|
886
|
+
inspectOwner,
|
|
887
|
+
{ recoverContainmentTombstones: true },
|
|
888
|
+
);
|
|
889
|
+
}
|