chainlesschain 0.165.3 → 0.165.5
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/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-BiHNUlbU.js → AIOps-CEeYTeDB.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-D-UFjDRQ.js → ActionButton-BeRUQ3V2.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DazVsDgW.js → Analytics-DrV718XF.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DXh9UfNJ.js → AppLayout-Cd2Frt7Q.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CZ6tsceY.js → Artifacts-CUJMlORU.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-RyxnBr_h.js → Audit-CXJcfT6p.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BWS51Q6X.js → BackgroundAgents-DOv6Z7H9.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BRtPcUul.js → Backup-BwdDyBI0.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CGd4vZYY.js → BaseInput-BmLn4kYH.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-h8ijVwCg.js → Chat-COg8pAW2.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-4gGkdGgA.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-NyF6dPl8.js → Checkbox-BCP4uoU8.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-Ci4OOxr5.js → Codegen-pRt6Y9JI.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CwTkc9BM.js → Col-DLaprKhS.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DW_tM82m.js → Community-DyATsz5f.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DEvHRV4S.js → Compact-BXAaupZK.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BbjBEEfD.js → Compliance-DpcJBELr.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-10IXBaBa.js → Cowork-DG4EFWyd.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BZkbpd5I.js → Cron-F6Oy5n2P.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CFQ8L_6v.js → Crosschain-BOdgtalX.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BDF6AkXe.js → DID-DT1dSkjH.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CoOdWMi4.js → Dashboard-NBUIqWzC.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DEsGmu8Y.js → Dropdown-CSpHeMYB.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BS89SWlK.js → EmailListRenderer-CEG0xISr.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-C1FpCHiI.js → FamilyGuardDashboard-BTZ0BZna.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BzvpVmLA.js → Federation-BuuCTn2V.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BeC259PQ.js → FormItemContext-KAVceevk.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-azgG3bmV.js → GenericCardRenderer-DnHpcQvW.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BmSSowc5.js → Git-C_B_0HwB.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CTBCSyQZ.js → Governance-DPSZuzuu.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DA5xyk4a.js → Inference-BV-06V7q.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-D1gPHNig.js → KnowledgeGraph-wpDuupn6.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BPmLf1AR.js → Logs-Bseca1_I.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-SVLOg49H.js → MarkdownRenderer-B4RLp2zs.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-DtUFcR0E.js → Marketplace-d3kM4DDL.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DS_kqBv-.js → McpTools-DQ_xZ23i.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-Fvh7XL7V.js → Memory-CRIeYO4L.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-C7itAP45.js → MobileBridge-BsllIa9l.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-BUuQy30X.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-B57MiNLK.js → Mtc-BOFDIzrC.js} +6 -6
- package/src/assets/web-panel/assets/{MtcAudit-BXfCUcM7.js → MtcAudit-BQg-vyvG.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-yaJ-ZcF6.js → Multisig-Bo1jrPdN.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CoaUCKrO.js → NLProgramming-Bmod8alw.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BKLM_65R.js → Notes-CGGFL8w-.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DoDtK9uZ.js → NotificationSettings-BsZvCJxC.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BJ28TGr-.js → OrderTableRenderer-C1-Quo_h.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-MuGsdoIb.js → Organization-BjOeDYOi.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BLUN9J4R.js → Overflow-DwVV3l5A.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DWuoBUPd.js → P2P-Cu3DQT9W.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CUi37YPj.js → PdhVaultBrowser-Cg0gR7dH.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BYQg2pcr.js → Permissions-CsAXxRN5.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CAxoV2xu.js → PersonalDataHub-C-nNmnYp.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-B33wGvIO.js → Pipeline-BOnW7nNc.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-A8yXTORP.js → Privacy-ClLSyGKt.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-D49I2I1O.js → ProjectInit-DuLvjjq7.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Ci73jJDw.js → ProjectSettings-DQYYbDD9.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-CF-yD6Cn.js → Projects-Drw8MBGK.js} +1 -1
- package/src/assets/web-panel/assets/Providers-ckqHQe6K.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-Brza0ykJ.js → QrScannerModal-DW5BXk-G.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DFydhjxL.js → QuickAsk-DDLnhNTp.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DLF8_NhA.js → Recommend-PJQ2C91U.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-dyuStW3x.js → RemoteSession-s6wmda34.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-5F3tA6yw.js → Reputation-DC2sb-I3.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Ca2A57S5.js → Row-hPGk1eGj.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B0bxBkq_.js → RssFeed-RIvfZql0.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CFNvHalZ.js → Search-DFg1G1eR.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DSoH3Hav.js → Security-D6H5p8Dp.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BHpfEmBQ.js → Services-nxYiFV_w.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Dsh-QNmn.js → Skeleton-BmgFNAdB.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BhT1NkSU.js → Skills-CsaaD2av.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DY9snPeJ.js → Sla-yM5y9yvK.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CWaX0_Cx.js → SpeechSettings-DPy1XMYh.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-B690y_TC.js → SyncSettings-CFVhEyyr.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-2N-w3zWd.js → Tasks-DdO02Inn.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BjwQOQYC.js → Templates-Eo2d8Llu.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CUqHErxY.js → Tenant-ChESOXgy.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-C13rXvNy.js → Terminal-BJ7oDxTI.js} +2 -2
- package/src/assets/web-panel/assets/TimelineRenderer-hWX0fUvz.js +1 -0
- package/src/assets/web-panel/assets/{Tokens-BXHLGXYC.js → Tokens-DVBZQnd-.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-1e5i65o3.js → Trigger-D89cQfVq.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-FLGqNHs5.js → Trust-BIVSb4-V.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BbaR_LJB.js → UkeySign-B58gWopl.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-NOp4RIov.js → VideoEditing-DhHI7bmG.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-B7DW7bG4.js → Wallet-C4FheWS_.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-BptSUWix.js → WebAuthn-DnPRq-Bx.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CAJnDOXS.js → WorkflowEditor-BerRPjpJ.js} +1 -1
- package/src/assets/web-panel/assets/{chat-I8LgG1-Z.js → chat-BzZ52iKr.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CWe7SOVD.js → colors-BI_E4BAn.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Cx3u3CA3.js → compact-item-d50mr3yk.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CgD5ArwY.js → createContext-C6FsQ1IH.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CagEcIex.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BfQWiXN8.js → hasIn-DLZEMgLP.js} +1 -1
- package/src/assets/web-panel/assets/{index-BuEQSorC.js → index-9H_M2jju.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiI-xPRH.js → index-A5CDWP7y.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAVy98u7.js → index-AHr7ryWf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUAR7RY-.js → index-B81mo1Et.js} +1 -1
- package/src/assets/web-panel/assets/{index-DlIyt1qF.js → index-BAE3hwev.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dcdm_Z2c.js → index-BB9SqVSt.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWZ3REg6.js → index-BRGx3w6T.js} +3 -3
- package/src/assets/web-panel/assets/{index-B6yChzt1.js → index-BTpIOTfR.js} +1 -1
- package/src/assets/web-panel/assets/{index-DeizoQVZ.js → index-BZjjdQ62.js} +1 -1
- package/src/assets/web-panel/assets/{index-e8BM1yY1.js → index-BcSUl9fA.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNYFQxAW.js → index-BeM4hfht.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGMYTN5W.js → index-Bmr_vxXD.js} +1 -1
- package/src/assets/web-panel/assets/{index-BREnPKGo.js → index-BoNZlzhQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1cX4Rxu.js → index-C3k8kevG.js} +1 -1
- package/src/assets/web-panel/assets/{index-3iKP9OTJ.js → index-CLoTQr0W.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1UjHdgZ.js → index-CO8DAF7U.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cy_cCJlr.js → index-CRN5TRTQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0nDKhWv.js → index-CUaEHfQ0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DslTuqro.js → index-CVzuawCX.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAUnl4Jv.js → index-CdKt9fOd.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMu16vPC.js → index-CgkoH3_g.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-rIKccl.js → index-CjuKZBqJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bx7BNIRH.js → index-Co7s9LsB.js} +1 -1
- package/src/assets/web-panel/assets/{index-Doc0Ftpd.js → index-D-aBumtY.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cd79NQAx.js → index-DE0V6cp-.js} +1 -1
- package/src/assets/web-panel/assets/{index-V-kIHzAV.js → index-DY4j5zkF.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bi24S7Dw.js → index-Dec0Kf_Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCJEY2T0.js → index-DoFtCVZ7.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0ztiqtB.js → index-DqkbHHg8.js} +1 -1
- package/src/assets/web-panel/assets/{index-CcX4FSPf.js → index-DsscwxPE.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdRkCwm4.js → index-GXE0D8Nx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmgtY58H.js → index-IPkwSAW_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQDKYbnv.js → index-KNGgO4Wm.js} +1 -1
- package/src/assets/web-panel/assets/{index-87gMxqhN.js → index-Pvf30m8Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmrdUwzU.js → index-Q5Tn735s.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFb-XIKy.js → index-TWG-_D1M.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsQcURkA.js → index-Y2-q7QLm.js} +1 -1
- package/src/assets/web-panel/assets/index-pzT9PpOU.js +1 -0
- package/src/assets/web-panel/assets/index-z1kFadaX.js +1 -0
- package/src/assets/web-panel/assets/{initDefaultProps-DiBszVdG.js → initDefaultProps-D-4Hzgej.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BGIFFozv.js → motion-D4Wl4xU7.js} +1 -1
- package/src/assets/web-panel/assets/{move-D5W7mp7B.js → move-Cd5eQvL0.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-JFIlxJ7F.js → mtc-parser-B4ilslCW.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CQ3o6LIU.js → omit-Ckarx-tf.js} +1 -1
- package/src/assets/web-panel/assets/parsers-YNLm7sQ0.js +4 -0
- package/src/assets/web-panel/assets/{pickAttrs-R8MIlXo3.js → pickAttrs-QJ0bpCmc.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-sW7ylSky.js → placementArrow-CKHm-tq8.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Cnl39Vbz.js → responsiveObserve-CV6a_w_Y.js} +1 -1
- package/src/assets/web-panel/assets/{slide-sn8tFd14.js → slide-mHpeQFgk.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-IKKY_QxZ.js → statusUtils-DIaQNQWA.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BNpV4Nqs.js → styleChecker-PJpzavV8.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BWyT_eoU.js → useFlexGapSupport-BH7zTw8S.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-D09p6_U9.js → useFs-BDNG3JDd.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CzW3TkJZ.js → usePersonalDataHub-CY2VWdUg.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-3s3WoAKB.js → vnode-D0kw9jhz.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Cjy_673s.js → zoom-B5g7TN7r.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 +598 -33
- package/src/commands/cowork.js +200 -5
- package/src/commands/session-budget.js +155 -0
- package/src/commands/session-location.js +968 -1
- package/src/commands/session.js +23 -2
- package/src/data/changelog.json +28 -2
- package/src/gateways/ws/artifact-protocol.js +77 -13
- package/src/gateways/ws/message-dispatcher.js +57 -0
- 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 +462 -0
- package/src/lib/artifact-cleanup-ledger.js +861 -0
- package/src/lib/artifact-deletion-ledger.js +678 -0
- package/src/lib/artifact-recovery.js +970 -0
- package/src/lib/artifact-store.js +256 -51
- package/src/lib/artifact-workbench-projection.js +171 -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 +3592 -112
- 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 +240 -3
- package/src/lib/managed-tool-checkpoint.js +47 -4
- package/src/lib/mcp-stdio-executable-identity.js +16 -0
- package/src/lib/mcp-stdio-native-code-policy.js +74 -0
- package/src/lib/mcp-stdio-package-materialization.js +12 -3
- package/src/lib/process-execution-broker/index.js +112 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +57 -3
- package/src/lib/process-execution-broker/workspace-transaction.js +29 -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-projection.js +126 -10
- 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/repl/agent-repl.js +237 -16
- package/src/runtime/agent-core.js +135 -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/assets/web-panel/assets/ChatBubbleRenderer-D7tcdDS9.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-DGbP36-3.js +0 -1
- package/src/assets/web-panel/assets/Providers-D-etEhg5.js +0 -1
- package/src/assets/web-panel/assets/TimelineRenderer-CpbWcya_.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DLv_872g.js +0 -1
- package/src/assets/web-panel/assets/index-CdiGCibe.js +0 -1
- package/src/assets/web-panel/assets/index-D_0z-gyz.js +0 -1
- package/src/assets/web-panel/assets/parsers-UEvh_ShA.js +0 -4
|
@@ -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
|
+
}
|