@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,818 @@
|
|
|
1
|
+
import type { GhostgetAuth } from "./auth";
|
|
2
|
+
import { loadAuth } from "./auth";
|
|
3
|
+
import {
|
|
4
|
+
canonicalJson,
|
|
5
|
+
sha256,
|
|
6
|
+
type LocalCliRecipe,
|
|
7
|
+
type OperationInput,
|
|
8
|
+
type WebSessionRecipe,
|
|
9
|
+
} from "./model";
|
|
10
|
+
import { cleanupPlanAssets } from "./plan-assets";
|
|
11
|
+
import { requireProviderPluginAuth } from "./provider-plugin-auth";
|
|
12
|
+
import {
|
|
13
|
+
runProviderPluginPlanConformance,
|
|
14
|
+
type ProviderPluginReconciliationContextV1,
|
|
15
|
+
type ProviderPluginReconciliationOptionsV1,
|
|
16
|
+
} from "./provider-plugin";
|
|
17
|
+
import { withLocalCliProviderCleanupAdmission } from "./local-cli-admission";
|
|
18
|
+
import type {
|
|
19
|
+
ProviderPluginOperationResolutionV1,
|
|
20
|
+
ProviderPluginRegistry,
|
|
21
|
+
} from "./provider-plugin-registry";
|
|
22
|
+
import { providerPluginRegistry } from "./provider-plugins";
|
|
23
|
+
import {
|
|
24
|
+
appendReconciliationObservation,
|
|
25
|
+
readProviderAcceptedMutationTargetEvidence,
|
|
26
|
+
readRecoveryCapsule,
|
|
27
|
+
removeProviderAcceptedMutationTargetEvidence,
|
|
28
|
+
removeRecoveryCapsule,
|
|
29
|
+
type ReconciliationObservation,
|
|
30
|
+
type RecoveryCapsule,
|
|
31
|
+
} from "./recovery";
|
|
32
|
+
import {
|
|
33
|
+
readRunReceipt,
|
|
34
|
+
releaseReconciledRunRecovery,
|
|
35
|
+
type RunReceipt,
|
|
36
|
+
} from "./runtime";
|
|
37
|
+
import {
|
|
38
|
+
getWebSessionContract,
|
|
39
|
+
isCompatibleWebSessionContractHash,
|
|
40
|
+
} from "./web-session-contracts";
|
|
41
|
+
import {
|
|
42
|
+
getLocalCliContract,
|
|
43
|
+
isCompatibleLocalCliContractHash,
|
|
44
|
+
} from "./local-cli-contracts";
|
|
45
|
+
|
|
46
|
+
type Environment = Readonly<Record<string, string | undefined>>;
|
|
47
|
+
|
|
48
|
+
export const LEGACY_X_CONTENT_SAVE_CONTRACT_HASH =
|
|
49
|
+
"7685de6bc2283991b5694b5f7ffda22f5251618ea76fe3e865d45ebe78f5d691";
|
|
50
|
+
export const LEGACY_X_CONTENT_SAVE_ADAPTER_HASH =
|
|
51
|
+
"731d3dee1bbf60c2d1ca33d4089039d8a3fb1ca2c9f3c5fe735c0531ef4539b4";
|
|
52
|
+
export const PRE_PROVIDER_PLUGIN_X_CONTENT_SAVE_CONTRACT_HASH =
|
|
53
|
+
"bcc3ae7768b24c0faa7654f9511570f2a85b4aced33e5f7f9bbfbbe559df99c9";
|
|
54
|
+
export const PRE_PROVIDER_PLUGIN_X_LIKES_SET_CONTRACT_HASH =
|
|
55
|
+
"994ebbb3206063a4a12d89c4ad73c1e73d6f8868e1135ca4278b3bc08c6dec4b";
|
|
56
|
+
export const PRE_PROVIDER_PLUGIN_X_ADAPTER_VERSION = "1.1.0";
|
|
57
|
+
export const PRE_PROVIDER_PLUGIN_X_ADAPTER_HASH =
|
|
58
|
+
"c9cec73197907ff846c4ef8142f3e8dd9d0cff3c9d4621102a35154d77f6265a";
|
|
59
|
+
|
|
60
|
+
export type ReconcileRunResult = {
|
|
61
|
+
readonly ok: boolean;
|
|
62
|
+
readonly status: "reconciliation-observed" | "reconciliation-inconclusive";
|
|
63
|
+
readonly runId: string;
|
|
64
|
+
readonly originalReceiptStatus: RunReceipt["status"];
|
|
65
|
+
readonly receiptUnchanged: true;
|
|
66
|
+
readonly providerWriteDispatched: false;
|
|
67
|
+
readonly recoveryArtifactsReleased: boolean;
|
|
68
|
+
readonly observation: ReconciliationObservation;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A transport-neutral deterministic observation seam for recovery tests.
|
|
73
|
+
* Production routing uses the resolved plugin binding's lazy reconciliation
|
|
74
|
+
* hook. Values cross an untrusted boundary and are validated below.
|
|
75
|
+
*/
|
|
76
|
+
export type WebSessionRecoveryDependencies = {
|
|
77
|
+
readonly observeActualState?: (
|
|
78
|
+
recipe: WebSessionRecipe,
|
|
79
|
+
input: OperationInput,
|
|
80
|
+
auth: GhostgetAuth,
|
|
81
|
+
context?: ProviderPluginReconciliationContextV1,
|
|
82
|
+
) => Promise<unknown>;
|
|
83
|
+
readonly observeLocalCliActualState?: (
|
|
84
|
+
recipe: LocalCliRecipe,
|
|
85
|
+
input: OperationInput,
|
|
86
|
+
auth: GhostgetAuth,
|
|
87
|
+
context?: ProviderPluginReconciliationContextV1,
|
|
88
|
+
options?: ProviderPluginReconciliationOptionsV1,
|
|
89
|
+
) => Promise<unknown>;
|
|
90
|
+
readonly releaseRecoveryArtifacts: (
|
|
91
|
+
runId: string,
|
|
92
|
+
planDigest: string,
|
|
93
|
+
environment: Environment,
|
|
94
|
+
) => void;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const defaultReleaseRecoveryArtifacts:
|
|
98
|
+
WebSessionRecoveryDependencies["releaseRecoveryArtifacts"] = (
|
|
99
|
+
runId,
|
|
100
|
+
planDigest,
|
|
101
|
+
environment,
|
|
102
|
+
) => {
|
|
103
|
+
// Remove the digest-bound media first so a cleanup failure leaves the
|
|
104
|
+
// encrypted capsule available for another inspection.
|
|
105
|
+
cleanupPlanAssets(planDigest, environment);
|
|
106
|
+
removeProviderAcceptedMutationTargetEvidence(runId, environment);
|
|
107
|
+
removeRecoveryCapsule(runId, environment);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
function isUnknownRecord(value: unknown): value is Record<string, unknown> {
|
|
111
|
+
return typeof value === "object"
|
|
112
|
+
&& value !== null
|
|
113
|
+
&& !Array.isArray(value);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type SelectedReconciliation = {
|
|
117
|
+
readonly receipt: ReconciliationReceipt;
|
|
118
|
+
readonly resolution: ProviderPluginOperationResolutionV1;
|
|
119
|
+
readonly input: OperationInput;
|
|
120
|
+
readonly inputSource: "capsule" | "provided";
|
|
121
|
+
readonly planDigest: string;
|
|
122
|
+
readonly contractIdentity:
|
|
123
|
+
| "current"
|
|
124
|
+
| "pre-provider-plugin-x"
|
|
125
|
+
| "ancient-x";
|
|
126
|
+
readonly reconciliationContext?: ProviderPluginReconciliationContextV1;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
type ReconciliationReceipt = Extract<
|
|
130
|
+
RunReceipt,
|
|
131
|
+
{ readonly schemaVersion: 4 | 7 }
|
|
132
|
+
>;
|
|
133
|
+
|
|
134
|
+
type PreProviderPluginXContract = {
|
|
135
|
+
readonly action: "content.save" | "likes.set";
|
|
136
|
+
readonly version: 1 | 2;
|
|
137
|
+
readonly hash: string;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const PRE_PROVIDER_PLUGIN_X_CONTRACTS = Object.freeze([
|
|
141
|
+
Object.freeze({
|
|
142
|
+
action: "content.save",
|
|
143
|
+
version: 1,
|
|
144
|
+
hash: PRE_PROVIDER_PLUGIN_X_CONTENT_SAVE_CONTRACT_HASH,
|
|
145
|
+
}),
|
|
146
|
+
Object.freeze({
|
|
147
|
+
action: "likes.set",
|
|
148
|
+
version: 2,
|
|
149
|
+
hash: PRE_PROVIDER_PLUGIN_X_LIKES_SET_CONTRACT_HASH,
|
|
150
|
+
}),
|
|
151
|
+
] as const satisfies readonly PreProviderPluginXContract[]);
|
|
152
|
+
|
|
153
|
+
function isUnsettled(status: RunReceipt["status"]): boolean {
|
|
154
|
+
return status === "pending"
|
|
155
|
+
|| status === "partial"
|
|
156
|
+
|| status === "indeterminate";
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function assertReconciliationReceipt(
|
|
160
|
+
receipt: RunReceipt,
|
|
161
|
+
): asserts receipt is ReconciliationReceipt {
|
|
162
|
+
if (!isUnsettled(receipt.status)) {
|
|
163
|
+
throw new Error("only an unsettled run can be reconciled");
|
|
164
|
+
}
|
|
165
|
+
if (receipt.planDigest === null) {
|
|
166
|
+
throw new Error("the unsettled run has no exact confirmation-plan digest");
|
|
167
|
+
}
|
|
168
|
+
if (receipt.risk !== "R2" && receipt.risk !== "R3") {
|
|
169
|
+
throw new Error(
|
|
170
|
+
"this reconciliation contract supports only exact R2/R3 desired-state writes",
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
if (
|
|
174
|
+
receipt.risk === "R3"
|
|
175
|
+
&& receipt.adapter.id === "x-web"
|
|
176
|
+
&& receipt.operation === "content.save"
|
|
177
|
+
) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
"this reconciliation contract supports only exact R2 X desired-state writes",
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
if (
|
|
183
|
+
receipt.dispatch.planned < 1
|
|
184
|
+
|| receipt.dispatch.started < 1
|
|
185
|
+
|| receipt.dispatch.started > receipt.dispatch.planned
|
|
186
|
+
|| receipt.dispatch.verified < 0
|
|
187
|
+
|| receipt.dispatch.verified > receipt.dispatch.started
|
|
188
|
+
) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
"the unsettled run does not have the exact one-dispatch or plugin-declared dispatched write schedule",
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
if (
|
|
194
|
+
(receipt.schemaVersion !== 4 || receipt.transport !== "web-session-api")
|
|
195
|
+
&& (receipt.schemaVersion !== 7 || receipt.transport !== "local-cli")
|
|
196
|
+
) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
"this reconciliation command supports authenticated session plugin runs",
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function isLegacyXContentSave(
|
|
204
|
+
receipt: Extract<RunReceipt, { readonly schemaVersion: 4 }>,
|
|
205
|
+
): boolean {
|
|
206
|
+
return receipt.operation === "content.save"
|
|
207
|
+
&& receipt.webSessionContractHash === LEGACY_X_CONTENT_SAVE_CONTRACT_HASH
|
|
208
|
+
&& receipt.adapter.id === "x-web"
|
|
209
|
+
&& receipt.adapter.version === "1.0.0"
|
|
210
|
+
&& receipt.adapter.hash === LEGACY_X_CONTENT_SAVE_ADAPTER_HASH;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function isPreProviderPluginXContract(
|
|
214
|
+
receipt: Extract<RunReceipt, { readonly schemaVersion: 4 }>,
|
|
215
|
+
capsule: RecoveryCapsule,
|
|
216
|
+
): boolean {
|
|
217
|
+
const contractIdentity = capsule.contract;
|
|
218
|
+
if (
|
|
219
|
+
receipt.adapter.id !== "x-web"
|
|
220
|
+
|| receipt.adapter.version !== PRE_PROVIDER_PLUGIN_X_ADAPTER_VERSION
|
|
221
|
+
|| receipt.adapter.hash !== PRE_PROVIDER_PLUGIN_X_ADAPTER_HASH
|
|
222
|
+
|| contractIdentity.transport !== "web-session-api"
|
|
223
|
+
|| contractIdentity.site !== "x"
|
|
224
|
+
) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
return PRE_PROVIDER_PLUGIN_X_CONTRACTS.some((contract) =>
|
|
228
|
+
receipt.operation === contract.action
|
|
229
|
+
&& receipt.webSessionContractHash === contract.hash
|
|
230
|
+
&& contractIdentity.action === contract.action
|
|
231
|
+
&& contractIdentity.version === contract.version
|
|
232
|
+
&& contractIdentity.hash === contract.hash
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function legacyXRecipe(): WebSessionRecipe {
|
|
237
|
+
return {
|
|
238
|
+
site: "x",
|
|
239
|
+
action: "content.save",
|
|
240
|
+
contractVersion: 1,
|
|
241
|
+
timeoutMs: 60_000,
|
|
242
|
+
maxOutputBytes: 2 * 1024 * 1024,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function assertCapsuleMatchesReceipt(
|
|
247
|
+
capsule: RecoveryCapsule,
|
|
248
|
+
receipt: ReconciliationReceipt,
|
|
249
|
+
): void {
|
|
250
|
+
const contractMatches = receipt.transport === "local-cli"
|
|
251
|
+
? capsule.contract.transport === "local-cli"
|
|
252
|
+
&& canonicalJson(capsule.contract.identity)
|
|
253
|
+
=== canonicalJson(receipt.localCliContract)
|
|
254
|
+
: capsule.contract.transport === "web-session-api"
|
|
255
|
+
&& capsule.contract.action === receipt.operation
|
|
256
|
+
&& capsule.contract.hash === receipt.webSessionContractHash;
|
|
257
|
+
if (
|
|
258
|
+
capsule.runId !== receipt.runId
|
|
259
|
+
|| capsule.createdAt !== receipt.startedAt
|
|
260
|
+
|| capsule.planDigest !== receipt.planDigest
|
|
261
|
+
|| canonicalJson(capsule.adapter) !== canonicalJson(receipt.adapter)
|
|
262
|
+
|| capsule.operation !== receipt.operation
|
|
263
|
+
|| capsule.risk !== receipt.risk
|
|
264
|
+
|| capsule.inputHash !== receipt.inputHash
|
|
265
|
+
|| canonicalJson(capsule.auth) !== canonicalJson(receipt.auth)
|
|
266
|
+
|| !contractMatches
|
|
267
|
+
) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
"encrypted recovery capsule does not match the immutable run receipt",
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function validateRecoveryInput(
|
|
275
|
+
resolution: ProviderPluginOperationResolutionV1,
|
|
276
|
+
value: OperationInput,
|
|
277
|
+
): OperationInput {
|
|
278
|
+
const issues = resolution.operation.validateInput(value);
|
|
279
|
+
if (issues.length > 0) {
|
|
280
|
+
throw new Error(`reconciliation input is invalid: ${issues.join("; ")}`);
|
|
281
|
+
}
|
|
282
|
+
// The encrypted capsule parser already checks the bounded OperationInput
|
|
283
|
+
// grammar. Canonicalizing here also rejects unsupported runtime values on
|
|
284
|
+
// the legacy caller-supplied path.
|
|
285
|
+
canonicalJson(value);
|
|
286
|
+
return value;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function resolveOperation(
|
|
290
|
+
transport: "web-session-api" | "local-cli",
|
|
291
|
+
site: string,
|
|
292
|
+
action: string,
|
|
293
|
+
version: number,
|
|
294
|
+
registry: ProviderPluginRegistry,
|
|
295
|
+
): ProviderPluginOperationResolutionV1 {
|
|
296
|
+
const binding = transport === "local-cli"
|
|
297
|
+
? registry.requireRoute("local-cli", site)
|
|
298
|
+
: registry.requireSessionRoute(site);
|
|
299
|
+
const resolution = registry.requireOperationDefinition(
|
|
300
|
+
binding.transport,
|
|
301
|
+
site,
|
|
302
|
+
action,
|
|
303
|
+
version,
|
|
304
|
+
);
|
|
305
|
+
if (resolution.binding.transport !== transport) {
|
|
306
|
+
throw new Error("reconciliation resolved to the wrong plugin transport");
|
|
307
|
+
}
|
|
308
|
+
if (
|
|
309
|
+
resolution.operation.reconciliation === undefined
|
|
310
|
+
|| resolution.binding.reconcile === undefined
|
|
311
|
+
) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`provider plugin operation ${site}/${action}@${version} has no registered reconciler`,
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
return resolution;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function providerBoundTargetReconciliationContext(
|
|
320
|
+
receipt: ReconciliationReceipt,
|
|
321
|
+
capsule: RecoveryCapsule,
|
|
322
|
+
resolution: ProviderPluginOperationResolutionV1,
|
|
323
|
+
input: OperationInput,
|
|
324
|
+
environment: Environment,
|
|
325
|
+
): ProviderPluginReconciliationContextV1 | undefined {
|
|
326
|
+
const reconciliation = resolution.operation.reconciliation;
|
|
327
|
+
if (
|
|
328
|
+
reconciliation?.kind !== "provider-accepted-target-presence"
|
|
329
|
+
&& reconciliation?.kind !== "provider-bound-target-desired-state"
|
|
330
|
+
) {
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
const targetKind = reconciliation.kind === "provider-accepted-target-presence"
|
|
334
|
+
? "provider-accepted"
|
|
335
|
+
: "provider-bound";
|
|
336
|
+
if (
|
|
337
|
+
receipt.dispatchStarted !== true
|
|
338
|
+
|| receipt.dispatch.planned !== 1
|
|
339
|
+
|| receipt.dispatch.started !== 1
|
|
340
|
+
|| receipt.dispatch.verified < 0
|
|
341
|
+
|| receipt.dispatch.verified > 1
|
|
342
|
+
) {
|
|
343
|
+
throw new Error(
|
|
344
|
+
`${targetKind} target reconciliation requires one exact started dispatch`,
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
const plannedDispatches = runProviderPluginPlanConformance(
|
|
348
|
+
resolution.operation,
|
|
349
|
+
input,
|
|
350
|
+
);
|
|
351
|
+
const plannedDispatch = plannedDispatches[0];
|
|
352
|
+
if (plannedDispatches.length !== 1 || plannedDispatch === undefined) {
|
|
353
|
+
throw new Error(
|
|
354
|
+
`${targetKind} target reconciliation requires one exact confirmed dispatch`,
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
const dispatch = Object.freeze({
|
|
358
|
+
id: plannedDispatch.id,
|
|
359
|
+
index: 1,
|
|
360
|
+
planned: 1,
|
|
361
|
+
});
|
|
362
|
+
const evidence = readProviderAcceptedMutationTargetEvidence(
|
|
363
|
+
capsule,
|
|
364
|
+
dispatch,
|
|
365
|
+
environment,
|
|
366
|
+
);
|
|
367
|
+
if (evidence === null) {
|
|
368
|
+
throw new Error(
|
|
369
|
+
reconciliation.kind === "provider-accepted-target-presence"
|
|
370
|
+
? "this provider-accepted target run has no encrypted response-derived target and is not safely reconcilable"
|
|
371
|
+
: "this provider-bound target run has no encrypted exact pre-dispatch target and is not safely reconcilable",
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
return Object.freeze({
|
|
375
|
+
schemaVersion: 1,
|
|
376
|
+
kind: reconciliation.kind,
|
|
377
|
+
dispatch,
|
|
378
|
+
target: evidence.target,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function selectReconciliation(
|
|
383
|
+
receipt: ReconciliationReceipt,
|
|
384
|
+
providedInput: unknown,
|
|
385
|
+
environment: Environment,
|
|
386
|
+
registry: ProviderPluginRegistry,
|
|
387
|
+
): SelectedReconciliation {
|
|
388
|
+
const capsule = readRecoveryCapsule(
|
|
389
|
+
receipt.runId,
|
|
390
|
+
receipt.auth.id,
|
|
391
|
+
receipt.auth.hash,
|
|
392
|
+
environment,
|
|
393
|
+
);
|
|
394
|
+
if (capsule !== null) {
|
|
395
|
+
assertCapsuleMatchesReceipt(capsule, receipt);
|
|
396
|
+
const recoveryRoute = capsule.contract.transport === "web-session-api"
|
|
397
|
+
? {
|
|
398
|
+
transport: "web-session-api" as const,
|
|
399
|
+
surface: capsule.contract.site,
|
|
400
|
+
action: capsule.contract.action,
|
|
401
|
+
version: capsule.contract.version,
|
|
402
|
+
}
|
|
403
|
+
: capsule.contract.transport === "local-cli"
|
|
404
|
+
? {
|
|
405
|
+
transport: "local-cli" as const,
|
|
406
|
+
surface: capsule.contract.identity.surface,
|
|
407
|
+
action: capsule.contract.identity.action,
|
|
408
|
+
version: capsule.contract.identity.version,
|
|
409
|
+
}
|
|
410
|
+
: null;
|
|
411
|
+
if (recoveryRoute === null) {
|
|
412
|
+
throw new Error("recovery capsule transport changed unexpectedly");
|
|
413
|
+
}
|
|
414
|
+
const resolution = resolveOperation(
|
|
415
|
+
recoveryRoute.transport,
|
|
416
|
+
recoveryRoute.surface,
|
|
417
|
+
recoveryRoute.action,
|
|
418
|
+
recoveryRoute.version,
|
|
419
|
+
registry,
|
|
420
|
+
);
|
|
421
|
+
const input = validateRecoveryInput(resolution, capsule.input);
|
|
422
|
+
if (
|
|
423
|
+
providedInput !== undefined
|
|
424
|
+
&& canonicalJson(providedInput) !== canonicalJson(input)
|
|
425
|
+
) {
|
|
426
|
+
throw new Error(
|
|
427
|
+
"provided reconciliation input does not match the encrypted recovery capsule",
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
const reconciliationContext =
|
|
431
|
+
providerBoundTargetReconciliationContext(
|
|
432
|
+
receipt,
|
|
433
|
+
capsule,
|
|
434
|
+
resolution,
|
|
435
|
+
input,
|
|
436
|
+
environment,
|
|
437
|
+
);
|
|
438
|
+
return {
|
|
439
|
+
receipt,
|
|
440
|
+
resolution,
|
|
441
|
+
input,
|
|
442
|
+
inputSource: "capsule",
|
|
443
|
+
planDigest: receipt.planDigest as string,
|
|
444
|
+
contractIdentity: receipt.schemaVersion === 4
|
|
445
|
+
&& isPreProviderPluginXContract(receipt, capsule)
|
|
446
|
+
? "pre-provider-plugin-x"
|
|
447
|
+
: "current",
|
|
448
|
+
...(reconciliationContext === undefined
|
|
449
|
+
? {}
|
|
450
|
+
: { reconciliationContext }),
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (receipt.schemaVersion !== 4 || !isLegacyXContentSave(receipt)) {
|
|
455
|
+
throw new Error(
|
|
456
|
+
"this run should have an encrypted recovery capsule; refusing caller-supplied replacement input",
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
if (
|
|
460
|
+
typeof providedInput !== "object"
|
|
461
|
+
|| providedInput === null
|
|
462
|
+
|| Array.isArray(providedInput)
|
|
463
|
+
) {
|
|
464
|
+
throw new Error(
|
|
465
|
+
"this legacy run has no encrypted recovery capsule; provide its exact original input with --input",
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
const recipe = legacyXRecipe();
|
|
469
|
+
const resolution = resolveOperation(
|
|
470
|
+
"web-session-api",
|
|
471
|
+
recipe.site,
|
|
472
|
+
recipe.action,
|
|
473
|
+
recipe.contractVersion,
|
|
474
|
+
registry,
|
|
475
|
+
);
|
|
476
|
+
const input = validateRecoveryInput(
|
|
477
|
+
resolution,
|
|
478
|
+
providedInput as OperationInput,
|
|
479
|
+
);
|
|
480
|
+
if (sha256(canonicalJson(input)) !== receipt.inputHash) {
|
|
481
|
+
throw new Error(
|
|
482
|
+
"provided reconciliation input does not match the run's canonical input hash",
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
if (receipt.planDigest === null) {
|
|
486
|
+
throw new Error("the unsettled run has no exact confirmation-plan digest");
|
|
487
|
+
}
|
|
488
|
+
return {
|
|
489
|
+
receipt,
|
|
490
|
+
resolution,
|
|
491
|
+
input,
|
|
492
|
+
inputSource: "provided",
|
|
493
|
+
planDigest: receipt.planDigest,
|
|
494
|
+
contractIdentity: "ancient-x",
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function assertExactHistoricalXSchedule(
|
|
499
|
+
receipt: Extract<RunReceipt, { readonly schemaVersion: 4 }>,
|
|
500
|
+
): void {
|
|
501
|
+
if (receipt.risk !== "R2") {
|
|
502
|
+
throw new Error(
|
|
503
|
+
"this reconciliation contract supports only exact R2 X desired-state writes",
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
if (
|
|
507
|
+
receipt.dispatchStarted !== true
|
|
508
|
+
|| receipt.dispatch.planned !== 1
|
|
509
|
+
|| receipt.dispatch.started !== 1
|
|
510
|
+
|| receipt.dispatch.verified < 0
|
|
511
|
+
|| receipt.dispatch.verified > 1
|
|
512
|
+
) {
|
|
513
|
+
throw new Error(
|
|
514
|
+
"the unsettled X run does not have the exact one-dispatch desired-state schedule",
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function assertSupportedContract(
|
|
520
|
+
selected: SelectedReconciliation,
|
|
521
|
+
registry: ProviderPluginRegistry,
|
|
522
|
+
): void {
|
|
523
|
+
const { binding, operation, contractVersion } = selected.resolution;
|
|
524
|
+
if (
|
|
525
|
+
binding.transport === "provider-api"
|
|
526
|
+
|| (
|
|
527
|
+
selected.receipt.transport === "local-cli"
|
|
528
|
+
? binding.transport !== "local-cli"
|
|
529
|
+
: binding.transport === "local-cli"
|
|
530
|
+
)
|
|
531
|
+
) {
|
|
532
|
+
throw new Error("reconciliation resolved to the wrong plugin transport");
|
|
533
|
+
}
|
|
534
|
+
if (operation.risk !== selected.receipt.risk) {
|
|
535
|
+
throw new Error(
|
|
536
|
+
"the unsettled run risk does not match its resolved plugin operation",
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
if (selected.contractIdentity !== "current") {
|
|
540
|
+
if (selected.receipt.schemaVersion !== 4) {
|
|
541
|
+
throw new Error("historical reconciliation identity is not a web-session receipt");
|
|
542
|
+
}
|
|
543
|
+
assertExactHistoricalXSchedule(selected.receipt);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const compatible = selected.receipt.transport === "local-cli"
|
|
547
|
+
? isCompatibleLocalCliContractHash(
|
|
548
|
+
getLocalCliContract({
|
|
549
|
+
surface: binding.surfaceId,
|
|
550
|
+
action: operation.name,
|
|
551
|
+
contractVersion,
|
|
552
|
+
timeoutMs: 60_000,
|
|
553
|
+
maxOutputBytes: 2 * 1024 * 1024,
|
|
554
|
+
}, registry),
|
|
555
|
+
selected.receipt.localCliContract.hash,
|
|
556
|
+
registry,
|
|
557
|
+
)
|
|
558
|
+
&& canonicalJson(selected.receipt.localCliContract.tool)
|
|
559
|
+
=== canonicalJson(
|
|
560
|
+
binding.transport === "local-cli" ? binding.tool : null,
|
|
561
|
+
)
|
|
562
|
+
: isCompatibleWebSessionContractHash(
|
|
563
|
+
getWebSessionContract({
|
|
564
|
+
site: binding.surfaceId,
|
|
565
|
+
action: operation.name,
|
|
566
|
+
contractVersion,
|
|
567
|
+
timeoutMs: 60_000,
|
|
568
|
+
maxOutputBytes: 2 * 1024 * 1024,
|
|
569
|
+
}, registry),
|
|
570
|
+
selected.receipt.webSessionContractHash,
|
|
571
|
+
registry,
|
|
572
|
+
);
|
|
573
|
+
if (!compatible) {
|
|
574
|
+
throw new Error(
|
|
575
|
+
"the unsettled run is bound to an unsupported authenticated session contract hash; its receipt, encrypted recovery capsule, and plan assets were retained. Run `ghostget doctor`, then use the exact predecessor build or complete a manual evidence review before reconciliation",
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
async function observeActualState(
|
|
581
|
+
selected: SelectedReconciliation,
|
|
582
|
+
auth: GhostgetAuth,
|
|
583
|
+
dependency:
|
|
584
|
+
| WebSessionRecoveryDependencies["observeActualState"]
|
|
585
|
+
| undefined,
|
|
586
|
+
localDependency:
|
|
587
|
+
| WebSessionRecoveryDependencies["observeLocalCliActualState"]
|
|
588
|
+
| undefined,
|
|
589
|
+
registry: ProviderPluginRegistry,
|
|
590
|
+
environment: Environment,
|
|
591
|
+
): Promise<boolean> {
|
|
592
|
+
const { binding, operation, contractVersion } = selected.resolution;
|
|
593
|
+
if (binding.transport === "provider-api" || binding.reconcile === undefined) {
|
|
594
|
+
throw new Error(
|
|
595
|
+
`provider plugin operation ${binding.surfaceId}/${operation.name}@${contractVersion} has no registered reconciler`,
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
const value = binding.transport === "local-cli"
|
|
599
|
+
? await withLocalCliProviderCleanupAdmission(
|
|
600
|
+
{
|
|
601
|
+
registry,
|
|
602
|
+
binding,
|
|
603
|
+
auth,
|
|
604
|
+
purpose: {
|
|
605
|
+
kind: "reconcile",
|
|
606
|
+
action: operation.name,
|
|
607
|
+
contractVersion,
|
|
608
|
+
recoveryRunId: selected.receipt.runId,
|
|
609
|
+
},
|
|
610
|
+
environment,
|
|
611
|
+
},
|
|
612
|
+
(registerCleanupBarrier) => localDependency === undefined
|
|
613
|
+
? binding.reconcile!(
|
|
614
|
+
operation.name,
|
|
615
|
+
selected.input,
|
|
616
|
+
auth,
|
|
617
|
+
selected.reconciliationContext,
|
|
618
|
+
{ environment, registerCleanupBarrier },
|
|
619
|
+
)
|
|
620
|
+
: localDependency(
|
|
621
|
+
{
|
|
622
|
+
surface: binding.surfaceId,
|
|
623
|
+
action: operation.name,
|
|
624
|
+
contractVersion,
|
|
625
|
+
timeoutMs: 60_000,
|
|
626
|
+
maxOutputBytes: 2 * 1024 * 1024,
|
|
627
|
+
},
|
|
628
|
+
selected.input,
|
|
629
|
+
auth,
|
|
630
|
+
selected.reconciliationContext,
|
|
631
|
+
{ environment, registerCleanupBarrier },
|
|
632
|
+
),
|
|
633
|
+
)
|
|
634
|
+
: dependency === undefined
|
|
635
|
+
? await binding.reconcile(
|
|
636
|
+
operation.name,
|
|
637
|
+
selected.input,
|
|
638
|
+
auth,
|
|
639
|
+
selected.reconciliationContext,
|
|
640
|
+
)
|
|
641
|
+
: await dependency(
|
|
642
|
+
{
|
|
643
|
+
site: binding.surfaceId,
|
|
644
|
+
action: operation.name,
|
|
645
|
+
contractVersion,
|
|
646
|
+
timeoutMs: 60_000,
|
|
647
|
+
maxOutputBytes: 2 * 1024 * 1024,
|
|
648
|
+
},
|
|
649
|
+
selected.input,
|
|
650
|
+
auth,
|
|
651
|
+
selected.reconciliationContext,
|
|
652
|
+
);
|
|
653
|
+
if (
|
|
654
|
+
!isUnknownRecord(value)
|
|
655
|
+
|| Object.keys(value).length !== 2
|
|
656
|
+
|| !Object.hasOwn(value, "actualState")
|
|
657
|
+
|| !Object.hasOwn(value, "reason")
|
|
658
|
+
|| typeof value.actualState !== "boolean"
|
|
659
|
+
|| typeof value.reason !== "string"
|
|
660
|
+
|| value.reason.length < 1
|
|
661
|
+
|| value.reason.length > 200
|
|
662
|
+
) {
|
|
663
|
+
throw new Error("provider plugin reconciliation returned an invalid readback");
|
|
664
|
+
}
|
|
665
|
+
return value.actualState;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
export async function reconcileWebSessionRun(
|
|
669
|
+
runId: string,
|
|
670
|
+
providedInput: unknown,
|
|
671
|
+
options: {
|
|
672
|
+
readonly environment?: Environment;
|
|
673
|
+
readonly now?: Date;
|
|
674
|
+
readonly dependencies?: Partial<WebSessionRecoveryDependencies>;
|
|
675
|
+
readonly registry?: ProviderPluginRegistry;
|
|
676
|
+
} = {},
|
|
677
|
+
): Promise<ReconcileRunResult> {
|
|
678
|
+
const environment = options.environment ?? process.env;
|
|
679
|
+
const registry = options.registry ?? providerPluginRegistry;
|
|
680
|
+
const receipt = readRunReceipt(runId, environment);
|
|
681
|
+
assertReconciliationReceipt(receipt);
|
|
682
|
+
const selected = selectReconciliation(
|
|
683
|
+
receipt,
|
|
684
|
+
providedInput,
|
|
685
|
+
environment,
|
|
686
|
+
registry,
|
|
687
|
+
);
|
|
688
|
+
assertSupportedContract(selected, registry);
|
|
689
|
+
|
|
690
|
+
const auth = loadAuth(receipt.auth.id, environment);
|
|
691
|
+
if (
|
|
692
|
+
sha256(canonicalJson(auth)) !== receipt.auth.hash
|
|
693
|
+
|| auth.kind !== receipt.auth.kind
|
|
694
|
+
) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
"the current auth locator no longer matches the unsettled run's exact realm",
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
requireProviderPluginAuth(selected.resolution.binding, auth);
|
|
700
|
+
if (
|
|
701
|
+
auth.subject === undefined
|
|
702
|
+
|| !selected.resolution.binding.subject.matches(auth.subject)
|
|
703
|
+
) {
|
|
704
|
+
throw new Error(
|
|
705
|
+
"the current auth locator no longer has the plugin's exact bound subject",
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
if (sha256(canonicalJson(selected.input)) !== receipt.inputHash) {
|
|
709
|
+
throw new Error(
|
|
710
|
+
"reconciliation input does not match the run's canonical input hash",
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const reconciliation = selected.resolution.operation.reconciliation;
|
|
715
|
+
if (reconciliation === undefined) {
|
|
716
|
+
throw new Error("provider plugin operation has no registered reconciler");
|
|
717
|
+
}
|
|
718
|
+
const desired = reconciliation.kind === "boolean-desired-state"
|
|
719
|
+
? reconciliation.desiredState(selected.input)
|
|
720
|
+
: reconciliation.kind === "provider-accepted-target-presence"
|
|
721
|
+
? true
|
|
722
|
+
: reconciliation.desiredState;
|
|
723
|
+
if (typeof desired !== "boolean") {
|
|
724
|
+
throw new Error(
|
|
725
|
+
"provider plugin reconciliation desired state is invalid",
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
const common = {
|
|
729
|
+
schemaVersion: 1 as const,
|
|
730
|
+
observationId: crypto.randomUUID(),
|
|
731
|
+
runId: receipt.runId,
|
|
732
|
+
observedAt: (options.now ?? new Date()).toISOString(),
|
|
733
|
+
receiptHash: sha256(canonicalJson(receipt)),
|
|
734
|
+
adapterHash: receipt.adapter.hash,
|
|
735
|
+
operation: receipt.operation,
|
|
736
|
+
inputHash: receipt.inputHash,
|
|
737
|
+
authHash: receipt.auth.hash,
|
|
738
|
+
contractHash: receipt.transport === "local-cli"
|
|
739
|
+
? receipt.localCliContract.hash
|
|
740
|
+
: receipt.webSessionContractHash,
|
|
741
|
+
inputSource: selected.inputSource,
|
|
742
|
+
};
|
|
743
|
+
let observation: ReconciliationObservation;
|
|
744
|
+
try {
|
|
745
|
+
const actual = await observeActualState(
|
|
746
|
+
selected,
|
|
747
|
+
auth,
|
|
748
|
+
options.dependencies?.observeActualState,
|
|
749
|
+
options.dependencies?.observeLocalCliActualState,
|
|
750
|
+
registry,
|
|
751
|
+
environment,
|
|
752
|
+
);
|
|
753
|
+
if (
|
|
754
|
+
reconciliation.kind === "provider-accepted-target-presence"
|
|
755
|
+
&& actual !== true
|
|
756
|
+
) {
|
|
757
|
+
throw new Error(
|
|
758
|
+
"provider-accepted target absence is not proof that the earlier write was not applied",
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
const matched = actual === desired;
|
|
762
|
+
observation = {
|
|
763
|
+
...common,
|
|
764
|
+
outcome: matched
|
|
765
|
+
? "desired-state-observed"
|
|
766
|
+
: "desired-state-not-observed",
|
|
767
|
+
desiredStateMatched: matched,
|
|
768
|
+
actualState: actual,
|
|
769
|
+
reason: "exact-readback",
|
|
770
|
+
};
|
|
771
|
+
} catch {
|
|
772
|
+
observation = {
|
|
773
|
+
...common,
|
|
774
|
+
outcome: "inconclusive",
|
|
775
|
+
desiredStateMatched: null,
|
|
776
|
+
actualState: null,
|
|
777
|
+
reason: "readback-failed",
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
appendReconciliationObservation(observation, environment);
|
|
781
|
+
|
|
782
|
+
let recoveryArtifactsReleased = false;
|
|
783
|
+
if (observation.outcome === "desired-state-observed") {
|
|
784
|
+
try {
|
|
785
|
+
const releaseKind = releaseReconciledRunRecovery(
|
|
786
|
+
receipt.runId,
|
|
787
|
+
common.receiptHash,
|
|
788
|
+
environment,
|
|
789
|
+
options.now ?? new Date(),
|
|
790
|
+
);
|
|
791
|
+
if (releaseKind === "legacy-no-journal") {
|
|
792
|
+
(
|
|
793
|
+
options.dependencies?.releaseRecoveryArtifacts
|
|
794
|
+
?? defaultReleaseRecoveryArtifacts
|
|
795
|
+
)(receipt.runId, selected.planDigest, environment);
|
|
796
|
+
}
|
|
797
|
+
recoveryArtifactsReleased =
|
|
798
|
+
releaseKind !== "journal-retained-for-duplicate-successor";
|
|
799
|
+
} catch (error) {
|
|
800
|
+
throw new Error(
|
|
801
|
+
"the desired-state observation was stored, but its recovery artifacts could not be fully released; rerun reconciliation",
|
|
802
|
+
{ cause: error },
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
return {
|
|
807
|
+
ok: observation.outcome === "desired-state-observed",
|
|
808
|
+
status: observation.outcome === "inconclusive"
|
|
809
|
+
? "reconciliation-inconclusive"
|
|
810
|
+
: "reconciliation-observed",
|
|
811
|
+
runId: receipt.runId,
|
|
812
|
+
originalReceiptStatus: receipt.status,
|
|
813
|
+
receiptUnchanged: true,
|
|
814
|
+
providerWriteDispatched: false,
|
|
815
|
+
recoveryArtifactsReleased,
|
|
816
|
+
observation,
|
|
817
|
+
};
|
|
818
|
+
}
|