chainlesschain 0.165.3 → 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-BiHNUlbU.js → AIOps-C__nnraW.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-D-UFjDRQ.js → ActionButton-CsKWHEZ-.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DazVsDgW.js → Analytics-BhIjfjWO.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DXh9UfNJ.js → AppLayout-BjuXFzWZ.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CZ6tsceY.js → Artifacts-k6whDkZJ.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-RyxnBr_h.js → Audit-C4FBMYRh.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BWS51Q6X.js → BackgroundAgents-C_vRrSXk.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BRtPcUul.js → Backup-CngFZeVp.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CGd4vZYY.js → BaseInput-B_H9EBRm.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-h8ijVwCg.js → Chat-DLEC5kt7.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-NyF6dPl8.js → Checkbox-m3ClR4xh.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-Ci4OOxr5.js → Codegen-CmLSFrgp.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CwTkc9BM.js → Col-DFGQteHN.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DW_tM82m.js → Community-EUUVORHE.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DEvHRV4S.js → Compact-2PSg0Yr7.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BbjBEEfD.js → Compliance-CcKur7gB.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-10IXBaBa.js → Cowork-DK_Fezfa.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BZkbpd5I.js → Cron-Dbw89xl7.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CFQ8L_6v.js → Crosschain-CH66h_Ec.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BDF6AkXe.js → DID-D1igRb17.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CoOdWMi4.js → Dashboard-DVAsrobk.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DEsGmu8Y.js → Dropdown-Dl7W8AQs.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BS89SWlK.js → EmailListRenderer-D5tpaEQC.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-C1FpCHiI.js → FamilyGuardDashboard-O_VQf8gq.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BzvpVmLA.js → Federation-BTVcgS9n.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BeC259PQ.js → FormItemContext-BRnkJAIz.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-azgG3bmV.js → GenericCardRenderer-8OWSCkDj.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BmSSowc5.js → Git-WXLYTQj2.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CTBCSyQZ.js → Governance-BezaUhtc.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DA5xyk4a.js → Inference-CuRay3Gu.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-D1gPHNig.js → KnowledgeGraph-DfWLDW1m.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BPmLf1AR.js → Logs-C55y2pPY.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-DtUFcR0E.js → Marketplace-Dk08Uycg.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DS_kqBv-.js → McpTools-C11fSB5V.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-Fvh7XL7V.js → Memory-mr0eZc97.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-C7itAP45.js → MobileBridge-C0iCSRbb.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-CY-xMG8Z.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-B57MiNLK.js → Mtc-Bplqc_Wc.js} +6 -6
- package/src/assets/web-panel/assets/{MtcAudit-BXfCUcM7.js → MtcAudit-BlzwsoSM.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-yaJ-ZcF6.js → Multisig-BBLmTty6.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CoaUCKrO.js → NLProgramming-DtT1uf1W.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BKLM_65R.js → Notes-DcqQAxTK.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DoDtK9uZ.js → NotificationSettings-cb0kWoZe.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BJ28TGr-.js → OrderTableRenderer-kw9HZQNN.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-MuGsdoIb.js → Organization-DlTlbkUN.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BLUN9J4R.js → Overflow-pp1qs-oY.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DWuoBUPd.js → P2P-BESLnyYz.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CUi37YPj.js → PdhVaultBrowser-B-J1mGuf.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BYQg2pcr.js → Permissions-B36PeT1_.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CAxoV2xu.js → PersonalDataHub-CTK2ad-3.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-B33wGvIO.js → Pipeline-zJNGXsac.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-A8yXTORP.js → Privacy-b9fz0Qf0.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-D49I2I1O.js → ProjectInit-DjZbZgiB.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Ci73jJDw.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-Dz-CAYYU.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-Brza0ykJ.js → QrScannerModal-A0yoQ3-3.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DFydhjxL.js → QuickAsk-DrFgFJNC.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DLF8_NhA.js → Recommend-KC8hF1g4.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-dyuStW3x.js → RemoteSession-DGTPIk6u.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-5F3tA6yw.js → Reputation-QmbwM7Yk.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Ca2A57S5.js → Row-BxjJylia.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B0bxBkq_.js → RssFeed-B7Wda1J6.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CFNvHalZ.js → Search-_plQ_YSA.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DSoH3Hav.js → Security-CD2YJZfx.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BHpfEmBQ.js → Services-qQE7NmwE.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Dsh-QNmn.js → Skeleton-DgQqkFf7.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BhT1NkSU.js → Skills-C4C0oFFt.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DY9snPeJ.js → Sla-Px2N2B5c.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CWaX0_Cx.js → SpeechSettings-DN6C3lQd.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-B690y_TC.js → SyncSettings-jm5aH_m7.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-2N-w3zWd.js → Tasks-DlkJcv67.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BjwQOQYC.js → Templates-DyrTHUBs.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CUqHErxY.js → Tenant-mC434PtL.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-C13rXvNy.js → Terminal-o0_s3NgK.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-CpbWcya_.js → TimelineRenderer-DAGlAXOw.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BXHLGXYC.js → Tokens-BnulS_bs.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-1e5i65o3.js → Trigger-suQbVXYM.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-FLGqNHs5.js → Trust-DbIJAPo5.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BbaR_LJB.js → UkeySign-CQNpbIHi.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-NOp4RIov.js → VideoEditing-D8aWCERk.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-B7DW7bG4.js → Wallet-CuLpYfZc.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-BptSUWix.js → WebAuthn-CsfG8ZMc.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CAJnDOXS.js → WorkflowEditor-BYsEAZbd.js} +1 -1
- package/src/assets/web-panel/assets/{chat-I8LgG1-Z.js → chat-FT4Um3-I.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CWe7SOVD.js → colors-CKbS_Wlt.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Cx3u3CA3.js → compact-item-CFJtzuJM.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CgD5ArwY.js → createContext-B9kSRW6K.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BfQWiXN8.js → hasIn-DR-x0Xpi.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAVy98u7.js → index-7jHr_A_k.js} +1 -1
- package/src/assets/web-panel/assets/{index-DlIyt1qF.js → index-B3ZX81mK.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWZ3REg6.js → index-B7osrXqy.js} +3 -3
- package/src/assets/web-panel/assets/{index-DGMYTN5W.js → index-BGbPnC5x.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFb-XIKy.js → index-BI5hP_qy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAUnl4Jv.js → index-BK3TiKVq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUAR7RY-.js → index-BVPU43b1.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cd79NQAx.js → index-BW-t9n-q.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiI-xPRH.js → index-BfFCQHL3.js} +1 -1
- package/src/assets/web-panel/assets/index-Bizb4hkw.js +1 -0
- package/src/assets/web-panel/assets/{index-B6yChzt1.js → index-Bt4fS2eL.js} +1 -1
- package/src/assets/web-panel/assets/{index-e8BM1yY1.js → index-BwR7yc2h.js} +1 -1
- package/src/assets/web-panel/assets/{index-V-kIHzAV.js → index-Bzz0C1BZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bx7BNIRH.js → index-C2JvmYRc.js} +1 -1
- package/src/assets/web-panel/assets/{index-87gMxqhN.js → index-CGaXUsIs.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cy_cCJlr.js → index-CJmIQ9XR.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1UjHdgZ.js → index-C_enk9I_.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bi24S7Dw.js → index-Cr2FLUsD.js} +1 -1
- package/src/assets/web-panel/assets/{index-CcX4FSPf.js → index-CvrCCqJt.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmrdUwzU.js → index-D4LfPvWA.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dcdm_Z2c.js → index-DMGmg497.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCJEY2T0.js → index-DShSkww-.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0ztiqtB.js → index-DTxNy0ZU.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-rIKccl.js → index-DW4BWjTk.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdRkCwm4.js → index-DWdqx7zI.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0nDKhWv.js → index-Dap9c3eD.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQDKYbnv.js → index-DcfxhKXR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BuEQSorC.js → index-DdkIhfi-.js} +1 -1
- package/src/assets/web-panel/assets/{index-3iKP9OTJ.js → index-DeSgw-WV.js} +1 -1
- package/src/assets/web-panel/assets/{index-Doc0Ftpd.js → index-DiNUEpSQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BREnPKGo.js → index-DjEP7WkH.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNYFQxAW.js → index-DtkFqPVA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DslTuqro.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-CMu16vPC.js → index-OSzjTzGM.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsQcURkA.js → index-QBSeNVO3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DeizoQVZ.js → index-WE08gMJP.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1cX4Rxu.js → index-r2VI1Ikk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmgtY58H.js → index-tqSEHPRT.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DiBszVdG.js → initDefaultProps-Dd7n7VgI.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BGIFFozv.js → motion-BwxouRw1.js} +1 -1
- package/src/assets/web-panel/assets/{move-D5W7mp7B.js → move-DObN0PG9.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-JFIlxJ7F.js → mtc-parser-B3nOUQc7.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CQ3o6LIU.js → omit-o9WU2Nbl.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-R8MIlXo3.js → pickAttrs-CL_vvfQ_.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-sW7ylSky.js → placementArrow-CYrM2tyK.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Cnl39Vbz.js → responsiveObserve-k_06cQ96.js} +1 -1
- package/src/assets/web-panel/assets/{slide-sn8tFd14.js → slide-CPKG0pxS.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-IKKY_QxZ.js → statusUtils-BDDEeLTw.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BNpV4Nqs.js → styleChecker-Cmb7RTh7.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BWyT_eoU.js → useFlexGapSupport-e2IedC4N.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-D09p6_U9.js → useFs-CE_gxVnm.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CzW3TkJZ.js → usePersonalDataHub-BkMIIKMz.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-3s3WoAKB.js → vnode-DW886XV1.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Cjy_673s.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 +13 -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/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/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/assets/web-panel/assets/ChatBubbleRenderer-D7tcdDS9.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-SVLOg49H.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-DGbP36-3.js +0 -1
- package/src/assets/web-panel/assets/Projects-CF-yD6Cn.js +0 -1
- package/src/assets/web-panel/assets/Providers-D-etEhg5.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
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { TextDecoder } from "node:util";
|
|
5
|
+
import {
|
|
6
|
+
sameFileStatIdentity,
|
|
7
|
+
samePathHandleFileIdentity,
|
|
8
|
+
withTrustedFileParentSync,
|
|
9
|
+
} from "./secure-file-identity.js";
|
|
10
|
+
import { canonicalJson } from "./scheduler-kernel/contract.js";
|
|
11
|
+
|
|
12
|
+
export const EXECUTION_LOCATION_RESULT_BUNDLE_SCHEMA =
|
|
13
|
+
"cc-execution-location-result-bundle/v1";
|
|
14
|
+
export const EXECUTION_LOCATION_RESULT_VERIFICATION_SCHEMA =
|
|
15
|
+
"cc-execution-location-result-verification/v1";
|
|
16
|
+
export const MAX_EXECUTION_LOCATION_RESULT_BUNDLE_BYTES = 24 * 1024 * 1024;
|
|
17
|
+
|
|
18
|
+
const MAX_RESULT_CONTENT_BYTES = 16 * 1024 * 1024;
|
|
19
|
+
const MAX_SUMMARY_BYTES = 256 * 1024;
|
|
20
|
+
const MAX_DIFF_BYTES = 8 * 1024 * 1024;
|
|
21
|
+
const MAX_RESULT_ITEMS = 64;
|
|
22
|
+
const SHA256_RE = /^sha256:[a-f0-9]{64}$/u;
|
|
23
|
+
const HEAD_RE = /^[a-f0-9]{64}$/u;
|
|
24
|
+
const RESULT_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
25
|
+
const AUTHORITY_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:@+-]{0,255}$/u;
|
|
26
|
+
const MEDIA_TYPE_RE =
|
|
27
|
+
/^[a-z0-9][a-z0-9!#$&^_.+-]{0,126}\/[a-z0-9][a-z0-9!#$&^_.+-]{0,126}$/u;
|
|
28
|
+
|
|
29
|
+
function digest(domain, value) {
|
|
30
|
+
return `sha256:${createHash("sha256")
|
|
31
|
+
.update(domain, "utf8")
|
|
32
|
+
.update(canonicalJson(value, "executionLocationResult"), "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 safeResultId(value) {
|
|
56
|
+
const resultId = String(value || "");
|
|
57
|
+
if (!RESULT_ID_RE.test(resultId)) {
|
|
58
|
+
throw new TypeError("execution location result id is invalid");
|
|
59
|
+
}
|
|
60
|
+
return resultId;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function safeMediaType(value, label) {
|
|
64
|
+
const mediaType = String(value || "").toLowerCase();
|
|
65
|
+
if (!MEDIA_TYPE_RE.test(mediaType)) {
|
|
66
|
+
throw new TypeError(`${label} media type is invalid`);
|
|
67
|
+
}
|
|
68
|
+
return mediaType;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizePositiveEventCount(value, label) {
|
|
72
|
+
const count = Number(value);
|
|
73
|
+
if (!Number.isSafeInteger(count) || count < 1) {
|
|
74
|
+
throw new TypeError(`${label} event count is invalid`);
|
|
75
|
+
}
|
|
76
|
+
return count;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function normalizeSessionAuthority(authority) {
|
|
80
|
+
if (
|
|
81
|
+
authority?.authority !== "verified-session-location-handoff" ||
|
|
82
|
+
!AUTHORITY_ID_RE.test(String(authority.sessionId || "")) ||
|
|
83
|
+
!HEAD_RE.test(String(authority.headHash || "")) ||
|
|
84
|
+
!HEAD_RE.test(String(authority.bindingEventHash || "")) ||
|
|
85
|
+
!SHA256_RE.test(String(authority.locationHandoff?.handoffId || "")) ||
|
|
86
|
+
!HEAD_RE.test(String(authority.locationHandoff?.source?.headHash || "")) ||
|
|
87
|
+
!SHA256_RE.test(
|
|
88
|
+
String(authority.locationHandoff?.source?.transcriptDigest || ""),
|
|
89
|
+
) ||
|
|
90
|
+
!SHA256_RE.test(
|
|
91
|
+
String(authority.locationHandoff?.target?.profileDigest || ""),
|
|
92
|
+
) ||
|
|
93
|
+
!SHA256_RE.test(
|
|
94
|
+
String(authority.locationHandoff?.target?.targetFactsDigest || ""),
|
|
95
|
+
) ||
|
|
96
|
+
!SHA256_RE.test(
|
|
97
|
+
String(authority.locationHandoff?.target?.attestationDigest || ""),
|
|
98
|
+
)
|
|
99
|
+
) {
|
|
100
|
+
throw new TypeError(
|
|
101
|
+
"execution location result requires a verified location-handoff session",
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const targetEventCount = normalizePositiveEventCount(
|
|
105
|
+
authority.eventCount,
|
|
106
|
+
"target session",
|
|
107
|
+
);
|
|
108
|
+
const bindingEventCount = normalizePositiveEventCount(
|
|
109
|
+
authority.bindingEventCount,
|
|
110
|
+
"target binding",
|
|
111
|
+
);
|
|
112
|
+
const sourceEventCount = normalizePositiveEventCount(
|
|
113
|
+
authority.locationHandoff.source.eventCount,
|
|
114
|
+
"source session",
|
|
115
|
+
);
|
|
116
|
+
if (
|
|
117
|
+
authority.locationHandoff.source.sessionId !== authority.sessionId ||
|
|
118
|
+
authority.bindingEventHash !== authority.locationHandoff.eventHash ||
|
|
119
|
+
authority.bindingEventCount !== authority.locationHandoff.eventCount ||
|
|
120
|
+
bindingEventCount !== sourceEventCount + 1 ||
|
|
121
|
+
targetEventCount < bindingEventCount
|
|
122
|
+
) {
|
|
123
|
+
throw new TypeError(
|
|
124
|
+
"execution location result handoff authority is inconsistent",
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
sessionId: authority.sessionId,
|
|
129
|
+
handoffId: authority.locationHandoff.handoffId,
|
|
130
|
+
source: {
|
|
131
|
+
headHash: authority.locationHandoff.source.headHash,
|
|
132
|
+
eventCount: sourceEventCount,
|
|
133
|
+
transcriptDigest: authority.locationHandoff.source.transcriptDigest,
|
|
134
|
+
},
|
|
135
|
+
target: {
|
|
136
|
+
headHash: authority.headHash,
|
|
137
|
+
eventCount: targetEventCount,
|
|
138
|
+
bindingEventHash: authority.bindingEventHash,
|
|
139
|
+
bindingEventCount,
|
|
140
|
+
profileDigest: authority.locationHandoff.target.profileDigest,
|
|
141
|
+
targetEvidenceId: String(
|
|
142
|
+
authority.locationHandoff.target.targetEvidenceId || "",
|
|
143
|
+
),
|
|
144
|
+
targetFactsDigest: authority.locationHandoff.target.targetFactsDigest,
|
|
145
|
+
attestationDigest: authority.locationHandoff.target.attestationDigest,
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function contentRecord(bytesInput, mediaType, label, maxBytes) {
|
|
151
|
+
const bytes = Buffer.isBuffer(bytesInput)
|
|
152
|
+
? Buffer.from(bytesInput)
|
|
153
|
+
: Buffer.from(bytesInput || "");
|
|
154
|
+
if (bytes.length > maxBytes) {
|
|
155
|
+
throw new TypeError(`${label} exceeds ${maxBytes} bytes`);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
mediaType: safeMediaType(mediaType, label),
|
|
159
|
+
byteLength: bytes.length,
|
|
160
|
+
digest: bytesDigest(bytes),
|
|
161
|
+
contentBase64: bytes.toString("base64"),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function assertStrictUtf8Content(record, label) {
|
|
166
|
+
try {
|
|
167
|
+
new TextDecoder("utf-8", { fatal: true }).decode(
|
|
168
|
+
Buffer.from(record.contentBase64, "base64"),
|
|
169
|
+
);
|
|
170
|
+
} catch {
|
|
171
|
+
throw new TypeError(`${label} must contain strict UTF-8`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function normalizeContentRecord(value, label, maxBytes) {
|
|
176
|
+
const input = exactObject(
|
|
177
|
+
value,
|
|
178
|
+
["mediaType", "byteLength", "digest", "contentBase64"],
|
|
179
|
+
label,
|
|
180
|
+
);
|
|
181
|
+
if (
|
|
182
|
+
typeof input.contentBase64 !== "string" ||
|
|
183
|
+
!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(
|
|
184
|
+
input.contentBase64,
|
|
185
|
+
)
|
|
186
|
+
) {
|
|
187
|
+
throw new TypeError(`${label} base64 is invalid`);
|
|
188
|
+
}
|
|
189
|
+
const bytes = Buffer.from(input.contentBase64, "base64");
|
|
190
|
+
if (
|
|
191
|
+
bytes.toString("base64") !== input.contentBase64 ||
|
|
192
|
+
!Number.isSafeInteger(input.byteLength) ||
|
|
193
|
+
input.byteLength !== bytes.length ||
|
|
194
|
+
bytes.length > maxBytes ||
|
|
195
|
+
input.digest !== bytesDigest(bytes)
|
|
196
|
+
) {
|
|
197
|
+
throw new TypeError(`${label} bytes or digest are invalid`);
|
|
198
|
+
}
|
|
199
|
+
return contentRecord(bytes, input.mediaType, label, maxBytes);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function normalizeItemList(value, label) {
|
|
203
|
+
if (!Array.isArray(value) || value.length > MAX_RESULT_ITEMS) {
|
|
204
|
+
throw new TypeError(`${label} list is invalid`);
|
|
205
|
+
}
|
|
206
|
+
const items = value
|
|
207
|
+
.map((entry, index) =>
|
|
208
|
+
normalizeContentRecord(
|
|
209
|
+
entry,
|
|
210
|
+
`${label} ${index}`,
|
|
211
|
+
MAX_RESULT_CONTENT_BYTES,
|
|
212
|
+
),
|
|
213
|
+
)
|
|
214
|
+
.sort((left, right) =>
|
|
215
|
+
canonicalJson(left, "executionLocationResultItem").localeCompare(
|
|
216
|
+
canonicalJson(right, "executionLocationResultItem"),
|
|
217
|
+
),
|
|
218
|
+
);
|
|
219
|
+
if (
|
|
220
|
+
new Set(items.map((entry) => `${entry.mediaType}\0${entry.digest}`)).size !==
|
|
221
|
+
items.length
|
|
222
|
+
) {
|
|
223
|
+
throw new TypeError(`${label} contains duplicate content`);
|
|
224
|
+
}
|
|
225
|
+
return items;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function normalizeBundleSession(value) {
|
|
229
|
+
const input = exactObject(
|
|
230
|
+
value,
|
|
231
|
+
["sessionId", "handoffId", "source", "target"],
|
|
232
|
+
"execution location result session",
|
|
233
|
+
);
|
|
234
|
+
const source = exactObject(
|
|
235
|
+
input.source,
|
|
236
|
+
["headHash", "eventCount", "transcriptDigest"],
|
|
237
|
+
"execution location result source",
|
|
238
|
+
);
|
|
239
|
+
const target = exactObject(
|
|
240
|
+
input.target,
|
|
241
|
+
[
|
|
242
|
+
"headHash",
|
|
243
|
+
"eventCount",
|
|
244
|
+
"bindingEventHash",
|
|
245
|
+
"bindingEventCount",
|
|
246
|
+
"profileDigest",
|
|
247
|
+
"targetEvidenceId",
|
|
248
|
+
"targetFactsDigest",
|
|
249
|
+
"attestationDigest",
|
|
250
|
+
],
|
|
251
|
+
"execution location result target",
|
|
252
|
+
);
|
|
253
|
+
const normalized = {
|
|
254
|
+
sessionId: String(input.sessionId || ""),
|
|
255
|
+
handoffId: String(input.handoffId || ""),
|
|
256
|
+
source: {
|
|
257
|
+
headHash: String(source.headHash || ""),
|
|
258
|
+
eventCount: normalizePositiveEventCount(source.eventCount, "source"),
|
|
259
|
+
transcriptDigest: String(source.transcriptDigest || ""),
|
|
260
|
+
},
|
|
261
|
+
target: {
|
|
262
|
+
headHash: String(target.headHash || ""),
|
|
263
|
+
eventCount: normalizePositiveEventCount(target.eventCount, "target"),
|
|
264
|
+
bindingEventHash: String(target.bindingEventHash || ""),
|
|
265
|
+
bindingEventCount: normalizePositiveEventCount(
|
|
266
|
+
target.bindingEventCount,
|
|
267
|
+
"target binding",
|
|
268
|
+
),
|
|
269
|
+
profileDigest: String(target.profileDigest || ""),
|
|
270
|
+
targetEvidenceId: String(target.targetEvidenceId || ""),
|
|
271
|
+
targetFactsDigest: String(target.targetFactsDigest || ""),
|
|
272
|
+
attestationDigest: String(target.attestationDigest || ""),
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
if (
|
|
276
|
+
!AUTHORITY_ID_RE.test(normalized.sessionId) ||
|
|
277
|
+
!SHA256_RE.test(normalized.handoffId) ||
|
|
278
|
+
!HEAD_RE.test(normalized.source.headHash) ||
|
|
279
|
+
!SHA256_RE.test(normalized.source.transcriptDigest) ||
|
|
280
|
+
!HEAD_RE.test(normalized.target.headHash) ||
|
|
281
|
+
!HEAD_RE.test(normalized.target.bindingEventHash) ||
|
|
282
|
+
!SHA256_RE.test(normalized.target.profileDigest) ||
|
|
283
|
+
!AUTHORITY_ID_RE.test(normalized.target.targetEvidenceId) ||
|
|
284
|
+
!SHA256_RE.test(normalized.target.targetFactsDigest) ||
|
|
285
|
+
!SHA256_RE.test(normalized.target.attestationDigest) ||
|
|
286
|
+
normalized.target.bindingEventCount !==
|
|
287
|
+
normalized.source.eventCount + 1 ||
|
|
288
|
+
normalized.target.eventCount < normalized.target.bindingEventCount
|
|
289
|
+
) {
|
|
290
|
+
throw new TypeError("execution location result session is invalid");
|
|
291
|
+
}
|
|
292
|
+
return normalized;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export function createExecutionLocationResultBundle(input = {}) {
|
|
296
|
+
const session = normalizeSessionAuthority(input.sessionAuthority);
|
|
297
|
+
const summary = contentRecord(
|
|
298
|
+
input.summaryBytes,
|
|
299
|
+
"text/plain",
|
|
300
|
+
"execution location result summary",
|
|
301
|
+
MAX_SUMMARY_BYTES,
|
|
302
|
+
);
|
|
303
|
+
if (summary.byteLength === 0) {
|
|
304
|
+
throw new TypeError("execution location result summary is empty");
|
|
305
|
+
}
|
|
306
|
+
assertStrictUtf8Content(summary, "execution location result summary");
|
|
307
|
+
const diff = contentRecord(
|
|
308
|
+
input.diffBytes,
|
|
309
|
+
"text/x-diff",
|
|
310
|
+
"execution location result diff",
|
|
311
|
+
MAX_DIFF_BYTES,
|
|
312
|
+
);
|
|
313
|
+
const normalizeInputItems = (items, label) => {
|
|
314
|
+
if (!Array.isArray(items) || items.length > MAX_RESULT_ITEMS) {
|
|
315
|
+
throw new TypeError(`${label} list is invalid`);
|
|
316
|
+
}
|
|
317
|
+
return items
|
|
318
|
+
.map((entry, index) =>
|
|
319
|
+
contentRecord(
|
|
320
|
+
entry?.bytes,
|
|
321
|
+
entry?.mediaType,
|
|
322
|
+
`${label} ${index}`,
|
|
323
|
+
MAX_RESULT_CONTENT_BYTES,
|
|
324
|
+
),
|
|
325
|
+
)
|
|
326
|
+
.sort((left, right) =>
|
|
327
|
+
canonicalJson(left, "executionLocationResultItem").localeCompare(
|
|
328
|
+
canonicalJson(right, "executionLocationResultItem"),
|
|
329
|
+
),
|
|
330
|
+
);
|
|
331
|
+
};
|
|
332
|
+
const artifacts = normalizeInputItems(input.artifacts || [], "artifact");
|
|
333
|
+
const evidence = normalizeInputItems(input.evidence || [], "evidence");
|
|
334
|
+
const contentIdentities = [...artifacts, ...evidence].map(
|
|
335
|
+
(entry) => `${entry.mediaType}\0${entry.digest}`,
|
|
336
|
+
);
|
|
337
|
+
if (artifacts.length + evidence.length > MAX_RESULT_ITEMS) {
|
|
338
|
+
throw new TypeError("execution location result item count is invalid");
|
|
339
|
+
}
|
|
340
|
+
if (new Set(contentIdentities).size !== contentIdentities.length) {
|
|
341
|
+
throw new TypeError("execution location result contains duplicate content");
|
|
342
|
+
}
|
|
343
|
+
const totalBytes = [summary, diff, ...artifacts, ...evidence].reduce(
|
|
344
|
+
(total, entry) => total + entry.byteLength,
|
|
345
|
+
0,
|
|
346
|
+
);
|
|
347
|
+
if (totalBytes > MAX_RESULT_CONTENT_BYTES) {
|
|
348
|
+
throw new TypeError(
|
|
349
|
+
`execution location result content exceeds ${MAX_RESULT_CONTENT_BYTES} bytes`,
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
const material = {
|
|
353
|
+
schema: EXECUTION_LOCATION_RESULT_BUNDLE_SCHEMA,
|
|
354
|
+
resultId: safeResultId(input.resultId),
|
|
355
|
+
session,
|
|
356
|
+
summary,
|
|
357
|
+
diff,
|
|
358
|
+
artifacts,
|
|
359
|
+
evidence,
|
|
360
|
+
totalBytes,
|
|
361
|
+
};
|
|
362
|
+
return Object.freeze({
|
|
363
|
+
...material,
|
|
364
|
+
bundleDigest: digest(
|
|
365
|
+
"chainlesschain.execution-location.result-bundle.v1\0",
|
|
366
|
+
material,
|
|
367
|
+
),
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export function normalizeExecutionLocationResultBundle(value) {
|
|
372
|
+
const input = exactObject(
|
|
373
|
+
value,
|
|
374
|
+
[
|
|
375
|
+
"schema",
|
|
376
|
+
"resultId",
|
|
377
|
+
"session",
|
|
378
|
+
"summary",
|
|
379
|
+
"diff",
|
|
380
|
+
"artifacts",
|
|
381
|
+
"evidence",
|
|
382
|
+
"totalBytes",
|
|
383
|
+
"bundleDigest",
|
|
384
|
+
],
|
|
385
|
+
"execution location result bundle",
|
|
386
|
+
);
|
|
387
|
+
if (input.schema !== EXECUTION_LOCATION_RESULT_BUNDLE_SCHEMA) {
|
|
388
|
+
throw new TypeError("execution location result bundle schema is invalid");
|
|
389
|
+
}
|
|
390
|
+
const material = {
|
|
391
|
+
schema: EXECUTION_LOCATION_RESULT_BUNDLE_SCHEMA,
|
|
392
|
+
resultId: safeResultId(input.resultId),
|
|
393
|
+
session: normalizeBundleSession(input.session),
|
|
394
|
+
summary: normalizeContentRecord(
|
|
395
|
+
input.summary,
|
|
396
|
+
"execution location result summary",
|
|
397
|
+
MAX_SUMMARY_BYTES,
|
|
398
|
+
),
|
|
399
|
+
diff: normalizeContentRecord(
|
|
400
|
+
input.diff,
|
|
401
|
+
"execution location result diff",
|
|
402
|
+
MAX_DIFF_BYTES,
|
|
403
|
+
),
|
|
404
|
+
artifacts: normalizeItemList(input.artifacts, "artifact"),
|
|
405
|
+
evidence: normalizeItemList(input.evidence, "evidence"),
|
|
406
|
+
totalBytes: Number(input.totalBytes),
|
|
407
|
+
};
|
|
408
|
+
if (material.summary.byteLength === 0) {
|
|
409
|
+
throw new TypeError("execution location result summary is empty");
|
|
410
|
+
}
|
|
411
|
+
if (
|
|
412
|
+
material.summary.mediaType !== "text/plain" ||
|
|
413
|
+
material.diff.mediaType !== "text/x-diff"
|
|
414
|
+
) {
|
|
415
|
+
throw new TypeError("execution location result core media type is invalid");
|
|
416
|
+
}
|
|
417
|
+
assertStrictUtf8Content(
|
|
418
|
+
material.summary,
|
|
419
|
+
"execution location result summary",
|
|
420
|
+
);
|
|
421
|
+
const contentIdentities = [
|
|
422
|
+
...material.artifacts,
|
|
423
|
+
...material.evidence,
|
|
424
|
+
].map((entry) => `${entry.mediaType}\0${entry.digest}`);
|
|
425
|
+
const calculatedBytes = [
|
|
426
|
+
material.summary,
|
|
427
|
+
material.diff,
|
|
428
|
+
...material.artifacts,
|
|
429
|
+
...material.evidence,
|
|
430
|
+
].reduce((total, entry) => total + entry.byteLength, 0);
|
|
431
|
+
if (
|
|
432
|
+
material.artifacts.length + material.evidence.length > MAX_RESULT_ITEMS ||
|
|
433
|
+
new Set(contentIdentities).size !== contentIdentities.length ||
|
|
434
|
+
!Number.isSafeInteger(material.totalBytes) ||
|
|
435
|
+
material.totalBytes !== calculatedBytes ||
|
|
436
|
+
material.totalBytes > MAX_RESULT_CONTENT_BYTES
|
|
437
|
+
) {
|
|
438
|
+
throw new TypeError("execution location result total bytes are invalid");
|
|
439
|
+
}
|
|
440
|
+
const bundleDigest = digest(
|
|
441
|
+
"chainlesschain.execution-location.result-bundle.v1\0",
|
|
442
|
+
material,
|
|
443
|
+
);
|
|
444
|
+
if (input.bundleDigest !== bundleDigest) {
|
|
445
|
+
throw new TypeError("execution location result bundle digest is invalid");
|
|
446
|
+
}
|
|
447
|
+
return { ...material, bundleDigest };
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export function verifyExecutionLocationResultBundle(input = {}) {
|
|
451
|
+
const bundle = normalizeExecutionLocationResultBundle(input.bundle);
|
|
452
|
+
const source = input.sourceAuthority;
|
|
453
|
+
const expectedHandoffId = String(input.expectedHandoffId || "");
|
|
454
|
+
if (
|
|
455
|
+
source?.sessionId !== bundle.session.sessionId ||
|
|
456
|
+
source?.headHash !== bundle.session.source.headHash ||
|
|
457
|
+
source?.eventCount !== bundle.session.source.eventCount ||
|
|
458
|
+
!SHA256_RE.test(expectedHandoffId) ||
|
|
459
|
+
expectedHandoffId !== bundle.session.handoffId
|
|
460
|
+
) {
|
|
461
|
+
throw new Error(
|
|
462
|
+
"execution location result source authority or handoff changed",
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
const material = {
|
|
466
|
+
schema: EXECUTION_LOCATION_RESULT_VERIFICATION_SCHEMA,
|
|
467
|
+
resultId: bundle.resultId,
|
|
468
|
+
sessionId: bundle.session.sessionId,
|
|
469
|
+
handoffId: bundle.session.handoffId,
|
|
470
|
+
sourceHeadHash: bundle.session.source.headHash,
|
|
471
|
+
sourceEventCount: bundle.session.source.eventCount,
|
|
472
|
+
targetHeadHash: bundle.session.target.headHash,
|
|
473
|
+
targetEventCount: bundle.session.target.eventCount,
|
|
474
|
+
bundleDigest: bundle.bundleDigest,
|
|
475
|
+
summary: {
|
|
476
|
+
byteLength: bundle.summary.byteLength,
|
|
477
|
+
digest: bundle.summary.digest,
|
|
478
|
+
},
|
|
479
|
+
diff: {
|
|
480
|
+
byteLength: bundle.diff.byteLength,
|
|
481
|
+
digest: bundle.diff.digest,
|
|
482
|
+
},
|
|
483
|
+
artifacts: bundle.artifacts.map(({ mediaType, byteLength, digest }) => ({
|
|
484
|
+
mediaType,
|
|
485
|
+
byteLength,
|
|
486
|
+
digest,
|
|
487
|
+
})),
|
|
488
|
+
evidence: bundle.evidence.map(({ mediaType, byteLength, digest }) => ({
|
|
489
|
+
mediaType,
|
|
490
|
+
byteLength,
|
|
491
|
+
digest,
|
|
492
|
+
})),
|
|
493
|
+
totalBytes: bundle.totalBytes,
|
|
494
|
+
applied: false,
|
|
495
|
+
};
|
|
496
|
+
return Object.freeze({
|
|
497
|
+
...material,
|
|
498
|
+
verificationDigest: digest(
|
|
499
|
+
"chainlesschain.execution-location.result-verification.v1\0",
|
|
500
|
+
material,
|
|
501
|
+
),
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function assertWithinBoundary(filePath, boundaryRoot, label) {
|
|
506
|
+
const resolved = path.resolve(filePath);
|
|
507
|
+
const root = path.resolve(boundaryRoot);
|
|
508
|
+
const relative = path.relative(root, resolved);
|
|
509
|
+
if (
|
|
510
|
+
relative === "" ||
|
|
511
|
+
(!relative.startsWith(`..${path.sep}`) &&
|
|
512
|
+
relative !== ".." &&
|
|
513
|
+
!path.isAbsolute(relative))
|
|
514
|
+
) {
|
|
515
|
+
return resolved;
|
|
516
|
+
}
|
|
517
|
+
throw new Error(`${label} escapes the execution location data boundary`);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export function readExecutionLocationResultFile(
|
|
521
|
+
filePath,
|
|
522
|
+
{
|
|
523
|
+
boundaryRoot,
|
|
524
|
+
maxBytes = MAX_EXECUTION_LOCATION_RESULT_BUNDLE_BYTES,
|
|
525
|
+
allowEmpty = false,
|
|
526
|
+
fs: runtimeFs = fs,
|
|
527
|
+
withTrustedFileParentSync: trustedParent = withTrustedFileParentSync,
|
|
528
|
+
samePathHandleFileIdentity: samePathHandle = samePathHandleFileIdentity,
|
|
529
|
+
sameFileStatIdentity: sameStat = sameFileStatIdentity,
|
|
530
|
+
runtime,
|
|
531
|
+
} = {},
|
|
532
|
+
) {
|
|
533
|
+
if (!boundaryRoot) {
|
|
534
|
+
throw new Error("execution location result data boundary is required");
|
|
535
|
+
}
|
|
536
|
+
const resolved = assertWithinBoundary(
|
|
537
|
+
filePath,
|
|
538
|
+
boundaryRoot,
|
|
539
|
+
"execution location result file",
|
|
540
|
+
);
|
|
541
|
+
return trustedParent(
|
|
542
|
+
runtimeFs,
|
|
543
|
+
resolved,
|
|
544
|
+
({ canonicalPath, parentDevice }) => {
|
|
545
|
+
const before = runtimeFs.lstatSync(canonicalPath, { bigint: true });
|
|
546
|
+
if (
|
|
547
|
+
before.isSymbolicLink() ||
|
|
548
|
+
!before.isFile() ||
|
|
549
|
+
Number(before.nlink) !== 1
|
|
550
|
+
) {
|
|
551
|
+
throw new Error(
|
|
552
|
+
"execution location result file must be regular and single-link",
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
const size = Number(before.size);
|
|
556
|
+
if ((!allowEmpty && size === 0) || size < 0 || size > maxBytes) {
|
|
557
|
+
throw new Error(
|
|
558
|
+
`execution location result file must be ${allowEmpty ? "0" : "1"}..${maxBytes} bytes`,
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
let descriptor = null;
|
|
562
|
+
try {
|
|
563
|
+
descriptor = runtimeFs.openSync(
|
|
564
|
+
canonicalPath,
|
|
565
|
+
runtimeFs.constants.O_RDONLY |
|
|
566
|
+
Number(runtimeFs.constants.O_NOFOLLOW || 0),
|
|
567
|
+
);
|
|
568
|
+
const opened = runtimeFs.fstatSync(descriptor, { bigint: true });
|
|
569
|
+
if (
|
|
570
|
+
!opened.isFile() ||
|
|
571
|
+
Number(opened.nlink) !== 1 ||
|
|
572
|
+
!samePathHandle(before, opened, parentDevice, runtime)
|
|
573
|
+
) {
|
|
574
|
+
throw new Error(
|
|
575
|
+
"execution location result file identity changed while opening",
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
const output = Buffer.allocUnsafe(size);
|
|
579
|
+
let offset = 0;
|
|
580
|
+
while (offset < size) {
|
|
581
|
+
const count = runtimeFs.readSync(
|
|
582
|
+
descriptor,
|
|
583
|
+
output,
|
|
584
|
+
offset,
|
|
585
|
+
size - offset,
|
|
586
|
+
null,
|
|
587
|
+
);
|
|
588
|
+
if (count === 0) break;
|
|
589
|
+
offset += count;
|
|
590
|
+
}
|
|
591
|
+
const after = runtimeFs.fstatSync(descriptor, { bigint: true });
|
|
592
|
+
if (offset !== size || !sameStat(opened, after)) {
|
|
593
|
+
throw new Error(
|
|
594
|
+
"execution location result file changed while being read",
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
return output;
|
|
598
|
+
} finally {
|
|
599
|
+
if (descriptor !== null) runtimeFs.closeSync(descriptor);
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
{ runtime },
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
export function readExecutionLocationResultBundle(filePath, options = {}) {
|
|
607
|
+
const bytes = readExecutionLocationResultFile(filePath, options);
|
|
608
|
+
let value;
|
|
609
|
+
try {
|
|
610
|
+
value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
611
|
+
} catch (error) {
|
|
612
|
+
throw new Error(
|
|
613
|
+
`execution location result bundle is not strict JSON: ${error.message}`,
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
return normalizeExecutionLocationResultBundle(value);
|
|
617
|
+
}
|