chainlesschain 0.166.65 → 0.166.69
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 +20 -12
- package/package.json +4 -4
- package/src/assets/web-panel/assets/{AIOps-Byz17CTN.js → AIOps-FhevRfL6.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-C5LiW2-J.js → ActionButton-rOeOykx5.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D1INcvZF.js → Analytics-BK_wYlfz.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DZzHO1VV.js → AppLayout-_2cCtZKV.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-WfYQL6Ia.js → Artifacts-D5MSvVzk.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DohBoGWO.js → Audit-yd-Ah-A3.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-Chtk-Hde.js → BackgroundAgents-yCaw_Gu_.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-B813PuMz.js → Backup-G3ADD4N-.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-DaDdWWgK.js → BaseInput-ByDhkLbF.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-D4pJ6oZ7.js → Chat-BMrc7qjz.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-nA7n0I6S.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-Clo9TvO6.js → Checkbox-BHYgyZWA.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-uejLg8Ih.js → Codegen-DJfcy0jC.js} +1 -1
- package/src/assets/web-panel/assets/{Col-B7nbx-iJ.js → Col-C27zROxc.js} +1 -1
- package/src/assets/web-panel/assets/{Community-FEyjPK1j.js → Community-CuS5tq_1.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-B6Fk0Kn6.js → Compact-BrYJNe7Q.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BQG8goUl.js → Compliance-8hfahYHT.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CbBeEfHp.js → Cowork-DdE0n2lp.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-DPzlFcEZ.js → Cron-CUq3aSiX.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-B8pWJrbg.js → Crosschain-BM4XSPWG.js} +1 -1
- package/src/assets/web-panel/assets/{DID-gGQHUlFN.js → DID-CdaBY57N.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-C2TMFHrn.js → Dashboard-Cwtd52So.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BVPxGbll.js → Dropdown-BvlObh6U.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CNPxs0hd.js → EmailListRenderer-e3fLhjnX.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-_XTiB7q1.js → EvolutionSettings-CTtk1Zo8.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-UyE3VWtI.js → FamilyGuardDashboard-BpPJUnCU.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CLt6FHK-.js → Federation-vzjZ6_nQ.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CdNQPRPw.js → FormItemContext-DEDNLhkE.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BJ0AMhrh.js → GenericCardRenderer-DEl2Te5f.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CQnvMfOM.js → Git-BzsYQnCq.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-deYsHC-o.js → Governance-zxJs6Y8s.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-qaSck43z.js → Inference-5JUsrmF1.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-De0aJ3Ls.js → KnowledgeGraph-Br8b5Htz.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-ZtS98jCm.js → Logs-D-tubDMJ.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-In4itZX2.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Byz3xCBN.js → Marketplace-s8MmWkS9.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DOZUUW6z.js → McpTools-Wu-WRg4M.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-C33wC2hA.js → Memory-B_i-eX9w.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-zZe-T_6M.js → MobileBridge-Ca64kjVx.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CRnUIume.js → MobileProjects-Cq9AFiO4.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-C1xlRVAN.js → Mtc-DKvGmG3y.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-D0U07bIi.js → MtcAudit-BVvWx8pF.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-DF8uvV3h.js → Multisig-wqeJ2O9x.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-bBPGAFlt.js → NLProgramming-DX87ysYu.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BHAaOsgg.js → Notes-ChenGdu0.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-Cl0fG_rA.js → NotificationSettings-BZRa68-x.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BEPAs8Zv.js → OrderTableRenderer-DZ6xG1M6.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BAxSwgk5.js → Organization-BWJp4VPX.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-D68KubAu.js → Overflow-BoM4bYt5.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-bhdT17zk.js → P2P-Cvi8YV09.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DqaO6wgH.js → PdhVaultBrowser-CNVC1NIj.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-Bq8fvkak.js → Permissions-DNd1MRjW.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BYd2JEic.js → PersonalDataHub-DGSBYZ5-.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-Csg_ja69.js → Pipeline-DkcoGMBo.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-lwxwLj4M.js → Privacy-DltwvMAW.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BdBZkH8z.js → ProjectInit-ChYbpHg8.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-ClWgFy-K.js → ProjectSettings-B8FWiHTJ.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Bu-qj8N1.js → Projects-DuyhtSm4.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DEjxrxw3.js → Providers-Bo18Tx_6.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-eccy-8Vi.js → QrScannerModal-6W85KlsG.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Vx3uDvFg.js → QuickAsk-DznRUedP.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CfUivNps.js → Recommend-CQDN3_p-.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BU4gk7GI.js → RemoteSession-CiJgokEj.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-CbPzdZ0C.js → Reputation-DexzGH7E.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CYLrgfKA.js → Row-BgZOIvSi.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DbY1oyuv.js → RssFeed-BWIZJ_Jr.js} +3 -3
- package/src/assets/web-panel/assets/{Search-QeW41K5r.js → Search-B376EwWJ.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CJxf1kLZ.js → Security-92mUR5OD.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CFO9ySZc.js → Services-ClSEAOHk.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CQFjeQN0.js → Skeleton-DVhLbv99.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CLLB4INW.js → Skills-JTcXs_xI.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DxeLIJk0.js → Sla-DnKY9zy9.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-D4FA9KYI.js → SpeechSettings-GO1JefAH.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-C6NOkcQK.js → SyncSettings-jX5tB2St.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-LmVPWDfQ.js → Tasks-BxRBzhUc.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BdEfUoVB.js → Templates-CAVkbTG1.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-IUlESacN.js → Tenant-5Y7sHBkz.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B9pDe7sn.js → Terminal-CbbLShYb.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-ZCwB-Pki.js → TimelineRenderer-2HptWihL.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-4qk16LX1.js → Tokens-B-jGxzHl.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-jXNyargn.js → Trigger-9DGa90DT.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-D13PydtT.js → Trust-BSJEkEOO.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-yPgws6mS.js → UkeySign-C807So5R.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B329WjpG.js → VideoEditing-ByiieAv-.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-T8VM-Ttm.js → Wallet-BixbIOUv.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-XuRibrpP.js → WebAuthn-z7akhor4.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-D1Gwz3YR.js → WorkflowEditor-KvijuUuR.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DjydtnT-.js → chat-BYKoh0YG.js} +1 -1
- package/src/assets/web-panel/assets/{colors-gFtBvPrr.js → colors-CLT8IYBt.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CndxhGfx.js → compact-item-DlBIyYHF.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DnfGpd0L.js → createContext-5XmAnNLr.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BmcCIHwY.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CmT5hRVW.js → hasIn-rPdRhm7K.js} +1 -1
- package/src/assets/web-panel/assets/{index-DoBKSzqu.js → index-8un-eUDI.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGbKeg8G.js → index-B-0nAxf5.js} +1 -1
- package/src/assets/web-panel/assets/{index-DreYqebI.js → index-B1VfBUPB.js} +1 -1
- package/src/assets/web-panel/assets/{index-zQf599Uq.js → index-BBt2CSAx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlTcxT_L.js → index-BDtlwjSr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqpR0Des.js → index-BIRl2Ii5.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3HvYeA3.js → index-BgevTkUv.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0WFjxWs.js → index-Bve7Wsgf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNHdO6Jr.js → index-Bzd0sFkp.js} +1 -1
- package/src/assets/web-panel/assets/{index-DV2PR3Fa.js → index-C2sFDVb_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2d7358Y.js → index-CErvHCDe.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaEPE2DC.js → index-CM_a9spK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKyuE_Y2.js → index-CY6TMc2m.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVM_w30N.js → index-ChlzaTV3.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZLDbPTa.js → index-CsnbOKdI.js} +1 -1
- package/src/assets/web-panel/assets/{index-HHP1zzo7.js → index-D3-NL-x-.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFif4Cev.js → index-D3f9Jzx2.js} +1 -1
- package/src/assets/web-panel/assets/{index-C14jryXi.js → index-DCDSycWo.js} +1 -1
- package/src/assets/web-panel/assets/{index-CB27echm.js → index-DIVrVGvf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGlmPbl_.js → index-DI_tYFBe.js} +1 -1
- package/src/assets/web-panel/assets/{index-BsRKmb6k.js → index-DPS4mWnI.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXNUTiW8.js → index-DTslLzPJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dbpk75MO.js → index-DW8vozBS.js} +1 -1
- package/src/assets/web-panel/assets/index-DY8rmd6x.js +1 -0
- package/src/assets/web-panel/assets/{index-DjY-3sMA.js → index-DZiSMYoD.js} +3 -3
- package/src/assets/web-panel/assets/{index-DV6NrfJx.js → index-DZjGcIGU.js} +1 -1
- package/src/assets/web-panel/assets/{index-ktN-K1Tf.js → index-Dea32nOK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjcR-CwS.js → index-Djd7uqwM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CuOx4Pbe.js → index-Dntid6oj.js} +1 -1
- package/src/assets/web-panel/assets/{index-BW3nWiu0.js → index-DqZU4_7J.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4Mqhmas.js → index-DuvweiCz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRqz8TtI.js → index-DzExCCLS.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5na0B7G.js → index-LKT-Ol0A.js} +1 -1
- package/src/assets/web-panel/assets/{index-_-wGLtAg.js → index-akwXS-FF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgMKElUh.js → index-b-rQCMkx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUlubR4l.js → index-eHbr3f5I.js} +1 -1
- package/src/assets/web-panel/assets/{index-D88-8bsB.js → index-kx6zDWew.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuAvYTmY.js → index-rRuCGdKW.js} +1 -1
- package/src/assets/web-panel/assets/index-sbwcUeRS.js +1 -0
- package/src/assets/web-panel/assets/{initDefaultProps-KV3V-PNH.js → initDefaultProps-a8411LiK.js} +1 -1
- package/src/assets/web-panel/assets/{motion-PFGQg7_X.js → motion-oVcQdpCm.js} +1 -1
- package/src/assets/web-panel/assets/{move-Dnl79wq4.js → move-B-eyzzzL.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-Bwl5JH9V.js → mtc-parser-BCYIxuG7.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DCcTtM1l.js → omit-DuFtWiYh.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Cl6BjjDf.js → pickAttrs-CVAoJhGu.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-fdqhyo42.js → placementArrow-Y2iub2bO.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DYQpmcDW.js → responsiveObserve-BHenWIVD.js} +1 -1
- package/src/assets/web-panel/assets/{slide-C4l-VfO4.js → slide-CKrMaL29.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DYDw_dyZ.js → statusUtils-w8eb_1da.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BEsubSMu.js → styleChecker-CCVi_Ww3.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Cj82ggQ_.js → useFlexGapSupport-BsmR_q0r.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-B7L1eJ0V.js → useFs-a8GMBjnc.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DVlJVD4d.js → usePersonalDataHub-BMUYSPn-.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BBAbRN_Y.js → vnode-DL2pI31V.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BYn48q-I.js → zoom-Cbi8fISi.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/data/changelog.json +33 -7
- package/src/lib/dependency-install-policy.js +1 -1
- package/src/lib/evolution/browser-download-action-authority.js +747 -0
- package/src/lib/evolution/browser-download-artifact-disposal-authority.js +436 -0
- package/src/lib/evolution/browser-filesystem-quarantine-custody.js +2453 -0
- package/src/lib/evolution/browser-filesystem-quarantine-disposal.js +36 -0
- package/src/lib/evolution/browser-keyboard-action-authority.js +560 -0
- package/src/lib/evolution/browser-navigation-action-authority.js +546 -0
- package/src/lib/evolution/browser-quarantine-lock-maintenance-authority.js +573 -0
- package/src/lib/evolution/browser-quarantine-operator-revocation-authority.js +442 -0
- package/src/lib/evolution/browser-quarantine-retention-authority.js +503 -0
- package/src/lib/evolution/browser-quarantine-retention-scheduler.js +207 -0
- package/src/lib/evolution/browser-quarantined-download-executor.js +525 -0
- package/src/lib/evolution/browser-tab-open-action-authority.js +491 -0
- package/src/lib/evolution/browser-vision-action-authority.js +444 -0
- package/src/lib/evolution/browser-vision-observation-authority.js +304 -0
- package/src/lib/evolution/evolution-deployment-loader.js +241 -1
- package/src/lib/evolution/evolution-eval-child-evidence-ledger-adapter.js +5 -0
- package/src/lib/evolution/evolution-eval-gate.js +2 -1
- package/src/lib/evolution/evolution-eval-process-supervisor-worker.mjs +71 -9
- package/src/lib/evolution/evolution-eval-process-supervisor.js +276 -9
- package/src/lib/evolution/evolution-workbench-metrics.js +81 -21
- package/src/lib/evolution/pm-exploration-benchmark.js +747 -0
- package/src/lib/evolution/pm-exploration-budget-executor.js +106 -9
- package/src/lib/evolution/pm-exploration-egress-authority.js +382 -0
- package/src/lib/evolution/pm-exploration-evidence-bundle.js +149 -44
- package/src/lib/evolution/pm-exploration-execution-host.js +1254 -8
- package/src/lib/evolution/pm-exploration-process-grader.js +458 -0
- package/src/lib/evolution/pm-exploration-process-reviewer.js +456 -0
- package/src/lib/evolution/pm-exploration-process-runner.js +500 -0
- package/src/lib/evolution/pm-exploration-receipts.js +211 -11
- package/src/lib/evolution/pm-exploration-recovery-snapshot-store.js +465 -46
- package/src/lib/evolution/pm-exploration-transition-committer.js +185 -34
- package/src/lib/evolution/skill-outcome-index-authority.js +7 -3
- package/src/lib/evolution/skill-writer-inventory-manifest.js +0 -39
- package/src/lib/evolution/volcengine-function-execution-authority.js +2949 -0
- package/src/lib/evolution/volcengine-function-process-executor.js +615 -0
- package/src/lib/evolution/volcengine-function-replay-store.js +836 -0
- package/src/lib/evolution/volcengine-function-revocation-status-floor-store.js +480 -0
- package/src/lib/governance-coverage.js +1 -1
- package/src/lib/headless-manifest.js +1 -1
- package/src/lib/protocol-replay.js +1 -1
- package/src/lib/skill-outcome-transcript-authority.js +45 -5
- package/src/runtime/agent-core.js +17 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-Cf3LLgDc.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-CTsYo9NN.js +0 -1
- package/src/assets/web-panel/assets/devWarning-D60SbiD-.js +0 -1
- package/src/assets/web-panel/assets/index-C0yR4_7h.js +0 -1
- package/src/assets/web-panel/assets/index-D4xuvo03.js +0 -1
|
@@ -0,0 +1,836 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { types } from "node:util";
|
|
5
|
+
|
|
6
|
+
import { withFileLock } from "../with-file-lock.js";
|
|
7
|
+
|
|
8
|
+
export const VOLCENGINE_FUNCTION_REPLAY_STORE_SCHEMA =
|
|
9
|
+
"chainlesschain.volcengine-function-replay-store/v3";
|
|
10
|
+
export const VOLCENGINE_FUNCTION_REPLAY_RESERVATION_SCHEMA =
|
|
11
|
+
"chainlesschain.volcengine-function-replay-reservation/v1";
|
|
12
|
+
export const VOLCENGINE_FUNCTION_REPLAY_MODE =
|
|
13
|
+
"cross-process-exclusive-file-fsync";
|
|
14
|
+
export const VOLCENGINE_FUNCTION_REVOCATION_SCHEMA =
|
|
15
|
+
"chainlesschain.volcengine-function-authority-revocation/v3";
|
|
16
|
+
export const VOLCENGINE_FUNCTION_REVOCATION_MODE =
|
|
17
|
+
"cross-process-durable-readback-poll";
|
|
18
|
+
|
|
19
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
20
|
+
const ID = /^[^\p{Cc}]{1,512}$/u;
|
|
21
|
+
const RECORD_FILE = /^[a-f0-9]{64}\.json$/u;
|
|
22
|
+
const MAX_RECORD_BYTES = 8 * 1024;
|
|
23
|
+
const MAX_RECORDS = 4_096;
|
|
24
|
+
const MAX_RETENTION_MS = 24 * 60 * 60 * 1000;
|
|
25
|
+
const stores = new WeakMap();
|
|
26
|
+
|
|
27
|
+
function canonical(value) {
|
|
28
|
+
if (value === null || typeof value !== "object") {
|
|
29
|
+
return JSON.stringify(value);
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
32
|
+
return `{${Object.keys(value)
|
|
33
|
+
.sort()
|
|
34
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
35
|
+
.join(",")}}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function digest(domain, value) {
|
|
39
|
+
return `sha256:${createHash("sha256")
|
|
40
|
+
.update(`${domain}\0`)
|
|
41
|
+
.update(canonical(value))
|
|
42
|
+
.digest("hex")}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function exactData(value, keys, label) {
|
|
46
|
+
if (
|
|
47
|
+
!value ||
|
|
48
|
+
typeof value !== "object" ||
|
|
49
|
+
Array.isArray(value) ||
|
|
50
|
+
types.isProxy(value) ||
|
|
51
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(value)) ||
|
|
52
|
+
Reflect.ownKeys(value).length !== keys.length ||
|
|
53
|
+
keys.some((key) => {
|
|
54
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
55
|
+
return !descriptor?.enumerable || !Object.hasOwn(descriptor, "value");
|
|
56
|
+
})
|
|
57
|
+
) {
|
|
58
|
+
throw new TypeError(`${label} has unexpected or accessor fields`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function ownData(owner, name, label) {
|
|
63
|
+
const descriptor = Object.getOwnPropertyDescriptor(owner, name);
|
|
64
|
+
if (!descriptor || !Object.hasOwn(descriptor, "value")) {
|
|
65
|
+
throw new TypeError(`${label} must be plain data`);
|
|
66
|
+
}
|
|
67
|
+
return descriptor.value;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function normalizeDescriptor(value) {
|
|
71
|
+
exactData(
|
|
72
|
+
value,
|
|
73
|
+
[
|
|
74
|
+
"schema",
|
|
75
|
+
"replayStoreId",
|
|
76
|
+
"authorityId",
|
|
77
|
+
"revocationAuthorityId",
|
|
78
|
+
"tenantId",
|
|
79
|
+
"handlerArtifactDigest",
|
|
80
|
+
"policyRevision",
|
|
81
|
+
"retentionMs",
|
|
82
|
+
"mode",
|
|
83
|
+
"revocationMode",
|
|
84
|
+
],
|
|
85
|
+
"Volcengine function replay store descriptor",
|
|
86
|
+
);
|
|
87
|
+
const descriptor = Object.freeze({
|
|
88
|
+
schema: ownData(value, "schema", "Volcengine replay store schema"),
|
|
89
|
+
replayStoreId: ownData(
|
|
90
|
+
value,
|
|
91
|
+
"replayStoreId",
|
|
92
|
+
"Volcengine replay store identifier",
|
|
93
|
+
),
|
|
94
|
+
authorityId: ownData(
|
|
95
|
+
value,
|
|
96
|
+
"authorityId",
|
|
97
|
+
"Volcengine replay store authority",
|
|
98
|
+
),
|
|
99
|
+
revocationAuthorityId: ownData(
|
|
100
|
+
value,
|
|
101
|
+
"revocationAuthorityId",
|
|
102
|
+
"Volcengine replay store revocation authority",
|
|
103
|
+
),
|
|
104
|
+
tenantId: ownData(value, "tenantId", "Volcengine replay store tenant"),
|
|
105
|
+
handlerArtifactDigest: ownData(
|
|
106
|
+
value,
|
|
107
|
+
"handlerArtifactDigest",
|
|
108
|
+
"Volcengine replay store artifact digest",
|
|
109
|
+
),
|
|
110
|
+
policyRevision: ownData(
|
|
111
|
+
value,
|
|
112
|
+
"policyRevision",
|
|
113
|
+
"Volcengine replay store policy revision",
|
|
114
|
+
),
|
|
115
|
+
retentionMs: ownData(
|
|
116
|
+
value,
|
|
117
|
+
"retentionMs",
|
|
118
|
+
"Volcengine replay store retention",
|
|
119
|
+
),
|
|
120
|
+
mode: ownData(value, "mode", "Volcengine replay store mode"),
|
|
121
|
+
revocationMode: ownData(
|
|
122
|
+
value,
|
|
123
|
+
"revocationMode",
|
|
124
|
+
"Volcengine function revocation mode",
|
|
125
|
+
),
|
|
126
|
+
});
|
|
127
|
+
if (
|
|
128
|
+
descriptor.schema !== VOLCENGINE_FUNCTION_REPLAY_STORE_SCHEMA ||
|
|
129
|
+
!ID.test(descriptor.replayStoreId) ||
|
|
130
|
+
!ID.test(descriptor.authorityId) ||
|
|
131
|
+
!ID.test(descriptor.revocationAuthorityId) ||
|
|
132
|
+
!ID.test(descriptor.tenantId) ||
|
|
133
|
+
!DIGEST.test(descriptor.handlerArtifactDigest) ||
|
|
134
|
+
!ID.test(descriptor.policyRevision) ||
|
|
135
|
+
!Number.isSafeInteger(descriptor.retentionMs) ||
|
|
136
|
+
descriptor.retentionMs < 1 ||
|
|
137
|
+
descriptor.retentionMs > MAX_RETENTION_MS ||
|
|
138
|
+
descriptor.mode !== VOLCENGINE_FUNCTION_REPLAY_MODE ||
|
|
139
|
+
descriptor.revocationMode !== VOLCENGINE_FUNCTION_REVOCATION_MODE
|
|
140
|
+
) {
|
|
141
|
+
throw new TypeError(
|
|
142
|
+
"Volcengine function replay store descriptor is invalid",
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return descriptor;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function normalizeReservation(value, descriptor) {
|
|
149
|
+
exactData(
|
|
150
|
+
value,
|
|
151
|
+
[
|
|
152
|
+
"schema",
|
|
153
|
+
"replayStoreId",
|
|
154
|
+
"authorityId",
|
|
155
|
+
"tenantId",
|
|
156
|
+
"handlerArtifactDigest",
|
|
157
|
+
"policyRevision",
|
|
158
|
+
"requestId",
|
|
159
|
+
"requestDigest",
|
|
160
|
+
"expiresAt",
|
|
161
|
+
"reservationDigest",
|
|
162
|
+
],
|
|
163
|
+
"Volcengine function replay reservation",
|
|
164
|
+
);
|
|
165
|
+
const core = Object.freeze({
|
|
166
|
+
schema: ownData(value, "schema", "Volcengine replay reservation schema"),
|
|
167
|
+
replayStoreId: ownData(
|
|
168
|
+
value,
|
|
169
|
+
"replayStoreId",
|
|
170
|
+
"Volcengine replay reservation store",
|
|
171
|
+
),
|
|
172
|
+
authorityId: ownData(
|
|
173
|
+
value,
|
|
174
|
+
"authorityId",
|
|
175
|
+
"Volcengine replay reservation authority",
|
|
176
|
+
),
|
|
177
|
+
tenantId: ownData(
|
|
178
|
+
value,
|
|
179
|
+
"tenantId",
|
|
180
|
+
"Volcengine replay reservation tenant",
|
|
181
|
+
),
|
|
182
|
+
handlerArtifactDigest: ownData(
|
|
183
|
+
value,
|
|
184
|
+
"handlerArtifactDigest",
|
|
185
|
+
"Volcengine replay reservation artifact digest",
|
|
186
|
+
),
|
|
187
|
+
policyRevision: ownData(
|
|
188
|
+
value,
|
|
189
|
+
"policyRevision",
|
|
190
|
+
"Volcengine replay reservation policy revision",
|
|
191
|
+
),
|
|
192
|
+
requestId: ownData(
|
|
193
|
+
value,
|
|
194
|
+
"requestId",
|
|
195
|
+
"Volcengine replay reservation request",
|
|
196
|
+
),
|
|
197
|
+
requestDigest: ownData(
|
|
198
|
+
value,
|
|
199
|
+
"requestDigest",
|
|
200
|
+
"Volcengine replay reservation request digest",
|
|
201
|
+
),
|
|
202
|
+
expiresAt: ownData(
|
|
203
|
+
value,
|
|
204
|
+
"expiresAt",
|
|
205
|
+
"Volcengine replay reservation expiry",
|
|
206
|
+
),
|
|
207
|
+
});
|
|
208
|
+
const reservationDigest = ownData(
|
|
209
|
+
value,
|
|
210
|
+
"reservationDigest",
|
|
211
|
+
"Volcengine replay reservation digest",
|
|
212
|
+
);
|
|
213
|
+
const expiresAtMs = Date.parse(core.expiresAt);
|
|
214
|
+
if (
|
|
215
|
+
core.schema !== VOLCENGINE_FUNCTION_REPLAY_RESERVATION_SCHEMA ||
|
|
216
|
+
core.replayStoreId !== descriptor.replayStoreId ||
|
|
217
|
+
core.authorityId !== descriptor.authorityId ||
|
|
218
|
+
core.tenantId !== descriptor.tenantId ||
|
|
219
|
+
core.handlerArtifactDigest !== descriptor.handlerArtifactDigest ||
|
|
220
|
+
core.policyRevision !== descriptor.policyRevision ||
|
|
221
|
+
!ID.test(core.requestId) ||
|
|
222
|
+
!DIGEST.test(core.requestDigest) ||
|
|
223
|
+
!Number.isFinite(expiresAtMs) ||
|
|
224
|
+
new Date(expiresAtMs).toISOString() !== core.expiresAt ||
|
|
225
|
+
reservationDigest !==
|
|
226
|
+
digest(VOLCENGINE_FUNCTION_REPLAY_RESERVATION_SCHEMA, core)
|
|
227
|
+
) {
|
|
228
|
+
throw new TypeError("Volcengine function replay reservation is invalid");
|
|
229
|
+
}
|
|
230
|
+
return Object.freeze({ ...core, reservationDigest, expiresAtMs });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function normalizeRevocation(value, descriptor) {
|
|
234
|
+
exactData(
|
|
235
|
+
value,
|
|
236
|
+
[
|
|
237
|
+
"schema",
|
|
238
|
+
"replayStoreId",
|
|
239
|
+
"authorityId",
|
|
240
|
+
"revocationAuthorityId",
|
|
241
|
+
"tenantId",
|
|
242
|
+
"handlerArtifactDigest",
|
|
243
|
+
"policyRevision",
|
|
244
|
+
"revocationId",
|
|
245
|
+
"reasonDigest",
|
|
246
|
+
"authorizationRequestDigest",
|
|
247
|
+
"authorizationEvidenceDigest",
|
|
248
|
+
"auditEventDigest",
|
|
249
|
+
"durabilityReceiptDigest",
|
|
250
|
+
"evidenceResolverDigest",
|
|
251
|
+
"evidenceReadbackDigest",
|
|
252
|
+
"revokedAt",
|
|
253
|
+
"revocationDigest",
|
|
254
|
+
],
|
|
255
|
+
"Volcengine function authority revocation",
|
|
256
|
+
);
|
|
257
|
+
const core = Object.freeze({
|
|
258
|
+
schema: ownData(value, "schema", "Volcengine revocation schema"),
|
|
259
|
+
replayStoreId: ownData(
|
|
260
|
+
value,
|
|
261
|
+
"replayStoreId",
|
|
262
|
+
"Volcengine revocation store",
|
|
263
|
+
),
|
|
264
|
+
authorityId: ownData(
|
|
265
|
+
value,
|
|
266
|
+
"authorityId",
|
|
267
|
+
"Volcengine revocation authority",
|
|
268
|
+
),
|
|
269
|
+
revocationAuthorityId: ownData(
|
|
270
|
+
value,
|
|
271
|
+
"revocationAuthorityId",
|
|
272
|
+
"Volcengine revocation decision authority",
|
|
273
|
+
),
|
|
274
|
+
tenantId: ownData(value, "tenantId", "Volcengine revocation tenant"),
|
|
275
|
+
handlerArtifactDigest: ownData(
|
|
276
|
+
value,
|
|
277
|
+
"handlerArtifactDigest",
|
|
278
|
+
"Volcengine revocation artifact digest",
|
|
279
|
+
),
|
|
280
|
+
policyRevision: ownData(
|
|
281
|
+
value,
|
|
282
|
+
"policyRevision",
|
|
283
|
+
"Volcengine revocation policy revision",
|
|
284
|
+
),
|
|
285
|
+
revocationId: ownData(
|
|
286
|
+
value,
|
|
287
|
+
"revocationId",
|
|
288
|
+
"Volcengine revocation identifier",
|
|
289
|
+
),
|
|
290
|
+
reasonDigest: ownData(
|
|
291
|
+
value,
|
|
292
|
+
"reasonDigest",
|
|
293
|
+
"Volcengine revocation reason digest",
|
|
294
|
+
),
|
|
295
|
+
authorizationRequestDigest: ownData(
|
|
296
|
+
value,
|
|
297
|
+
"authorizationRequestDigest",
|
|
298
|
+
"Volcengine revocation authorization request digest",
|
|
299
|
+
),
|
|
300
|
+
authorizationEvidenceDigest: ownData(
|
|
301
|
+
value,
|
|
302
|
+
"authorizationEvidenceDigest",
|
|
303
|
+
"Volcengine revocation authorization evidence digest",
|
|
304
|
+
),
|
|
305
|
+
auditEventDigest: ownData(
|
|
306
|
+
value,
|
|
307
|
+
"auditEventDigest",
|
|
308
|
+
"Volcengine revocation audit event digest",
|
|
309
|
+
),
|
|
310
|
+
durabilityReceiptDigest: ownData(
|
|
311
|
+
value,
|
|
312
|
+
"durabilityReceiptDigest",
|
|
313
|
+
"Volcengine revocation durability receipt digest",
|
|
314
|
+
),
|
|
315
|
+
evidenceResolverDigest: ownData(
|
|
316
|
+
value,
|
|
317
|
+
"evidenceResolverDigest",
|
|
318
|
+
"Volcengine revocation evidence resolver digest",
|
|
319
|
+
),
|
|
320
|
+
evidenceReadbackDigest: ownData(
|
|
321
|
+
value,
|
|
322
|
+
"evidenceReadbackDigest",
|
|
323
|
+
"Volcengine revocation evidence readback digest",
|
|
324
|
+
),
|
|
325
|
+
revokedAt: ownData(value, "revokedAt", "Volcengine revocation time"),
|
|
326
|
+
});
|
|
327
|
+
const revocationDigest = ownData(
|
|
328
|
+
value,
|
|
329
|
+
"revocationDigest",
|
|
330
|
+
"Volcengine authority revocation digest",
|
|
331
|
+
);
|
|
332
|
+
const revokedAtMs = Date.parse(core.revokedAt);
|
|
333
|
+
if (
|
|
334
|
+
core.schema !== VOLCENGINE_FUNCTION_REVOCATION_SCHEMA ||
|
|
335
|
+
core.replayStoreId !== descriptor.replayStoreId ||
|
|
336
|
+
core.authorityId !== descriptor.authorityId ||
|
|
337
|
+
core.revocationAuthorityId !== descriptor.revocationAuthorityId ||
|
|
338
|
+
core.tenantId !== descriptor.tenantId ||
|
|
339
|
+
core.handlerArtifactDigest !== descriptor.handlerArtifactDigest ||
|
|
340
|
+
core.policyRevision !== descriptor.policyRevision ||
|
|
341
|
+
!ID.test(core.revocationId) ||
|
|
342
|
+
!DIGEST.test(core.reasonDigest) ||
|
|
343
|
+
!DIGEST.test(core.authorizationRequestDigest) ||
|
|
344
|
+
!DIGEST.test(core.authorizationEvidenceDigest) ||
|
|
345
|
+
!DIGEST.test(core.auditEventDigest) ||
|
|
346
|
+
!DIGEST.test(core.durabilityReceiptDigest) ||
|
|
347
|
+
!DIGEST.test(core.evidenceResolverDigest) ||
|
|
348
|
+
!DIGEST.test(core.evidenceReadbackDigest) ||
|
|
349
|
+
!Number.isFinite(revokedAtMs) ||
|
|
350
|
+
new Date(revokedAtMs).toISOString() !== core.revokedAt ||
|
|
351
|
+
revocationDigest !== digest(VOLCENGINE_FUNCTION_REVOCATION_SCHEMA, core)
|
|
352
|
+
) {
|
|
353
|
+
throw new TypeError("Volcengine function authority revocation is invalid");
|
|
354
|
+
}
|
|
355
|
+
return Object.freeze({ ...core, revocationDigest, revokedAtMs });
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function syncDirectory(directory) {
|
|
359
|
+
const descriptor = fs.openSync(
|
|
360
|
+
directory,
|
|
361
|
+
process.platform === "win32" ? "r+" : "r",
|
|
362
|
+
);
|
|
363
|
+
try {
|
|
364
|
+
fs.fsyncSync(descriptor);
|
|
365
|
+
} finally {
|
|
366
|
+
fs.closeSync(descriptor);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function ensureDirectory(directory) {
|
|
371
|
+
const requested = path.resolve(directory);
|
|
372
|
+
const missing = [];
|
|
373
|
+
let cursor = requested;
|
|
374
|
+
for (;;) {
|
|
375
|
+
try {
|
|
376
|
+
const info = fs.lstatSync(cursor);
|
|
377
|
+
if (!info.isDirectory() || info.isSymbolicLink()) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
"Volcengine function replay store path is not a directory",
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
break;
|
|
383
|
+
} catch (error) {
|
|
384
|
+
if (error?.code !== "ENOENT") throw error;
|
|
385
|
+
missing.unshift(cursor);
|
|
386
|
+
const parent = path.dirname(cursor);
|
|
387
|
+
if (parent === cursor) throw error;
|
|
388
|
+
cursor = parent;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
fs.mkdirSync(requested, { recursive: true, mode: 0o700 });
|
|
392
|
+
for (const created of missing) syncDirectory(path.dirname(created));
|
|
393
|
+
const info = fs.lstatSync(requested);
|
|
394
|
+
if (!info.isDirectory() || info.isSymbolicLink()) {
|
|
395
|
+
throw new Error("Volcengine function replay store path is not a directory");
|
|
396
|
+
}
|
|
397
|
+
return fs.realpathSync(requested);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function recordName(descriptor, requestId) {
|
|
401
|
+
return `${createHash("sha256")
|
|
402
|
+
.update("chainlesschain.volcengine-function-replay-record/v1\0")
|
|
403
|
+
.update(descriptor.replayStoreId)
|
|
404
|
+
.update("\0")
|
|
405
|
+
.update(descriptor.authorityId)
|
|
406
|
+
.update("\0")
|
|
407
|
+
.update(requestId)
|
|
408
|
+
.digest("hex")}.json`;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function readRecord(recordPath, descriptor) {
|
|
412
|
+
const info = fs.lstatSync(recordPath);
|
|
413
|
+
if (
|
|
414
|
+
!info.isFile() ||
|
|
415
|
+
info.isSymbolicLink() ||
|
|
416
|
+
info.nlink !== 1 ||
|
|
417
|
+
info.size < 2 ||
|
|
418
|
+
info.size > MAX_RECORD_BYTES
|
|
419
|
+
) {
|
|
420
|
+
throw new Error("Volcengine function replay record is invalid");
|
|
421
|
+
}
|
|
422
|
+
const bytes = fs.readFileSync(recordPath);
|
|
423
|
+
if (bytes.length !== info.size) {
|
|
424
|
+
throw new Error(
|
|
425
|
+
"Volcengine function replay record changed during readback",
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
let value;
|
|
429
|
+
try {
|
|
430
|
+
value = JSON.parse(bytes.toString("utf8"));
|
|
431
|
+
} catch (cause) {
|
|
432
|
+
throw new Error("Volcengine function replay record is invalid", { cause });
|
|
433
|
+
}
|
|
434
|
+
return normalizeReservation(value, descriptor);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function readRevocationRecord(recordPath, descriptor) {
|
|
438
|
+
const info = fs.lstatSync(recordPath);
|
|
439
|
+
if (
|
|
440
|
+
!info.isFile() ||
|
|
441
|
+
info.isSymbolicLink() ||
|
|
442
|
+
info.nlink !== 1 ||
|
|
443
|
+
info.size < 2 ||
|
|
444
|
+
info.size > MAX_RECORD_BYTES
|
|
445
|
+
) {
|
|
446
|
+
throw new Error("Volcengine function revocation record is invalid");
|
|
447
|
+
}
|
|
448
|
+
const bytes = fs.readFileSync(recordPath);
|
|
449
|
+
if (bytes.length !== info.size) {
|
|
450
|
+
throw new Error(
|
|
451
|
+
"Volcengine function revocation record changed during readback",
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
let value;
|
|
455
|
+
try {
|
|
456
|
+
value = JSON.parse(bytes.toString("utf8"));
|
|
457
|
+
} catch (cause) {
|
|
458
|
+
throw new Error("Volcengine function revocation record is invalid", {
|
|
459
|
+
cause,
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
return normalizeRevocation(value, descriptor);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function revocationDurabilityError(cause) {
|
|
466
|
+
const error = new Error(
|
|
467
|
+
"Volcengine function revocation durability is unknown",
|
|
468
|
+
{ cause },
|
|
469
|
+
);
|
|
470
|
+
error.code = "CC_VOLCENGINE_FUNCTION_REVOCATION_DURABILITY_UNKNOWN";
|
|
471
|
+
return error;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function writeRevocation(recordPath, root, revocation) {
|
|
475
|
+
const storedRevocation = Object.freeze({
|
|
476
|
+
schema: revocation.schema,
|
|
477
|
+
replayStoreId: revocation.replayStoreId,
|
|
478
|
+
authorityId: revocation.authorityId,
|
|
479
|
+
revocationAuthorityId: revocation.revocationAuthorityId,
|
|
480
|
+
tenantId: revocation.tenantId,
|
|
481
|
+
handlerArtifactDigest: revocation.handlerArtifactDigest,
|
|
482
|
+
policyRevision: revocation.policyRevision,
|
|
483
|
+
revocationId: revocation.revocationId,
|
|
484
|
+
reasonDigest: revocation.reasonDigest,
|
|
485
|
+
authorizationRequestDigest: revocation.authorizationRequestDigest,
|
|
486
|
+
authorizationEvidenceDigest: revocation.authorizationEvidenceDigest,
|
|
487
|
+
auditEventDigest: revocation.auditEventDigest,
|
|
488
|
+
durabilityReceiptDigest: revocation.durabilityReceiptDigest,
|
|
489
|
+
evidenceResolverDigest: revocation.evidenceResolverDigest,
|
|
490
|
+
evidenceReadbackDigest: revocation.evidenceReadbackDigest,
|
|
491
|
+
revokedAt: revocation.revokedAt,
|
|
492
|
+
revocationDigest: revocation.revocationDigest,
|
|
493
|
+
});
|
|
494
|
+
const encoded = Buffer.from(JSON.stringify(storedRevocation), "utf8");
|
|
495
|
+
if (encoded.length < 2 || encoded.length > MAX_RECORD_BYTES) {
|
|
496
|
+
throw new TypeError(
|
|
497
|
+
"Volcengine function authority revocation is too large",
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
let descriptor;
|
|
501
|
+
try {
|
|
502
|
+
descriptor = fs.openSync(recordPath, "wx", 0o600);
|
|
503
|
+
let offset = 0;
|
|
504
|
+
while (offset < encoded.length) {
|
|
505
|
+
const written = fs.writeSync(
|
|
506
|
+
descriptor,
|
|
507
|
+
encoded,
|
|
508
|
+
offset,
|
|
509
|
+
encoded.length - offset,
|
|
510
|
+
);
|
|
511
|
+
if (!Number.isSafeInteger(written) || written < 1) {
|
|
512
|
+
throw new Error("Volcengine function revocation write stalled");
|
|
513
|
+
}
|
|
514
|
+
offset += written;
|
|
515
|
+
}
|
|
516
|
+
fs.fsyncSync(descriptor);
|
|
517
|
+
fs.closeSync(descriptor);
|
|
518
|
+
descriptor = undefined;
|
|
519
|
+
syncDirectory(root);
|
|
520
|
+
const readback = readRevocationRecord(recordPath, {
|
|
521
|
+
replayStoreId: revocation.replayStoreId,
|
|
522
|
+
authorityId: revocation.authorityId,
|
|
523
|
+
revocationAuthorityId: revocation.revocationAuthorityId,
|
|
524
|
+
tenantId: revocation.tenantId,
|
|
525
|
+
handlerArtifactDigest: revocation.handlerArtifactDigest,
|
|
526
|
+
policyRevision: revocation.policyRevision,
|
|
527
|
+
});
|
|
528
|
+
if (readback.revocationDigest !== revocation.revocationDigest) {
|
|
529
|
+
throw new Error("Volcengine function revocation readback differs");
|
|
530
|
+
}
|
|
531
|
+
return readback;
|
|
532
|
+
} catch (cause) {
|
|
533
|
+
if (descriptor !== undefined) {
|
|
534
|
+
try {
|
|
535
|
+
fs.closeSync(descriptor);
|
|
536
|
+
} catch {
|
|
537
|
+
// Preserve the record because its durability is unknown.
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
throw revocationDurabilityError(cause);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function writeReservation(recordPath, reservationsDirectory, reservation) {
|
|
545
|
+
const storedReservation = Object.freeze({
|
|
546
|
+
schema: reservation.schema,
|
|
547
|
+
replayStoreId: reservation.replayStoreId,
|
|
548
|
+
authorityId: reservation.authorityId,
|
|
549
|
+
tenantId: reservation.tenantId,
|
|
550
|
+
handlerArtifactDigest: reservation.handlerArtifactDigest,
|
|
551
|
+
policyRevision: reservation.policyRevision,
|
|
552
|
+
requestId: reservation.requestId,
|
|
553
|
+
requestDigest: reservation.requestDigest,
|
|
554
|
+
expiresAt: reservation.expiresAt,
|
|
555
|
+
reservationDigest: reservation.reservationDigest,
|
|
556
|
+
});
|
|
557
|
+
const encoded = Buffer.from(JSON.stringify(storedReservation), "utf8");
|
|
558
|
+
if (encoded.length < 2 || encoded.length > MAX_RECORD_BYTES) {
|
|
559
|
+
throw new TypeError("Volcengine function replay reservation is too large");
|
|
560
|
+
}
|
|
561
|
+
let descriptor;
|
|
562
|
+
try {
|
|
563
|
+
descriptor = fs.openSync(recordPath, "wx", 0o600);
|
|
564
|
+
let offset = 0;
|
|
565
|
+
while (offset < encoded.length) {
|
|
566
|
+
const written = fs.writeSync(
|
|
567
|
+
descriptor,
|
|
568
|
+
encoded,
|
|
569
|
+
offset,
|
|
570
|
+
encoded.length - offset,
|
|
571
|
+
);
|
|
572
|
+
if (!Number.isSafeInteger(written) || written < 1) {
|
|
573
|
+
throw new Error("Volcengine function replay reservation write stalled");
|
|
574
|
+
}
|
|
575
|
+
offset += written;
|
|
576
|
+
}
|
|
577
|
+
fs.fsyncSync(descriptor);
|
|
578
|
+
fs.closeSync(descriptor);
|
|
579
|
+
descriptor = undefined;
|
|
580
|
+
syncDirectory(reservationsDirectory);
|
|
581
|
+
} catch (cause) {
|
|
582
|
+
if (descriptor !== undefined) {
|
|
583
|
+
try {
|
|
584
|
+
fs.closeSync(descriptor);
|
|
585
|
+
} catch {
|
|
586
|
+
// Preserve the record because its durability and side-effect status are unknown.
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const error = new Error(
|
|
590
|
+
"Volcengine function replay reservation durability is unknown",
|
|
591
|
+
{ cause },
|
|
592
|
+
);
|
|
593
|
+
error.code = "CC_VOLCENGINE_FUNCTION_REPLAY_DURABILITY_UNKNOWN";
|
|
594
|
+
throw error;
|
|
595
|
+
}
|
|
596
|
+
try {
|
|
597
|
+
const readback = readRecord(recordPath, {
|
|
598
|
+
replayStoreId: reservation.replayStoreId,
|
|
599
|
+
authorityId: reservation.authorityId,
|
|
600
|
+
tenantId: reservation.tenantId,
|
|
601
|
+
handlerArtifactDigest: reservation.handlerArtifactDigest,
|
|
602
|
+
policyRevision: reservation.policyRevision,
|
|
603
|
+
});
|
|
604
|
+
if (readback.reservationDigest !== reservation.reservationDigest) {
|
|
605
|
+
throw new Error(
|
|
606
|
+
"Volcengine function replay reservation readback differs",
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
return readback;
|
|
610
|
+
} catch (cause) {
|
|
611
|
+
const error = new Error(
|
|
612
|
+
"Volcengine function replay reservation durability is unknown",
|
|
613
|
+
{ cause },
|
|
614
|
+
);
|
|
615
|
+
error.code = "CC_VOLCENGINE_FUNCTION_REPLAY_DURABILITY_UNKNOWN";
|
|
616
|
+
throw error;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function removeExpiredRecords(captured, nowMs) {
|
|
621
|
+
const names = fs.readdirSync(captured.reservationsDirectory);
|
|
622
|
+
if (names.some((name) => !RECORD_FILE.test(name))) {
|
|
623
|
+
throw new Error(
|
|
624
|
+
"Volcengine function replay store contains unknown entries",
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
let removed = 0;
|
|
628
|
+
for (const name of names) {
|
|
629
|
+
const recordPath = path.join(captured.reservationsDirectory, name);
|
|
630
|
+
let record;
|
|
631
|
+
try {
|
|
632
|
+
record = readRecord(recordPath, captured.descriptor);
|
|
633
|
+
} catch {
|
|
634
|
+
// A crash can leave an incomplete reservation. Retain and count it so
|
|
635
|
+
// the affected request remains blocked without disabling unrelated IDs.
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
if (record.expiresAtMs > nowMs) continue;
|
|
639
|
+
fs.unlinkSync(recordPath);
|
|
640
|
+
removed += 1;
|
|
641
|
+
}
|
|
642
|
+
if (removed > 0) syncDirectory(captured.reservationsDirectory);
|
|
643
|
+
return names.length - removed;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
export function createVolcengineFunctionReplayStore({
|
|
647
|
+
descriptor,
|
|
648
|
+
rootDir,
|
|
649
|
+
now = () => Date.now(),
|
|
650
|
+
} = {}) {
|
|
651
|
+
const normalizedDescriptor = normalizeDescriptor(descriptor);
|
|
652
|
+
if (
|
|
653
|
+
typeof rootDir !== "string" ||
|
|
654
|
+
rootDir.length < 1 ||
|
|
655
|
+
rootDir.length > 4096 ||
|
|
656
|
+
rootDir.includes("\0") ||
|
|
657
|
+
!path.isAbsolute(rootDir)
|
|
658
|
+
) {
|
|
659
|
+
throw new TypeError("Volcengine function replay store root is invalid");
|
|
660
|
+
}
|
|
661
|
+
if (typeof now !== "function" || types.isProxy(now)) {
|
|
662
|
+
throw new TypeError("Volcengine function replay store clock is invalid");
|
|
663
|
+
}
|
|
664
|
+
const root = ensureDirectory(rootDir);
|
|
665
|
+
const reservationsDirectory = ensureDirectory(
|
|
666
|
+
path.join(root, "reservations"),
|
|
667
|
+
);
|
|
668
|
+
if (path.dirname(reservationsDirectory) !== root) {
|
|
669
|
+
throw new Error("Volcengine function replay store directory escaped root");
|
|
670
|
+
}
|
|
671
|
+
const store = Object.freeze({});
|
|
672
|
+
stores.set(store, {
|
|
673
|
+
descriptor: normalizedDescriptor,
|
|
674
|
+
root,
|
|
675
|
+
reservationsDirectory,
|
|
676
|
+
lockTarget: path.join(root, "reservations-index"),
|
|
677
|
+
revocationPath: path.join(root, "authority-revocation.json"),
|
|
678
|
+
now,
|
|
679
|
+
});
|
|
680
|
+
return store;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
export function captureVolcengineFunctionReplayStore(value) {
|
|
684
|
+
const captured = stores.get(value);
|
|
685
|
+
if (!captured) {
|
|
686
|
+
throw new TypeError(
|
|
687
|
+
"A branded Volcengine function replay store is required",
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
return Object.freeze({
|
|
691
|
+
descriptor: captured.descriptor,
|
|
692
|
+
readRevocation() {
|
|
693
|
+
try {
|
|
694
|
+
const revocation = readRevocationRecord(
|
|
695
|
+
captured.revocationPath,
|
|
696
|
+
captured.descriptor,
|
|
697
|
+
);
|
|
698
|
+
return Object.freeze({
|
|
699
|
+
schema: revocation.schema,
|
|
700
|
+
replayStoreId: revocation.replayStoreId,
|
|
701
|
+
authorityId: revocation.authorityId,
|
|
702
|
+
revocationAuthorityId: revocation.revocationAuthorityId,
|
|
703
|
+
tenantId: revocation.tenantId,
|
|
704
|
+
handlerArtifactDigest: revocation.handlerArtifactDigest,
|
|
705
|
+
policyRevision: revocation.policyRevision,
|
|
706
|
+
revocationId: revocation.revocationId,
|
|
707
|
+
reasonDigest: revocation.reasonDigest,
|
|
708
|
+
authorizationRequestDigest: revocation.authorizationRequestDigest,
|
|
709
|
+
authorizationEvidenceDigest: revocation.authorizationEvidenceDigest,
|
|
710
|
+
auditEventDigest: revocation.auditEventDigest,
|
|
711
|
+
durabilityReceiptDigest: revocation.durabilityReceiptDigest,
|
|
712
|
+
evidenceResolverDigest: revocation.evidenceResolverDigest,
|
|
713
|
+
evidenceReadbackDigest: revocation.evidenceReadbackDigest,
|
|
714
|
+
revokedAt: revocation.revokedAt,
|
|
715
|
+
revocationDigest: revocation.revocationDigest,
|
|
716
|
+
});
|
|
717
|
+
} catch (error) {
|
|
718
|
+
if (error?.code === "ENOENT") return null;
|
|
719
|
+
throw error;
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
revoke(value) {
|
|
723
|
+
const revocation = normalizeRevocation(value, captured.descriptor);
|
|
724
|
+
const nowMs = captured.now();
|
|
725
|
+
if (!Number.isFinite(nowMs) || revocation.revokedAtMs > nowMs + 5_000) {
|
|
726
|
+
throw new TypeError(
|
|
727
|
+
"Volcengine function authority revocation time is invalid",
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
return withFileLock(
|
|
731
|
+
captured.lockTarget,
|
|
732
|
+
() => {
|
|
733
|
+
let existing;
|
|
734
|
+
try {
|
|
735
|
+
existing = readRevocationRecord(
|
|
736
|
+
captured.revocationPath,
|
|
737
|
+
captured.descriptor,
|
|
738
|
+
);
|
|
739
|
+
} catch (error) {
|
|
740
|
+
if (error?.code !== "ENOENT") {
|
|
741
|
+
throw revocationDurabilityError(error);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (existing) {
|
|
745
|
+
if (existing.revocationDigest !== revocation.revocationDigest) {
|
|
746
|
+
throw new Error(
|
|
747
|
+
"Volcengine function authority revocation conflicts with existing record",
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
try {
|
|
751
|
+
syncDirectory(captured.root);
|
|
752
|
+
const readback = readRevocationRecord(
|
|
753
|
+
captured.revocationPath,
|
|
754
|
+
captured.descriptor,
|
|
755
|
+
);
|
|
756
|
+
if (readback.revocationDigest !== revocation.revocationDigest) {
|
|
757
|
+
throw new Error(
|
|
758
|
+
"Volcengine function revocation readback differs",
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
return Object.freeze({
|
|
762
|
+
revocationDigest: readback.revocationDigest,
|
|
763
|
+
durable: true,
|
|
764
|
+
readbackVerified: true,
|
|
765
|
+
});
|
|
766
|
+
} catch (cause) {
|
|
767
|
+
throw revocationDurabilityError(cause);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
const readback = writeRevocation(
|
|
771
|
+
captured.revocationPath,
|
|
772
|
+
captured.root,
|
|
773
|
+
revocation,
|
|
774
|
+
);
|
|
775
|
+
return Object.freeze({
|
|
776
|
+
revocationDigest: readback.revocationDigest,
|
|
777
|
+
durable: true,
|
|
778
|
+
readbackVerified: true,
|
|
779
|
+
});
|
|
780
|
+
},
|
|
781
|
+
{ failIfUnavailable: true, timeoutMs: 5_000 },
|
|
782
|
+
);
|
|
783
|
+
},
|
|
784
|
+
reserve(value) {
|
|
785
|
+
const reservation = normalizeReservation(value, captured.descriptor);
|
|
786
|
+
const nowMs = captured.now();
|
|
787
|
+
if (!Number.isFinite(nowMs) || reservation.expiresAtMs <= nowMs) {
|
|
788
|
+
throw new TypeError(
|
|
789
|
+
"Volcengine function replay reservation is expired",
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
return withFileLock(
|
|
793
|
+
captured.lockTarget,
|
|
794
|
+
() => {
|
|
795
|
+
const count = removeExpiredRecords(captured, nowMs);
|
|
796
|
+
const name = recordName(captured.descriptor, reservation.requestId);
|
|
797
|
+
const recordPath = path.join(captured.reservationsDirectory, name);
|
|
798
|
+
try {
|
|
799
|
+
const existing = readRecord(recordPath, captured.descriptor);
|
|
800
|
+
if (existing.requestId !== reservation.requestId) {
|
|
801
|
+
throw new Error(
|
|
802
|
+
"Volcengine function replay record name collided",
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
const error = new TypeError(
|
|
806
|
+
"Volcengine function request was replayed",
|
|
807
|
+
);
|
|
808
|
+
error.code = "CC_VOLCENGINE_FUNCTION_REQUEST_REPLAYED";
|
|
809
|
+
throw error;
|
|
810
|
+
} catch (error) {
|
|
811
|
+
if (error?.code !== "ENOENT") throw error;
|
|
812
|
+
}
|
|
813
|
+
if (count >= MAX_RECORDS) {
|
|
814
|
+
throw new Error("Volcengine function replay store is full");
|
|
815
|
+
}
|
|
816
|
+
const readback = writeReservation(
|
|
817
|
+
recordPath,
|
|
818
|
+
captured.reservationsDirectory,
|
|
819
|
+
reservation,
|
|
820
|
+
);
|
|
821
|
+
if (readback.reservationDigest !== reservation.reservationDigest) {
|
|
822
|
+
throw new Error(
|
|
823
|
+
"Volcengine function replay reservation readback differs",
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
return Object.freeze({
|
|
827
|
+
reservationDigest: reservation.reservationDigest,
|
|
828
|
+
durable: true,
|
|
829
|
+
readbackVerified: true,
|
|
830
|
+
});
|
|
831
|
+
},
|
|
832
|
+
{ failIfUnavailable: true, timeoutMs: 5_000 },
|
|
833
|
+
);
|
|
834
|
+
},
|
|
835
|
+
});
|
|
836
|
+
}
|