chainlesschain 0.158.0 → 0.160.1
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/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/AIOps-BHiKMxFI.js +1 -0
- package/src/assets/web-panel/assets/AIOps-Cchx1iXI.css +1 -0
- package/src/assets/web-panel/assets/ActionButton-5QD5uzWP.js +1 -0
- package/src/assets/web-panel/assets/Analytics-C1-TXmTC.css +1 -0
- package/src/assets/web-panel/assets/Analytics-D1JxsGBN.js +3 -0
- package/src/assets/web-panel/assets/AppLayout-CtGprHSx.css +1 -0
- package/src/assets/web-panel/assets/AppLayout-DykU9tOE.js +1 -0
- package/src/assets/web-panel/assets/Audit-6ZMsXmrO.css +1 -0
- package/src/assets/web-panel/assets/Audit-TGBqld9c.js +1 -0
- package/src/assets/web-panel/assets/Backup-CY9QozR7.css +1 -0
- package/src/assets/web-panel/assets/Backup-DjpzIwA6.js +1 -0
- package/src/assets/web-panel/assets/BaseInput-joQlVauq.js +1 -0
- package/src/assets/web-panel/assets/Chat-9TYfosy-.js +2 -0
- package/src/assets/web-panel/assets/{Chat-DmX5bWvL.css → Chat-ByiYUboW.css} +1 -1
- package/src/assets/web-panel/assets/Checkbox-OEOFA9GM.js +1 -0
- package/src/assets/web-panel/assets/Codegen-5H5UgHJu.js +1 -0
- package/src/assets/web-panel/assets/Codegen-BLP7id2a.css +1 -0
- package/src/assets/web-panel/assets/Col-BnLUipDp.js +1 -0
- package/src/assets/web-panel/assets/Community-BF3R5GAl.js +1 -0
- package/src/assets/web-panel/assets/Community-C2RejeOY.css +1 -0
- package/src/assets/web-panel/assets/Compact-C1EkTFek.js +1 -0
- package/src/assets/web-panel/assets/Compliance-CpP-ODRU.js +1 -0
- package/src/assets/web-panel/assets/Compliance-DOys4Ov1.css +1 -0
- package/src/assets/web-panel/assets/{Cowork-pgKzBNDo.js → Cowork-CCgGSKVR.js} +3 -3
- package/src/assets/web-panel/assets/Cron-CZ5pjRxn.js +2 -0
- package/src/assets/web-panel/assets/Crosschain-C0P-5sm3.js +1 -0
- package/src/assets/web-panel/assets/Crosschain-C7Le4Pte.css +1 -0
- package/src/assets/web-panel/assets/DID-BX6k3jZi.css +1 -0
- package/src/assets/web-panel/assets/DID-DPZKMApP.js +2 -0
- package/src/assets/web-panel/assets/Dashboard-G-BDDAov.js +3 -0
- package/src/assets/web-panel/assets/Dashboard-MFDcsVcM.css +1 -0
- package/src/assets/web-panel/assets/Dropdown-CeywCcVQ.js +1 -0
- package/src/assets/web-panel/assets/Federation-CgmfLbx1.css +1 -0
- package/src/assets/web-panel/assets/Federation-DuFRY867.js +1 -0
- package/src/assets/web-panel/assets/{FormItemContext-CnzbixSE.js → FormItemContext-CFSiPqbu.js} +1 -1
- package/src/assets/web-panel/assets/Git-CjVRJDLg.js +2 -0
- package/src/assets/web-panel/assets/{Git-DGcuBXST.css → Git-DPuaGtg7.css} +1 -1
- package/src/assets/web-panel/assets/Governance-BoipmXaM.css +1 -0
- package/src/assets/web-panel/assets/Governance-C0lyocJc.js +1 -0
- package/src/assets/web-panel/assets/Inference-BVSAexgk.js +1 -0
- package/src/assets/web-panel/assets/Inference-BWxYJF9-.css +1 -0
- package/src/assets/web-panel/assets/KnowledgeGraph-CztPDA96.css +1 -0
- package/src/assets/web-panel/assets/KnowledgeGraph-SE4jCwIn.js +1 -0
- package/src/assets/web-panel/assets/Logs-BD5C-wTx.js +2 -0
- package/src/assets/web-panel/assets/Marketplace-CL93dFBs.js +1 -0
- package/src/assets/web-panel/assets/Marketplace-Djp5q9dS.css +1 -0
- package/src/assets/web-panel/assets/McpTools-CDjHmzxH.css +1 -0
- package/src/assets/web-panel/assets/McpTools-x-Tibae-.js +5 -0
- package/src/assets/web-panel/assets/Memory-Bcc2hxOA.css +1 -0
- package/src/assets/web-panel/assets/Memory-CR8LXq37.js +2 -0
- package/src/assets/web-panel/assets/Mtc-C-PfF5B3.css +1 -0
- package/src/assets/web-panel/assets/Mtc-CEtRtMcc.js +1 -0
- package/src/assets/web-panel/assets/NLProgramming-B09F6gt2.js +1 -0
- package/src/assets/web-panel/assets/NLProgramming-CLOvy-35.css +1 -0
- package/src/assets/web-panel/assets/Notes-BYIn2GOe.js +7 -0
- package/src/assets/web-panel/assets/Notes-DKkPfXlY.css +1 -0
- package/src/assets/web-panel/assets/Organization-BX-cIO8M.css +1 -0
- package/src/assets/web-panel/assets/Organization-CybJTFN9.js +4 -0
- package/src/assets/web-panel/assets/Overflow-W4YLQ7yY.js +1 -0
- package/src/assets/web-panel/assets/{OverrideContext-DEW2m7K6.js → OverrideContext-Nubhv68k.js} +1 -1
- package/src/assets/web-panel/assets/P2P-Cx88UaiD.css +1 -0
- package/src/assets/web-panel/assets/P2P-kVj43R4j.js +2 -0
- package/src/assets/web-panel/assets/Permissions-CfYE4XFJ.js +4 -0
- package/src/assets/web-panel/assets/Pipeline-BVLo32Ak.js +1 -0
- package/src/assets/web-panel/assets/Pipeline-DxkXqrH2.css +1 -0
- package/src/assets/web-panel/assets/Privacy-CrsfSFKd.css +1 -0
- package/src/assets/web-panel/assets/Privacy-Efyb3xpJ.js +1 -0
- package/src/assets/web-panel/assets/ProjectSettings-cBqrIhNN.js +2 -0
- package/src/assets/web-panel/assets/Projects-B5IgXt-x.css +1 -0
- package/src/assets/web-panel/assets/Projects-BYY38oZd.js +2 -0
- package/src/assets/web-panel/assets/Providers-BsS27cWs.js +2 -0
- package/src/assets/web-panel/assets/QuickAsk-6FgX9DC6.js +1 -0
- package/src/assets/web-panel/assets/Recommend-BvMXwWFN.js +1 -0
- package/src/assets/web-panel/assets/Recommend-CH6wKzGo.css +1 -0
- package/src/assets/web-panel/assets/Reputation-D6VPNEd0.css +1 -0
- package/src/assets/web-panel/assets/Reputation-DmwTtBfl.js +1 -0
- package/src/assets/web-panel/assets/Row-N-X7EJ3w.js +1 -0
- package/src/assets/web-panel/assets/RssFeed-D9TjnwgF.js +3 -0
- package/src/assets/web-panel/assets/Search-B6RalzTB.css +1 -0
- package/src/assets/web-panel/assets/Search-Hapv-QkV.js +1 -0
- package/src/assets/web-panel/assets/Security-13K57V_v.css +1 -0
- package/src/assets/web-panel/assets/Security-DWbFJK10.js +4 -0
- package/src/assets/web-panel/assets/Services-BPUmhVoH.js +2 -0
- package/src/assets/web-panel/assets/Skeleton-Bo5qPHbE.js +8 -0
- package/src/assets/web-panel/assets/Skills-JJ8uInMW.js +1 -0
- package/src/assets/web-panel/assets/Sla-CEDF9zdV.js +1 -0
- package/src/assets/web-panel/assets/Sla-K19oOyQc.css +1 -0
- package/src/assets/web-panel/assets/SpeechSettings-DYPJTDKz.css +1 -0
- package/src/assets/web-panel/assets/SpeechSettings-oIoX_vCx.js +1 -0
- package/src/assets/web-panel/assets/Tasks-Cx5wgv5Z.js +1 -0
- package/src/assets/web-panel/assets/Templates-BWTV8-2E.css +1 -0
- package/src/assets/web-panel/assets/Templates-BomcBlkN.js +1 -0
- package/src/assets/web-panel/assets/Tenant-BxSQZUNh.js +1 -0
- package/src/assets/web-panel/assets/Tenant-D3zkSAV0.css +1 -0
- package/src/assets/web-panel/assets/Tokens-BBOdNRHQ.css +1 -0
- package/src/assets/web-panel/assets/Tokens-BlPPoB3C.js +1 -0
- package/src/assets/web-panel/assets/Trigger-Bhjmjsc5.js +1 -0
- package/src/assets/web-panel/assets/Trust-DeOo0xAh.css +1 -0
- package/src/assets/web-panel/assets/Trust-Dsjv7rkb.js +1 -0
- package/src/assets/web-panel/assets/UkeySign-Cux8_Ib_.js +1 -0
- package/src/assets/web-panel/assets/VideoEditing-BsVR1PN8.js +1 -0
- package/src/assets/web-panel/assets/VideoEditing-DksiizfS.css +1 -0
- package/src/assets/web-panel/assets/Wallet-dcRAYsdL.js +4 -0
- package/src/assets/web-panel/assets/Wallet-gR0ZvZbK.css +1 -0
- package/src/assets/web-panel/assets/WebAuthn-SSh5VhVO.css +1 -0
- package/src/assets/web-panel/assets/WebAuthn-oqIS5PCi.js +5 -0
- package/src/assets/web-panel/assets/WorkflowEditor-C_fYMBvB.js +1 -0
- package/src/assets/web-panel/assets/WorkflowEditor-IiwsD8Kh.css +1 -0
- package/src/assets/web-panel/assets/{chat-CVn-r3oV.js → chat-BQ-Nk2XY.js} +1 -1
- package/src/assets/web-panel/assets/{collapseMotion-D3P4mRvK.js → collapseMotion-BIjDVXtT.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BiB5aDao.js → colors-D2P6CqS5.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CHderV9n.js → compact-item-CG7qutT_.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DMZ-5dy2.js → createContext-y4UPKgbA.js} +1 -1
- package/src/assets/web-panel/assets/echarts-DmBLM6YO.js +19 -0
- package/src/assets/web-panel/assets/{hasIn-BKDBYP3s.js → hasIn-Butbu9jZ.js} +1 -1
- package/src/assets/web-panel/assets/icons-DvZE-RKs.js +57 -0
- package/src/assets/web-panel/assets/index-38mVlGHc.js +1 -0
- package/src/assets/web-panel/assets/{index-C1p_dZw0.js → index-89HJLKZ-.js} +3 -3
- package/src/assets/web-panel/assets/index-B4Jfv4EB.js +1 -0
- package/src/assets/web-panel/assets/index-B5FRjJMb.js +1 -0
- package/src/assets/web-panel/assets/{index-DvI8Xtzv.js → index-B6U6cYUa.js} +8 -8
- package/src/assets/web-panel/assets/index-B7FV5EnN.js +1 -0
- package/src/assets/web-panel/assets/index-BCQ0WlB2.js +1 -0
- package/src/assets/web-panel/assets/index-BEfvpbz-.js +1 -0
- package/src/assets/web-panel/assets/index-BJN_3RTO.js +1 -0
- package/src/assets/web-panel/assets/index-BQfow_sh.js +1 -0
- package/src/assets/web-panel/assets/index-BQr8Y0o5.js +1 -0
- package/src/assets/web-panel/assets/index-BYZPJS7A.js +1 -0
- package/src/assets/web-panel/assets/index-Bs9aHxDD.js +13 -0
- package/src/assets/web-panel/assets/index-BtuwtDUE.js +1 -0
- package/src/assets/web-panel/assets/index-BvJgRWBq.js +3 -0
- package/src/assets/web-panel/assets/index-C1mK1Ga3.js +1 -0
- package/src/assets/web-panel/assets/index-C1ucrJLg.js +1 -0
- package/src/assets/web-panel/assets/index-C2K61jP8.js +55 -0
- package/src/assets/web-panel/assets/index-CAeKBs9n.js +1 -0
- package/src/assets/web-panel/assets/{index-BBWUR4u4.js → index-CWh3IxEh.js} +2 -2
- package/src/assets/web-panel/assets/{index-CUT3Nmzx.js → index-CYlDKn3O.js} +3 -3
- package/src/assets/web-panel/assets/index-C_8hWf5_.js +6 -0
- package/src/assets/web-panel/assets/index-Cc77JZKd.js +1 -0
- package/src/assets/web-panel/assets/index-Ceaxjpqh.js +13 -0
- package/src/assets/web-panel/assets/index-CfX1DEtk.css +1 -0
- package/src/assets/web-panel/assets/index-Ci6jXp3l.js +7 -0
- package/src/assets/web-panel/assets/index-CyqU4Tck.js +65 -0
- package/src/assets/web-panel/assets/{index-7fxCGMoL.js → index-DLMJy9pE.js} +6 -6
- package/src/assets/web-panel/assets/index-DdgjeX4z.js +1 -0
- package/src/assets/web-panel/assets/index-DnI4Aq0q.js +3 -0
- package/src/assets/web-panel/assets/index-DrVnyYpX.js +1 -0
- package/src/assets/web-panel/assets/index-DtNHlrxp.js +1 -0
- package/src/assets/web-panel/assets/index-Dx_ZTZo_.js +1 -0
- package/src/assets/web-panel/assets/{index-CBYNsy51.js → index-YmGOWX7h.js} +2 -2
- package/src/assets/web-panel/assets/index-gWmZm8_Q.js +21 -0
- package/src/assets/web-panel/assets/index-hSilB_Q-.js +12 -0
- package/src/assets/web-panel/assets/index-qXvwlbkq.js +3 -0
- package/src/assets/web-panel/assets/index-rIbVsjde.js +12 -0
- package/src/assets/web-panel/assets/{index-B7fivpG2.js → index-vC5cTycG.js} +2 -2
- package/src/assets/web-panel/assets/{initDefaultProps-B2vQoQga.js → initDefaultProps-CZRZ-1bk.js} +1 -1
- package/src/assets/web-panel/assets/motion-CvU8SiWF.js +11 -0
- package/src/assets/web-panel/assets/move-ipAfWhya.js +4 -0
- package/src/assets/web-panel/assets/mtc-parser-pGMSt10g.js +1 -0
- package/src/assets/web-panel/assets/{omit-DvLh7wH1.js → omit-D6bJEjz9.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BsseBclw.js → pickAttrs-Dpvzf7sL.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-XLLtr9qD.js → placementArrow-D_tEolP1.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-ByCb5zZK.js → responsiveObserve-BEFI7neO.js} +1 -1
- package/src/assets/web-panel/assets/slide-Bte_KOqM.js +4 -0
- package/src/assets/web-panel/assets/statusUtils-K4xaDRuO.js +1 -0
- package/src/assets/web-panel/assets/{styleChecker-Cz5mJgc8.js → styleChecker-Cl9YgOVY.js} +1 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-DNstl1wK.js +1 -0
- package/src/assets/web-panel/assets/useFs-BD-YRwbU.js +1 -0
- package/src/assets/web-panel/assets/{useMergedState-DbHXwBgv.js → useMergedState-TP9VIF2K.js} +1 -1
- package/src/assets/web-panel/assets/{useRefs-DRyTVik7.js → useRefs-BhIz_lC3.js} +1 -1
- package/src/assets/web-panel/assets/useShellMode-CgR0wCYM.js +1 -0
- package/src/assets/web-panel/assets/{useState-DoAhV80c.js → useState-CpKsyozn.js} +1 -1
- package/src/assets/web-panel/assets/vendor-B6ToihkA.js +1 -0
- package/src/assets/web-panel/assets/vnode-ChB-8cXr.js +1 -0
- package/src/assets/web-panel/assets/ws-D_5-FRIb.js +1 -0
- package/src/assets/web-panel/assets/zoom-meTNBulL.js +4 -0
- package/src/assets/web-panel/index.html +4 -4
- package/src/commands/audit.js +296 -0
- package/src/commands/init.js +16 -1
- package/src/commands/mtc.js +1401 -86
- package/src/gateways/ws/ws-session-gateway.js +12 -1
- package/src/lib/audit-mtc.js +504 -0
- package/src/lib/listen-with-port-fallback.js +103 -0
- package/src/runtime/agent-runtime.js +80 -18
- package/src/assets/web-panel/assets/AIOps-9QEKGr43.js +0 -1
- package/src/assets/web-panel/assets/AIOps-Bfzpnnlg.css +0 -1
- package/src/assets/web-panel/assets/ActionButton-BQElVBgD.js +0 -1
- package/src/assets/web-panel/assets/Analytics-BFI7jbwM.css +0 -1
- package/src/assets/web-panel/assets/Analytics-DbZbFR_a.js +0 -3
- package/src/assets/web-panel/assets/AppLayout-BbfBwVfE.js +0 -1
- package/src/assets/web-panel/assets/AppLayout-CKMl4NLf.css +0 -1
- package/src/assets/web-panel/assets/Audit-DzddAZVx.js +0 -1
- package/src/assets/web-panel/assets/Audit-L4_ApS01.css +0 -1
- package/src/assets/web-panel/assets/Backup-D46qSgzQ.js +0 -1
- package/src/assets/web-panel/assets/Backup-fZqtfC1m.css +0 -1
- package/src/assets/web-panel/assets/BaseInput-60t9IMEz.js +0 -1
- package/src/assets/web-panel/assets/Chat-3Kj2JQP4.js +0 -2
- package/src/assets/web-panel/assets/Checkbox-Ba1Rf2Ea.js +0 -1
- package/src/assets/web-panel/assets/Codegen-AVAcL7NA.css +0 -1
- package/src/assets/web-panel/assets/Codegen-WiYmL3oB.js +0 -1
- package/src/assets/web-panel/assets/Col-D2zhpSqE.js +0 -1
- package/src/assets/web-panel/assets/Community-DgqmUGwT.js +0 -1
- package/src/assets/web-panel/assets/Community-DqDfLQui.css +0 -1
- package/src/assets/web-panel/assets/Compact-D_dbJDbi.js +0 -1
- package/src/assets/web-panel/assets/Compliance-BVcVb-e2.js +0 -1
- package/src/assets/web-panel/assets/Compliance-CKxw6vIq.css +0 -1
- package/src/assets/web-panel/assets/Cron-Cs5QaKTP.js +0 -2
- package/src/assets/web-panel/assets/Crosschain-DThGgQk8.css +0 -1
- package/src/assets/web-panel/assets/Crosschain-pjWfIM8s.js +0 -1
- package/src/assets/web-panel/assets/DID-BDvsVa08.css +0 -1
- package/src/assets/web-panel/assets/DID-DQtsjz3X.js +0 -2
- package/src/assets/web-panel/assets/Dashboard-Cviwdc26.css +0 -1
- package/src/assets/web-panel/assets/Dashboard-sqj42zKx.js +0 -3
- package/src/assets/web-panel/assets/Dropdown-BM_ljd87.js +0 -1
- package/src/assets/web-panel/assets/Federation-BftELHDw.css +0 -1
- package/src/assets/web-panel/assets/Federation-CId-3zn6.js +0 -1
- package/src/assets/web-panel/assets/Git-B38QeOPF.js +0 -2
- package/src/assets/web-panel/assets/Governance-BfmfQBGB.css +0 -1
- package/src/assets/web-panel/assets/Governance-DrGkAjd1.js +0 -1
- package/src/assets/web-panel/assets/Inference-BjkHB4sD.js +0 -1
- package/src/assets/web-panel/assets/Inference-EFFc7eNZ.css +0 -1
- package/src/assets/web-panel/assets/Keyframes-C7fCrnlS.js +0 -1
- package/src/assets/web-panel/assets/KnowledgeGraph-Trs1XJFK.js +0 -19
- package/src/assets/web-panel/assets/KnowledgeGraph-U8ps3aGJ.css +0 -1
- package/src/assets/web-panel/assets/Logs-B39LUZs9.js +0 -2
- package/src/assets/web-panel/assets/Marketplace-B-4uYu_j.css +0 -1
- package/src/assets/web-panel/assets/Marketplace-BnpOoITi.js +0 -1
- package/src/assets/web-panel/assets/McpTools-CyhSLDwf.css +0 -1
- package/src/assets/web-panel/assets/McpTools-DbwkdBUE.js +0 -4
- package/src/assets/web-panel/assets/Memory-BHXHpldt.js +0 -2
- package/src/assets/web-panel/assets/Memory-DRghrGJr.css +0 -1
- package/src/assets/web-panel/assets/NLProgramming-B3BALajs.js +0 -1
- package/src/assets/web-panel/assets/NLProgramming-jURs-f-a.css +0 -1
- package/src/assets/web-panel/assets/Notes-BG69sJKi.css +0 -1
- package/src/assets/web-panel/assets/Notes-u6lLTv0t.js +0 -2
- package/src/assets/web-panel/assets/Organization-CQ1X722j.js +0 -4
- package/src/assets/web-panel/assets/Organization-DdOOM4ic.css +0 -1
- package/src/assets/web-panel/assets/Overflow-DHKu8e8g.js +0 -1
- package/src/assets/web-panel/assets/P2P-BflX74Hm.js +0 -2
- package/src/assets/web-panel/assets/P2P-OEzOeMZX.css +0 -1
- package/src/assets/web-panel/assets/Permissions-B2XrAUBW.js +0 -4
- package/src/assets/web-panel/assets/Pipeline-C0K9-DnR.js +0 -1
- package/src/assets/web-panel/assets/Pipeline-DyqCLFVr.css +0 -1
- package/src/assets/web-panel/assets/Portal-CHsntPCQ.js +0 -1
- package/src/assets/web-panel/assets/Privacy-BKPoZngH.js +0 -1
- package/src/assets/web-panel/assets/Privacy-B_cAicd1.css +0 -1
- package/src/assets/web-panel/assets/ProjectSettings-C34C7hFt.js +0 -2
- package/src/assets/web-panel/assets/Projects-DLvUzeIB.js +0 -2
- package/src/assets/web-panel/assets/Projects-DxKelI5h.css +0 -1
- package/src/assets/web-panel/assets/Providers-BFiMVWaM.js +0 -2
- package/src/assets/web-panel/assets/Recommend-BmUhysGC.js +0 -1
- package/src/assets/web-panel/assets/Recommend-DgNSCgRX.css +0 -1
- package/src/assets/web-panel/assets/Reputation-BlfSvJww.js +0 -1
- package/src/assets/web-panel/assets/Reputation-y-46ThW8.css +0 -1
- package/src/assets/web-panel/assets/Row-Dl74576S.js +0 -1
- package/src/assets/web-panel/assets/RssFeed-9_MkDUxZ.js +0 -3
- package/src/assets/web-panel/assets/Search-BTk9rglb.css +0 -1
- package/src/assets/web-panel/assets/Search-CSRY_TYa.js +0 -1
- package/src/assets/web-panel/assets/Security-DfDoKfkl.js +0 -4
- package/src/assets/web-panel/assets/Security-Dwxw7rfP.css +0 -1
- package/src/assets/web-panel/assets/Services-7Blny0Jc.js +0 -2
- package/src/assets/web-panel/assets/Skeleton-6CF_3QUY.js +0 -8
- package/src/assets/web-panel/assets/Skills-DowQcRe1.js +0 -1
- package/src/assets/web-panel/assets/Sla-B93c8j92.js +0 -1
- package/src/assets/web-panel/assets/Sla-C1WYuQKf.css +0 -1
- package/src/assets/web-panel/assets/Tasks-Db9TRjE7.js +0 -1
- package/src/assets/web-panel/assets/Templates-DJ_hvs5c.js +0 -1
- package/src/assets/web-panel/assets/Templates-DOY_oZnm.css +0 -1
- package/src/assets/web-panel/assets/Tenant-BJr-h-_0.css +0 -1
- package/src/assets/web-panel/assets/Tenant-BXS8s6vl.js +0 -1
- package/src/assets/web-panel/assets/Tokens-CvmTVU7E.js +0 -1
- package/src/assets/web-panel/assets/Tokens-KvJRHQcl.css +0 -1
- package/src/assets/web-panel/assets/Trigger-BenSsaoN.js +0 -1
- package/src/assets/web-panel/assets/Trust-BLI308Ik.css +0 -1
- package/src/assets/web-panel/assets/Trust-HMz40LfZ.js +0 -1
- package/src/assets/web-panel/assets/VideoEditing-BA1N-5kq.css +0 -1
- package/src/assets/web-panel/assets/VideoEditing-DEhH42sI.js +0 -1
- package/src/assets/web-panel/assets/Wallet-AGwfOdTM.js +0 -4
- package/src/assets/web-panel/assets/Wallet-DnIumafl.css +0 -1
- package/src/assets/web-panel/assets/WebAuthn-CNPl2VQR.css +0 -1
- package/src/assets/web-panel/assets/WebAuthn-DK5tKWul.js +0 -5
- package/src/assets/web-panel/assets/WorkflowEditor-D5bX6woe.css +0 -1
- package/src/assets/web-panel/assets/WorkflowEditor-vlujdL6I.js +0 -1
- package/src/assets/web-panel/assets/_plugin-vue_export-helper-DlAUqK2U.js +0 -1
- package/src/assets/web-panel/assets/icons-ukMaZ3tx.js +0 -57
- package/src/assets/web-panel/assets/index-7pqZqPD2.js +0 -1
- package/src/assets/web-panel/assets/index-A4RpHMOa.js +0 -1
- package/src/assets/web-panel/assets/index-AIdelwOe.js +0 -12
- package/src/assets/web-panel/assets/index-B5oebI6W.js +0 -1
- package/src/assets/web-panel/assets/index-BBk2e7vJ.js +0 -13
- package/src/assets/web-panel/assets/index-BRRFK-im.js +0 -1
- package/src/assets/web-panel/assets/index-BVOI707f.js +0 -3
- package/src/assets/web-panel/assets/index-BadelvSE.js +0 -1
- package/src/assets/web-panel/assets/index-BfKFNptm.js +0 -14
- package/src/assets/web-panel/assets/index-BgDX-tzO.js +0 -1
- package/src/assets/web-panel/assets/index-BjSojphX.js +0 -21
- package/src/assets/web-panel/assets/index-BlXskar6.js +0 -1
- package/src/assets/web-panel/assets/index-BryAkWUJ.js +0 -1
- package/src/assets/web-panel/assets/index-CEpcKl17.js +0 -3
- package/src/assets/web-panel/assets/index-CbpCY2ch.js +0 -3
- package/src/assets/web-panel/assets/index-CyGyEIVX.css +0 -1
- package/src/assets/web-panel/assets/index-D3PGpBPm.js +0 -1
- package/src/assets/web-panel/assets/index-D3lD9wgX.js +0 -1
- package/src/assets/web-panel/assets/index-D5Beu9ZA.js +0 -1
- package/src/assets/web-panel/assets/index-DRRzH4Ge.js +0 -13
- package/src/assets/web-panel/assets/index-DRcTAUJA.js +0 -1
- package/src/assets/web-panel/assets/index-DZ8MjPLC.js +0 -7
- package/src/assets/web-panel/assets/index-DfcXDy2A.js +0 -36
- package/src/assets/web-panel/assets/index-DtDFSCfE.js +0 -12
- package/src/assets/web-panel/assets/index-DvfAkHw8.js +0 -1
- package/src/assets/web-panel/assets/index-DyXslU_B.js +0 -1
- package/src/assets/web-panel/assets/index-MoLxDrkb.js +0 -55
- package/src/assets/web-panel/assets/index-Ncxqj2UN.js +0 -1
- package/src/assets/web-panel/assets/index-U4Zd5IK6.js +0 -8
- package/src/assets/web-panel/assets/index-YgC7QI1N.js +0 -1
- package/src/assets/web-panel/assets/index-_sntSncx.js +0 -6
- package/src/assets/web-panel/assets/index-uujjC_4g.js +0 -1
- package/src/assets/web-panel/assets/index-yMt4C1Gh.js +0 -1
- package/src/assets/web-panel/assets/motion-D2eluOJu.js +0 -11
- package/src/assets/web-panel/assets/move-CgcvSoZ7.js +0 -4
- package/src/assets/web-panel/assets/slide-CeP5JCiZ.js +0 -4
- package/src/assets/web-panel/assets/statusUtils-DMVoo96t.js +0 -1
- package/src/assets/web-panel/assets/transition-DSe9CgZe.js +0 -1
- package/src/assets/web-panel/assets/useConfigInject-DCcT0grw.js +0 -2
- package/src/assets/web-panel/assets/useFlexGapSupport-BZkoDuF3.js +0 -1
- package/src/assets/web-panel/assets/vendor-BVetwVfk.js +0 -1
- package/src/assets/web-panel/assets/vnode-Dexy2NN9.js +0 -1
- package/src/assets/web-panel/assets/ws-Bv7YH0VB.js +0 -1
- package/src/assets/web-panel/assets/zoom-CDfM_sRk.js +0 -4
|
@@ -581,13 +581,19 @@ export class WSSessionManager {
|
|
|
581
581
|
});
|
|
582
582
|
}
|
|
583
583
|
|
|
584
|
-
// DB sessions (exclude already-listed in-memory ones
|
|
584
|
+
// DB sessions (exclude already-listed in-memory ones AND closed ones —
|
|
585
|
+
// closeSession persists `status: "closed"` to metadata before deleting
|
|
586
|
+
// from in-memory; without this filter the closed session keeps showing
|
|
587
|
+
// up in `session-list`, contradicting the contract).
|
|
585
588
|
if (this.db) {
|
|
586
589
|
try {
|
|
587
590
|
const dbSessions = dbListSessions(this.db, { limit: 20 });
|
|
588
591
|
const inMemoryIds = new Set(this.sessions.keys());
|
|
589
592
|
for (const dbs of dbSessions) {
|
|
590
593
|
const metadata = this._normalizeSessionMetadata(dbs.metadata);
|
|
594
|
+
if (metadata && metadata.status === "closed") {
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
591
597
|
if (!inMemoryIds.has(dbs.id)) {
|
|
592
598
|
results.push({
|
|
593
599
|
id: dbs.id,
|
|
@@ -1289,6 +1295,11 @@ export class WSSessionManager {
|
|
|
1289
1295
|
return {
|
|
1290
1296
|
version: 1,
|
|
1291
1297
|
sessionType: session.type || "agent",
|
|
1298
|
+
// Persist status so listSessions can exclude closed sessions when
|
|
1299
|
+
// re-hydrating from DB. Without this, a closed session keeps
|
|
1300
|
+
// showing up in `session-list` because closeSession deletes from
|
|
1301
|
+
// in-memory map BEFORE the DB row is filtered.
|
|
1302
|
+
status: session.status || "active",
|
|
1292
1303
|
projectRoot: session.projectRoot || null,
|
|
1293
1304
|
baseProjectRoot: session.baseProjectRoot || session.projectRoot || null,
|
|
1294
1305
|
baseUrl: session.baseUrl || null,
|
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit MTC double-track scaffolding (Phase 2 audit, off-by-default).
|
|
3
|
+
*
|
|
4
|
+
* Design ref: docs/design/默克尔树证书_MTC_落地方案.md §6.3 + 评审清单 §7.
|
|
5
|
+
* Compliance status (2026-05-01): Q-COMP-1 (等保三级最终性窗口) + Q-COMP-2
|
|
6
|
+
* (T/ZGCMCA 023—2025 条款摘要) legal sign-off received. This module still
|
|
7
|
+
* ships `enabled=false` so each tenant decides when to switch on for their
|
|
8
|
+
* own org via explicit `cc audit mtc enable`. See landing plan §14.2.
|
|
9
|
+
*
|
|
10
|
+
* Layout under <configDir>/audit-mtc/:
|
|
11
|
+
* config.json enabled, batch_interval_seconds, namespace_prefix, issuer, ...
|
|
12
|
+
* keys/issuer.hex Ed25519 secret key (0o600)
|
|
13
|
+
* staging/<event-id>.json one Ed25519-signed event per file (track 1: realtime)
|
|
14
|
+
* batches/<batch-id>/ one closed batch:
|
|
15
|
+
* manifest.json schema=audit-batch-manifest/v1, batch_id, tree_head_id, event_ids, ...
|
|
16
|
+
* landmark.json MTC landmark (track 2: batch finality)
|
|
17
|
+
* envelope-<event-id>.json one per event with inclusion_proof
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import fs from "node:fs";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import crypto from "node:crypto";
|
|
23
|
+
import { ed25519 as nobleEd25519 } from "@noble/curves/ed25519";
|
|
24
|
+
import mtcLib from "@chainlesschain/core-mtc";
|
|
25
|
+
|
|
26
|
+
const { sha256, jcs, encodeHashStr, ed25519, assembleBatch } = mtcLib;
|
|
27
|
+
|
|
28
|
+
const CONFIG_DEFAULTS = Object.freeze({
|
|
29
|
+
enabled: false,
|
|
30
|
+
// 3600 = Q-COMP-1 lenient path (approved unless legal requires sub-minute finality);
|
|
31
|
+
// 60 = strict path; both fully supported, just flip the number.
|
|
32
|
+
batch_interval_seconds: 3600,
|
|
33
|
+
namespace_prefix: "mtc/v1/audit/local",
|
|
34
|
+
issuer: "mtca:cc:audit-local",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const SCHEMA_EVENT = "audit-event/v1";
|
|
38
|
+
const SCHEMA_MANIFEST = "audit-batch-manifest/v1";
|
|
39
|
+
|
|
40
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
41
|
+
// Path helpers
|
|
42
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
export function getAuditMtcDir(configDir) {
|
|
45
|
+
if (!configDir) throw new Error("getAuditMtcDir: configDir required");
|
|
46
|
+
return path.join(configDir, "audit-mtc");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function configPath(dir) {
|
|
50
|
+
return path.join(dir, "config.json");
|
|
51
|
+
}
|
|
52
|
+
function stagingDir(dir) {
|
|
53
|
+
return path.join(dir, "staging");
|
|
54
|
+
}
|
|
55
|
+
function batchesDir(dir) {
|
|
56
|
+
return path.join(dir, "batches");
|
|
57
|
+
}
|
|
58
|
+
function keyPath(dir) {
|
|
59
|
+
return path.join(dir, "keys", "issuer.hex");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function ensureDirs(dir) {
|
|
63
|
+
for (const p of [
|
|
64
|
+
dir,
|
|
65
|
+
stagingDir(dir),
|
|
66
|
+
batchesDir(dir),
|
|
67
|
+
path.dirname(keyPath(dir)),
|
|
68
|
+
]) {
|
|
69
|
+
if (!fs.existsSync(p)) fs.mkdirSync(p, { recursive: true });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
74
|
+
// Config
|
|
75
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
export function loadAuditMtcConfig(dir) {
|
|
78
|
+
ensureDirs(dir);
|
|
79
|
+
const p = configPath(dir);
|
|
80
|
+
if (!fs.existsSync(p)) return { ...CONFIG_DEFAULTS };
|
|
81
|
+
try {
|
|
82
|
+
const raw = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
83
|
+
return { ...CONFIG_DEFAULTS, ...raw };
|
|
84
|
+
} catch (err) {
|
|
85
|
+
throw new Error(`audit-mtc config malformed at ${p}: ${err.message}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function saveAuditMtcConfig(dir, patch) {
|
|
90
|
+
ensureDirs(dir);
|
|
91
|
+
const merged = { ...loadAuditMtcConfig(dir), ...patch };
|
|
92
|
+
// Validate
|
|
93
|
+
if (typeof merged.enabled !== "boolean") {
|
|
94
|
+
throw new TypeError("config.enabled must be boolean");
|
|
95
|
+
}
|
|
96
|
+
if (
|
|
97
|
+
!Number.isInteger(merged.batch_interval_seconds) ||
|
|
98
|
+
merged.batch_interval_seconds < 1
|
|
99
|
+
) {
|
|
100
|
+
throw new RangeError(
|
|
101
|
+
"config.batch_interval_seconds must be positive integer",
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (typeof merged.namespace_prefix !== "string" || !merged.namespace_prefix) {
|
|
105
|
+
throw new TypeError("config.namespace_prefix must be non-empty string");
|
|
106
|
+
}
|
|
107
|
+
if (typeof merged.issuer !== "string" || !merged.issuer) {
|
|
108
|
+
throw new TypeError("config.issuer must be non-empty string");
|
|
109
|
+
}
|
|
110
|
+
fs.writeFileSync(configPath(dir), JSON.stringify(merged, null, 2), "utf-8");
|
|
111
|
+
return merged;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
115
|
+
// Key management — Ed25519 issuer key for tree-head signing.
|
|
116
|
+
// SLH-DSA swap-out is a one-line change here when @noble/post-quantum lands.
|
|
117
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
function readIssuerKeyFile(p) {
|
|
120
|
+
const secretKey = Buffer.from(fs.readFileSync(p, "utf-8").trim(), "hex");
|
|
121
|
+
if (secretKey.length !== 32) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`issuer key file ${p} must contain 32 bytes (64 hex chars)`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
const publicKey = Buffer.from(nobleEd25519.getPublicKey(secretKey));
|
|
127
|
+
return { secretKey, publicKey, pubkeyId: ed25519.pubkeyId(publicKey) };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function loadOrCreateIssuerKey(dir) {
|
|
131
|
+
ensureDirs(dir);
|
|
132
|
+
const p = keyPath(dir);
|
|
133
|
+
if (fs.existsSync(p)) {
|
|
134
|
+
return readIssuerKeyFile(p);
|
|
135
|
+
}
|
|
136
|
+
// Race-safe create: 'wx' flag fails with EEXIST if another writer beat us;
|
|
137
|
+
// in that case re-read what they wrote so all callers agree on one key.
|
|
138
|
+
// audit-mtc stays on Ed25519 for both realtime and tree-head signatures
|
|
139
|
+
// (small/fast sig is the realtime track's value prop). SLH-DSA opt-in
|
|
140
|
+
// is exposed at the cc mtc batch* / publish-skills surfaces only.
|
|
141
|
+
const keys = ed25519.generateKeyPair();
|
|
142
|
+
try {
|
|
143
|
+
fs.writeFileSync(p, keys.secretKey.toString("hex"), {
|
|
144
|
+
mode: 0o600,
|
|
145
|
+
flag: "wx",
|
|
146
|
+
});
|
|
147
|
+
return keys;
|
|
148
|
+
} catch (err) {
|
|
149
|
+
if (err.code !== "EEXIST") throw err;
|
|
150
|
+
return readIssuerKeyFile(p);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
155
|
+
// Track 1: emit — write event with realtime Ed25519 signature
|
|
156
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
function makeEventId() {
|
|
159
|
+
// Time-prefixed so listing by name = chronological order within a batch.
|
|
160
|
+
const ts = new Date().toISOString().replace(/[-:T]/g, "").slice(0, 14);
|
|
161
|
+
const rand = crypto.randomBytes(6).toString("hex");
|
|
162
|
+
return `${ts}-${rand}`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Submit one audit event. Always writes an Ed25519-signed staging file
|
|
167
|
+
* (so the event is queryable + cryptographically pinned within milliseconds).
|
|
168
|
+
* The event will land in a Merkle batch on the next reconcile() call.
|
|
169
|
+
*
|
|
170
|
+
* @param {string} dir - audit-mtc root
|
|
171
|
+
* @param {object} body - { event_type, operation, actor?, target?, details?, risk_level?, occurred_at? }
|
|
172
|
+
* @param {object} [opts]
|
|
173
|
+
* @param {boolean} [opts.requireEnabled=true] - when false, allow emit even if config.enabled=false (for tests/admin)
|
|
174
|
+
*/
|
|
175
|
+
export function emitEvent(dir, body, opts = {}) {
|
|
176
|
+
ensureDirs(dir);
|
|
177
|
+
const cfg = loadAuditMtcConfig(dir);
|
|
178
|
+
const requireEnabled = opts.requireEnabled !== false;
|
|
179
|
+
if (requireEnabled && !cfg.enabled) {
|
|
180
|
+
const e = new Error(
|
|
181
|
+
"audit-mtc disabled: run `cc audit mtc enable` to activate (or pass --force for one-off admin/CI use)",
|
|
182
|
+
);
|
|
183
|
+
e.code = "AUDIT_MTC_DISABLED";
|
|
184
|
+
throw e;
|
|
185
|
+
}
|
|
186
|
+
if (!body || typeof body !== "object") {
|
|
187
|
+
throw new TypeError("emitEvent: body must be an object");
|
|
188
|
+
}
|
|
189
|
+
if (!body.event_type || !body.operation) {
|
|
190
|
+
throw new TypeError(
|
|
191
|
+
"emitEvent: body.event_type and body.operation required",
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const keys = loadOrCreateIssuerKey(dir);
|
|
196
|
+
const eventId = makeEventId();
|
|
197
|
+
const occurredAt = body.occurred_at || new Date().toISOString();
|
|
198
|
+
|
|
199
|
+
const normalizedBody = {
|
|
200
|
+
event_type: body.event_type,
|
|
201
|
+
operation: body.operation,
|
|
202
|
+
actor: body.actor || null,
|
|
203
|
+
target: body.target || null,
|
|
204
|
+
details: body.details || null,
|
|
205
|
+
risk_level: body.risk_level || "low",
|
|
206
|
+
occurred_at: occurredAt,
|
|
207
|
+
};
|
|
208
|
+
const contentHash = encodeHashStr(sha256(jcs(normalizedBody)));
|
|
209
|
+
|
|
210
|
+
const signingInput = Buffer.concat([
|
|
211
|
+
Buffer.from("audit-event/v1\n", "utf-8"),
|
|
212
|
+
Buffer.from(contentHash, "utf-8"),
|
|
213
|
+
]);
|
|
214
|
+
const sig = ed25519.signRaw(signingInput, keys.secretKey);
|
|
215
|
+
|
|
216
|
+
const record = {
|
|
217
|
+
schema: SCHEMA_EVENT,
|
|
218
|
+
event_id: eventId,
|
|
219
|
+
body: normalizedBody,
|
|
220
|
+
content_hash: contentHash,
|
|
221
|
+
ed25519_sig: {
|
|
222
|
+
alg: "Ed25519",
|
|
223
|
+
pubkey_id: ed25519.pubkeyId(keys.publicKey),
|
|
224
|
+
sig: sig.toString("base64url"),
|
|
225
|
+
},
|
|
226
|
+
queued_at: new Date().toISOString(),
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const filePath = path.join(stagingDir(dir), `${eventId}.json`);
|
|
230
|
+
fs.writeFileSync(filePath, JSON.stringify(record, null, 2), "utf-8");
|
|
231
|
+
return { eventId, path: filePath, record };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
235
|
+
// Track 2: reconcile — close current batch, build tree, write envelopes.
|
|
236
|
+
// Idempotent: re-running with no staging events is a no-op; caller can
|
|
237
|
+
// drive it on a timer (config.batch_interval_seconds) without coordination.
|
|
238
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
239
|
+
|
|
240
|
+
function nextBatchId(dir) {
|
|
241
|
+
const existing = fs.existsSync(batchesDir(dir))
|
|
242
|
+
? fs.readdirSync(batchesDir(dir)).filter((n) => /^\d{6}$/.test(n))
|
|
243
|
+
: [];
|
|
244
|
+
if (existing.length === 0) return "000001";
|
|
245
|
+
const max = existing
|
|
246
|
+
.map((n) => parseInt(n, 10))
|
|
247
|
+
.reduce((a, b) => Math.max(a, b), 0);
|
|
248
|
+
return String(max + 1).padStart(6, "0");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function listStagingEvents(dir) {
|
|
252
|
+
const sd = stagingDir(dir);
|
|
253
|
+
if (!fs.existsSync(sd)) return [];
|
|
254
|
+
return fs
|
|
255
|
+
.readdirSync(sd)
|
|
256
|
+
.filter((n) => n.endsWith(".json"))
|
|
257
|
+
.sort() // event_id is time-prefixed → sort = chronological
|
|
258
|
+
.map((n) => {
|
|
259
|
+
const full = path.join(sd, n);
|
|
260
|
+
const expectedId = n.slice(0, -".json".length);
|
|
261
|
+
try {
|
|
262
|
+
const obj = JSON.parse(fs.readFileSync(full, "utf-8"));
|
|
263
|
+
if (obj.schema !== SCHEMA_EVENT) {
|
|
264
|
+
return {
|
|
265
|
+
eventId: null,
|
|
266
|
+
file: full,
|
|
267
|
+
error: `unexpected schema: ${obj.schema}`,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (obj.event_id !== expectedId) {
|
|
271
|
+
return {
|
|
272
|
+
eventId: null,
|
|
273
|
+
file: full,
|
|
274
|
+
error: `filename/event_id mismatch (file=${expectedId}, body=${obj.event_id})`,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
return { eventId: obj.event_id, file: full, record: obj };
|
|
278
|
+
} catch (err) {
|
|
279
|
+
return { eventId: null, file: full, error: err.message };
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Close the current batch: scan staging, build Merkle tree, emit landmark +
|
|
286
|
+
* one envelope per event, write manifest, and remove staging files.
|
|
287
|
+
*
|
|
288
|
+
* Returns `{ skipped: true }` when staging is empty (Q-OPS-1 idempotency).
|
|
289
|
+
*
|
|
290
|
+
* @param {string} dir
|
|
291
|
+
* @param {object} [opts]
|
|
292
|
+
* @param {string} [opts.namespace] - override config.namespace_prefix
|
|
293
|
+
* @param {string} [opts.issuer] - override config.issuer
|
|
294
|
+
*/
|
|
295
|
+
export function closeBatch(dir, opts = {}) {
|
|
296
|
+
ensureDirs(dir);
|
|
297
|
+
const cfg = loadAuditMtcConfig(dir);
|
|
298
|
+
const events = listStagingEvents(dir);
|
|
299
|
+
|
|
300
|
+
// Skip malformed entries — collected so caller can surface them
|
|
301
|
+
const malformed = events.filter((e) => e.error);
|
|
302
|
+
const valid = events.filter((e) => !e.error);
|
|
303
|
+
|
|
304
|
+
if (valid.length === 0) {
|
|
305
|
+
return { skipped: true, reason: "no staged events", malformed };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const batchId = nextBatchId(dir);
|
|
309
|
+
const namespacePrefix = opts.namespace || cfg.namespace_prefix;
|
|
310
|
+
const namespace = `${namespacePrefix}/${batchId}`;
|
|
311
|
+
const issuer = opts.issuer || cfg.issuer;
|
|
312
|
+
const keys = loadOrCreateIssuerKey(dir);
|
|
313
|
+
|
|
314
|
+
// Leaves: hash the realtime event record (already Ed25519-signed) so the
|
|
315
|
+
// inclusion proof simultaneously proves "this event existed" + "in this batch".
|
|
316
|
+
const rawLeaves = valid.map((e) => ({
|
|
317
|
+
kind: "audit-event",
|
|
318
|
+
subject: e.eventId,
|
|
319
|
+
content_hash: e.record.content_hash,
|
|
320
|
+
queued_at: e.record.queued_at,
|
|
321
|
+
realtime_sig: e.record.ed25519_sig,
|
|
322
|
+
}));
|
|
323
|
+
|
|
324
|
+
const { landmark, envelopes, treeHeadId } = assembleBatch(rawLeaves, keys, {
|
|
325
|
+
namespace,
|
|
326
|
+
issuer,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Atomic-ish write: stage in tmp dir under batches/, rename in.
|
|
330
|
+
const finalDir = path.join(batchesDir(dir), batchId);
|
|
331
|
+
if (fs.existsSync(finalDir)) {
|
|
332
|
+
// Defensive: nextBatchId already ensures this is a fresh number;
|
|
333
|
+
// if we're here it means a previous close crashed mid-rename — clean up.
|
|
334
|
+
fs.rmSync(finalDir, { recursive: true, force: true });
|
|
335
|
+
}
|
|
336
|
+
const tmpDir = path.join(batchesDir(dir), `.${batchId}.tmp`);
|
|
337
|
+
if (fs.existsSync(tmpDir))
|
|
338
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
339
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
340
|
+
|
|
341
|
+
fs.writeFileSync(
|
|
342
|
+
path.join(tmpDir, "landmark.json"),
|
|
343
|
+
JSON.stringify(landmark, null, 2),
|
|
344
|
+
"utf-8",
|
|
345
|
+
);
|
|
346
|
+
for (let i = 0; i < envelopes.length; i++) {
|
|
347
|
+
const env = envelopes[i];
|
|
348
|
+
const eventId = valid[i].eventId;
|
|
349
|
+
fs.writeFileSync(
|
|
350
|
+
path.join(tmpDir, `envelope-${eventId}.json`),
|
|
351
|
+
JSON.stringify(env, null, 2),
|
|
352
|
+
"utf-8",
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const manifest = {
|
|
357
|
+
schema: SCHEMA_MANIFEST,
|
|
358
|
+
batch_id: batchId,
|
|
359
|
+
namespace,
|
|
360
|
+
issuer,
|
|
361
|
+
tree_head_id: treeHeadId,
|
|
362
|
+
tree_size: valid.length,
|
|
363
|
+
closed_at: new Date().toISOString(),
|
|
364
|
+
event_ids: valid.map((e) => e.eventId),
|
|
365
|
+
envelope_files: valid.map((e) => `envelope-${e.eventId}.json`),
|
|
366
|
+
malformed_skipped: malformed.map((m) => ({ file: m.file, error: m.error })),
|
|
367
|
+
};
|
|
368
|
+
fs.writeFileSync(
|
|
369
|
+
path.join(tmpDir, "manifest.json"),
|
|
370
|
+
JSON.stringify(manifest, null, 2),
|
|
371
|
+
"utf-8",
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
fs.renameSync(tmpDir, finalDir);
|
|
375
|
+
|
|
376
|
+
// Remove staging files only AFTER the rename succeeds (so a crash before
|
|
377
|
+
// rename leaves staging intact and the next reconcile can retry).
|
|
378
|
+
for (const e of valid) {
|
|
379
|
+
try {
|
|
380
|
+
fs.unlinkSync(e.file);
|
|
381
|
+
} catch (_err) {
|
|
382
|
+
/* file may have been removed concurrently — non-fatal */
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
skipped: false,
|
|
388
|
+
batchId,
|
|
389
|
+
namespace,
|
|
390
|
+
treeHeadId,
|
|
391
|
+
treeSize: valid.length,
|
|
392
|
+
eventIds: valid.map((e) => e.eventId),
|
|
393
|
+
batchDir: finalDir,
|
|
394
|
+
malformed: manifest.malformed_skipped,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Find which closed batch contains the given event id.
|
|
400
|
+
* Returns { found: false } if event is still in staging or unknown.
|
|
401
|
+
*
|
|
402
|
+
* @param {string} dir
|
|
403
|
+
* @param {string} eventId
|
|
404
|
+
* @returns {{ found: boolean, batchId?: string, treeHeadId?: string, namespace?: string, leafIndex?: number, envelopePath?: string, staging?: boolean }}
|
|
405
|
+
*/
|
|
406
|
+
export function reconcileCheck(dir, eventId) {
|
|
407
|
+
ensureDirs(dir);
|
|
408
|
+
if (!eventId || typeof eventId !== "string") {
|
|
409
|
+
throw new TypeError("reconcileCheck: eventId required");
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Still in staging?
|
|
413
|
+
const stagingFile = path.join(stagingDir(dir), `${eventId}.json`);
|
|
414
|
+
if (fs.existsSync(stagingFile)) {
|
|
415
|
+
return { found: false, staging: true };
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const bd = batchesDir(dir);
|
|
419
|
+
if (!fs.existsSync(bd)) return { found: false };
|
|
420
|
+
const batches = fs
|
|
421
|
+
.readdirSync(bd)
|
|
422
|
+
.filter((n) => /^\d{6}$/.test(n))
|
|
423
|
+
.sort();
|
|
424
|
+
for (const batchId of batches) {
|
|
425
|
+
const manifestPath = path.join(bd, batchId, "manifest.json");
|
|
426
|
+
if (!fs.existsSync(manifestPath)) continue;
|
|
427
|
+
let manifest;
|
|
428
|
+
try {
|
|
429
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
430
|
+
} catch (_err) {
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
const idx = (manifest.event_ids || []).indexOf(eventId);
|
|
434
|
+
if (idx >= 0) {
|
|
435
|
+
return {
|
|
436
|
+
found: true,
|
|
437
|
+
batchId,
|
|
438
|
+
treeHeadId: manifest.tree_head_id,
|
|
439
|
+
namespace: manifest.namespace,
|
|
440
|
+
leafIndex: idx,
|
|
441
|
+
envelopePath: path.join(bd, batchId, `envelope-${eventId}.json`),
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return { found: false };
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Snapshot of queue + last-batch state. Cheap to compute, suitable for `cc audit mtc status`.
|
|
450
|
+
*/
|
|
451
|
+
export function getStatus(dir) {
|
|
452
|
+
ensureDirs(dir);
|
|
453
|
+
const cfg = loadAuditMtcConfig(dir);
|
|
454
|
+
const staging = listStagingEvents(dir);
|
|
455
|
+
const bd = batchesDir(dir);
|
|
456
|
+
const batches = fs.existsSync(bd)
|
|
457
|
+
? fs
|
|
458
|
+
.readdirSync(bd)
|
|
459
|
+
.filter((n) => /^\d{6}$/.test(n))
|
|
460
|
+
.sort()
|
|
461
|
+
: [];
|
|
462
|
+
let lastBatch = null;
|
|
463
|
+
if (batches.length > 0) {
|
|
464
|
+
const lastId = batches[batches.length - 1];
|
|
465
|
+
const manifestPath = path.join(bd, lastId, "manifest.json");
|
|
466
|
+
if (fs.existsSync(manifestPath)) {
|
|
467
|
+
try {
|
|
468
|
+
lastBatch = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
469
|
+
} catch (_err) {
|
|
470
|
+
/* malformed — treat as no-last-batch */
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
// Find oldest queued_at across valid records — guard against the
|
|
475
|
+
// alphabetically-first staging entry being malformed.
|
|
476
|
+
const oldestValid = staging.find((e) => e.record && e.record.queued_at);
|
|
477
|
+
return {
|
|
478
|
+
config: cfg,
|
|
479
|
+
staging: {
|
|
480
|
+
count: staging.length,
|
|
481
|
+
malformed: staging.filter((e) => e.error).length,
|
|
482
|
+
oldest_queued_at: oldestValid ? oldestValid.record.queued_at : null,
|
|
483
|
+
},
|
|
484
|
+
batches: {
|
|
485
|
+
count: batches.length,
|
|
486
|
+
last_batch_id: batches.length ? batches[batches.length - 1] : null,
|
|
487
|
+
last_closed_at: lastBatch ? lastBatch.closed_at : null,
|
|
488
|
+
last_tree_size: lastBatch ? lastBatch.tree_size : null,
|
|
489
|
+
last_tree_head_id: lastBatch ? lastBatch.tree_head_id : null,
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
export const _internals = {
|
|
495
|
+
CONFIG_DEFAULTS,
|
|
496
|
+
SCHEMA_EVENT,
|
|
497
|
+
SCHEMA_MANIFEST,
|
|
498
|
+
configPath,
|
|
499
|
+
stagingDir,
|
|
500
|
+
batchesDir,
|
|
501
|
+
keyPath,
|
|
502
|
+
nextBatchId,
|
|
503
|
+
listStagingEvents,
|
|
504
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* listen-with-port-fallback.js
|
|
3
|
+
*
|
|
4
|
+
* Try to bind a server on the preferred port. If that port is taken
|
|
5
|
+
* (EADDRINUSE), walk through adjacent ports up to `maxAttempts`. If
|
|
6
|
+
* every adjacent port is also taken, fall back once more to port 0
|
|
7
|
+
* (OS-assigned ephemeral). Surfaces the actually bound port so the
|
|
8
|
+
* caller can log it / propagate it to dependent servers (e.g. the
|
|
9
|
+
* HTTP server's `__CC_CONFIG__.wsPort` injection).
|
|
10
|
+
*
|
|
11
|
+
* Used by `cc ui`'s startUiServer for both the WS and HTTP servers
|
|
12
|
+
* — port collisions are common during development (multiple `cc ui`
|
|
13
|
+
* instances, prior crashes that left listeners orphaned, the desktop
|
|
14
|
+
* web-shell already running, etc.) and crashing on EADDRINUSE makes
|
|
15
|
+
* for poor UX.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @template T
|
|
20
|
+
* @param {(port: number) => Promise<T>} listenFn
|
|
21
|
+
* Per-attempt binder. Must reject with `err.code === "EADDRINUSE"` on
|
|
22
|
+
* conflict; any other error propagates immediately. Resolved value is
|
|
23
|
+
* passed through verbatim so the caller can pull the actual port from
|
|
24
|
+
* their server handle (the listenFn is expected to readback the bound
|
|
25
|
+
* port from `address()` after listen — see ws-server.js for the pattern).
|
|
26
|
+
*
|
|
27
|
+
* @param {number} preferred
|
|
28
|
+
* First port to attempt. Must be in [1, 65535]; pass 0 to skip the
|
|
29
|
+
* sequential walk and go straight to OS-assigned.
|
|
30
|
+
*
|
|
31
|
+
* @param {object} [opts]
|
|
32
|
+
* @param {number} [opts.maxAttempts=20]
|
|
33
|
+
* How many sequential adjacent ports to try after `preferred` (so the
|
|
34
|
+
* walk covers `preferred`, `preferred+1`, …, `preferred + maxAttempts - 1`).
|
|
35
|
+
* @param {(msg: string) => void} [opts.onFallback]
|
|
36
|
+
* Optional notifier called once when the preferred port is taken,
|
|
37
|
+
* with a human-readable message summarising the fallback decision.
|
|
38
|
+
*
|
|
39
|
+
* @returns {Promise<T>} the listenFn's resolved value for the port that bound.
|
|
40
|
+
*/
|
|
41
|
+
export async function listenWithPortFallback(listenFn, preferred, opts = {}) {
|
|
42
|
+
const { maxAttempts = 20, onFallback } = opts;
|
|
43
|
+
|
|
44
|
+
if (typeof listenFn !== "function") {
|
|
45
|
+
throw new TypeError("listenWithPortFallback: listenFn must be a function");
|
|
46
|
+
}
|
|
47
|
+
if (!Number.isInteger(preferred) || preferred < 0 || preferred > 65535) {
|
|
48
|
+
throw new RangeError(
|
|
49
|
+
`listenWithPortFallback: preferred port out of range: ${preferred}`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// preferred === 0 means caller already wants OS-assigned, no fallback needed.
|
|
54
|
+
if (preferred === 0) {
|
|
55
|
+
return listenFn(0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const upper = Math.min(preferred + maxAttempts, 65536);
|
|
59
|
+
const tried = [];
|
|
60
|
+
|
|
61
|
+
for (let port = preferred; port < upper; port++) {
|
|
62
|
+
try {
|
|
63
|
+
const result = await listenFn(port);
|
|
64
|
+
if (port !== preferred && typeof onFallback === "function") {
|
|
65
|
+
try {
|
|
66
|
+
onFallback(
|
|
67
|
+
`Port ${preferred} in use; bound on ${port} instead (tried ${tried.join(", ")}).`,
|
|
68
|
+
);
|
|
69
|
+
} catch {
|
|
70
|
+
/* swallow — onFallback is informational */
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
} catch (err) {
|
|
75
|
+
if (err && err.code === "EADDRINUSE") {
|
|
76
|
+
tried.push(port);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// All sequential ports busy — final fallback to OS-assigned.
|
|
84
|
+
if (typeof onFallback === "function") {
|
|
85
|
+
try {
|
|
86
|
+
onFallback(
|
|
87
|
+
`Ports ${tried.join(", ")} all in use; falling back to OS-assigned port.`,
|
|
88
|
+
);
|
|
89
|
+
} catch {
|
|
90
|
+
/* swallow */
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
return await listenFn(0);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
if (err && err.code === "EADDRINUSE") {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Could not bind any port. Tried: ${tried.join(", ")} and OS-assigned. Last error: ${err.message}`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
}
|