chainlesschain 0.165.2 → 0.165.4
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/README.md +151 -29
- package/package.json +2 -2
- package/src/assets/web-panel/assets/{AIOps-lS1R0Cza.js → AIOps-C__nnraW.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BmlgBPsj.js → ActionButton-CsKWHEZ-.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CNKczv1S.js → Analytics-BhIjfjWO.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BuZGJARQ.js → AppLayout-BjuXFzWZ.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-Dm73Sl-O.js → Artifacts-k6whDkZJ.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BGMW7A5e.js → Audit-C4FBMYRh.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BQ6M54JR.js → BackgroundAgents-C_vRrSXk.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DBzGP0bR.js → Backup-CngFZeVp.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-D1mLCoOV.js → BaseInput-B_H9EBRm.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BUfcN7p8.js → Chat-DLEC5kt7.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-Du0Yg5Pi.js → Checkbox-m3ClR4xh.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B2RXziHW.js → Codegen-CmLSFrgp.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DWhekOrR.js → Col-DFGQteHN.js} +1 -1
- package/src/assets/web-panel/assets/{Community-HQhrkon4.js → Community-EUUVORHE.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-izHNb4uz.js → Compact-2PSg0Yr7.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BDwLy41-.js → Compliance-CcKur7gB.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DFJhLa7K.js → Cowork-DK_Fezfa.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DYY9RG7-.js → Cron-Dbw89xl7.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-rr7tJTtv.js → Crosschain-CH66h_Ec.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D2sfHRLi.js → DID-D1igRb17.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-q_eGdhha.js → Dashboard-DVAsrobk.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-Cufrh2Bz.js → Dropdown-Dl7W8AQs.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-B-kdv10f.js → EmailListRenderer-D5tpaEQC.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-D21XjgfG.js → FamilyGuardDashboard-O_VQf8gq.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CAwRTLZH.js → Federation-BTVcgS9n.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-vx3daNDF.js → FormItemContext-BRnkJAIz.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CRWY2wU4.js → GenericCardRenderer-8OWSCkDj.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Q6SwauyD.js → Git-WXLYTQj2.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BJ1q4IQZ.js → Governance-BezaUhtc.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CSWjROgD.js → Inference-CuRay3Gu.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-fzuQsWuY.js → KnowledgeGraph-DfWLDW1m.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DVF8Mnns.js → Logs-C55y2pPY.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BQP7LOA4.js → Marketplace-Dk08Uycg.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BNlJCmNc.js → McpTools-C11fSB5V.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-DLLg5WQ4.js → Memory-mr0eZc97.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CR2skNAF.js → MobileBridge-C0iCSRbb.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BxVavObl.js → MobileProjects-CY-xMG8Z.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-B17pgqMI.js → Mtc-Bplqc_Wc.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-4UmV5cIL.js → MtcAudit-BlzwsoSM.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-DoMEXEcU.js → Multisig-BBLmTty6.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-h9DMXFX4.js → NLProgramming-DtT1uf1W.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Cihn-xX8.js → Notes-DcqQAxTK.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BE6ZGa3R.js → NotificationSettings-cb0kWoZe.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CnwojoSI.js → OrderTableRenderer-kw9HZQNN.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-B2vpwmjb.js → Organization-DlTlbkUN.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-C4z28R0-.js → Overflow-pp1qs-oY.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BVlFkwhW.js → P2P-BESLnyYz.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BXbEdL5B.js → PdhVaultBrowser-B-J1mGuf.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-C28xs4lp.js → Permissions-B36PeT1_.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dgl_rBjM.js → PersonalDataHub-CTK2ad-3.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-3QUIhrKI.js → Pipeline-zJNGXsac.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-8Gh33OZ5.js → Privacy-b9fz0Qf0.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BNmg4tZi.js → ProjectInit-DjZbZgiB.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-vgMfeCf9.js → ProjectSettings-C-hHJXlD.js} +2 -2
- package/src/assets/web-panel/assets/Projects-BC9KSDM6.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CXoOa8eX.js → Providers-Dz-CAYYU.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BhfTF0oS.js → QrScannerModal-A0yoQ3-3.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-C746HEAj.js → QuickAsk-DrFgFJNC.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BSej8Eo0.js → Recommend-KC8hF1g4.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-f3gmnlG3.js → RemoteSession-DGTPIk6u.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-CyvybpkN.js → Reputation-QmbwM7Yk.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DwV2PeYn.js → Row-BxjJylia.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-11GrzjZy.js → RssFeed-B7Wda1J6.js} +3 -3
- package/src/assets/web-panel/assets/{Search-BjKXkrAY.js → Search-_plQ_YSA.js} +1 -1
- package/src/assets/web-panel/assets/{Security-eWiFb9Yk.js → Security-CD2YJZfx.js} +2 -2
- package/src/assets/web-panel/assets/{Services-CdCsMAtt.js → Services-qQE7NmwE.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-4Q2UTKQ5.js → Skeleton-DgQqkFf7.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-v4rtYrRj.js → Skills-C4C0oFFt.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CRhsFvd_.js → Sla-Px2N2B5c.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-DN6C3lQd.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-D16zonl7.js → SyncSettings-jm5aH_m7.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Xxozz0i_.js → Tasks-DlkJcv67.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CXJpPtCT.js → Templates-DyrTHUBs.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-XRV0L1ZF.js → Tenant-mC434PtL.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CYuO1i_w.js → Terminal-o0_s3NgK.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BXqitWsu.js → TimelineRenderer-DAGlAXOw.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BvPrDHIy.js → Tokens-BnulS_bs.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DhCrSTf5.js → Trigger-suQbVXYM.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DmsuZD6K.js → Trust-DbIJAPo5.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-D_yrts5C.js → UkeySign-CQNpbIHi.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B01ihPrm.js → VideoEditing-D8aWCERk.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DpUjKa-N.js → Wallet-CuLpYfZc.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CL07yON6.js → WebAuthn-CsfG8ZMc.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-CLMflnRr.js → WorkflowEditor-BYsEAZbd.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DtUqe8h_.js → chat-FT4Um3-I.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DPKrgVfy.js → colors-CKbS_Wlt.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BTt2GtOm.js → compact-item-CFJtzuJM.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-D_VeUc1J.js → createContext-B9kSRW6K.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-1kndiylj.js → hasIn-DR-x0Xpi.js} +1 -1
- package/src/assets/web-panel/assets/{index-dVc-CXY4.js → index-7jHr_A_k.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFIZ_1Sf.js → index-B3ZX81mK.js} +1 -1
- package/src/assets/web-panel/assets/{index-fk2Rjacw.js → index-B7osrXqy.js} +3 -3
- package/src/assets/web-panel/assets/{index-CHG-KcS4.js → index-BGbPnC5x.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dbbymxpj.js → index-BI5hP_qy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BaZAbw9A.js → index-BK3TiKVq.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzjkKUmR.js → index-BVPU43b1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAXfRKfG.js → index-BW-t9n-q.js} +1 -1
- package/src/assets/web-panel/assets/{index-8nz_fmlj.js → index-BfFCQHL3.js} +1 -1
- package/src/assets/web-panel/assets/index-Bizb4hkw.js +1 -0
- package/src/assets/web-panel/assets/{index-DdkplL9R.js → index-Bt4fS2eL.js} +1 -1
- package/src/assets/web-panel/assets/{index-DG2MZQ3f.js → index-BwR7yc2h.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfKBRdSS.js → index-Bzz0C1BZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-GsoUQ2M7.js → index-C2JvmYRc.js} +1 -1
- package/src/assets/web-panel/assets/{index-l1BETrDb.js → index-CGaXUsIs.js} +1 -1
- package/src/assets/web-panel/assets/{index-4bna2_SR.js → index-CJmIQ9XR.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfGl8CqG.js → index-C_enk9I_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D95OFht7.js → index-Cr2FLUsD.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsJCDUJG.js → index-CvrCCqJt.js} +1 -1
- package/src/assets/web-panel/assets/{index-sDoMHTNA.js → index-D4LfPvWA.js} +1 -1
- package/src/assets/web-panel/assets/{index-By5n8up9.js → index-DMGmg497.js} +1 -1
- package/src/assets/web-panel/assets/{index-ocf06EVQ.js → index-DShSkww-.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bhudc_Ez.js → index-DTxNy0ZU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CX81qSM4.js → index-DW4BWjTk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlDNVgBt.js → index-DWdqx7zI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CcO04eDn.js → index-Dap9c3eD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dwqmtk65.js → index-DcfxhKXR.js} +1 -1
- package/src/assets/web-panel/assets/{index-2eyAXE3d.js → index-DdkIhfi-.js} +1 -1
- package/src/assets/web-panel/assets/{index-brCInrzq.js → index-DeSgw-WV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJ8vXGOa.js → index-DiNUEpSQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHpBMWCh.js → index-DjEP7WkH.js} +1 -1
- package/src/assets/web-panel/assets/{index-m31Ka8hZ.js → index-DtkFqPVA.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtF7EN49.js → index-KqDaCApw.js} +1 -1
- package/src/assets/web-panel/assets/index-OHSl-xCh.js +1 -0
- package/src/assets/web-panel/assets/{index-B9KvcS7R.js → index-OSzjTzGM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CauoBQkH.js → index-QBSeNVO3.js} +1 -1
- package/src/assets/web-panel/assets/{index-FzdwcSy2.js → index-WE08gMJP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DxdTdBix.js → index-r2VI1Ikk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BepLZwgB.js → index-tqSEHPRT.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CvyYJd7z.js → initDefaultProps-Dd7n7VgI.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DYZkhUdz.js → motion-BwxouRw1.js} +1 -1
- package/src/assets/web-panel/assets/{move-slnJRVT1.js → move-DObN0PG9.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DVc0CKmy.js → mtc-parser-B3nOUQc7.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BCXAtHCq.js → omit-o9WU2Nbl.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DBsemz_K.js → pickAttrs-CL_vvfQ_.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BRtkJ-nv.js → placementArrow-CYrM2tyK.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Cebq7EZn.js → responsiveObserve-k_06cQ96.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DT7qvId2.js → slide-CPKG0pxS.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-pQiITLkG.js → statusUtils-BDDEeLTw.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-c-19BVKQ.js → styleChecker-Cmb7RTh7.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DsS5-0bE.js → useFlexGapSupport-e2IedC4N.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CxGiMe5n.js → useFs-CE_gxVnm.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-D9btVID5.js → usePersonalDataHub-BkMIIKMz.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CUYFsFEN.js → vnode-DW886XV1.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CXmn_35j.js → zoom-Dwo_adaf.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/agent.js +105 -35
- package/src/commands/artifacts.js +363 -33
- package/src/commands/cowork.js +202 -36
- package/src/commands/session-budget.js +155 -0
- package/src/commands/session-location.js +948 -0
- package/src/commands/session.js +72 -46
- package/src/data/changelog.json +26 -0
- package/src/gateways/ws/artifact-protocol.js +77 -13
- package/src/gateways/ws/session-protocol.js +391 -24
- package/src/gateways/ws/ws-agent-handler.js +191 -14
- package/src/gateways/ws/ws-server.js +14 -3
- package/src/gateways/ws/ws-session-gateway.js +101 -5
- package/src/harness/jsonl-session-store.js +1210 -0
- package/src/lib/agent-team/team-runner.js +43 -13
- package/src/lib/artifact-access-ledger.js +461 -0
- package/src/lib/artifact-cleanup-ledger.js +861 -0
- package/src/lib/artifact-deletion-ledger.js +677 -0
- package/src/lib/artifact-store.js +256 -51
- package/src/lib/background-agent-keeper-protocol.js +75 -4
- package/src/lib/background-agent-launch-barrier.js +74 -0
- package/src/lib/background-agent-supervisor.js +149 -5
- package/src/lib/background-turn-bootstrap-protocol.js +4 -0
- package/src/lib/command-lifecycle-report.js +8 -4
- package/src/lib/cowork-adapter.js +9 -5
- package/src/lib/cowork-task-runner.js +20 -0
- package/src/lib/cowork-workflow.js +178 -20
- package/src/lib/direct-model-usage.js +62 -22
- package/src/lib/dynamic-workflow-runtime.js +3184 -111
- package/src/lib/execution-location-result-apply.js +339 -0
- package/src/lib/execution-location-result-artifact.js +271 -0
- package/src/lib/execution-location-result-review.js +253 -0
- package/src/lib/execution-location-result-store.js +331 -0
- package/src/lib/execution-location-result.js +617 -0
- package/src/lib/execution-location-target.js +236 -1
- package/src/lib/managed-tool-checkpoint.js +47 -4
- package/src/lib/process-execution-broker/workspace-transaction.js +3 -0
- package/src/lib/session-budget-production-root.js +376 -0
- package/src/lib/session-budget-runtime.js +109 -18
- package/src/lib/session-budget-usage.js +63 -0
- package/src/lib/session-host-snapshot.js +19 -2
- package/src/lib/session-resource-budget.js +766 -105
- package/src/lib/sub-agent-context.js +69 -22
- package/src/lib/web-ui-server.js +12 -1
- package/src/lib/with-file-lock.js +175 -26
- package/src/repl/agent-repl.js +237 -16
- package/src/runtime/agent-core.js +123 -0
- package/src/runtime/headless-runner.js +135 -46
- package/src/runtime/headless-stream.js +133 -25
- package/src/runtime/policies/agent-policy.js +10 -0
- package/src/workers/background-agent-keeper.js +112 -19
- package/src/workers/background-agent-worker.js +383 -134
- package/src/workers/background-turn-bootstrap.js +13 -8
- package/src/assets/web-panel/assets/ChatBubbleRenderer-CY5m8ZHg.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-BLif4QSg.js +0 -1
- package/src/assets/web-panel/assets/Projects-BhtAVilc.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-DLjKsDVu.js +0 -1
- package/src/assets/web-panel/assets/devWarning-Bj5nUW2v.js +0 -1
- package/src/assets/web-panel/assets/index-CPcM-eqo.js +0 -1
- package/src/assets/web-panel/assets/index-xeSyVoRe.js +0 -1
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
normalizeExecutionLocationResultBundle,
|
|
4
|
+
verifyExecutionLocationResultBundle,
|
|
5
|
+
} from "./execution-location-result.js";
|
|
6
|
+
import { normalizeExecutionLocationResultStoreReceipt } from "./execution-location-result-store.js";
|
|
7
|
+
import { canonicalJson } from "./scheduler-kernel/contract.js";
|
|
8
|
+
|
|
9
|
+
export const EXECUTION_LOCATION_RESULT_REVIEW_SCHEMA =
|
|
10
|
+
"cc-execution-location-result-review/v1";
|
|
11
|
+
|
|
12
|
+
const RESULT_SETTLEMENT_RECEIPT_SCHEMA =
|
|
13
|
+
"chainlesschain.session-execution-location-result-collection-receipt/v2";
|
|
14
|
+
const SHA256_RE = /^sha256:[a-f0-9]{64}$/u;
|
|
15
|
+
const HEAD_RE = /^[a-f0-9]{64}$/u;
|
|
16
|
+
const AUTHORITY_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:@+-]{0,255}$/u;
|
|
17
|
+
const RESULT_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
18
|
+
|
|
19
|
+
function digest(domain, value) {
|
|
20
|
+
return `sha256:${createHash("sha256")
|
|
21
|
+
.update(domain, "utf8")
|
|
22
|
+
.update(canonicalJson(value, "executionLocationResultReview"), "utf8")
|
|
23
|
+
.digest("hex")}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function exactObject(value, fields, label) {
|
|
27
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
28
|
+
throw new TypeError(`${label} must be an object`);
|
|
29
|
+
}
|
|
30
|
+
const actual = Object.keys(value).sort();
|
|
31
|
+
const expected = [...fields].sort();
|
|
32
|
+
if (
|
|
33
|
+
actual.length !== expected.length ||
|
|
34
|
+
actual.some((field, index) => field !== expected[index])
|
|
35
|
+
) {
|
|
36
|
+
throw new TypeError(`${label} has an invalid schema`);
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function positiveCount(value, label) {
|
|
42
|
+
const count = Number(value);
|
|
43
|
+
if (!Number.isSafeInteger(count) || count < 1) {
|
|
44
|
+
throw new TypeError(`${label} is invalid`);
|
|
45
|
+
}
|
|
46
|
+
return count;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function requireDigest(value, label) {
|
|
50
|
+
const result = String(value || "");
|
|
51
|
+
if (!SHA256_RE.test(result)) {
|
|
52
|
+
throw new TypeError(`${label} is invalid`);
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function requireHead(value, label) {
|
|
58
|
+
const result = String(value || "");
|
|
59
|
+
if (!HEAD_RE.test(result)) {
|
|
60
|
+
throw new TypeError(`${label} is invalid`);
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function normalizeSettlement(input) {
|
|
66
|
+
const value = exactObject(
|
|
67
|
+
input,
|
|
68
|
+
[
|
|
69
|
+
"schema",
|
|
70
|
+
"sessionId",
|
|
71
|
+
"settlementId",
|
|
72
|
+
"requestId",
|
|
73
|
+
"requestDigest",
|
|
74
|
+
"resultId",
|
|
75
|
+
"handoffId",
|
|
76
|
+
"sourceHeadHash",
|
|
77
|
+
"sourceEventCount",
|
|
78
|
+
"settlementEventHash",
|
|
79
|
+
"settlementEventCount",
|
|
80
|
+
"targetHeadHash",
|
|
81
|
+
"targetEventCount",
|
|
82
|
+
"bundleDigest",
|
|
83
|
+
"verificationDigest",
|
|
84
|
+
"collectionDigest",
|
|
85
|
+
"storage",
|
|
86
|
+
"totalBytes",
|
|
87
|
+
"applied",
|
|
88
|
+
"receiptDigest",
|
|
89
|
+
],
|
|
90
|
+
"execution-location result settlement receipt",
|
|
91
|
+
);
|
|
92
|
+
const sessionId = String(value.sessionId || "");
|
|
93
|
+
const requestId = String(value.requestId || "");
|
|
94
|
+
const resultId = String(value.resultId || "");
|
|
95
|
+
const sourceEventCount = positiveCount(
|
|
96
|
+
value.sourceEventCount,
|
|
97
|
+
"source event count",
|
|
98
|
+
);
|
|
99
|
+
const settlementEventCount = positiveCount(
|
|
100
|
+
value.settlementEventCount,
|
|
101
|
+
"settlement event count",
|
|
102
|
+
);
|
|
103
|
+
const targetEventCount = positiveCount(
|
|
104
|
+
value.targetEventCount,
|
|
105
|
+
"target event count",
|
|
106
|
+
);
|
|
107
|
+
const totalBytes = Number(value.totalBytes);
|
|
108
|
+
if (
|
|
109
|
+
value.schema !== RESULT_SETTLEMENT_RECEIPT_SCHEMA ||
|
|
110
|
+
!AUTHORITY_ID_RE.test(sessionId) ||
|
|
111
|
+
!AUTHORITY_ID_RE.test(requestId) ||
|
|
112
|
+
!RESULT_ID_RE.test(resultId) ||
|
|
113
|
+
settlementEventCount !== sourceEventCount + 1 ||
|
|
114
|
+
!Number.isSafeInteger(totalBytes) ||
|
|
115
|
+
totalBytes < 1 ||
|
|
116
|
+
value.applied !== false
|
|
117
|
+
) {
|
|
118
|
+
throw new TypeError(
|
|
119
|
+
"execution-location result settlement receipt is invalid",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
return Object.freeze({
|
|
123
|
+
schema: value.schema,
|
|
124
|
+
sessionId,
|
|
125
|
+
settlementId: requireDigest(value.settlementId, "settlement id"),
|
|
126
|
+
requestId,
|
|
127
|
+
requestDigest: requireDigest(value.requestDigest, "request digest"),
|
|
128
|
+
resultId,
|
|
129
|
+
handoffId: requireDigest(value.handoffId, "handoff id"),
|
|
130
|
+
sourceHeadHash: requireHead(value.sourceHeadHash, "source head"),
|
|
131
|
+
sourceEventCount,
|
|
132
|
+
settlementEventHash: requireHead(
|
|
133
|
+
value.settlementEventHash,
|
|
134
|
+
"settlement event hash",
|
|
135
|
+
),
|
|
136
|
+
settlementEventCount,
|
|
137
|
+
targetHeadHash: requireHead(value.targetHeadHash, "target head"),
|
|
138
|
+
targetEventCount,
|
|
139
|
+
bundleDigest: requireDigest(value.bundleDigest, "bundle digest"),
|
|
140
|
+
verificationDigest: requireDigest(
|
|
141
|
+
value.verificationDigest,
|
|
142
|
+
"verification digest",
|
|
143
|
+
),
|
|
144
|
+
collectionDigest: requireDigest(
|
|
145
|
+
value.collectionDigest,
|
|
146
|
+
"collection digest",
|
|
147
|
+
),
|
|
148
|
+
storage: normalizeExecutionLocationResultStoreReceipt(value.storage),
|
|
149
|
+
totalBytes,
|
|
150
|
+
applied: false,
|
|
151
|
+
receiptDigest: requireDigest(
|
|
152
|
+
value.receiptDigest,
|
|
153
|
+
"settlement receipt digest",
|
|
154
|
+
),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function metadata(record) {
|
|
159
|
+
return Object.freeze({
|
|
160
|
+
mediaType: record.mediaType,
|
|
161
|
+
byteLength: record.byteLength,
|
|
162
|
+
digest: record.digest,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function createExecutionLocationResultReview(input = {}) {
|
|
167
|
+
const settlement = normalizeSettlement(input.settlement);
|
|
168
|
+
const bundle = normalizeExecutionLocationResultBundle(input.bundle);
|
|
169
|
+
const verification = verifyExecutionLocationResultBundle({
|
|
170
|
+
bundle,
|
|
171
|
+
sourceAuthority: {
|
|
172
|
+
sessionId: settlement.sessionId,
|
|
173
|
+
headHash: settlement.sourceHeadHash,
|
|
174
|
+
eventCount: settlement.sourceEventCount,
|
|
175
|
+
},
|
|
176
|
+
expectedHandoffId: settlement.handoffId,
|
|
177
|
+
});
|
|
178
|
+
const storage = settlement.storage;
|
|
179
|
+
if (
|
|
180
|
+
settlement.resultId !== bundle.resultId ||
|
|
181
|
+
settlement.bundleDigest !== bundle.bundleDigest ||
|
|
182
|
+
settlement.verificationDigest !== verification.verificationDigest ||
|
|
183
|
+
settlement.totalBytes !== bundle.totalBytes ||
|
|
184
|
+
settlement.targetHeadHash !== bundle.session.target.headHash ||
|
|
185
|
+
settlement.targetEventCount !== bundle.session.target.eventCount ||
|
|
186
|
+
storage.sessionId !== settlement.sessionId ||
|
|
187
|
+
storage.resultId !== settlement.resultId ||
|
|
188
|
+
storage.handoffId !== settlement.handoffId ||
|
|
189
|
+
storage.bundleDigest !== settlement.bundleDigest
|
|
190
|
+
) {
|
|
191
|
+
throw new Error("stored result bundle does not match settlement authority");
|
|
192
|
+
}
|
|
193
|
+
const material = {
|
|
194
|
+
schema: EXECUTION_LOCATION_RESULT_REVIEW_SCHEMA,
|
|
195
|
+
sessionId: settlement.sessionId,
|
|
196
|
+
requestId: settlement.requestId,
|
|
197
|
+
requestDigest: settlement.requestDigest,
|
|
198
|
+
resultId: settlement.resultId,
|
|
199
|
+
handoffId: settlement.handoffId,
|
|
200
|
+
settlement: {
|
|
201
|
+
settlementId: settlement.settlementId,
|
|
202
|
+
eventHash: settlement.settlementEventHash,
|
|
203
|
+
eventCount: settlement.settlementEventCount,
|
|
204
|
+
receiptDigest: settlement.receiptDigest,
|
|
205
|
+
},
|
|
206
|
+
source: {
|
|
207
|
+
headHash: settlement.sourceHeadHash,
|
|
208
|
+
eventCount: settlement.sourceEventCount,
|
|
209
|
+
},
|
|
210
|
+
target: {
|
|
211
|
+
headHash: settlement.targetHeadHash,
|
|
212
|
+
eventCount: settlement.targetEventCount,
|
|
213
|
+
},
|
|
214
|
+
bundleDigest: bundle.bundleDigest,
|
|
215
|
+
verificationDigest: verification.verificationDigest,
|
|
216
|
+
collectionDigest: settlement.collectionDigest,
|
|
217
|
+
storage: {
|
|
218
|
+
storeId: storage.storeId,
|
|
219
|
+
receiptDigest: storage.receiptDigest,
|
|
220
|
+
canonicalBytesDigest: storage.canonicalBytesDigest,
|
|
221
|
+
byteLength: storage.byteLength,
|
|
222
|
+
format: storage.format,
|
|
223
|
+
retention: storage.retention,
|
|
224
|
+
},
|
|
225
|
+
summary: metadata(bundle.summary),
|
|
226
|
+
diff: metadata(bundle.diff),
|
|
227
|
+
artifacts: bundle.artifacts.map(metadata),
|
|
228
|
+
evidence: bundle.evidence.map(metadata),
|
|
229
|
+
totalBytes: bundle.totalBytes,
|
|
230
|
+
applied: false,
|
|
231
|
+
applyPolicy: {
|
|
232
|
+
automaticApply: false,
|
|
233
|
+
requirements: [
|
|
234
|
+
"explicit-review-digest",
|
|
235
|
+
"exact-source-git-identity",
|
|
236
|
+
"managed-workspace-transaction",
|
|
237
|
+
"session-apply-reservation",
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
gaps: [
|
|
241
|
+
"stored-result-not-applied",
|
|
242
|
+
"local-store-not-worm-or-off-box",
|
|
243
|
+
"ide-review-ui-not-integrated",
|
|
244
|
+
],
|
|
245
|
+
};
|
|
246
|
+
return Object.freeze({
|
|
247
|
+
...material,
|
|
248
|
+
reviewDigest: digest(
|
|
249
|
+
"chainlesschain.execution-location.result-review.v1\0",
|
|
250
|
+
material,
|
|
251
|
+
),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
+
import { TextDecoder } from "node:util";
|
|
5
|
+
import { getHomeDir } from "./paths.js";
|
|
6
|
+
import { ensurePrivateDirectory, ensurePrivateFile } from "./secure-fs.js";
|
|
7
|
+
import {
|
|
8
|
+
sameFileStatIdentity,
|
|
9
|
+
samePathHandleFileIdentity,
|
|
10
|
+
withTrustedFileParentSync,
|
|
11
|
+
} from "./secure-file-identity.js";
|
|
12
|
+
import { withFileLock } from "./with-file-lock.js";
|
|
13
|
+
import { canonicalJson } from "./scheduler-kernel/contract.js";
|
|
14
|
+
import {
|
|
15
|
+
MAX_EXECUTION_LOCATION_RESULT_BUNDLE_BYTES,
|
|
16
|
+
normalizeExecutionLocationResultBundle,
|
|
17
|
+
} from "./execution-location-result.js";
|
|
18
|
+
|
|
19
|
+
export const EXECUTION_LOCATION_RESULT_STORE_RECEIPT_SCHEMA =
|
|
20
|
+
"chainlesschain.execution-location-result-store-receipt/v1";
|
|
21
|
+
|
|
22
|
+
const SHA256_RE = /^sha256:[a-f0-9]{64}$/u;
|
|
23
|
+
const STORE_ID_RE = /^result-sha256-[a-f0-9]{64}$/u;
|
|
24
|
+
|
|
25
|
+
export const _executionLocationResultStoreFaultHooks = Object.seal({
|
|
26
|
+
afterFilePublish: null,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
function digest(domain, value) {
|
|
30
|
+
return `sha256:${createHash("sha256")
|
|
31
|
+
.update(domain, "utf8")
|
|
32
|
+
.update(canonicalJson(value, "executionLocationResultStore"), "utf8")
|
|
33
|
+
.digest("hex")}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function bytesDigest(bytes) {
|
|
37
|
+
return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function exactObject(value, fields, label) {
|
|
41
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
42
|
+
throw new TypeError(`${label} must be an object`);
|
|
43
|
+
}
|
|
44
|
+
const actual = Object.keys(value).sort();
|
|
45
|
+
const expected = [...fields].sort();
|
|
46
|
+
if (
|
|
47
|
+
actual.length !== expected.length ||
|
|
48
|
+
actual.some((field, index) => field !== expected[index])
|
|
49
|
+
) {
|
|
50
|
+
throw new TypeError(`${label} has an invalid schema`);
|
|
51
|
+
}
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function canonicalBundleBytes(bundleInput) {
|
|
56
|
+
const bundle = normalizeExecutionLocationResultBundle(bundleInput);
|
|
57
|
+
const bytes = Buffer.from(
|
|
58
|
+
canonicalJson(bundle, "executionLocationResultStoreBundle"),
|
|
59
|
+
"utf8",
|
|
60
|
+
);
|
|
61
|
+
if (
|
|
62
|
+
bytes.length <= 0 ||
|
|
63
|
+
bytes.length > MAX_EXECUTION_LOCATION_RESULT_BUNDLE_BYTES
|
|
64
|
+
) {
|
|
65
|
+
throw new Error("canonical execution-location result bundle is oversized");
|
|
66
|
+
}
|
|
67
|
+
return Object.freeze({ bundle, bytes });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function createReceipt(bundle, bytes) {
|
|
71
|
+
const material = {
|
|
72
|
+
schema: EXECUTION_LOCATION_RESULT_STORE_RECEIPT_SCHEMA,
|
|
73
|
+
storeId: `result-${bundle.bundleDigest.replace(":", "-")}`,
|
|
74
|
+
sessionId: bundle.session.sessionId,
|
|
75
|
+
resultId: bundle.resultId,
|
|
76
|
+
handoffId: bundle.session.handoffId,
|
|
77
|
+
bundleDigest: bundle.bundleDigest,
|
|
78
|
+
canonicalBytesDigest: bytesDigest(bytes),
|
|
79
|
+
byteLength: bytes.length,
|
|
80
|
+
format: "canonical-json",
|
|
81
|
+
retention: "explicit-delete-local-not-worm",
|
|
82
|
+
};
|
|
83
|
+
return Object.freeze({
|
|
84
|
+
...material,
|
|
85
|
+
receiptDigest: digest(
|
|
86
|
+
"chainlesschain.execution-location.result-store-receipt.v1\0",
|
|
87
|
+
material,
|
|
88
|
+
),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function normalizeExecutionLocationResultStoreReceipt(input) {
|
|
93
|
+
const value = exactObject(
|
|
94
|
+
input,
|
|
95
|
+
[
|
|
96
|
+
"schema",
|
|
97
|
+
"storeId",
|
|
98
|
+
"sessionId",
|
|
99
|
+
"resultId",
|
|
100
|
+
"handoffId",
|
|
101
|
+
"bundleDigest",
|
|
102
|
+
"canonicalBytesDigest",
|
|
103
|
+
"byteLength",
|
|
104
|
+
"format",
|
|
105
|
+
"retention",
|
|
106
|
+
"receiptDigest",
|
|
107
|
+
],
|
|
108
|
+
"execution-location result store receipt",
|
|
109
|
+
);
|
|
110
|
+
if (
|
|
111
|
+
value.schema !== EXECUTION_LOCATION_RESULT_STORE_RECEIPT_SCHEMA ||
|
|
112
|
+
!STORE_ID_RE.test(String(value.storeId || "")) ||
|
|
113
|
+
!SHA256_RE.test(String(value.handoffId || "")) ||
|
|
114
|
+
!SHA256_RE.test(String(value.bundleDigest || "")) ||
|
|
115
|
+
!SHA256_RE.test(String(value.canonicalBytesDigest || "")) ||
|
|
116
|
+
!SHA256_RE.test(String(value.receiptDigest || "")) ||
|
|
117
|
+
value.storeId !== `result-${value.bundleDigest.replace(":", "-")}` ||
|
|
118
|
+
!Number.isSafeInteger(Number(value.byteLength)) ||
|
|
119
|
+
Number(value.byteLength) <= 0 ||
|
|
120
|
+
Number(value.byteLength) > MAX_EXECUTION_LOCATION_RESULT_BUNDLE_BYTES ||
|
|
121
|
+
value.format !== "canonical-json" ||
|
|
122
|
+
value.retention !== "explicit-delete-local-not-worm"
|
|
123
|
+
) {
|
|
124
|
+
throw new TypeError("execution-location result store receipt is invalid");
|
|
125
|
+
}
|
|
126
|
+
const material = {
|
|
127
|
+
schema: value.schema,
|
|
128
|
+
storeId: value.storeId,
|
|
129
|
+
sessionId: String(value.sessionId || ""),
|
|
130
|
+
resultId: String(value.resultId || ""),
|
|
131
|
+
handoffId: value.handoffId,
|
|
132
|
+
bundleDigest: value.bundleDigest,
|
|
133
|
+
canonicalBytesDigest: value.canonicalBytesDigest,
|
|
134
|
+
byteLength: Number(value.byteLength),
|
|
135
|
+
format: value.format,
|
|
136
|
+
retention: value.retention,
|
|
137
|
+
};
|
|
138
|
+
if (
|
|
139
|
+
material.sessionId.length === 0 ||
|
|
140
|
+
material.sessionId.length > 256 ||
|
|
141
|
+
material.resultId.length === 0 ||
|
|
142
|
+
material.resultId.length > 128 ||
|
|
143
|
+
value.receiptDigest !==
|
|
144
|
+
digest(
|
|
145
|
+
"chainlesschain.execution-location.result-store-receipt.v1\0",
|
|
146
|
+
material,
|
|
147
|
+
)
|
|
148
|
+
) {
|
|
149
|
+
throw new TypeError("execution-location result store receipt is invalid");
|
|
150
|
+
}
|
|
151
|
+
return Object.freeze({ ...material, receiptDigest: value.receiptDigest });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function executionLocationResultStoreDir() {
|
|
155
|
+
return path.join(getHomeDir(), "execution-location-results");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function resultPath(receipt, dir) {
|
|
159
|
+
return path.join(dir, `${receipt.storeId}.json`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function syncDirectory(directory, runtimeFs, platform) {
|
|
163
|
+
if (platform === "win32") return;
|
|
164
|
+
const descriptor = runtimeFs.openSync(directory, "r");
|
|
165
|
+
try {
|
|
166
|
+
runtimeFs.fsyncSync(descriptor);
|
|
167
|
+
} finally {
|
|
168
|
+
runtimeFs.closeSync(descriptor);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function readStoredBytes(receipt, dir, options = {}) {
|
|
173
|
+
const runtimeFs = options.fs || fs;
|
|
174
|
+
const filePath = resultPath(receipt, dir);
|
|
175
|
+
return (options.withTrustedFileParentSync || withTrustedFileParentSync)(
|
|
176
|
+
runtimeFs,
|
|
177
|
+
filePath,
|
|
178
|
+
({ canonicalPath, parentDevice }) => {
|
|
179
|
+
const before = runtimeFs.lstatSync(canonicalPath, { bigint: true });
|
|
180
|
+
if (
|
|
181
|
+
before.isSymbolicLink() ||
|
|
182
|
+
!before.isFile() ||
|
|
183
|
+
Number(before.nlink) !== 1 ||
|
|
184
|
+
Number(before.size) !== receipt.byteLength
|
|
185
|
+
) {
|
|
186
|
+
throw new Error("stored execution-location result identity is invalid");
|
|
187
|
+
}
|
|
188
|
+
let descriptor = null;
|
|
189
|
+
try {
|
|
190
|
+
descriptor = runtimeFs.openSync(
|
|
191
|
+
canonicalPath,
|
|
192
|
+
runtimeFs.constants.O_RDONLY |
|
|
193
|
+
Number(runtimeFs.constants.O_NOFOLLOW || 0),
|
|
194
|
+
);
|
|
195
|
+
const opened = runtimeFs.fstatSync(descriptor, { bigint: true });
|
|
196
|
+
if (
|
|
197
|
+
!opened.isFile() ||
|
|
198
|
+
Number(opened.nlink) !== 1 ||
|
|
199
|
+
!(options.samePathHandleFileIdentity || samePathHandleFileIdentity)(
|
|
200
|
+
before,
|
|
201
|
+
opened,
|
|
202
|
+
parentDevice,
|
|
203
|
+
options.runtime,
|
|
204
|
+
)
|
|
205
|
+
) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
"stored execution-location result changed while opening",
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
const bytes = runtimeFs.readFileSync(descriptor);
|
|
211
|
+
const after = runtimeFs.fstatSync(descriptor, { bigint: true });
|
|
212
|
+
if (
|
|
213
|
+
bytes.length !== receipt.byteLength ||
|
|
214
|
+
!(options.sameFileStatIdentity || sameFileStatIdentity)(opened, after)
|
|
215
|
+
) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
"stored execution-location result changed while reading",
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
return bytes;
|
|
221
|
+
} finally {
|
|
222
|
+
if (descriptor !== null) runtimeFs.closeSync(descriptor);
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function readStoredExecutionLocationResultBundle(
|
|
229
|
+
receiptInput,
|
|
230
|
+
options = {},
|
|
231
|
+
) {
|
|
232
|
+
const receipt = normalizeExecutionLocationResultStoreReceipt(receiptInput);
|
|
233
|
+
const dir = path.resolve(options.dir || executionLocationResultStoreDir());
|
|
234
|
+
(options.ensurePrivateDirectory || ensurePrivateDirectory)(dir);
|
|
235
|
+
(options.ensurePrivateFile || ensurePrivateFile)(resultPath(receipt, dir));
|
|
236
|
+
const bytes = readStoredBytes(receipt, dir, options);
|
|
237
|
+
if (bytesDigest(bytes) !== receipt.canonicalBytesDigest) {
|
|
238
|
+
throw new Error("stored execution-location result byte digest mismatch");
|
|
239
|
+
}
|
|
240
|
+
let parsed;
|
|
241
|
+
try {
|
|
242
|
+
parsed = JSON.parse(
|
|
243
|
+
new TextDecoder("utf-8", { fatal: true }).decode(bytes),
|
|
244
|
+
);
|
|
245
|
+
} catch {
|
|
246
|
+
throw new Error("stored execution-location result JSON is invalid");
|
|
247
|
+
}
|
|
248
|
+
const canonical = canonicalBundleBytes(parsed);
|
|
249
|
+
if (
|
|
250
|
+
canonical.bundle.bundleDigest !== receipt.bundleDigest ||
|
|
251
|
+
canonical.bundle.resultId !== receipt.resultId ||
|
|
252
|
+
canonical.bundle.session.sessionId !== receipt.sessionId ||
|
|
253
|
+
canonical.bundle.session.handoffId !== receipt.handoffId ||
|
|
254
|
+
!canonical.bytes.equals(bytes)
|
|
255
|
+
) {
|
|
256
|
+
throw new Error("stored execution-location result receipt mismatch");
|
|
257
|
+
}
|
|
258
|
+
return canonical.bundle;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function storeExecutionLocationResultBundle(bundleInput, options = {}) {
|
|
262
|
+
const canonical = canonicalBundleBytes(bundleInput);
|
|
263
|
+
const receipt = createReceipt(canonical.bundle, canonical.bytes);
|
|
264
|
+
const dir = path.resolve(options.dir || executionLocationResultStoreDir());
|
|
265
|
+
const runtimeFs = options.fs || fs;
|
|
266
|
+
(options.ensurePrivateDirectory || ensurePrivateDirectory)(dir);
|
|
267
|
+
const filePath = resultPath(receipt, dir);
|
|
268
|
+
return (options.withFileLock || withFileLock)(
|
|
269
|
+
filePath,
|
|
270
|
+
() => {
|
|
271
|
+
if (runtimeFs.existsSync(filePath)) {
|
|
272
|
+
readStoredExecutionLocationResultBundle(receipt, { ...options, dir });
|
|
273
|
+
return Object.freeze({ receipt, stored: false });
|
|
274
|
+
}
|
|
275
|
+
const temporaryPath = path.join(
|
|
276
|
+
dir,
|
|
277
|
+
`.${receipt.storeId}.${process.pid}.${randomUUID()}.tmp`,
|
|
278
|
+
);
|
|
279
|
+
let descriptor = null;
|
|
280
|
+
let published = false;
|
|
281
|
+
try {
|
|
282
|
+
descriptor = runtimeFs.openSync(temporaryPath, "wx", 0o600);
|
|
283
|
+
runtimeFs.writeFileSync(descriptor, canonical.bytes);
|
|
284
|
+
runtimeFs.fsyncSync(descriptor);
|
|
285
|
+
runtimeFs.closeSync(descriptor);
|
|
286
|
+
descriptor = null;
|
|
287
|
+
runtimeFs.linkSync(temporaryPath, filePath);
|
|
288
|
+
published = true;
|
|
289
|
+
runtimeFs.unlinkSync(temporaryPath);
|
|
290
|
+
(options.ensurePrivateFile || ensurePrivateFile)(filePath);
|
|
291
|
+
syncDirectory(dir, runtimeFs, options.platform || process.platform);
|
|
292
|
+
if (
|
|
293
|
+
typeof _executionLocationResultStoreFaultHooks.afterFilePublish ===
|
|
294
|
+
"function"
|
|
295
|
+
) {
|
|
296
|
+
_executionLocationResultStoreFaultHooks.afterFilePublish({
|
|
297
|
+
filePath,
|
|
298
|
+
receipt,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
} catch (error) {
|
|
302
|
+
if (descriptor !== null) {
|
|
303
|
+
try {
|
|
304
|
+
runtimeFs.closeSync(descriptor);
|
|
305
|
+
} catch {
|
|
306
|
+
// Preserve the original publication error.
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
runtimeFs.unlinkSync(temporaryPath);
|
|
311
|
+
} catch {
|
|
312
|
+
// Best-effort staging cleanup.
|
|
313
|
+
}
|
|
314
|
+
if (!published && error?.code === "EEXIST") {
|
|
315
|
+
readStoredExecutionLocationResultBundle(receipt, { ...options, dir });
|
|
316
|
+
return Object.freeze({ receipt, stored: false });
|
|
317
|
+
}
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
readStoredExecutionLocationResultBundle(receipt, { ...options, dir });
|
|
321
|
+
return Object.freeze({ receipt, stored: true });
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
failIfUnavailable: true,
|
|
325
|
+
timeoutMs: 30_000,
|
|
326
|
+
retryMs: 1,
|
|
327
|
+
maxRetryMs: 8,
|
|
328
|
+
retryJitterMs: 4,
|
|
329
|
+
},
|
|
330
|
+
);
|
|
331
|
+
}
|