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,573 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { types } from "node:util";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
BROWSER_FILESYSTEM_QUARANTINE_LOCK_DIAGNOSTIC_SCHEMA,
|
|
6
|
+
BROWSER_FILESYSTEM_QUARANTINE_LOCK_RELEASE_ACK_SCHEMA,
|
|
7
|
+
captureBrowserFilesystemQuarantineCustody,
|
|
8
|
+
} from "./browser-filesystem-quarantine-custody.js";
|
|
9
|
+
export { BROWSER_FILESYSTEM_QUARANTINE_LOCK_MAINTENANCE_DESCRIPTOR_SCHEMA } from "./browser-filesystem-quarantine-custody.js";
|
|
10
|
+
|
|
11
|
+
export const BROWSER_QUARANTINE_LOCK_MAINTENANCE_REQUEST_SCHEMA =
|
|
12
|
+
"chainlesschain.browser-quarantine-lock-maintenance-request/v1";
|
|
13
|
+
export const BROWSER_QUARANTINE_LOCK_MAINTENANCE_RECEIPT_SCHEMA =
|
|
14
|
+
"chainlesschain.browser-quarantine-lock-maintenance-receipt/v1";
|
|
15
|
+
export const BROWSER_QUARANTINE_LOCK_MAINTENANCE_OUTCOME_REQUEST_SCHEMA =
|
|
16
|
+
"chainlesschain.browser-quarantine-lock-maintenance-outcome-request/v1";
|
|
17
|
+
export const BROWSER_QUARANTINE_LOCK_MAINTENANCE_OUTCOME_ACK_SCHEMA =
|
|
18
|
+
"chainlesschain.browser-quarantine-lock-maintenance-outcome-ack/v1";
|
|
19
|
+
export const BROWSER_QUARANTINE_LOCK_MAINTENANCE_RESULT_SCHEMA =
|
|
20
|
+
"chainlesschain.browser-quarantine-lock-maintenance-result/v1";
|
|
21
|
+
|
|
22
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
23
|
+
const ID = /^[A-Za-z0-9._:-]{1,128}$/u;
|
|
24
|
+
const ARTIFACT_REF = /^quarantine:[A-Za-z0-9][A-Za-z0-9._-]{0,95}$/u;
|
|
25
|
+
const LOCK_STATUSES = new Set([
|
|
26
|
+
"absent",
|
|
27
|
+
"owned-live",
|
|
28
|
+
"owned-dead",
|
|
29
|
+
"owned-invalid",
|
|
30
|
+
"owner-initializing",
|
|
31
|
+
"owner-orphaned",
|
|
32
|
+
]);
|
|
33
|
+
const LOCK_OPERATIONS = new Set([
|
|
34
|
+
"write",
|
|
35
|
+
"scan",
|
|
36
|
+
"complete",
|
|
37
|
+
"dispose",
|
|
38
|
+
"recover",
|
|
39
|
+
]);
|
|
40
|
+
const authorities = new WeakMap();
|
|
41
|
+
|
|
42
|
+
function canonical(value) {
|
|
43
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
44
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
45
|
+
return `{${Object.keys(value)
|
|
46
|
+
.sort()
|
|
47
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
48
|
+
.join(",")}}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function digest(domain, value) {
|
|
52
|
+
return `sha256:${createHash("sha256")
|
|
53
|
+
.update(`${domain}\0`)
|
|
54
|
+
.update(canonical(value))
|
|
55
|
+
.digest("hex")}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function exact(value, keys, label) {
|
|
59
|
+
if (
|
|
60
|
+
!value ||
|
|
61
|
+
typeof value !== "object" ||
|
|
62
|
+
Array.isArray(value) ||
|
|
63
|
+
types.isProxy(value) ||
|
|
64
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(value)) ||
|
|
65
|
+
Reflect.ownKeys(value).length !== keys.length ||
|
|
66
|
+
keys.some((key) => {
|
|
67
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
68
|
+
return !descriptor?.enumerable || !("value" in descriptor);
|
|
69
|
+
})
|
|
70
|
+
)
|
|
71
|
+
throw new TypeError(`${label} has unexpected or accessor fields`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function snapshotJson(value, label) {
|
|
75
|
+
const visit = (entry, depth) => {
|
|
76
|
+
if (depth > 8) throw new TypeError(`${label} exceeds its depth budget`);
|
|
77
|
+
if (
|
|
78
|
+
entry === null ||
|
|
79
|
+
typeof entry === "string" ||
|
|
80
|
+
typeof entry === "boolean" ||
|
|
81
|
+
(typeof entry === "number" && Number.isFinite(entry))
|
|
82
|
+
)
|
|
83
|
+
return entry;
|
|
84
|
+
if (
|
|
85
|
+
!entry ||
|
|
86
|
+
typeof entry !== "object" ||
|
|
87
|
+
types.isProxy(entry) ||
|
|
88
|
+
![Object.prototype, null, Array.prototype].includes(
|
|
89
|
+
Object.getPrototypeOf(entry),
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
throw new TypeError(`${label} must be finite plain JSON data`);
|
|
93
|
+
if (Array.isArray(entry))
|
|
94
|
+
return Object.freeze(entry.map((item) => visit(item, depth + 1)));
|
|
95
|
+
const result = {};
|
|
96
|
+
for (const key of Object.keys(entry)) {
|
|
97
|
+
const descriptor = Object.getOwnPropertyDescriptor(entry, key);
|
|
98
|
+
if (!descriptor?.enumerable || !("value" in descriptor))
|
|
99
|
+
throw new TypeError(`${label} must contain plain data`);
|
|
100
|
+
result[key] = visit(descriptor.value, depth + 1);
|
|
101
|
+
}
|
|
102
|
+
return Object.freeze(result);
|
|
103
|
+
};
|
|
104
|
+
const result = visit(value, 0);
|
|
105
|
+
if (Buffer.byteLength(canonical(result), "utf8") > 16 * 1024)
|
|
106
|
+
throw new TypeError(`${label} exceeds its byte budget`);
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function normalizeRequest(value) {
|
|
111
|
+
exact(
|
|
112
|
+
value,
|
|
113
|
+
[
|
|
114
|
+
"schema",
|
|
115
|
+
"requestId",
|
|
116
|
+
"operation",
|
|
117
|
+
"artifactRef",
|
|
118
|
+
"expectedLockStateDigest",
|
|
119
|
+
"operatorIdDigest",
|
|
120
|
+
"authorization",
|
|
121
|
+
"requestedAt",
|
|
122
|
+
],
|
|
123
|
+
"operator lock maintenance request",
|
|
124
|
+
);
|
|
125
|
+
if (
|
|
126
|
+
value.schema !== BROWSER_QUARANTINE_LOCK_MAINTENANCE_REQUEST_SCHEMA ||
|
|
127
|
+
!ID.test(value.requestId) ||
|
|
128
|
+
!["inspect", "release"].includes(value.operation) ||
|
|
129
|
+
!ARTIFACT_REF.test(value.artifactRef) ||
|
|
130
|
+
(value.operation === "inspect"
|
|
131
|
+
? value.expectedLockStateDigest !== null
|
|
132
|
+
: !DIGEST.test(value.expectedLockStateDigest)) ||
|
|
133
|
+
!DIGEST.test(value.operatorIdDigest) ||
|
|
134
|
+
!Number.isFinite(Date.parse(value.requestedAt))
|
|
135
|
+
)
|
|
136
|
+
throw new TypeError("operator lock maintenance request is invalid");
|
|
137
|
+
return Object.freeze({
|
|
138
|
+
...value,
|
|
139
|
+
authorization: snapshotJson(
|
|
140
|
+
value.authorization,
|
|
141
|
+
"operator lock maintenance authorization",
|
|
142
|
+
),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function normalizeDecision(value, nowMs, maxGrantTtlMs) {
|
|
147
|
+
if (
|
|
148
|
+
!value ||
|
|
149
|
+
typeof value !== "object" ||
|
|
150
|
+
Array.isArray(value) ||
|
|
151
|
+
types.isProxy(value) ||
|
|
152
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(value))
|
|
153
|
+
)
|
|
154
|
+
throw new TypeError("operator lock maintenance decision is invalid");
|
|
155
|
+
if (value.decision === "deny") {
|
|
156
|
+
exact(value, ["decision", "reason"], "operator lock maintenance denial");
|
|
157
|
+
if (
|
|
158
|
+
typeof value.reason !== "string" ||
|
|
159
|
+
value.reason.length < 1 ||
|
|
160
|
+
value.reason.length > 512
|
|
161
|
+
)
|
|
162
|
+
throw new TypeError("operator lock maintenance denial is invalid");
|
|
163
|
+
return Object.freeze({ ...value });
|
|
164
|
+
}
|
|
165
|
+
exact(
|
|
166
|
+
value,
|
|
167
|
+
["decision", "operatorEvidenceRef", "validUntil"],
|
|
168
|
+
"operator lock maintenance decision",
|
|
169
|
+
);
|
|
170
|
+
const validUntilMs = Date.parse(value.validUntil);
|
|
171
|
+
if (
|
|
172
|
+
value.decision !== "allow" ||
|
|
173
|
+
typeof value.operatorEvidenceRef !== "string" ||
|
|
174
|
+
value.operatorEvidenceRef.length < 1 ||
|
|
175
|
+
value.operatorEvidenceRef.length > 512 ||
|
|
176
|
+
!Number.isFinite(validUntilMs) ||
|
|
177
|
+
validUntilMs <= nowMs ||
|
|
178
|
+
validUntilMs > nowMs + maxGrantTtlMs
|
|
179
|
+
)
|
|
180
|
+
throw new Error("operator lock maintenance decision is invalid");
|
|
181
|
+
return Object.freeze({ ...value, validUntilMs });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function normalizeDiagnostic(value, descriptor, artifactRef) {
|
|
185
|
+
exact(
|
|
186
|
+
value,
|
|
187
|
+
[
|
|
188
|
+
"schema",
|
|
189
|
+
"custodyId",
|
|
190
|
+
"tenantId",
|
|
191
|
+
"handlerArtifactDigest",
|
|
192
|
+
"artifactRefDigest",
|
|
193
|
+
"status",
|
|
194
|
+
"ownerOperation",
|
|
195
|
+
"ownerPid",
|
|
196
|
+
"ownerAcquiredAt",
|
|
197
|
+
"ownerEvidenceDigest",
|
|
198
|
+
"lockDirectoryIdentityDigest",
|
|
199
|
+
"observedAt",
|
|
200
|
+
"lockStateDigest",
|
|
201
|
+
],
|
|
202
|
+
"operator lock diagnostic",
|
|
203
|
+
);
|
|
204
|
+
const artifactRefDigest = digest(
|
|
205
|
+
"chainlesschain.browser-download-artifact-ref/v1",
|
|
206
|
+
artifactRef,
|
|
207
|
+
);
|
|
208
|
+
const hasOwner = ["owned-live", "owned-dead"].includes(value.status);
|
|
209
|
+
const absent = value.status === "absent";
|
|
210
|
+
const stateCore = Object.freeze({
|
|
211
|
+
custodyId: value.custodyId,
|
|
212
|
+
tenantId: value.tenantId,
|
|
213
|
+
handlerArtifactDigest: value.handlerArtifactDigest,
|
|
214
|
+
artifactRefDigest: value.artifactRefDigest,
|
|
215
|
+
lockDirectoryIdentityDigest: value.lockDirectoryIdentityDigest,
|
|
216
|
+
ownerEvidenceDigest: value.ownerEvidenceDigest,
|
|
217
|
+
});
|
|
218
|
+
if (
|
|
219
|
+
value.schema !== BROWSER_FILESYSTEM_QUARANTINE_LOCK_DIAGNOSTIC_SCHEMA ||
|
|
220
|
+
!ID.test(value.custodyId) ||
|
|
221
|
+
value.tenantId !== descriptor.tenantId ||
|
|
222
|
+
value.handlerArtifactDigest !== descriptor.handlerArtifactDigest ||
|
|
223
|
+
value.artifactRefDigest !== artifactRefDigest ||
|
|
224
|
+
!LOCK_STATUSES.has(value.status) ||
|
|
225
|
+
(hasOwner
|
|
226
|
+
? !LOCK_OPERATIONS.has(value.ownerOperation) ||
|
|
227
|
+
!Number.isSafeInteger(value.ownerPid) ||
|
|
228
|
+
value.ownerPid < 1 ||
|
|
229
|
+
!Number.isFinite(Date.parse(value.ownerAcquiredAt))
|
|
230
|
+
: value.ownerOperation !== null ||
|
|
231
|
+
value.ownerPid !== null ||
|
|
232
|
+
value.ownerAcquiredAt !== null) ||
|
|
233
|
+
(absent
|
|
234
|
+
? value.ownerEvidenceDigest !== null ||
|
|
235
|
+
value.lockDirectoryIdentityDigest !== null
|
|
236
|
+
: !DIGEST.test(value.ownerEvidenceDigest) ||
|
|
237
|
+
!DIGEST.test(value.lockDirectoryIdentityDigest)) ||
|
|
238
|
+
!Number.isFinite(Date.parse(value.observedAt)) ||
|
|
239
|
+
value.lockStateDigest !==
|
|
240
|
+
digest(BROWSER_FILESYSTEM_QUARANTINE_LOCK_DIAGNOSTIC_SCHEMA, stateCore)
|
|
241
|
+
)
|
|
242
|
+
throw new Error("operator lock diagnostic is invalid");
|
|
243
|
+
return Object.freeze({ ...value });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function normalizeReleaseAck(
|
|
247
|
+
value,
|
|
248
|
+
descriptor,
|
|
249
|
+
request,
|
|
250
|
+
receiptDigest,
|
|
251
|
+
requestDigest,
|
|
252
|
+
) {
|
|
253
|
+
exact(
|
|
254
|
+
value,
|
|
255
|
+
[
|
|
256
|
+
"schema",
|
|
257
|
+
"custodyId",
|
|
258
|
+
"authorityId",
|
|
259
|
+
"tenantId",
|
|
260
|
+
"handlerArtifactDigest",
|
|
261
|
+
"policyRevision",
|
|
262
|
+
"actionReceiptDigest",
|
|
263
|
+
"requestDigest",
|
|
264
|
+
"artifactRefDigest",
|
|
265
|
+
"releasedLockStateDigest",
|
|
266
|
+
"releasedAt",
|
|
267
|
+
"releaseReceiptDigest",
|
|
268
|
+
"authenticated",
|
|
269
|
+
"durable",
|
|
270
|
+
"readbackVerified",
|
|
271
|
+
"artifactBytesChanged",
|
|
272
|
+
"qualifiesForPromotion",
|
|
273
|
+
],
|
|
274
|
+
"operator lock release acknowledgement",
|
|
275
|
+
);
|
|
276
|
+
const core = Object.freeze({
|
|
277
|
+
schema: value.schema,
|
|
278
|
+
custodyId: value.custodyId,
|
|
279
|
+
authorityId: value.authorityId,
|
|
280
|
+
tenantId: value.tenantId,
|
|
281
|
+
handlerArtifactDigest: value.handlerArtifactDigest,
|
|
282
|
+
policyRevision: value.policyRevision,
|
|
283
|
+
actionReceiptDigest: value.actionReceiptDigest,
|
|
284
|
+
requestDigest: value.requestDigest,
|
|
285
|
+
artifactRefDigest: value.artifactRefDigest,
|
|
286
|
+
releasedLockStateDigest: value.releasedLockStateDigest,
|
|
287
|
+
releasedAt: value.releasedAt,
|
|
288
|
+
});
|
|
289
|
+
if (
|
|
290
|
+
value.schema !== BROWSER_FILESYSTEM_QUARANTINE_LOCK_RELEASE_ACK_SCHEMA ||
|
|
291
|
+
!ID.test(value.custodyId) ||
|
|
292
|
+
value.authorityId !== descriptor.authorityId ||
|
|
293
|
+
value.tenantId !== descriptor.tenantId ||
|
|
294
|
+
value.handlerArtifactDigest !== descriptor.handlerArtifactDigest ||
|
|
295
|
+
value.policyRevision !== descriptor.policyRevision ||
|
|
296
|
+
value.actionReceiptDigest !== receiptDigest ||
|
|
297
|
+
value.requestDigest !== requestDigest ||
|
|
298
|
+
value.artifactRefDigest !==
|
|
299
|
+
digest(
|
|
300
|
+
"chainlesschain.browser-download-artifact-ref/v1",
|
|
301
|
+
request.artifactRef,
|
|
302
|
+
) ||
|
|
303
|
+
value.releasedLockStateDigest !== request.expectedLockStateDigest ||
|
|
304
|
+
!Number.isFinite(Date.parse(value.releasedAt)) ||
|
|
305
|
+
value.releaseReceiptDigest !==
|
|
306
|
+
digest(BROWSER_FILESYSTEM_QUARANTINE_LOCK_RELEASE_ACK_SCHEMA, core) ||
|
|
307
|
+
value.authenticated !== true ||
|
|
308
|
+
value.durable !== true ||
|
|
309
|
+
value.readbackVerified !== true ||
|
|
310
|
+
value.artifactBytesChanged !== false ||
|
|
311
|
+
value.qualifiesForPromotion !== false
|
|
312
|
+
)
|
|
313
|
+
throw new Error("operator lock release acknowledgement is invalid");
|
|
314
|
+
return Object.freeze({ ...value });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function normalizeOutcomeAck(value, descriptor, request) {
|
|
318
|
+
exact(
|
|
319
|
+
value,
|
|
320
|
+
[
|
|
321
|
+
"schema",
|
|
322
|
+
"authorityId",
|
|
323
|
+
"tenantId",
|
|
324
|
+
"handlerArtifactDigest",
|
|
325
|
+
"maintenanceReceiptDigest",
|
|
326
|
+
"outcomeRequestDigest",
|
|
327
|
+
"auditEventDigest",
|
|
328
|
+
"durabilityReceiptDigest",
|
|
329
|
+
"authenticated",
|
|
330
|
+
"durable",
|
|
331
|
+
"readbackVerified",
|
|
332
|
+
"qualifiesForPromotion",
|
|
333
|
+
],
|
|
334
|
+
"operator lock maintenance outcome acknowledgement",
|
|
335
|
+
);
|
|
336
|
+
if (
|
|
337
|
+
value.schema !== BROWSER_QUARANTINE_LOCK_MAINTENANCE_OUTCOME_ACK_SCHEMA ||
|
|
338
|
+
value.authorityId !== descriptor.authorityId ||
|
|
339
|
+
value.tenantId !== descriptor.tenantId ||
|
|
340
|
+
value.handlerArtifactDigest !== descriptor.handlerArtifactDigest ||
|
|
341
|
+
value.maintenanceReceiptDigest !== request.maintenanceReceiptDigest ||
|
|
342
|
+
value.outcomeRequestDigest !== request.outcomeRequestDigest ||
|
|
343
|
+
!DIGEST.test(value.auditEventDigest) ||
|
|
344
|
+
!DIGEST.test(value.durabilityReceiptDigest) ||
|
|
345
|
+
value.authenticated !== true ||
|
|
346
|
+
value.durable !== true ||
|
|
347
|
+
value.readbackVerified !== true ||
|
|
348
|
+
value.qualifiesForPromotion !== false
|
|
349
|
+
)
|
|
350
|
+
throw new Error(
|
|
351
|
+
"operator lock maintenance outcome acknowledgement is invalid",
|
|
352
|
+
);
|
|
353
|
+
return Object.freeze({ ...value });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function createBrowserQuarantineLockMaintenanceAuthority(options) {
|
|
357
|
+
exact(
|
|
358
|
+
options,
|
|
359
|
+
["descriptor", "custody", "authorizeMaintenance", "recordOutcome", "now"],
|
|
360
|
+
"browser quarantine lock maintenance authority",
|
|
361
|
+
);
|
|
362
|
+
const custody = captureBrowserFilesystemQuarantineCustody(options.custody);
|
|
363
|
+
const maintenance = custody.bindLockMaintenanceAuthority(options.descriptor);
|
|
364
|
+
for (const [name, port] of Object.entries({
|
|
365
|
+
authorizeMaintenance: options.authorizeMaintenance,
|
|
366
|
+
recordOutcome: options.recordOutcome,
|
|
367
|
+
now: options.now,
|
|
368
|
+
})) {
|
|
369
|
+
if (typeof port !== "function" || types.isProxy(port))
|
|
370
|
+
throw new TypeError(`operator lock maintenance ${name} port is invalid`);
|
|
371
|
+
}
|
|
372
|
+
const authority = Object.freeze({});
|
|
373
|
+
authorities.set(authority, {
|
|
374
|
+
descriptor: Object.freeze({ ...options.descriptor }),
|
|
375
|
+
inspectLock: maintenance.inspectLock,
|
|
376
|
+
releaseLock: maintenance.releaseLock,
|
|
377
|
+
authorizeMaintenance: options.authorizeMaintenance,
|
|
378
|
+
recordOutcome: options.recordOutcome,
|
|
379
|
+
now: options.now,
|
|
380
|
+
});
|
|
381
|
+
return authority;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export function captureBrowserQuarantineLockMaintenanceAuthority(value) {
|
|
385
|
+
const state = authorities.get(value);
|
|
386
|
+
if (!state)
|
|
387
|
+
throw new TypeError(
|
|
388
|
+
"A branded browser quarantine lock maintenance authority is required",
|
|
389
|
+
);
|
|
390
|
+
return Object.freeze({
|
|
391
|
+
descriptor: state.descriptor,
|
|
392
|
+
maintainLock: async (input) => {
|
|
393
|
+
const request = normalizeRequest(input);
|
|
394
|
+
const nowMs = state.now();
|
|
395
|
+
if (!Number.isFinite(nowMs))
|
|
396
|
+
throw new Error("operator lock maintenance clock is invalid");
|
|
397
|
+
const artifactRefDigest = digest(
|
|
398
|
+
"chainlesschain.browser-download-artifact-ref/v1",
|
|
399
|
+
request.artifactRef,
|
|
400
|
+
);
|
|
401
|
+
const authorizationDigest = digest(
|
|
402
|
+
"chainlesschain.browser-quarantine-lock-maintenance-authorization/v1",
|
|
403
|
+
request.authorization,
|
|
404
|
+
);
|
|
405
|
+
const requestCore = Object.freeze({
|
|
406
|
+
schema: BROWSER_QUARANTINE_LOCK_MAINTENANCE_REQUEST_SCHEMA,
|
|
407
|
+
requestId: request.requestId,
|
|
408
|
+
operation: request.operation,
|
|
409
|
+
artifactRefDigest,
|
|
410
|
+
expectedLockStateDigest: request.expectedLockStateDigest,
|
|
411
|
+
operatorIdDigest: request.operatorIdDigest,
|
|
412
|
+
authorizationDigest,
|
|
413
|
+
requestedAt: request.requestedAt,
|
|
414
|
+
});
|
|
415
|
+
const requestDigest = digest(
|
|
416
|
+
BROWSER_QUARANTINE_LOCK_MAINTENANCE_REQUEST_SCHEMA,
|
|
417
|
+
requestCore,
|
|
418
|
+
);
|
|
419
|
+
const decision = normalizeDecision(
|
|
420
|
+
await state.authorizeMaintenance(
|
|
421
|
+
Object.freeze({
|
|
422
|
+
...requestCore,
|
|
423
|
+
requestDigest,
|
|
424
|
+
authorization: request.authorization,
|
|
425
|
+
}),
|
|
426
|
+
),
|
|
427
|
+
nowMs,
|
|
428
|
+
state.descriptor.maxGrantTtlMs,
|
|
429
|
+
);
|
|
430
|
+
if (decision.decision === "deny") {
|
|
431
|
+
const error = new Error(decision.reason);
|
|
432
|
+
error.code = "BROWSER_QUARANTINE_LOCK_MAINTENANCE_DENIED";
|
|
433
|
+
throw error;
|
|
434
|
+
}
|
|
435
|
+
const receiptCore = Object.freeze({
|
|
436
|
+
schema: BROWSER_QUARANTINE_LOCK_MAINTENANCE_RECEIPT_SCHEMA,
|
|
437
|
+
authorityId: state.descriptor.authorityId,
|
|
438
|
+
tenantId: state.descriptor.tenantId,
|
|
439
|
+
handlerArtifactDigest: state.descriptor.handlerArtifactDigest,
|
|
440
|
+
policyRevision: state.descriptor.policyRevision,
|
|
441
|
+
requestId: request.requestId,
|
|
442
|
+
operation: request.operation,
|
|
443
|
+
artifactRefDigest,
|
|
444
|
+
expectedLockStateDigest: request.expectedLockStateDigest,
|
|
445
|
+
operatorIdDigest: request.operatorIdDigest,
|
|
446
|
+
requestDigest,
|
|
447
|
+
operatorEvidenceRef: decision.operatorEvidenceRef,
|
|
448
|
+
authorizedAt: new Date(nowMs).toISOString(),
|
|
449
|
+
validUntil: decision.validUntil,
|
|
450
|
+
});
|
|
451
|
+
const receiptDigest = digest(
|
|
452
|
+
BROWSER_QUARANTINE_LOCK_MAINTENANCE_RECEIPT_SCHEMA,
|
|
453
|
+
receiptCore,
|
|
454
|
+
);
|
|
455
|
+
let diagnostic = null;
|
|
456
|
+
let release = null;
|
|
457
|
+
let executionError = null;
|
|
458
|
+
try {
|
|
459
|
+
const executionMs = state.now();
|
|
460
|
+
if (
|
|
461
|
+
!Number.isFinite(executionMs) ||
|
|
462
|
+
executionMs >= decision.validUntilMs
|
|
463
|
+
)
|
|
464
|
+
throw new Error(
|
|
465
|
+
"operator lock maintenance grant expired before execution",
|
|
466
|
+
);
|
|
467
|
+
diagnostic = normalizeDiagnostic(
|
|
468
|
+
await state.inspectLock(request.artifactRef),
|
|
469
|
+
state.descriptor,
|
|
470
|
+
request.artifactRef,
|
|
471
|
+
);
|
|
472
|
+
if (request.operation === "release") {
|
|
473
|
+
if (diagnostic.lockStateDigest !== request.expectedLockStateDigest)
|
|
474
|
+
throw new Error("operator lock maintenance target changed");
|
|
475
|
+
release = normalizeReleaseAck(
|
|
476
|
+
await state.releaseLock({
|
|
477
|
+
actionReceiptDigest: receiptDigest,
|
|
478
|
+
requestDigest,
|
|
479
|
+
artifactRef: request.artifactRef,
|
|
480
|
+
expectedLockStateDigest: request.expectedLockStateDigest,
|
|
481
|
+
}),
|
|
482
|
+
state.descriptor,
|
|
483
|
+
request,
|
|
484
|
+
receiptDigest,
|
|
485
|
+
requestDigest,
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
} catch (error) {
|
|
489
|
+
executionError = error;
|
|
490
|
+
}
|
|
491
|
+
const recordedAtMs = state.now();
|
|
492
|
+
if (!Number.isFinite(recordedAtMs))
|
|
493
|
+
throw new Error("operator lock maintenance clock is invalid");
|
|
494
|
+
const outcomeCore = Object.freeze({
|
|
495
|
+
schema: BROWSER_QUARANTINE_LOCK_MAINTENANCE_OUTCOME_REQUEST_SCHEMA,
|
|
496
|
+
authorityId: state.descriptor.authorityId,
|
|
497
|
+
tenantId: state.descriptor.tenantId,
|
|
498
|
+
handlerArtifactDigest: state.descriptor.handlerArtifactDigest,
|
|
499
|
+
policyRevision: state.descriptor.policyRevision,
|
|
500
|
+
maintenanceReceiptDigest: receiptDigest,
|
|
501
|
+
requestDigest,
|
|
502
|
+
operation: request.operation,
|
|
503
|
+
artifactRefDigest,
|
|
504
|
+
operatorIdDigest: request.operatorIdDigest,
|
|
505
|
+
status: executionError === null ? "succeeded" : "failed",
|
|
506
|
+
lockStatus: diagnostic?.status ?? null,
|
|
507
|
+
lockStateDigest: diagnostic?.lockStateDigest ?? null,
|
|
508
|
+
releaseReceiptDigest: release?.releaseReceiptDigest ?? null,
|
|
509
|
+
failureDigest:
|
|
510
|
+
executionError === null
|
|
511
|
+
? null
|
|
512
|
+
: digest(
|
|
513
|
+
"chainlesschain.browser-quarantine-lock-maintenance-failure/v1",
|
|
514
|
+
{
|
|
515
|
+
name: executionError?.name ?? "Error",
|
|
516
|
+
code: executionError?.code ?? null,
|
|
517
|
+
},
|
|
518
|
+
),
|
|
519
|
+
recordedAt: new Date(recordedAtMs).toISOString(),
|
|
520
|
+
});
|
|
521
|
+
const outcomeRequest = Object.freeze({
|
|
522
|
+
...outcomeCore,
|
|
523
|
+
outcomeRequestDigest: digest(
|
|
524
|
+
BROWSER_QUARANTINE_LOCK_MAINTENANCE_OUTCOME_REQUEST_SCHEMA,
|
|
525
|
+
outcomeCore,
|
|
526
|
+
),
|
|
527
|
+
});
|
|
528
|
+
const outcomeAck = normalizeOutcomeAck(
|
|
529
|
+
await state.recordOutcome(outcomeRequest),
|
|
530
|
+
state.descriptor,
|
|
531
|
+
outcomeRequest,
|
|
532
|
+
);
|
|
533
|
+
if (executionError !== null) {
|
|
534
|
+
const uncertain = new Error(
|
|
535
|
+
"operator lock maintenance execution failed",
|
|
536
|
+
{ cause: executionError },
|
|
537
|
+
);
|
|
538
|
+
uncertain.code = "BROWSER_QUARANTINE_LOCK_MAINTENANCE_FAILED";
|
|
539
|
+
throw uncertain;
|
|
540
|
+
}
|
|
541
|
+
const ownerProcessDigest =
|
|
542
|
+
diagnostic.ownerPid === null
|
|
543
|
+
? null
|
|
544
|
+
: digest("chainlesschain.browser-quarantine-lock-owner-process/v1", {
|
|
545
|
+
pid: diagnostic.ownerPid,
|
|
546
|
+
acquiredAt: diagnostic.ownerAcquiredAt,
|
|
547
|
+
});
|
|
548
|
+
const resultCore = Object.freeze({
|
|
549
|
+
schema: BROWSER_QUARANTINE_LOCK_MAINTENANCE_RESULT_SCHEMA,
|
|
550
|
+
status: request.operation === "release" ? "released" : "inspected",
|
|
551
|
+
operation: request.operation,
|
|
552
|
+
artifactRefDigest,
|
|
553
|
+
lockStatus: diagnostic.status,
|
|
554
|
+
ownerOperation: diagnostic.ownerOperation,
|
|
555
|
+
ownerProcessDigest,
|
|
556
|
+
ownerAcquiredAt: diagnostic.ownerAcquiredAt,
|
|
557
|
+
lockStateDigest: diagnostic.lockStateDigest,
|
|
558
|
+
releaseReceiptDigest: release?.releaseReceiptDigest ?? null,
|
|
559
|
+
operatorIdDigest: request.operatorIdDigest,
|
|
560
|
+
outcomeRequestDigest: outcomeRequest.outcomeRequestDigest,
|
|
561
|
+
auditEventDigest: outcomeAck.auditEventDigest,
|
|
562
|
+
durabilityReceiptDigest: outcomeAck.durabilityReceiptDigest,
|
|
563
|
+
});
|
|
564
|
+
return Object.freeze({
|
|
565
|
+
...resultCore,
|
|
566
|
+
resultDigest: digest(
|
|
567
|
+
BROWSER_QUARANTINE_LOCK_MAINTENANCE_RESULT_SCHEMA,
|
|
568
|
+
resultCore,
|
|
569
|
+
),
|
|
570
|
+
});
|
|
571
|
+
},
|
|
572
|
+
});
|
|
573
|
+
}
|