@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,1055 @@
|
|
|
1
|
+
import * as Context from "effect/Context";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import { GENERIC_EXECUTOR_TERMINATION, type BoundedExecution, type LedgerEntry, type LedgerSnapshot, type RunPreparedOptions } from "./confirmed-write-model";
|
|
4
|
+
import type { PreparedInvocation, StoredPlan, InvocationPlan, InvocationDuplicateRiskV1, InvocationResult, RunReceipt, ConfirmationClaimSnapshot, ConfirmationClaimRepairReport, RunJournalRepairReport, confirmInvocation } from "./runtime";
|
|
5
|
+
import { ConfirmedWriteFailure, confirmedWriteAttempt, type ConfirmedWritePhase } from "./confirmed-write-failure";
|
|
6
|
+
|
|
7
|
+
import { redactSensitiveText } from "@hraness/kb/clip/persist";
|
|
8
|
+
|
|
9
|
+
import { executeBrowserRecipe, PreservedBrowserArtifactsError, type BrowserDispatchEvent } from "./browser";
|
|
10
|
+
import { canonicalJson, DOM_ACTION_TRANSPORT_DISABLED_MESSAGE, expandBrowserRecipe, isLocalCliOperation, isProviderOperation, isReviewedTemplateOperation, isWebSessionOperation, manifestHash, sha256, type FileInputValue, type InputValue, type GhostgetManifest } from "./model";
|
|
11
|
+
|
|
12
|
+
import { localCliContractIdentity } from "./local-cli-contracts";
|
|
13
|
+
import type { LocalCliDispatchEvent, LocalCliExecutionOptions } from "./local-cli-execution";
|
|
14
|
+
import { runLocalCliOperationWithDeadline } from "./local-cli-execution";
|
|
15
|
+
import { localCliToolArtifactForCurrentRuntime } from "./local-cli-tool-identity";
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
getProviderContract,
|
|
19
|
+
providerContractHash,
|
|
20
|
+
} from "./provider-contracts";
|
|
21
|
+
import {
|
|
22
|
+
executeProviderOperation,
|
|
23
|
+
type ProviderDispatchEvent,
|
|
24
|
+
} from "./provider";
|
|
25
|
+
import { runProviderPluginPlanConformance } from "./provider-plugin";
|
|
26
|
+
|
|
27
|
+
import type {
|
|
28
|
+
ProviderPluginOperationResolutionV1,
|
|
29
|
+
ProviderPluginRegistry,
|
|
30
|
+
} from "./provider-plugin-registry";
|
|
31
|
+
import { isProviderPluginOperationName } from "./provider-plugin-identifiers";
|
|
32
|
+
import { type PortableOperationIdentityV1 } from "./provider-plugin-portable-identity";
|
|
33
|
+
import {
|
|
34
|
+
acquirePortableProviderPluginInvocationLease,
|
|
35
|
+
createPortableProviderPluginInvocationLeaseContainmentController,
|
|
36
|
+
releasePortableProviderPluginInvocationLease,
|
|
37
|
+
} from "./provider-plugin-invocation-lease";
|
|
38
|
+
import {
|
|
39
|
+
createPortableProviderPluginCleanupContext,
|
|
40
|
+
} from "./provider-plugin-cleanup-barrier";
|
|
41
|
+
|
|
42
|
+
import { providerPluginRegistry } from "./provider-plugins";
|
|
43
|
+
import {
|
|
44
|
+
getWebSessionContract,
|
|
45
|
+
webSessionContractHash,
|
|
46
|
+
} from "./web-session-contracts";
|
|
47
|
+
import { runWebSessionOperationWithDeadline, WebSessionCleanupUnverifiedError, type WebSessionExecutionOptions, type WebSessionDispatchEvent, type WebSessionProviderAcceptedMutationTargetEvent, type WebSessionProviderBoundMutationTargetEvent } from "./web-session-execution";
|
|
48
|
+
import {
|
|
49
|
+
WebSessionCleanupAdmissionBlockedError,
|
|
50
|
+
acquireWebSessionCleanupAdmission,
|
|
51
|
+
type WebSessionCleanupAdmissionIdentity,
|
|
52
|
+
} from "./web-session-cleanup-admission";
|
|
53
|
+
import { isPublicWebSessionInvocationAuthority, persistedAuthAuthority, type InvocationAuthority } from "./web-session-authentication-policy";
|
|
54
|
+
import { executeReviewedTemplateOperation, planReviewedTemplateDispatches, reviewedTemplateHash, type ReviewedTemplateDispatchEvent } from "./reviewed-template";
|
|
55
|
+
import { cleanupPlanAssets, resolvePlanAssetFiles } from "./plan-assets";
|
|
56
|
+
import { writeProviderAcceptedMutationTargetEvidence, writeRecoveryCapsule, type RecoveryContractIdentity } from "./recovery";
|
|
57
|
+
import { createRunJournal, initialRunJournal, readRunJournal, updateRunJournal, type RunJournal, type RunJournalSnapshot } from "./run-journal";
|
|
58
|
+
import { currentProcessStartIdentity } from "./process-identity";
|
|
59
|
+
|
|
60
|
+
import { loadInstalledManifest, removePrivateStateFile } from "./storage";
|
|
61
|
+
|
|
62
|
+
export interface ConfirmedWriteKernel {
|
|
63
|
+
readonly isInputArray: (value: InputValue) => value is readonly (string | number | boolean | FileInputValue)[];
|
|
64
|
+
readonly isFileInputValue: (value: unknown) => value is FileInputValue;
|
|
65
|
+
readonly resolveCodeOwnedPluginOperation: (operation: GhostgetManifest["operations"][string], registry: ProviderPluginRegistry) => ProviderPluginOperationResolutionV1 | null;
|
|
66
|
+
readonly hasExactKeys: (value: Record<string, unknown>, keys: readonly string[]) => boolean;
|
|
67
|
+
readonly revalidatePreparedInvocation: (invocation: PreparedInvocation, registry: ProviderPluginRegistry) => {
|
|
68
|
+
readonly invocation: PreparedInvocation;
|
|
69
|
+
readonly operation: GhostgetManifest["operations"][string];
|
|
70
|
+
};
|
|
71
|
+
readonly planPath: (digest: string, environment: Readonly<Record<string, string | undefined>>) => string;
|
|
72
|
+
readonly acquireConfirmationClaim: (digest: string, runId: string, environment: Readonly<Record<string, string | undefined>>, now: Date) => ConfirmationClaimSnapshot;
|
|
73
|
+
readonly releaseConfirmationClaim: (snapshot: ConfirmationClaimSnapshot, environment: Readonly<Record<string, string | undefined>>) => boolean;
|
|
74
|
+
readonly authHash: (auth: InvocationAuthority) => string;
|
|
75
|
+
readonly finalOrigin: (value: string | null, origins: readonly string[]) => string | null;
|
|
76
|
+
readonly boundedRecoveryHandle: (value: string | undefined) => string | null;
|
|
77
|
+
readonly loadInstalledManifestWithRegistry: (adapterId: string, environment: Readonly<Record<string, string | undefined>>, registry: ProviderPluginRegistry) => ReturnType<typeof loadInstalledManifest>;
|
|
78
|
+
readonly resolveInvocationDuplicateRisk: (plan: InvocationPlan, requestedRunIds: readonly string[], environment: Readonly<Record<string, string | undefined>>) => InvocationDuplicateRiskV1 | undefined;
|
|
79
|
+
readonly claimDuplicateRiskSource: (binding: InvocationDuplicateRiskV1, successorRunId: string, environment: Readonly<Record<string, string | undefined>>, now: Date) => void;
|
|
80
|
+
readonly loadInvocationPlan: (digest: string, environment: Readonly<Record<string, string | undefined>>) => StoredPlan;
|
|
81
|
+
readonly planDigestHasRetainingJournal: (digest: string, environment: Readonly<Record<string, string | undefined>>) => boolean;
|
|
82
|
+
readonly validateFreshPlan: (stored: StoredPlan, environment: Readonly<Record<string, string | undefined>>, now: Date, registry: ProviderPluginRegistry, loadManifest: typeof loadInstalledManifest) => PreparedInvocation;
|
|
83
|
+
readonly isDispatchProgress: (value: unknown) => value is RunReceipt["dispatch"];
|
|
84
|
+
readonly ledgerPath: (adapterHash: string, authHashValue: string, operationId: string, inputHash: string, environment: Readonly<Record<string, string | undefined>>, duplicateIntentHash?: string) => string;
|
|
85
|
+
readonly acquireLedger: (path: string, entry: LedgerEntry, environment: Readonly<Record<string, string | undefined>>, now: Date) => | { readonly acquired: true; readonly snapshot: LedgerSnapshot }
|
|
86
|
+
| { readonly acquired: false; readonly existing: LedgerEntry };
|
|
87
|
+
readonly writeReceipt: (receipt: RunReceipt, environment: Readonly<Record<string, string | undefined>>) => void;
|
|
88
|
+
readonly runJournalReceipt: (journal: RunJournal) => RunReceipt;
|
|
89
|
+
readonly relativeStatePath: (path: string, environment: Readonly<Record<string, string | undefined>>) => string;
|
|
90
|
+
readonly projectRunJournal: (journal: RunJournal, environment: Readonly<Record<string, string | undefined>>) => void;
|
|
91
|
+
readonly repairInterruptedConfirmationClaims: (environment: Readonly<Record<string, string | undefined>>) => ConfirmationClaimRepairReport;
|
|
92
|
+
readonly repairInterruptedRunJournals: (environment: Readonly<Record<string, string | undefined>>, now: Date) => RunJournalRepairReport;
|
|
93
|
+
readonly foreignDataRecord: (value: unknown) => Record<string, unknown> | null;
|
|
94
|
+
readonly executionOutputLimit: (operation: GhostgetManifest["operations"][string]) => number;
|
|
95
|
+
readonly boundedThrownExecutorReason: (error: unknown) => string;
|
|
96
|
+
readonly boundedExecutionResult: (value: unknown, kind: | "browser"
|
|
97
|
+
| "provider"
|
|
98
|
+
| "web-session"
|
|
99
|
+
| "local-cli"
|
|
100
|
+
| "reviewed-template", maxOutputBytes: number) => BoundedExecution;
|
|
101
|
+
readonly readRunReceipt: (runId: string, environment: Readonly<Record<string, string | undefined>>) => RunReceipt;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
type ConfirmOptions = Parameters<typeof confirmInvocation>[1];
|
|
106
|
+
type NativeOutcome = { readonly status: "fulfilled"; readonly value: unknown } | { readonly status: "rejected"; readonly reason: unknown };
|
|
107
|
+
|
|
108
|
+
/** Each instance belongs to exactly one confirmation, including its native ALS context. */
|
|
109
|
+
export function makeConfirmedWritePlatform(kernel: ConfirmedWriteKernel, original: ConfirmOptions) {
|
|
110
|
+
const environment = original.environment ?? process.env;
|
|
111
|
+
let portableContext: ReturnType<typeof createPortableProviderPluginCleanupContext> | null = null;
|
|
112
|
+
const attempt = <A>(phase: ConfirmedWritePhase, work: () => A) =>
|
|
113
|
+
confirmedWriteAttempt(phase, () => portableContext === null ? work() : portableContext.run(work));
|
|
114
|
+
const native = <A>(phase: ConfirmedWritePhase, work: () => Promise<A>) =>
|
|
115
|
+
Effect.tryPromise({
|
|
116
|
+
try: () => portableContext === null ? work() : portableContext.run(work),
|
|
117
|
+
catch: cause => new ConfirmedWriteFailure({ phase, cause }),
|
|
118
|
+
}).pipe(Effect.uninterruptible);
|
|
119
|
+
const {
|
|
120
|
+
isInputArray, isFileInputValue,
|
|
121
|
+
resolveCodeOwnedPluginOperation,
|
|
122
|
+
hasExactKeys,
|
|
123
|
+
revalidatePreparedInvocation,
|
|
124
|
+
planPath,
|
|
125
|
+
acquireConfirmationClaim,
|
|
126
|
+
releaseConfirmationClaim,
|
|
127
|
+
authHash,
|
|
128
|
+
finalOrigin,
|
|
129
|
+
boundedRecoveryHandle,
|
|
130
|
+
loadInstalledManifestWithRegistry,
|
|
131
|
+
resolveInvocationDuplicateRisk,
|
|
132
|
+
claimDuplicateRiskSource,
|
|
133
|
+
loadInvocationPlan,
|
|
134
|
+
planDigestHasRetainingJournal,
|
|
135
|
+
validateFreshPlan,
|
|
136
|
+
isDispatchProgress,
|
|
137
|
+
ledgerPath,
|
|
138
|
+
acquireLedger,
|
|
139
|
+
writeReceipt,
|
|
140
|
+
runJournalReceipt,
|
|
141
|
+
relativeStatePath,
|
|
142
|
+
projectRunJournal,
|
|
143
|
+
repairInterruptedConfirmationClaims,
|
|
144
|
+
repairInterruptedRunJournals,
|
|
145
|
+
foreignDataRecord,
|
|
146
|
+
executionOutputLimit,
|
|
147
|
+
boundedThrownExecutorReason,
|
|
148
|
+
boundedExecutionResult,
|
|
149
|
+
readRunReceipt
|
|
150
|
+
} = kernel;
|
|
151
|
+
|
|
152
|
+
const now = () => original.now ?? new Date();
|
|
153
|
+
function execution(checked: ReturnType<ConfirmedWriteKernel["revalidatePreparedInvocation"]>, planDigest: string, options: RunPreparedOptions) {
|
|
154
|
+
const registry = options.registry ?? providerPluginRegistry;
|
|
155
|
+
const invocation = checked.invocation;
|
|
156
|
+
const operation = checked.operation;
|
|
157
|
+
if (operation.risk === "R4") throw new Error("R4 capabilities are blocked by wrench");
|
|
158
|
+
if (operation.risk !== "R2" && operation.risk !== "R3") throw new Error("only R2 and R3 plans use confirmation");
|
|
159
|
+
const risk = operation.risk;
|
|
160
|
+
const isWrite = true;
|
|
161
|
+
const persistReceipt = options.persistReceipt ?? writeReceipt;
|
|
162
|
+
const observedTime = () => options.now ?? new Date();
|
|
163
|
+
const providerOperation = isProviderOperation(operation);
|
|
164
|
+
const webSessionOperation = isWebSessionOperation(operation);
|
|
165
|
+
const localCliOperation = isLocalCliOperation(operation);
|
|
166
|
+
const reviewedTemplateOperation = isReviewedTemplateOperation(operation);
|
|
167
|
+
const pluginResolution = resolveCodeOwnedPluginOperation(operation, registry);
|
|
168
|
+
const plannedDispatches = pluginResolution !== null
|
|
169
|
+
? runProviderPluginPlanConformance(
|
|
170
|
+
pluginResolution.operation,
|
|
171
|
+
invocation.input,
|
|
172
|
+
)
|
|
173
|
+
: reviewedTemplateOperation
|
|
174
|
+
? planReviewedTemplateDispatches(invocation.operationId, operation.risk, operation.reviewedTemplate)
|
|
175
|
+
: operation.browser === undefined
|
|
176
|
+
? (() => {
|
|
177
|
+
throw new Error(DOM_ACTION_TRANSPORT_DISABLED_MESSAGE);
|
|
178
|
+
})()
|
|
179
|
+
: expandBrowserRecipe(operation.browser, invocation.input).dispatches;
|
|
180
|
+
if (
|
|
181
|
+
options.confirmedDispatches !== undefined
|
|
182
|
+
&& canonicalJson(plannedDispatches) !== canonicalJson(options.confirmedDispatches)
|
|
183
|
+
) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
"planned dispatch schedule changed before execution; preview the action again",
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
const planned = plannedDispatches.length;
|
|
189
|
+
const currentProviderContractHash = providerOperation
|
|
190
|
+
? providerContractHash(
|
|
191
|
+
getProviderContract(operation.provider, registry),
|
|
192
|
+
registry,
|
|
193
|
+
)
|
|
194
|
+
: null;
|
|
195
|
+
const currentWebSessionContractHash = webSessionOperation
|
|
196
|
+
? webSessionContractHash(
|
|
197
|
+
getWebSessionContract(operation.webSession, registry),
|
|
198
|
+
registry,
|
|
199
|
+
)
|
|
200
|
+
: null;
|
|
201
|
+
const currentLocalCliContractIdentity = localCliOperation
|
|
202
|
+
? localCliContractIdentity(operation.localCli, registry)
|
|
203
|
+
: null;
|
|
204
|
+
const currentReviewedTemplateContractHash = reviewedTemplateOperation
|
|
205
|
+
? reviewedTemplateHash(operation.reviewedTemplate)
|
|
206
|
+
: null;
|
|
207
|
+
const currentPortablePluginContract =
|
|
208
|
+
pluginResolution?.portableIdentity ?? null;
|
|
209
|
+
const recoveryContract = (): RecoveryContractIdentity => {
|
|
210
|
+
if (currentPortablePluginContract !== null) {
|
|
211
|
+
return {
|
|
212
|
+
transport: "portable-provider-plugin",
|
|
213
|
+
identity: currentPortablePluginContract,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (providerOperation) {
|
|
217
|
+
if (currentProviderContractHash === null) throw new Error("official provider contract hash is unavailable");
|
|
218
|
+
return {
|
|
219
|
+
transport: "provider-api",
|
|
220
|
+
provider: operation.provider.provider,
|
|
221
|
+
action: operation.provider.action,
|
|
222
|
+
version: operation.provider.contractVersion,
|
|
223
|
+
hash: currentProviderContractHash,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
if (webSessionOperation) {
|
|
227
|
+
if (currentWebSessionContractHash === null) throw new Error("authenticated web contract hash is unavailable");
|
|
228
|
+
return {
|
|
229
|
+
transport: "web-session-api",
|
|
230
|
+
site: operation.webSession.site,
|
|
231
|
+
action: operation.webSession.action,
|
|
232
|
+
version: operation.webSession.contractVersion,
|
|
233
|
+
hash: currentWebSessionContractHash,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
if (localCliOperation) {
|
|
237
|
+
if (currentLocalCliContractIdentity === null) {
|
|
238
|
+
throw new Error("local CLI contract identity is unavailable");
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
transport: "local-cli",
|
|
242
|
+
identity: currentLocalCliContractIdentity,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
if (reviewedTemplateOperation) {
|
|
246
|
+
if (currentReviewedTemplateContractHash === null) throw new Error("reviewed template contract hash is unavailable");
|
|
247
|
+
return {
|
|
248
|
+
transport: "reviewed-template-api",
|
|
249
|
+
version: operation.reviewedTemplate.contractVersion,
|
|
250
|
+
hash: currentReviewedTemplateContractHash,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
throw new Error(DOM_ACTION_TRANSPORT_DISABLED_MESSAGE);
|
|
254
|
+
};
|
|
255
|
+
const inputHash = sha256(canonicalJson(invocation.input));
|
|
256
|
+
const runId = options.runId ?? crypto.randomUUID();
|
|
257
|
+
if (!/^[0-9a-f-]{36}$/u.test(runId)) {
|
|
258
|
+
throw new Error("run ID is malformed");
|
|
259
|
+
}
|
|
260
|
+
const startedAt = (options.now ?? new Date()).toISOString();
|
|
261
|
+
const adapter = {
|
|
262
|
+
id: invocation.manifest.id,
|
|
263
|
+
version: invocation.manifest.version,
|
|
264
|
+
hash: manifestHash(invocation.manifest),
|
|
265
|
+
};
|
|
266
|
+
const auth = { id: invocation.auth.id, hash: authHash(invocation.auth), kind: invocation.auth.kind };
|
|
267
|
+
const durableWriteAuth = (): InvocationPlan["auth"] => {
|
|
268
|
+
if (!isWrite) throw new Error("read invocation has no durable write auth");
|
|
269
|
+
const selected = persistedAuthAuthority(invocation.auth);
|
|
270
|
+
return {
|
|
271
|
+
id: selected.id,
|
|
272
|
+
hash: authHash(selected),
|
|
273
|
+
kind: selected.kind,
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
const withTransport = (value: Omit<RunReceipt, "schemaVersion" | "transport" | "providerContractHash" | "webSessionContractHash" | "localCliContract" | "reviewedTemplateContractHash" | "portablePluginContract">): RunReceipt => {
|
|
277
|
+
if (currentPortablePluginContract !== null) {
|
|
278
|
+
return {
|
|
279
|
+
...value,
|
|
280
|
+
schemaVersion: 6,
|
|
281
|
+
transport: "portable-provider-plugin",
|
|
282
|
+
portablePluginContract: currentPortablePluginContract,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
if (providerOperation) {
|
|
286
|
+
if (currentProviderContractHash === null) throw new Error("official provider contract hash is unavailable");
|
|
287
|
+
return {
|
|
288
|
+
...value,
|
|
289
|
+
schemaVersion: 3,
|
|
290
|
+
transport: "provider-api",
|
|
291
|
+
providerContractHash: currentProviderContractHash,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (webSessionOperation) {
|
|
295
|
+
if (currentWebSessionContractHash === null) throw new Error("authenticated web contract hash is unavailable");
|
|
296
|
+
return {
|
|
297
|
+
...value,
|
|
298
|
+
schemaVersion: 4,
|
|
299
|
+
transport: "web-session-api",
|
|
300
|
+
webSessionContractHash: currentWebSessionContractHash,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
if (localCliOperation) {
|
|
304
|
+
if (currentLocalCliContractIdentity === null) {
|
|
305
|
+
throw new Error("local CLI contract identity is unavailable");
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
...value,
|
|
309
|
+
schemaVersion: 7,
|
|
310
|
+
transport: "local-cli",
|
|
311
|
+
localCliContract: currentLocalCliContractIdentity,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
if (reviewedTemplateOperation) {
|
|
315
|
+
if (currentReviewedTemplateContractHash === null) throw new Error("reviewed template contract hash is unavailable");
|
|
316
|
+
return {
|
|
317
|
+
...value,
|
|
318
|
+
schemaVersion: 5,
|
|
319
|
+
transport: "reviewed-template-api",
|
|
320
|
+
reviewedTemplateContractHash: currentReviewedTemplateContractHash,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
return { ...value, schemaVersion: 2, transport: "browser" };
|
|
324
|
+
};
|
|
325
|
+
let journal: RunJournalSnapshot | null = null;
|
|
326
|
+
let durableReceipt: RunReceipt = withTransport({
|
|
327
|
+
runId,
|
|
328
|
+
planDigest,
|
|
329
|
+
adapter,
|
|
330
|
+
operation: invocation.operationId,
|
|
331
|
+
risk: operation.risk,
|
|
332
|
+
inputHash,
|
|
333
|
+
auth,
|
|
334
|
+
status: "pending",
|
|
335
|
+
dispatchStarted: false,
|
|
336
|
+
dispatch: { planned, started: 0, verified: 0 },
|
|
337
|
+
startedAt,
|
|
338
|
+
finishedAt: startedAt,
|
|
339
|
+
finalOrigin: null,
|
|
340
|
+
error: "execution was prepared but no durable final outcome was recorded",
|
|
341
|
+
});
|
|
342
|
+
let duplicateSourceClaimed = false;
|
|
343
|
+
const persistDispatchProgress = (
|
|
344
|
+
event:
|
|
345
|
+
| BrowserDispatchEvent
|
|
346
|
+
| ProviderDispatchEvent
|
|
347
|
+
| WebSessionDispatchEvent
|
|
348
|
+
| LocalCliDispatchEvent
|
|
349
|
+
| ReviewedTemplateDispatchEvent,
|
|
350
|
+
phase: "starting" | "verified",
|
|
351
|
+
): Promise<void> => {
|
|
352
|
+
const expectedDispatch = plannedDispatches[event.index - 1];
|
|
353
|
+
const prior = durableReceipt.dispatch;
|
|
354
|
+
const expectedPrior = phase === "starting"
|
|
355
|
+
? { planned, started: event.index - 1, verified: event.index - 1 }
|
|
356
|
+
: { planned, started: event.index, verified: event.index - 1 };
|
|
357
|
+
if (
|
|
358
|
+
!isDispatchProgress(event.progress)
|
|
359
|
+
|| event.progress.planned !== planned
|
|
360
|
+
|| event.index < 1
|
|
361
|
+
|| event.index > planned
|
|
362
|
+
|| expectedDispatch === undefined
|
|
363
|
+
|| event.id !== expectedDispatch.id
|
|
364
|
+
|| canonicalJson(prior) !== canonicalJson(expectedPrior)
|
|
365
|
+
|| (phase === "starting"
|
|
366
|
+
? event.progress.started !== event.index - 1
|
|
367
|
+
|| event.progress.verified !== event.index - 1
|
|
368
|
+
: event.progress.started !== event.index
|
|
369
|
+
|| event.progress.verified !== event.index)
|
|
370
|
+
) {
|
|
371
|
+
return Promise.reject(new Error("dispatch progress diverged from the confirmed schedule"));
|
|
372
|
+
}
|
|
373
|
+
const dispatch = phase === "starting"
|
|
374
|
+
? { planned, started: event.index, verified: event.progress.verified }
|
|
375
|
+
: event.progress;
|
|
376
|
+
if (!isDispatchProgress(dispatch)) return Promise.reject(new Error("dispatch progress is malformed"));
|
|
377
|
+
const progressAt = (options.now ?? new Date()).toISOString();
|
|
378
|
+
let next: RunReceipt = {
|
|
379
|
+
...durableReceipt,
|
|
380
|
+
status: "pending",
|
|
381
|
+
dispatchStarted: dispatch.started > 0,
|
|
382
|
+
dispatch,
|
|
383
|
+
finishedAt: progressAt,
|
|
384
|
+
error: phase === "starting"
|
|
385
|
+
? "a dispatch was durably marked before provider submission; a missing final outcome requires reconciliation"
|
|
386
|
+
: "verified dispatch progress was stored; execution has not reached a durable final outcome",
|
|
387
|
+
};
|
|
388
|
+
try {
|
|
389
|
+
if (
|
|
390
|
+
phase === "starting"
|
|
391
|
+
&& event.index === 1
|
|
392
|
+
&& options.duplicateRisk !== undefined
|
|
393
|
+
&& !duplicateSourceClaimed
|
|
394
|
+
) {
|
|
395
|
+
claimDuplicateRiskSource(
|
|
396
|
+
options.duplicateRisk,
|
|
397
|
+
runId,
|
|
398
|
+
options.environment,
|
|
399
|
+
options.now ?? new Date(),
|
|
400
|
+
);
|
|
401
|
+
duplicateSourceClaimed = true;
|
|
402
|
+
}
|
|
403
|
+
if (journal !== null) {
|
|
404
|
+
journal = updateRunJournal(journal, {
|
|
405
|
+
type: phase === "starting" ? "dispatch-started" : "dispatch-verified",
|
|
406
|
+
index: event.index,
|
|
407
|
+
at: progressAt,
|
|
408
|
+
}, options.environment);
|
|
409
|
+
next = runJournalReceipt(journal.journal);
|
|
410
|
+
durableReceipt = next;
|
|
411
|
+
} else {
|
|
412
|
+
persistReceipt(next, options.environment);
|
|
413
|
+
}
|
|
414
|
+
durableReceipt = next;
|
|
415
|
+
return Promise.resolve();
|
|
416
|
+
} catch (error) {
|
|
417
|
+
return Promise.reject(new Error("refusing provider dispatch because durable progress could not be stored", { cause: error }));
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
const persistProviderBoundMutationTarget = (
|
|
421
|
+
eventValue:
|
|
422
|
+
| WebSessionProviderAcceptedMutationTargetEvent
|
|
423
|
+
| WebSessionProviderBoundMutationTargetEvent,
|
|
424
|
+
): Promise<void> => {
|
|
425
|
+
const event = foreignDataRecord(eventValue);
|
|
426
|
+
if (
|
|
427
|
+
event === null
|
|
428
|
+
|| !hasExactKeys(event, ["id", "index", "target"])
|
|
429
|
+
|| typeof event.id !== "string"
|
|
430
|
+
|| !Number.isSafeInteger(event.index)
|
|
431
|
+
) {
|
|
432
|
+
return Promise.reject(new Error(
|
|
433
|
+
"provider-accepted mutation target event is malformed",
|
|
434
|
+
));
|
|
435
|
+
}
|
|
436
|
+
const index = event.index as number;
|
|
437
|
+
const expectedDispatch = plannedDispatches[index - 1];
|
|
438
|
+
const current = durableReceipt.dispatch;
|
|
439
|
+
if (
|
|
440
|
+
!isWrite
|
|
441
|
+
|| (!webSessionOperation && !localCliOperation)
|
|
442
|
+
|| journal === null
|
|
443
|
+
|| expectedDispatch === undefined
|
|
444
|
+
|| event.id !== expectedDispatch.id
|
|
445
|
+
|| index < 1
|
|
446
|
+
|| index > planned
|
|
447
|
+
|| current.planned !== planned
|
|
448
|
+
|| current.started !== index
|
|
449
|
+
|| current.verified !== index - 1
|
|
450
|
+
) {
|
|
451
|
+
return Promise.reject(new Error(
|
|
452
|
+
"provider-accepted mutation target diverged from the active dispatch",
|
|
453
|
+
));
|
|
454
|
+
}
|
|
455
|
+
const contract = recoveryContract();
|
|
456
|
+
if (
|
|
457
|
+
contract.transport !== "web-session-api"
|
|
458
|
+
&& contract.transport !== "local-cli"
|
|
459
|
+
) {
|
|
460
|
+
return Promise.reject(new Error(
|
|
461
|
+
"provider-accepted mutation target requires a session or local CLI contract",
|
|
462
|
+
));
|
|
463
|
+
}
|
|
464
|
+
try {
|
|
465
|
+
writeProviderAcceptedMutationTargetEvidence({
|
|
466
|
+
schemaVersion: 1,
|
|
467
|
+
runId,
|
|
468
|
+
acceptedAt: (options.now ?? new Date()).toISOString(),
|
|
469
|
+
planDigest,
|
|
470
|
+
adapter,
|
|
471
|
+
operation: invocation.operationId,
|
|
472
|
+
inputHash,
|
|
473
|
+
auth,
|
|
474
|
+
contract,
|
|
475
|
+
dispatch: {
|
|
476
|
+
id: event.id,
|
|
477
|
+
index,
|
|
478
|
+
planned,
|
|
479
|
+
},
|
|
480
|
+
target: event.target,
|
|
481
|
+
}, options.environment);
|
|
482
|
+
return Promise.resolve();
|
|
483
|
+
} catch (error) {
|
|
484
|
+
return Promise.reject(new Error(
|
|
485
|
+
"provider-accepted mutation target could not be stored",
|
|
486
|
+
{ cause: error },
|
|
487
|
+
));
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
const executionKind = providerOperation
|
|
491
|
+
? "provider"
|
|
492
|
+
: webSessionOperation
|
|
493
|
+
? "web-session"
|
|
494
|
+
: localCliOperation
|
|
495
|
+
? "local-cli"
|
|
496
|
+
: reviewedTemplateOperation ? "reviewed-template" : "browser";
|
|
497
|
+
const exactTargetReconciliationKind =
|
|
498
|
+
pluginResolution?.operation.reconciliation?.kind;
|
|
499
|
+
const publicWebSessionOperation = webSessionOperation
|
|
500
|
+
&& isPublicWebSessionInvocationAuthority(invocation.auth);
|
|
501
|
+
|
|
502
|
+
const requireJournal = () => {
|
|
503
|
+
if (journal === null) throw new Error("remote write has no run journal");
|
|
504
|
+
return journal;
|
|
505
|
+
};
|
|
506
|
+
const record = (event: Parameters<typeof updateRunJournal>[1]) => {
|
|
507
|
+
journal = updateRunJournal(requireJournal(), event, options.environment);
|
|
508
|
+
};
|
|
509
|
+
return {
|
|
510
|
+
startedAt, runId, inputHash, adapter, auth,
|
|
511
|
+
get journalTerminal() { return journal?.journal.phase === "terminal"; },
|
|
512
|
+
journalRequest: attempt("journal", () => {
|
|
513
|
+
if (planDigest === null) throw new Error("remote writes require a confirmation plan");
|
|
514
|
+
if (
|
|
515
|
+
options.confirmationClaim === undefined
|
|
516
|
+
|| options.confirmationClaim.claim.digest !== planDigest
|
|
517
|
+
|| options.confirmationClaim.claim.runId !== runId
|
|
518
|
+
) {
|
|
519
|
+
throw new Error(
|
|
520
|
+
"remote writes require an exact durable confirmation ownership claim",
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
if (!isProviderPluginOperationName(invocation.operationId)) {
|
|
524
|
+
throw new Error("run journal operation name is malformed");
|
|
525
|
+
}
|
|
526
|
+
const contract = recoveryContract();
|
|
527
|
+
const timeoutMs = providerOperation
|
|
528
|
+
? operation.provider.timeoutMs
|
|
529
|
+
: webSessionOperation
|
|
530
|
+
? operation.webSession.timeoutMs
|
|
531
|
+
: localCliOperation
|
|
532
|
+
? operation.localCli.timeoutMs
|
|
533
|
+
: reviewedTemplateOperation
|
|
534
|
+
? operation.reviewedTemplate.state === "reviewed"
|
|
535
|
+
? operation.reviewedTemplate.timeoutMs
|
|
536
|
+
: 10 * 60_000
|
|
537
|
+
: operation.browser?.timeoutMs ?? 10 * 60_000;
|
|
538
|
+
const processIdentity = currentProcessStartIdentity();
|
|
539
|
+
const operationId = invocation.operationId;
|
|
540
|
+
return () => {
|
|
541
|
+
journal = createRunJournal(initialRunJournal({
|
|
542
|
+
runId,
|
|
543
|
+
planDigest,
|
|
544
|
+
adapter,
|
|
545
|
+
operation: operationId,
|
|
546
|
+
risk,
|
|
547
|
+
inputHash,
|
|
548
|
+
auth: durableWriteAuth(),
|
|
549
|
+
contract: contract.transport === "portable-provider-plugin"
|
|
550
|
+
|| contract.transport === "local-cli"
|
|
551
|
+
? contract
|
|
552
|
+
: {
|
|
553
|
+
transport: contract.transport,
|
|
554
|
+
hash: contract.hash,
|
|
555
|
+
},
|
|
556
|
+
...(options.duplicateRisk === undefined
|
|
557
|
+
? {}
|
|
558
|
+
: {
|
|
559
|
+
duplicateIntent: {
|
|
560
|
+
schemaVersion: 1 as const,
|
|
561
|
+
intentHash: options.duplicateRisk.intentHash,
|
|
562
|
+
sourceRunId: options.duplicateRisk.sourceRunId,
|
|
563
|
+
},
|
|
564
|
+
}),
|
|
565
|
+
plannedDispatches: planned,
|
|
566
|
+
hasPlanAssets: options.hasPlanAssets === true,
|
|
567
|
+
owner: {
|
|
568
|
+
pid: process.pid,
|
|
569
|
+
token: crypto.randomUUID(),
|
|
570
|
+
...processIdentity,
|
|
571
|
+
leaseUntil: new Date(
|
|
572
|
+
Date.parse(startedAt) + timeoutMs + 30_000,
|
|
573
|
+
).toISOString(),
|
|
574
|
+
},
|
|
575
|
+
startedAt,
|
|
576
|
+
dedupeExpiresAt: new Date(
|
|
577
|
+
Date.parse(startedAt) + operation.dedupeWindowMs,
|
|
578
|
+
).toISOString(),
|
|
579
|
+
}), options.environment);
|
|
580
|
+
};
|
|
581
|
+
}),
|
|
582
|
+
createJournal: (create: () => void) => attempt("journal", create),
|
|
583
|
+
removePlan: attempt("confirmation", () => removePrivateStateFile(planPath(planDigest, options.environment), options.environment)),
|
|
584
|
+
releaseClaim: attempt("confirmation", () => {
|
|
585
|
+
if (options.confirmationClaim === undefined) throw new Error("remote writes require an exact durable confirmation ownership claim");
|
|
586
|
+
return releaseConfirmationClaim(options.confirmationClaim, options.environment);
|
|
587
|
+
}),
|
|
588
|
+
record: (event: Parameters<typeof updateRunJournal>[1]) => attempt("journal", () => record(event)),
|
|
589
|
+
projectJournal: attempt("projection", () => projectRunJournal(requireJournal().journal, options.environment)),
|
|
590
|
+
refreshReceipt: attempt("projection", () => { durableReceipt = runJournalReceipt(requireJournal().journal); }),
|
|
591
|
+
persistProvisional: attempt("projection", () => persistReceipt(durableReceipt, options.environment)),
|
|
592
|
+
clock: () => attempt("journal", () => observedTime().toISOString()),
|
|
593
|
+
// Path and entry construction happen before the acquire-specific recovery branch.
|
|
594
|
+
ledgerRequest: attempt("journal", () => ({
|
|
595
|
+
path: ledgerPath(adapter.hash, auth.hash, invocation.operationId, inputHash, options.environment, options.duplicateRisk?.intentHash),
|
|
596
|
+
entry: {
|
|
597
|
+
schemaVersion: options.duplicateRisk === undefined ? 2 : 3,
|
|
598
|
+
keyHash: options.duplicateRisk?.intentHash ?? inputHash,
|
|
599
|
+
adapterHash: adapter.hash, authHash: auth.hash, inputHash, planDigest,
|
|
600
|
+
status: "pending", dispatch: durableReceipt.dispatch, runId, updatedAt: startedAt,
|
|
601
|
+
expiresAt: new Date(Date.parse(startedAt) + operation.dedupeWindowMs).toISOString(),
|
|
602
|
+
...(options.duplicateRisk === undefined ? {} : { duplicateIntentHash: options.duplicateRisk.intentHash }),
|
|
603
|
+
} satisfies LedgerEntry,
|
|
604
|
+
})),
|
|
605
|
+
acquireLedger: (request: { readonly path: string; readonly entry: LedgerEntry }) => attempt("journal", () => acquireLedger(request.path, request.entry, options.environment, observedTime())),
|
|
606
|
+
ledgerRelativePath: (path: string) => attempt("journal", () => relativeStatePath(path, options.environment)),
|
|
607
|
+
readReceipt: (id: string) => attempt("projection", () => readRunReceipt(id, options.environment)),
|
|
608
|
+
storeCapsule: attempt("journal", () => writeRecoveryCapsule({
|
|
609
|
+
schemaVersion: 1, runId, createdAt: startedAt, planDigest, adapter,
|
|
610
|
+
operation: invocation.operationId, risk, input: invocation.input,
|
|
611
|
+
inputHash, auth: durableWriteAuth(), contract: recoveryContract(),
|
|
612
|
+
}, options.environment)),
|
|
613
|
+
outputLimit: attempt("dispatch", () => executionOutputLimit(operation)),
|
|
614
|
+
dispatch: native("dispatch", async () => {
|
|
615
|
+
if (options.preflightFailure !== undefined) {
|
|
616
|
+
throw options.preflightFailure;
|
|
617
|
+
}
|
|
618
|
+
return providerOperation
|
|
619
|
+
? await (options.executeProvider ?? executeProviderOperation)(
|
|
620
|
+
invocation.manifest,
|
|
621
|
+
operation.provider,
|
|
622
|
+
invocation.input,
|
|
623
|
+
persistedAuthAuthority(invocation.auth),
|
|
624
|
+
{
|
|
625
|
+
registry,
|
|
626
|
+
...(options.fileResolver === undefined ? {} : { fileResolver: options.fileResolver }),
|
|
627
|
+
...(options.now === undefined ? {} : { now: options.now }),
|
|
628
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
629
|
+
environment: options.environment,
|
|
630
|
+
beforeDispatch: (event) => persistDispatchProgress(event, "starting"),
|
|
631
|
+
afterDispatchVerified: (event) => persistDispatchProgress(event, "verified"),
|
|
632
|
+
},
|
|
633
|
+
)
|
|
634
|
+
: webSessionOperation
|
|
635
|
+
? await runWebSessionOperationWithDeadline(
|
|
636
|
+
operation.webSession,
|
|
637
|
+
{
|
|
638
|
+
...(options.fileResolver === undefined ? {} : { fileResolver: options.fileResolver }),
|
|
639
|
+
environment: options.environment,
|
|
640
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
641
|
+
...(options.registerCleanupBarrier === undefined
|
|
642
|
+
? {}
|
|
643
|
+
: {
|
|
644
|
+
registerCleanupBarrier: options.registerCleanupBarrier,
|
|
645
|
+
}),
|
|
646
|
+
beforeDispatch: (event) => persistDispatchProgress(event, "starting"),
|
|
647
|
+
...(isWrite
|
|
648
|
+
&& exactTargetReconciliationKind
|
|
649
|
+
=== "provider-accepted-target-presence"
|
|
650
|
+
? {
|
|
651
|
+
afterProviderAcceptedMutationTarget:
|
|
652
|
+
persistProviderBoundMutationTarget,
|
|
653
|
+
}
|
|
654
|
+
: {}),
|
|
655
|
+
...(isWrite
|
|
656
|
+
&& exactTargetReconciliationKind
|
|
657
|
+
=== "provider-bound-target-desired-state"
|
|
658
|
+
? {
|
|
659
|
+
afterProviderBoundMutationTarget:
|
|
660
|
+
persistProviderBoundMutationTarget,
|
|
661
|
+
}
|
|
662
|
+
: {}),
|
|
663
|
+
afterDispatchVerified: (event) => persistDispatchProgress(event, "verified"),
|
|
664
|
+
},
|
|
665
|
+
async (executionOptions: WebSessionExecutionOptions) => {
|
|
666
|
+
if (
|
|
667
|
+
pluginResolution === null
|
|
668
|
+
|| (
|
|
669
|
+
pluginResolution.binding.transport !== "web-session-api"
|
|
670
|
+
&& pluginResolution.binding.transport !== "linked-device"
|
|
671
|
+
)
|
|
672
|
+
) {
|
|
673
|
+
throw new Error(
|
|
674
|
+
"authenticated session operation resolved to the wrong plugin transport",
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
if (publicWebSessionOperation) {
|
|
678
|
+
if (pluginResolution.binding.transport !== "web-session-api") {
|
|
679
|
+
throw new Error(
|
|
680
|
+
"public access is available only to a web-session plugin binding",
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
const executePublic = options.executePublicWebSession
|
|
684
|
+
?? pluginResolution.binding.executePublic;
|
|
685
|
+
if (executePublic === undefined) {
|
|
686
|
+
throw new Error(
|
|
687
|
+
"reviewed public web-session operation has no public runtime hook",
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
return executePublic(
|
|
691
|
+
invocation.manifest,
|
|
692
|
+
operation.webSession,
|
|
693
|
+
invocation.input,
|
|
694
|
+
executionOptions,
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
return (options.executeWebSession
|
|
698
|
+
?? pluginResolution.binding.execute)(
|
|
699
|
+
invocation.manifest,
|
|
700
|
+
operation.webSession,
|
|
701
|
+
invocation.input,
|
|
702
|
+
persistedAuthAuthority(invocation.auth),
|
|
703
|
+
executionOptions,
|
|
704
|
+
);
|
|
705
|
+
},
|
|
706
|
+
)
|
|
707
|
+
: localCliOperation
|
|
708
|
+
? await runLocalCliOperationWithDeadline(
|
|
709
|
+
operation.localCli,
|
|
710
|
+
{
|
|
711
|
+
...(options.fileResolver === undefined
|
|
712
|
+
? {}
|
|
713
|
+
: { fileResolver: options.fileResolver }),
|
|
714
|
+
environment: options.environment,
|
|
715
|
+
...(options.signal === undefined
|
|
716
|
+
? {}
|
|
717
|
+
: { signal: options.signal }),
|
|
718
|
+
...(options.registerCleanupBarrier === undefined
|
|
719
|
+
? {}
|
|
720
|
+
: { registerCleanupBarrier: options.registerCleanupBarrier }),
|
|
721
|
+
beforeDispatch: (event) =>
|
|
722
|
+
persistDispatchProgress(event, "starting"),
|
|
723
|
+
...(isWrite
|
|
724
|
+
&& exactTargetReconciliationKind
|
|
725
|
+
=== "provider-accepted-target-presence"
|
|
726
|
+
? {
|
|
727
|
+
afterProviderAcceptedMutationTarget:
|
|
728
|
+
persistProviderBoundMutationTarget,
|
|
729
|
+
}
|
|
730
|
+
: {}),
|
|
731
|
+
...(isWrite
|
|
732
|
+
&& exactTargetReconciliationKind
|
|
733
|
+
=== "provider-bound-target-desired-state"
|
|
734
|
+
? {
|
|
735
|
+
afterProviderBoundMutationTarget:
|
|
736
|
+
persistProviderBoundMutationTarget,
|
|
737
|
+
}
|
|
738
|
+
: {}),
|
|
739
|
+
afterDispatchVerified: (event) =>
|
|
740
|
+
persistDispatchProgress(event, "verified"),
|
|
741
|
+
},
|
|
742
|
+
async (executionOptions: LocalCliExecutionOptions) => {
|
|
743
|
+
if (
|
|
744
|
+
pluginResolution === null
|
|
745
|
+
|| pluginResolution.binding.transport !== "local-cli"
|
|
746
|
+
) {
|
|
747
|
+
throw new Error(
|
|
748
|
+
"local CLI operation resolved to the wrong plugin transport",
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
return (options.executeLocalCli
|
|
752
|
+
?? pluginResolution.binding.execute)(
|
|
753
|
+
invocation.manifest,
|
|
754
|
+
operation.localCli,
|
|
755
|
+
invocation.input,
|
|
756
|
+
persistedAuthAuthority(invocation.auth),
|
|
757
|
+
executionOptions,
|
|
758
|
+
);
|
|
759
|
+
},
|
|
760
|
+
)
|
|
761
|
+
: reviewedTemplateOperation
|
|
762
|
+
? await (options.executeReviewedTemplate ?? executeReviewedTemplateOperation)(
|
|
763
|
+
invocation.manifest,
|
|
764
|
+
invocation.operationId,
|
|
765
|
+
operation.reviewedTemplate,
|
|
766
|
+
invocation.input,
|
|
767
|
+
persistedAuthAuthority(invocation.auth),
|
|
768
|
+
{
|
|
769
|
+
beforeDispatch: (event) => persistDispatchProgress(event, "starting"),
|
|
770
|
+
afterDispatchVerified: (event) => persistDispatchProgress(event, "verified"),
|
|
771
|
+
},
|
|
772
|
+
)
|
|
773
|
+
: await (options.executeRecipe ?? executeBrowserRecipe)(
|
|
774
|
+
invocation.manifest,
|
|
775
|
+
operation.browser,
|
|
776
|
+
invocation.input,
|
|
777
|
+
persistedAuthAuthority(invocation.auth),
|
|
778
|
+
{
|
|
779
|
+
headed: options.headed,
|
|
780
|
+
...(options.fileResolver === undefined ? {} : { fileResolver: options.fileResolver }),
|
|
781
|
+
beforeDispatch: (event) => persistDispatchProgress(event, "starting"),
|
|
782
|
+
afterDispatchVerified: (event) => persistDispatchProgress(event, "verified"),
|
|
783
|
+
},
|
|
784
|
+
);
|
|
785
|
+
|
|
786
|
+
}),
|
|
787
|
+
projectExecution: (outcome: NativeOutcome, maxOutputBytes: number) => attempt("projection", () => {
|
|
788
|
+
let execution: BoundedExecution;
|
|
789
|
+
if (outcome.status === "fulfilled") {
|
|
790
|
+
const rawExecution = outcome.value;
|
|
791
|
+
try {
|
|
792
|
+
execution = boundedExecutionResult(rawExecution, executionKind, maxOutputBytes);
|
|
793
|
+
} catch {
|
|
794
|
+
const { started } = durableReceipt.dispatch;
|
|
795
|
+
execution = {
|
|
796
|
+
status: started > 0 ? "indeterminate" : "failed",
|
|
797
|
+
output: null,
|
|
798
|
+
finalUrl: null,
|
|
799
|
+
dispatchStarted: started > 0,
|
|
800
|
+
dispatch: durableReceipt.dispatch,
|
|
801
|
+
error: GENERIC_EXECUTOR_TERMINATION,
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
} else {
|
|
805
|
+
const error = outcome.reason;
|
|
806
|
+
const { started } = durableReceipt.dispatch;
|
|
807
|
+
const preservedArtifactsError =
|
|
808
|
+
error instanceof PreservedBrowserArtifactsError
|
|
809
|
+
? error
|
|
810
|
+
: error instanceof WebSessionCleanupUnverifiedError
|
|
811
|
+
&& error.cause instanceof PreservedBrowserArtifactsError
|
|
812
|
+
? error.cause
|
|
813
|
+
: null;
|
|
814
|
+
const cleanupRequired = error instanceof WebSessionCleanupUnverifiedError
|
|
815
|
+
|| error instanceof WebSessionCleanupAdmissionBlockedError;
|
|
816
|
+
execution = {
|
|
817
|
+
status: started > 0 ? "indeterminate" : "failed",
|
|
818
|
+
output: null,
|
|
819
|
+
finalUrl: null,
|
|
820
|
+
dispatchStarted: started > 0,
|
|
821
|
+
dispatch: durableReceipt.dispatch,
|
|
822
|
+
error: preservedArtifactsError !== null
|
|
823
|
+
? "provider browser cleanup could not be verified; private artifacts were preserved and durable cleanup admission requires ghostget doctor before retry"
|
|
824
|
+
: cleanupRequired
|
|
825
|
+
? localCliOperation
|
|
826
|
+
? "local CLI child/private-root cleanup could not be verified; durable cleanup admission blocks retry until ghostget doctor proves every pinned process group quiescent and removes the exact private root"
|
|
827
|
+
: "authenticated web cleanup could not be verified; durable cleanup admission blocks retry until ghostget doctor proves and completes exact browser-session recovery"
|
|
828
|
+
: boundedThrownExecutorReason(error),
|
|
829
|
+
...(preservedArtifactsError === null
|
|
830
|
+
? {}
|
|
831
|
+
: {
|
|
832
|
+
privateArtifactsPreserved: true,
|
|
833
|
+
recoveryHandle: preservedArtifactsError.recoveryHandle,
|
|
834
|
+
}),
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
const executionNoOp = "noOp" in execution && execution.noOp === true;
|
|
838
|
+
const validExecutionProgress = isDispatchProgress(execution.dispatch)
|
|
839
|
+
&& execution.dispatch.planned === planned
|
|
840
|
+
&& execution.readFailure === undefined
|
|
841
|
+
&& execution.dispatch.started === durableReceipt.dispatch.started
|
|
842
|
+
&& execution.dispatch.verified === durableReceipt.dispatch.verified
|
|
843
|
+
&& execution.dispatchStarted === (execution.dispatch.started > 0)
|
|
844
|
+
&& (!executionNoOp || (
|
|
845
|
+
isWrite
|
|
846
|
+
&& planned > 0
|
|
847
|
+
&& execution.status === "succeeded"
|
|
848
|
+
&& execution.dispatch.started === 0
|
|
849
|
+
&& execution.dispatch.verified === 0
|
|
850
|
+
&& execution.dispatchStarted === false
|
|
851
|
+
))
|
|
852
|
+
&& (execution.status !== "succeeded" || executionNoOp || (
|
|
853
|
+
execution.dispatch.started === planned && execution.dispatch.verified === planned
|
|
854
|
+
))
|
|
855
|
+
&& (execution.status !== "failed" || execution.dispatch.started === 0)
|
|
856
|
+
&& (execution.status !== "partial" || (
|
|
857
|
+
execution.dispatch.verified > 0
|
|
858
|
+
&& execution.dispatch.started === execution.dispatch.verified
|
|
859
|
+
&& execution.dispatch.verified < planned
|
|
860
|
+
))
|
|
861
|
+
&& (execution.status !== "indeterminate" || execution.dispatch.started > 0);
|
|
862
|
+
if (!validExecutionProgress) {
|
|
863
|
+
const { started } = durableReceipt.dispatch;
|
|
864
|
+
execution = {
|
|
865
|
+
status: started > 0 ? "indeterminate" : "failed",
|
|
866
|
+
output: null,
|
|
867
|
+
finalUrl: null,
|
|
868
|
+
dispatchStarted: started > 0,
|
|
869
|
+
dispatch: durableReceipt.dispatch,
|
|
870
|
+
error: "provider executor returned invalid dispatch progress",
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
const receiptStatus: RunReceipt["status"] = execution.status === "succeeded"
|
|
874
|
+
? isWrite && !executionNoOp ? "submitted" : "succeeded"
|
|
875
|
+
: execution.status;
|
|
876
|
+
const publishedOutput = execution.output;
|
|
877
|
+
const finishedAt = (options.now ?? new Date()).toISOString();
|
|
878
|
+
const privateArtifactsPreserved = "privateArtifactsPreserved" in execution
|
|
879
|
+
&& execution.privateArtifactsPreserved === true;
|
|
880
|
+
const recoveryHandle = boundedRecoveryHandle("recoveryHandle" in execution ? execution.recoveryHandle : undefined);
|
|
881
|
+
const privateArtifactRecoveryMessage = privateArtifactsPreserved
|
|
882
|
+
? `private browser artifacts were preserved; ghostget doctor must prove and complete exact browser-session recovery before retry${recoveryHandle === null
|
|
883
|
+
? ""
|
|
884
|
+
: `; recovery handle: ${recoveryHandle}`}`
|
|
885
|
+
: null;
|
|
886
|
+
const apiOperation = providerOperation
|
|
887
|
+
|| webSessionOperation
|
|
888
|
+
|| localCliOperation
|
|
889
|
+
|| reviewedTemplateOperation;
|
|
890
|
+
const transportLabel = providerOperation
|
|
891
|
+
? "official API"
|
|
892
|
+
: webSessionOperation
|
|
893
|
+
? "authenticated web API"
|
|
894
|
+
: localCliOperation
|
|
895
|
+
? "local CLI"
|
|
896
|
+
: reviewedTemplateOperation
|
|
897
|
+
? "reviewed authenticated API"
|
|
898
|
+
: "browser";
|
|
899
|
+
const providerReason = apiOperation && execution.error !== undefined
|
|
900
|
+
? redactSensitiveText(execution.error).slice(0, 2_000)
|
|
901
|
+
: null;
|
|
902
|
+
const withProviderReason = (message: string): string => providerReason === null
|
|
903
|
+
? message
|
|
904
|
+
: `${message}; reason: ${providerReason}`;
|
|
905
|
+
let receipt: RunReceipt = withTransport({
|
|
906
|
+
runId,
|
|
907
|
+
planDigest,
|
|
908
|
+
adapter,
|
|
909
|
+
operation: invocation.operationId,
|
|
910
|
+
risk: operation.risk,
|
|
911
|
+
inputHash,
|
|
912
|
+
auth,
|
|
913
|
+
status: receiptStatus,
|
|
914
|
+
dispatchStarted: execution.dispatch.started > 0,
|
|
915
|
+
dispatch: execution.dispatch,
|
|
916
|
+
startedAt,
|
|
917
|
+
finishedAt,
|
|
918
|
+
finalOrigin: finalOrigin(execution.finalUrl, invocation.manifest.origins),
|
|
919
|
+
error: execution.error === undefined
|
|
920
|
+
? null
|
|
921
|
+
: privateArtifactRecoveryMessage !== null
|
|
922
|
+
? privateArtifactRecoveryMessage
|
|
923
|
+
: redactSensitiveText(receiptStatus === "partial"
|
|
924
|
+
? withProviderReason(`${transportLabel} stopped after verified dispatches before completing the confirmed schedule; reconcile before retrying`)
|
|
925
|
+
: receiptStatus === "indeterminate"
|
|
926
|
+
? withProviderReason(`${transportLabel} result is indeterminate after the dispatch boundary`)
|
|
927
|
+
: apiOperation
|
|
928
|
+
? withProviderReason(`${transportLabel} operation failed before the dispatch boundary`)
|
|
929
|
+
: "browser recipe failed before the dispatch boundary"),
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
return {
|
|
933
|
+
terminalEvent: {
|
|
934
|
+
type: "finished", status: receiptStatus, finalOrigin: receipt.finalOrigin,
|
|
935
|
+
error: receipt.error, ...(executionNoOp ? { noOp: true as const } : {}), at: finishedAt,
|
|
936
|
+
} as const,
|
|
937
|
+
// This projection reads the one current journal cell after terminal CAS/readback.
|
|
938
|
+
result: (): InvocationResult => {
|
|
939
|
+
if (journal === null) throw new Error("remote write has no run journal");
|
|
940
|
+
if (journal.journal.phase === "terminal") {
|
|
941
|
+
receipt = runJournalReceipt(journal.journal);
|
|
942
|
+
return {
|
|
943
|
+
receipt,
|
|
944
|
+
output: publishedOutput,
|
|
945
|
+
replayed: false,
|
|
946
|
+
privateArtifactsPreserved,
|
|
947
|
+
...(recoveryHandle === null ? {} : { recoveryHandle }),
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
const pending = runJournalReceipt(journal.journal);
|
|
951
|
+
return {
|
|
952
|
+
receipt: {
|
|
953
|
+
...pending,
|
|
954
|
+
error: `${execution.dispatch.started > 0
|
|
955
|
+
? `${transportLabel} execution crossed dispatch, but its final run journal could not be stored; reconcile this run before any retry`
|
|
956
|
+
: `${transportLabel} execution ended before dispatch, but its final run journal could not be stored; run ghostget doctor before retrying`}${privateArtifactRecoveryMessage === null
|
|
957
|
+
? ""
|
|
958
|
+
: `; ${privateArtifactRecoveryMessage}`}`,
|
|
959
|
+
},
|
|
960
|
+
output: null,
|
|
961
|
+
replayed: false,
|
|
962
|
+
privateArtifactsPreserved,
|
|
963
|
+
...(recoveryHandle === null ? {} : { recoveryHandle }),
|
|
964
|
+
};
|
|
965
|
+
},
|
|
966
|
+
};
|
|
967
|
+
}),
|
|
968
|
+
reloadJournal: attempt("journal", () => { const reloaded = readRunJournal(runId, options.environment); if (reloaded !== null) journal = reloaded; }),
|
|
969
|
+
result: (project: () => InvocationResult) => attempt("projection", project),
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
return {
|
|
974
|
+
initialPlan: (digest: string) => attempt("confirmation", () => loadInvocationPlan(digest, environment)),
|
|
975
|
+
configure: attempt("confirmation", () => ({ registry: original.registry ?? providerPluginRegistry, observedAt: now() })),
|
|
976
|
+
repairClaims: attempt("confirmation", () => repairInterruptedConfirmationClaims(environment)),
|
|
977
|
+
repairJournals: (at: Date) => attempt("journal", () => repairInterruptedRunJournals(environment, at)),
|
|
978
|
+
newRunId: attempt("confirmation", () => crypto.randomUUID()),
|
|
979
|
+
claim: (digest: string, runId: string, at: Date) => attempt("confirmation", () => acquireConfirmationClaim(digest, runId, environment, at)),
|
|
980
|
+
releaseClaim: (claim: ConfirmationClaimSnapshot) => attempt("confirmation", () => releaseConfirmationClaim(claim, environment)),
|
|
981
|
+
removePlan: (digest: string) => attempt("confirmation", () => removePrivateStateFile(planPath(digest, environment), environment)),
|
|
982
|
+
retainingJournal: (digest: string) => attempt("confirmation", () => planDigestHasRetainingJournal(digest, environment)),
|
|
983
|
+
cleanupAssets: (digest: string) => attempt("cleanup", () => cleanupPlanAssets(digest, environment)),
|
|
984
|
+
freshPlan: (stored: StoredPlan, registry: ProviderPluginRegistry, at: Date) => attempt("confirmation", () => {
|
|
985
|
+
const loadManifest: typeof loadInstalledManifest = original.loadManifest
|
|
986
|
+
?? ((adapterId, selectedEnvironment = process.env) => loadInstalledManifestWithRegistry(adapterId, selectedEnvironment, registry));
|
|
987
|
+
return validateFreshPlan(stored, environment, at, registry, loadManifest);
|
|
988
|
+
}),
|
|
989
|
+
duplicateRisk: (stored: StoredPlan) => attempt("confirmation", () => stored.plan.duplicateRisk === undefined ? undefined : resolveInvocationDuplicateRisk(stored.plan, [stored.plan.duplicateRisk.sourceRunId], environment)),
|
|
990
|
+
prepareOptions: (invocation: PreparedInvocation, stored: StoredPlan, claim: ConfirmationClaimSnapshot, runId: string, registry: ProviderPluginRegistry) => attempt("confirmation", () => {
|
|
991
|
+
const fileInputs: FileInputValue[] = [];
|
|
992
|
+
for (const value of Object.values(invocation.input)) {
|
|
993
|
+
if (isInputArray(value)) { for (const item of value) if (isFileInputValue(item)) fileInputs.push(item); }
|
|
994
|
+
else if (isFileInputValue(value)) fileInputs.push(value);
|
|
995
|
+
}
|
|
996
|
+
if (fileInputs.length > 0) resolvePlanAssetFiles(fileInputs, stored.digest, environment);
|
|
997
|
+
const operation = invocation.manifest.operations[invocation.operationId];
|
|
998
|
+
if (operation?.risk !== "R2" && operation?.risk !== "R3") throw new Error("only R2 and R3 plans use confirmation");
|
|
999
|
+
return {
|
|
1000
|
+
headed: original.headed, environment, registry,
|
|
1001
|
+
...(fileInputs.length === 0 ? {} : { fileResolver: (files) => Promise.resolve(resolvePlanAssetFiles(files, stored.digest, environment)) }),
|
|
1002
|
+
hasPlanAssets: fileInputs.length > 0, runId, confirmationClaim: claim,
|
|
1003
|
+
confirmedDispatches: stored.plan.dispatches,
|
|
1004
|
+
...(stored.plan.duplicateRisk === undefined ? {} : { duplicateRisk: stored.plan.duplicateRisk }),
|
|
1005
|
+
...(original.now === undefined ? {} : { now: original.now }),
|
|
1006
|
+
...(original.executeRecipe === undefined ? {} : { executeRecipe: original.executeRecipe }),
|
|
1007
|
+
...(original.executeProvider === undefined ? {} : { executeProvider: original.executeProvider }),
|
|
1008
|
+
...(original.executeWebSession === undefined ? {} : { executeWebSession: original.executeWebSession }),
|
|
1009
|
+
...(original.executeLocalCli === undefined ? {} : { executeLocalCli: original.executeLocalCli }),
|
|
1010
|
+
...(original.executeReviewedTemplate === undefined ? {} : { executeReviewedTemplate: original.executeReviewedTemplate }),
|
|
1011
|
+
...(original.signal === undefined ? {} : { signal: original.signal }),
|
|
1012
|
+
...(original.persistReceipt === undefined ? {} : { persistReceipt: original.persistReceipt }),
|
|
1013
|
+
} satisfies RunPreparedOptions;
|
|
1014
|
+
}),
|
|
1015
|
+
admission: (invocation: PreparedInvocation, options: RunPreparedOptions) => attempt("admission", () => {
|
|
1016
|
+
const registry = options.registry ?? providerPluginRegistry;
|
|
1017
|
+
const checked = revalidatePreparedInvocation(invocation, registry);
|
|
1018
|
+
const portableIdentity = checked.invocation.portablePluginContract ?? null;
|
|
1019
|
+
const runId = options.runId ?? crypto.randomUUID();
|
|
1020
|
+
const pluginResolution = resolveCodeOwnedPluginOperation(checked.operation, registry);
|
|
1021
|
+
let cleanupIdentity: WebSessionCleanupAdmissionIdentity | null = null;
|
|
1022
|
+
if (portableIdentity === null && (isWebSessionOperation(checked.operation) || isLocalCliOperation(checked.operation))
|
|
1023
|
+
&& pluginResolution !== null && !isPublicWebSessionInvocationAuthority(checked.invocation.auth)) {
|
|
1024
|
+
const executionIdentityHash = pluginResolution.binding.transport === "local-cli"
|
|
1025
|
+
? sha256(canonicalJson({ transport: "local-cli", tool: pluginResolution.binding.tool, artifact: localCliToolArtifactForCurrentRuntime(pluginResolution.binding.tool) }))
|
|
1026
|
+
: registry.implementationHash(pluginResolution.binding).toString("hex");
|
|
1027
|
+
cleanupIdentity = {
|
|
1028
|
+
runId, pluginId: pluginResolution.plugin.id, pluginVersion: pluginResolution.plugin.version,
|
|
1029
|
+
pluginImplementationHash: registry.implementationHash(pluginResolution.binding).toString("hex"),
|
|
1030
|
+
adapterId: checked.invocation.manifest.id, adapterHash: manifestHash(checked.invocation.manifest),
|
|
1031
|
+
surfaceId: pluginResolution.binding.surfaceId, authId: checked.invocation.auth.id, authHash: authHash(checked.invocation.auth),
|
|
1032
|
+
transport: isLocalCliOperation(checked.operation) ? "local-cli" : "web-session-api", executionIdentityHash,
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
return { invocation: checked.invocation, options: { ...options, runId }, portableIdentity, cleanupIdentity };
|
|
1036
|
+
}),
|
|
1037
|
+
acquireWeb: (identity: WebSessionCleanupAdmissionIdentity, at: Date | undefined) => attempt("admission", () => acquireWebSessionCleanupAdmission(identity, environment, at)),
|
|
1038
|
+
nativeCleanup: (work: () => Promise<void>) => native("cleanup", work),
|
|
1039
|
+
cleanup: (work: () => void) => attempt("cleanup", work),
|
|
1040
|
+
acquirePortable: (identity: PortableOperationIdentityV1, runId: string, at: Date | undefined) => attempt("admission", () => {
|
|
1041
|
+
const lease = acquirePortableProviderPluginInvocationLease(identity, runId, environment, at);
|
|
1042
|
+
const containment = createPortableProviderPluginInvocationLeaseContainmentController(lease, environment);
|
|
1043
|
+
const context = createPortableProviderPluginCleanupContext({ containment, cleanupComplete: containment.cleanupComplete });
|
|
1044
|
+
portableContext = context;
|
|
1045
|
+
return { context, release: () => releasePortableProviderPluginInvocationLease(containment.current, environment, new Date()) };
|
|
1046
|
+
}),
|
|
1047
|
+
checkExecution: (invocation: PreparedInvocation, options: RunPreparedOptions) => attempt("journal", () => revalidatePreparedInvocation(invocation, options.registry ?? providerPluginRegistry)),
|
|
1048
|
+
currentTime: (options: RunPreparedOptions) => attempt("journal", () => options.now ?? new Date()),
|
|
1049
|
+
execution: (checked: ReturnType<ConfirmedWriteKernel["revalidatePreparedInvocation"]>, digest: string, options: RunPreparedOptions) => attempt("journal", () => execution(checked, digest, options)),
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
export class ConfirmedWritePlatform extends Context.Tag("wrench/ConfirmedWritePlatform/v1")<
|
|
1054
|
+
ConfirmedWritePlatform, ReturnType<typeof makeConfirmedWritePlatform>
|
|
1055
|
+
>() { }
|