shieldcortex 4.31.2 → 4.32.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/README.md +78 -2
- package/dashboard/.next/standalone/dashboard/.next/BUILD_ID +1 -1
- package/dashboard/.next/standalone/dashboard/.next/build-manifest.json +2 -2
- package/dashboard/.next/standalone/dashboard/.next/prerender-manifest.json +3 -3
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/!KGRhc2hib2FyZCk/admin/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/!KGRhc2hib2FyZCk/admin.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/!KGRhc2hib2FyZCk/cloud/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/!KGRhc2hib2FyZCk/cloud.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/!KGRhc2hib2FyZCk/memory/capture/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/!KGRhc2hib2FyZCk/memory/capture.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/!KGRhc2hib2FyZCk/memory/graph/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/!KGRhc2hib2FyZCk/memory/graph.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/!KGRhc2hib2FyZCk/memory/recall/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/!KGRhc2hib2FyZCk/memory/recall.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/replay.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/replay.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/replay.segments/!KGRhc2hib2FyZCk/memory/replay/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/replay.segments/!KGRhc2hib2FyZCk/memory/replay.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/replay.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/replay.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/replay.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/replay.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/replay.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/replay.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/!KGRhc2hib2FyZCk/memory/review/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/!KGRhc2hib2FyZCk/memory/review.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/!KGRhc2hib2FyZCk/memory/timeline/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/!KGRhc2hib2FyZCk/memory/timeline.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/!KGRhc2hib2FyZCk/memory/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/!KGRhc2hib2FyZCk/overview/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/!KGRhc2hib2FyZCk/overview.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/!KGRhc2hib2FyZCk/protection/audit/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/!KGRhc2hib2FyZCk/protection/audit.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/!KGRhc2hib2FyZCk/protection.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/!KGRhc2hib2FyZCk/protection/intercepts/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/!KGRhc2hib2FyZCk/protection/intercepts.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/!KGRhc2hib2FyZCk/protection.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/!KGRhc2hib2FyZCk/protection/iron-dome/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/!KGRhc2hib2FyZCk/protection/iron-dome.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/!KGRhc2hib2FyZCk/protection.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/!KGRhc2hib2FyZCk/protection/policies/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/!KGRhc2hib2FyZCk/protection/policies.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/!KGRhc2hib2FyZCk/protection.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/!KGRhc2hib2FyZCk/protection/quarantine/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/!KGRhc2hib2FyZCk/protection/quarantine.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/!KGRhc2hib2FyZCk/protection.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/!KGRhc2hib2FyZCk/protection/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/!KGRhc2hib2FyZCk/protection.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/!KGRhc2hib2FyZCk/settings/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/!KGRhc2hib2FyZCk/settings.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/!KGRhc2hib2FyZCk/supply-chain/xray/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/!KGRhc2hib2FyZCk/supply-chain/xray.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/!KGRhc2hib2FyZCk/supply-chain.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/!KGRhc2hib2FyZCk/supply-chain/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/!KGRhc2hib2FyZCk/supply-chain.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/!KGRhc2hib2FyZCk/xray/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/!KGRhc2hib2FyZCk/xray.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/pages/404.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/pages/500.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.js +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.json +1 -1
- package/dist/api/control.d.ts +2 -0
- package/dist/api/control.js +119 -2
- package/dist/api/routes/memories.js +19 -14
- package/dist/api/routes/system.js +2 -3
- package/dist/api/visualization-server.d.ts +13 -1
- package/dist/api/visualization-server.js +57 -1
- package/dist/audit/env-scanner.js +5 -2
- package/dist/audit/index.d.ts +4 -1
- package/dist/audit/index.js +2 -1
- package/dist/audit/mcp-config-scanner.d.ts +23 -0
- package/dist/audit/mcp-config-scanner.js +110 -0
- package/dist/audit/mcp-tools-scanner.d.ts +112 -0
- package/dist/audit/mcp-tools-scanner.js +299 -0
- package/dist/cli/audit.d.ts +1 -0
- package/dist/cli/audit.js +12 -1
- package/dist/cli/doctor.js +4 -1
- package/dist/cli/mcp.d.ts +13 -0
- package/dist/cli/mcp.js +0 -0
- package/dist/cli/remember.d.ts +75 -0
- package/dist/cli/remember.js +195 -0
- package/dist/cli/repair.d.ts +8 -0
- package/dist/cli/repair.js +34 -0
- package/dist/cli/update.js +34 -0
- package/dist/cloud/config.d.ts +23 -1
- package/dist/cloud/config.js +453 -193
- package/dist/cloud/quarantine-sync.d.ts +12 -2
- package/dist/cloud/quarantine-sync.js +28 -6
- package/dist/cloud/sync-queue.d.ts +21 -2
- package/dist/cloud/sync-queue.js +124 -29
- package/dist/database/better-sqlite3-guard.d.ts +21 -2
- package/dist/database/better-sqlite3-guard.js +29 -5
- package/dist/database/init.js +68 -16
- package/dist/database/inline-schema.js +35 -1
- package/dist/database/migrations.js +104 -8
- package/dist/database/schema.sql +39 -1
- package/dist/defence/audit/queries.d.ts +10 -2
- package/dist/defence/audit/queries.js +30 -4
- package/dist/defence/audit/retention.d.ts +50 -0
- package/dist/defence/audit/retention.js +161 -0
- package/dist/defence/credential-leak/entropy.d.ts +11 -0
- package/dist/defence/credential-leak/entropy.js +27 -0
- package/dist/defence/credential-leak/index.js +27 -1
- package/dist/defence/credential-leak/patterns.d.ts +9 -0
- package/dist/defence/credential-leak/patterns.js +21 -0
- package/dist/defence/custom-patterns/store.js +8 -1
- package/dist/defence/custom-rules/store.d.ts +18 -0
- package/dist/defence/custom-rules/store.js +63 -0
- package/dist/defence/firewall/confusables.d.ts +30 -0
- package/dist/defence/firewall/confusables.js +87 -0
- package/dist/defence/firewall/encoding-detector.js +23 -9
- package/dist/defence/firewall/index.d.ts +11 -1
- package/dist/defence/firewall/index.js +34 -1
- package/dist/defence/firewall/instruction-detector.js +18 -7
- package/dist/defence/firewall/markdown-image-detector.d.ts +34 -0
- package/dist/defence/firewall/markdown-image-detector.js +83 -0
- package/dist/defence/fragmentation/entity-extractor.js +17 -6
- package/dist/defence/index.d.ts +5 -0
- package/dist/defence/index.js +8 -0
- package/dist/defence/iron-dome/index.js +7 -1
- package/dist/defence/pipeline.js +62 -10
- package/dist/defence/scan-windows.d.ts +41 -0
- package/dist/defence/scan-windows.js +61 -0
- package/dist/defence/semantic/attack-corpus.d.ts +22 -0
- package/dist/defence/semantic/attack-corpus.js +75 -0
- package/dist/defence/semantic/index.d.ts +67 -0
- package/dist/defence/semantic/index.js +138 -0
- package/dist/defence/skill-scanner/deep-scan.js +35 -15
- package/dist/defence/skill-scanner/patterns.d.ts +1 -1
- package/dist/defence/skill-scanner/patterns.js +8 -7
- package/dist/defence/tool-response-scanner.d.ts +21 -5
- package/dist/defence/tool-response-scanner.js +111 -22
- package/dist/defence/types.d.ts +11 -1
- package/dist/index.d.ts +29 -0
- package/dist/index.js +112 -21
- package/dist/memory/consolidate.js +1 -1
- package/dist/memory/decay.js +3 -1
- package/dist/memory/embedding.d.ts +18 -2
- package/dist/memory/embedding.js +32 -11
- package/dist/memory/expiry.js +1 -1
- package/dist/memory/search-recall.js +107 -49
- package/dist/memory/search.d.ts +19 -3
- package/dist/memory/search.js +25 -10
- package/dist/memory/store.d.ts +13 -2
- package/dist/memory/store.js +115 -11
- package/dist/scan-only.d.ts +64 -0
- package/dist/scan-only.js +173 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +6 -4
- package/dist/setup/claude-md.js +39 -34
- package/dist/setup/codex.js +9 -2
- package/dist/setup/copilot.js +160 -47
- package/dist/setup/json-config.d.ts +99 -0
- package/dist/setup/json-config.js +167 -0
- package/dist/setup/migrate.js +1 -1
- package/dist/setup/native-binding.d.ts +75 -0
- package/dist/setup/native-binding.js +146 -0
- package/dist/setup/settings-hooks.js +8 -13
- package/dist/setup/uninstall.js +1 -21
- package/dist/tools/context.d.ts +8 -8
- package/dist/tools/forget.d.ts +9 -8
- package/dist/tools/forget.js +17 -4
- package/dist/tools/recall.d.ts +13 -13
- package/dist/tools/remember.d.ts +16 -16
- package/dist/tools/remember.js +19 -8
- package/dist/worker/brain-worker.d.ts +1 -0
- package/dist/worker/brain-worker.js +79 -16
- package/dist/worker/types.d.ts +8 -0
- package/dist/worker/types.js +8 -0
- package/dist/xray/dir-scanner.d.ts +18 -0
- package/dist/xray/dir-scanner.js +23 -1
- package/dist/xray/file-scanner.js +16 -1
- package/dist/xray/findings-store.js +9 -1
- package/dist/xray/index.d.ts +2 -0
- package/dist/xray/index.js +10 -1
- package/dist/xray/npm-inspector.d.ts +31 -0
- package/dist/xray/npm-inspector.js +135 -29
- package/dist/xray/patterns.d.ts +1 -1
- package/dist/xray/patterns.js +20 -23
- package/dist/xray/sarif.d.ts +78 -0
- package/dist/xray/sarif.js +166 -0
- package/dist/xray/watch.d.ts +1 -0
- package/dist/xray/watch.js +10 -1
- package/hooks/openclaw/cortex-memory/handler.ts +122 -18
- package/hooks/openclaw/cortex-memory/runtime.mjs +10 -4
- package/package.json +10 -3
- package/scripts/postinstall.mjs +8 -3
- package/dist/memory/embedding-cache.d.ts +0 -20
- package/dist/memory/embedding-cache.js +0 -91
- /package/dashboard/.next/standalone/dashboard/.next/static/{_j4TeMpss-w79QtNNWqZw → tjJ3X8xQ-2_WQTPGF3zCA}/_buildManifest.js +0 -0
- /package/dashboard/.next/standalone/dashboard/.next/static/{_j4TeMpss-w79QtNNWqZw → tjJ3X8xQ-2_WQTPGF3zCA}/_clientMiddlewareManifest.json +0 -0
- /package/dashboard/.next/standalone/dashboard/.next/static/{_j4TeMpss-w79QtNNWqZw → tjJ3X8xQ-2_WQTPGF3zCA}/_ssgManifest.js +0 -0
package/dist/cloud/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync } from 'fs';
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, statSync, renameSync, rmSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { homedir, hostname } from 'os';
|
|
4
4
|
import { randomUUID, randomBytes, createHmac, timingSafeEqual } from 'crypto';
|
|
@@ -42,6 +42,15 @@ const DEFAULT_REVIEW_COPILOT_CONFIG = {
|
|
|
42
42
|
// Cache to avoid repeated file reads
|
|
43
43
|
let cachedConfig = null;
|
|
44
44
|
let cachedConfigFile = null;
|
|
45
|
+
// Raw-config cache keyed on (file path, mtimeMs). The defence pipeline reads
|
|
46
|
+
// the config on every scan via getDefenceMode() and ~30 other accessors all
|
|
47
|
+
// funnel through readRawConfig(); without this each call re-read + re-parsed +
|
|
48
|
+
// re-HMAC-verified the file. The mtime key means an external write (any other
|
|
49
|
+
// process) is picked up on its next stat, while a hot loop in one process pays
|
|
50
|
+
// for the read+parse+verify exactly once per actual file change.
|
|
51
|
+
let cachedRawConfig = null;
|
|
52
|
+
let cachedRawConfigFile = null;
|
|
53
|
+
let cachedRawConfigMtimeMs = null;
|
|
45
54
|
// ── Config Integrity (HMAC) ──────────────────────────────
|
|
46
55
|
let cachedIntegrityKey = null;
|
|
47
56
|
let cachedIntegrityKeyFile = null;
|
|
@@ -74,6 +83,25 @@ function getIntegrityKey() {
|
|
|
74
83
|
function signConfig(jsonContent) {
|
|
75
84
|
return createHmac('sha256', getIntegrityKey()).update(jsonContent, 'utf-8').digest('hex');
|
|
76
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* The exact byte string the HMAC is computed over for the embedded-`_sig`
|
|
88
|
+
* scheme. Defined once and used by BOTH the write path and the verify path so
|
|
89
|
+
* they can never diverge: the canonical body is the config object with `_sig`
|
|
90
|
+
* stripped, serialised with `JSON.stringify(rest, null, 2)`. Whatever the
|
|
91
|
+
* writer signs, the verifier recomputes over the identical bytes.
|
|
92
|
+
*/
|
|
93
|
+
function canonicalBodyForSig(obj) {
|
|
94
|
+
const rest = { ...obj };
|
|
95
|
+
delete rest._sig;
|
|
96
|
+
return JSON.stringify(rest, null, 2);
|
|
97
|
+
}
|
|
98
|
+
function constantTimeEqualHex(storedSig, computedSig) {
|
|
99
|
+
const a = Buffer.from(storedSig, 'utf-8');
|
|
100
|
+
const b = Buffer.from(computedSig, 'utf-8');
|
|
101
|
+
if (a.length !== b.length)
|
|
102
|
+
return false;
|
|
103
|
+
return timingSafeEqual(a, b);
|
|
104
|
+
}
|
|
77
105
|
function writeConfigSignature(jsonContent) {
|
|
78
106
|
const sig = signConfig(jsonContent);
|
|
79
107
|
const sigFile = getSigFile();
|
|
@@ -83,21 +111,41 @@ function writeConfigSignature(jsonContent) {
|
|
|
83
111
|
}
|
|
84
112
|
catch { /* best-effort */ }
|
|
85
113
|
}
|
|
86
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Verify a parsed config object's integrity.
|
|
116
|
+
*
|
|
117
|
+
* Two on-disk formats are supported for backward compatibility:
|
|
118
|
+
* - **Embedded (new, v4.32+):** the object carries a top-level `_sig` field;
|
|
119
|
+
* verify it against the HMAC of `canonicalBodyForSig(obj)`.
|
|
120
|
+
* - **Legacy (pre-v4.32):** no `_sig` field — the signature lives in a
|
|
121
|
+
* separate `.config-sig` file, computed over the EXACT file bytes
|
|
122
|
+
* (`rawFileContent`). This is the original scheme; we keep reading it so an
|
|
123
|
+
* install that hasn't been rewritten yet never false-tampers. The next
|
|
124
|
+
* write upgrades it to the embedded format.
|
|
125
|
+
*
|
|
126
|
+
* `rawFileContent` is the literal bytes read from disk (needed for the legacy
|
|
127
|
+
* whole-file signature). On a missing legacy sig file we adopt the current
|
|
128
|
+
* content as trusted (first run after upgrade), matching prior behaviour.
|
|
129
|
+
*/
|
|
130
|
+
function verifyConfigIntegrity(parsed, rawFileContent) {
|
|
87
131
|
try {
|
|
132
|
+
if (typeof parsed._sig === 'string') {
|
|
133
|
+
// Embedded scheme: recompute over the canonical body (object minus _sig).
|
|
134
|
+
const computed = signConfig(canonicalBodyForSig(parsed));
|
|
135
|
+
return constantTimeEqualHex(parsed._sig, computed);
|
|
136
|
+
}
|
|
137
|
+
// Legacy scheme: separate .config-sig file signed over the whole file.
|
|
88
138
|
const sigFile = getSigFile();
|
|
89
139
|
if (!existsSync(sigFile)) {
|
|
90
|
-
// First run after upgrade —
|
|
91
|
-
|
|
140
|
+
// First run after upgrade with no sig yet — adopt as trusted, write a
|
|
141
|
+
// legacy sig so a subsequent read (before the next write upgrades it)
|
|
142
|
+
// still verifies. Matches the prior behaviour to avoid a false tamper.
|
|
143
|
+
writeConfigSignature(rawFileContent);
|
|
92
144
|
return true;
|
|
93
145
|
}
|
|
94
146
|
const storedSig = readFileSync(sigFile, 'utf-8').trim();
|
|
95
|
-
const computedSig = signConfig(
|
|
96
|
-
|
|
97
|
-
const b = Buffer.from(computedSig, 'utf-8');
|
|
98
|
-
if (a.length !== b.length)
|
|
99
|
-
return false;
|
|
100
|
-
return timingSafeEqual(a, b);
|
|
147
|
+
const computedSig = signConfig(rawFileContent);
|
|
148
|
+
return constantTimeEqualHex(storedSig, computedSig);
|
|
101
149
|
}
|
|
102
150
|
catch {
|
|
103
151
|
return false;
|
|
@@ -111,55 +159,42 @@ export function getCloudConfig() {
|
|
|
111
159
|
const configFile = getConfigFile();
|
|
112
160
|
if (cachedConfig && cachedConfigFile === configFile)
|
|
113
161
|
return cachedConfig;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
return cachedConfig;
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
return { cloudApiKey: null, cloudBaseUrl: DEFAULT_BASE_URL, cloudEnabled: false };
|
|
129
|
-
}
|
|
162
|
+
// Route through the shared, mtime-cached, integrity-verified read so the
|
|
163
|
+
// cloud config reflects the same view (and `_sig` stripping) as every other
|
|
164
|
+
// accessor — no second bespoke parse path that could drift.
|
|
165
|
+
const raw = readRawConfig();
|
|
166
|
+
cachedConfig = {
|
|
167
|
+
cloudApiKey: raw.cloudApiKey ?? null,
|
|
168
|
+
cloudBaseUrl: raw.cloudBaseUrl ?? DEFAULT_BASE_URL,
|
|
169
|
+
cloudEnabled: raw.cloudEnabled ?? false,
|
|
170
|
+
};
|
|
171
|
+
cachedConfigFile = configFile;
|
|
172
|
+
return cachedConfig;
|
|
130
173
|
}
|
|
131
174
|
export function setCloudConfig(updates) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
existing.cloudEnabled = updates.cloudEnabled;
|
|
148
|
-
const configDir = getConfigDir();
|
|
149
|
-
const configFile = getConfigFile();
|
|
150
|
-
mkdirSync(configDir, { recursive: true });
|
|
151
|
-
const content = JSON.stringify(existing, null, 2) + '\n';
|
|
152
|
-
writeFileSync(configFile, content);
|
|
153
|
-
writeConfigSignature(content);
|
|
154
|
-
// Invalidate cache
|
|
175
|
+
// Read-modify-write through mutateRawConfig: preserves every other field
|
|
176
|
+
// (defenceMode, deviceId, sync controls…) and writes atomically with the
|
|
177
|
+
// embedded `_sig`. On a corrupt/unreadable file we do NOT silently start
|
|
178
|
+
// fresh — that would wipe a credential the user is still relying on; the
|
|
179
|
+
// helper throws so the caller surfaces the problem rather than losing data.
|
|
180
|
+
mutateRawConfig((existing) => {
|
|
181
|
+
if (updates.cloudApiKey !== undefined)
|
|
182
|
+
existing.cloudApiKey = updates.cloudApiKey;
|
|
183
|
+
if (updates.cloudBaseUrl !== undefined)
|
|
184
|
+
existing.cloudBaseUrl = updates.cloudBaseUrl;
|
|
185
|
+
if (updates.cloudEnabled !== undefined)
|
|
186
|
+
existing.cloudEnabled = updates.cloudEnabled;
|
|
187
|
+
});
|
|
188
|
+
// Invalidate the derived cloud-config cache (writeRawConfig already cleared
|
|
189
|
+
// the raw cache and tamper flag).
|
|
155
190
|
cachedConfig = null;
|
|
156
191
|
cachedConfigFile = null;
|
|
157
|
-
configTampered = false;
|
|
158
192
|
}
|
|
159
193
|
/** Reset the in-memory cache (useful for testing) */
|
|
160
194
|
export function clearCloudConfigCache() {
|
|
161
195
|
cachedConfig = null;
|
|
162
196
|
cachedConfigFile = null;
|
|
197
|
+
invalidateRawConfigCache();
|
|
163
198
|
}
|
|
164
199
|
function normalizeProjectList(value) {
|
|
165
200
|
if (!Array.isArray(value))
|
|
@@ -170,19 +205,31 @@ function normalizeProjectList(value) {
|
|
|
170
205
|
.filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
171
206
|
}
|
|
172
207
|
export function getCloudSyncControls() {
|
|
173
|
-
|
|
208
|
+
// This is a READER. On a parse failure we must return the in-memory defaults
|
|
209
|
+
// and NOT write — readRawConfigState preserves `parseFailed` so the one-shot
|
|
210
|
+
// migration below never runs against a `{}` derived from a corrupt file
|
|
211
|
+
// (which would persist a near-empty, signed config and wipe cloudApiKey).
|
|
212
|
+
const { data: raw, parseFailed } = readRawConfigState();
|
|
174
213
|
// One-shot v4.27 migration: pre-upgrade configs with cloud sync enabled
|
|
175
214
|
// were silently shipping CONFIDENTIAL+ content because `excludeSensitive`
|
|
176
215
|
// defaulted to false. Detect those configs (cloud on, no explicit setting,
|
|
177
216
|
// no prior migration stamp) and rewrite them to the new safe default. If
|
|
178
217
|
// the user later opts back in via `--cloud-include-sensitive`, that writes
|
|
179
218
|
// `cloudSyncExcludeSensitive: false` explicitly and this branch is skipped.
|
|
180
|
-
|
|
219
|
+
// Skip entirely on parseFailed: never write through a reader path.
|
|
220
|
+
if (!parseFailed &&
|
|
221
|
+
raw.cloudEnabled === true &&
|
|
181
222
|
typeof raw.cloudSyncExcludeSensitive !== 'boolean' &&
|
|
182
223
|
typeof raw.cloudSyncDefaultsMigratedAt !== 'string') {
|
|
224
|
+
// Route the migration write through the guarded helper (skip-policy: a
|
|
225
|
+
// reader must never throw). The earlier !parseFailed guard makes the skip
|
|
226
|
+
// path unreachable here, but going through mutateRawConfig keeps the
|
|
227
|
+
// single-source-of-truth invariant: no bare writeRawConfig outside it.
|
|
228
|
+
mutateRawConfig((m) => {
|
|
229
|
+
m.cloudSyncExcludeSensitive = true;
|
|
230
|
+
m.cloudSyncDefaultsMigratedAt = new Date().toISOString();
|
|
231
|
+
}, 'skip');
|
|
183
232
|
raw.cloudSyncExcludeSensitive = true;
|
|
184
|
-
raw.cloudSyncDefaultsMigratedAt = new Date().toISOString();
|
|
185
|
-
writeRawConfig(raw);
|
|
186
233
|
}
|
|
187
234
|
const projectMode = raw.cloudSyncProjectMode;
|
|
188
235
|
const contentMode = raw.cloudSyncContentMode;
|
|
@@ -198,16 +245,16 @@ export function getCloudSyncControls() {
|
|
|
198
245
|
};
|
|
199
246
|
}
|
|
200
247
|
export function setCloudSyncControls(updates) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
248
|
+
mutateRawConfig((raw) => {
|
|
249
|
+
if (updates.projectMode !== undefined)
|
|
250
|
+
raw.cloudSyncProjectMode = updates.projectMode;
|
|
251
|
+
if (updates.projects !== undefined)
|
|
252
|
+
raw.cloudSyncProjects = normalizeProjectList(updates.projects);
|
|
253
|
+
if (updates.contentMode !== undefined)
|
|
254
|
+
raw.cloudSyncContentMode = updates.contentMode;
|
|
255
|
+
if (updates.excludeSensitive !== undefined)
|
|
256
|
+
raw.cloudSyncExcludeSensitive = updates.excludeSensitive;
|
|
257
|
+
});
|
|
211
258
|
}
|
|
212
259
|
export function shouldSyncProject(project, controls = getCloudSyncControls()) {
|
|
213
260
|
const normalized = (project ?? '').trim();
|
|
@@ -222,70 +269,211 @@ export function isSensitiveLevel(level) {
|
|
|
222
269
|
const normalized = level.trim().toUpperCase();
|
|
223
270
|
return normalized.length > 0 && normalized !== 'PUBLIC' && normalized !== 'INTERNAL';
|
|
224
271
|
}
|
|
225
|
-
|
|
226
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Read + verify the raw config, distinguishing "file absent/empty" (safe to
|
|
274
|
+
* write) from "file present but unparseable" (must stay read-only). Backed by
|
|
275
|
+
* an mtime cache so a hot path (per-scan getDefenceMode) doesn't re-read,
|
|
276
|
+
* re-parse and re-HMAC the file on every call.
|
|
277
|
+
*/
|
|
278
|
+
function readRawConfigState() {
|
|
279
|
+
const configFile = getConfigFile();
|
|
280
|
+
// mtime cache: if the file is unchanged since the last successful read,
|
|
281
|
+
// return the cached parsed object without touching disk again.
|
|
282
|
+
try {
|
|
283
|
+
const mtimeMs = statSync(configFile).mtimeMs;
|
|
284
|
+
if (cachedRawConfig !== null &&
|
|
285
|
+
cachedRawConfigFile === configFile &&
|
|
286
|
+
cachedRawConfigMtimeMs === mtimeMs) {
|
|
287
|
+
// Return a shallow copy so callers can mutate freely without poisoning
|
|
288
|
+
// the cache (accessors push to arrays / set fields before writing back).
|
|
289
|
+
return { data: { ...cachedRawConfig }, parseFailed: false };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
// stat failed (file likely absent) — fall through to the real read.
|
|
294
|
+
}
|
|
227
295
|
try {
|
|
228
|
-
const configFile = getConfigFile();
|
|
229
296
|
if (existsSync(configFile)) {
|
|
297
|
+
// Capture mtime BEFORE reading the bytes. The cache key must describe the
|
|
298
|
+
// exact bytes we're about to read: if we stat AFTER readFileSync, a
|
|
299
|
+
// concurrent write between read and stat would cache the OLD bytes under
|
|
300
|
+
// the NEW mtime, so a later read at that mtime would serve stale content.
|
|
301
|
+
// Stat-before-read closes that window (a write after this stat bumps the
|
|
302
|
+
// mtime past the cached key, forcing a re-read next time).
|
|
303
|
+
let mtimeMsForCache = null;
|
|
304
|
+
try {
|
|
305
|
+
mtimeMsForCache = statSync(configFile).mtimeMs;
|
|
306
|
+
}
|
|
307
|
+
catch { /* best-effort */ }
|
|
230
308
|
const content = readFileSync(configFile, 'utf-8');
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
309
|
+
// An empty file is treated as "no config yet", not a parse failure.
|
|
310
|
+
if (content.trim().length === 0) {
|
|
311
|
+
return { data: {}, parseFailed: false };
|
|
312
|
+
}
|
|
313
|
+
let data;
|
|
314
|
+
try {
|
|
315
|
+
data = JSON.parse(content);
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
// File exists but is corrupt/torn. Do NOT return {} as writable —
|
|
319
|
+
// signal parseFailed so persisters skip the write and we never clobber
|
|
320
|
+
// a momentarily-unreadable config.
|
|
321
|
+
return { data: {}, parseFailed: true };
|
|
322
|
+
}
|
|
323
|
+
// Verify HMAC integrity (embedded `_sig`, else legacy `.config-sig`).
|
|
324
|
+
if (!verifyConfigIntegrity(data, content)) {
|
|
234
325
|
configTampered = true;
|
|
235
326
|
console.error('[ShieldCortex] WARNING: Config file integrity check failed — possible tampering detected. Falling back to strict mode.');
|
|
236
327
|
// Force strict mode on tampered config
|
|
237
328
|
data.defenceMode = 'strict';
|
|
238
329
|
}
|
|
239
|
-
|
|
330
|
+
// `_sig` is an integrity artefact, never config data — strip it so it
|
|
331
|
+
// can't leak into CloudConfig or get re-serialised as a normal field.
|
|
332
|
+
delete data._sig;
|
|
333
|
+
// Populate the mtime cache from the parsed (sig-stripped) object, keyed on
|
|
334
|
+
// the mtime captured BEFORE the read (matches the bytes actually parsed).
|
|
335
|
+
if (mtimeMsForCache !== null) {
|
|
336
|
+
cachedRawConfig = { ...data };
|
|
337
|
+
cachedRawConfigFile = configFile;
|
|
338
|
+
cachedRawConfigMtimeMs = mtimeMsForCache;
|
|
339
|
+
}
|
|
340
|
+
return { data, parseFailed: false };
|
|
240
341
|
}
|
|
241
342
|
}
|
|
242
|
-
catch { /* ignore */ }
|
|
243
|
-
return {};
|
|
343
|
+
catch { /* ignore — treat as absent */ }
|
|
344
|
+
return { data: {}, parseFailed: false };
|
|
244
345
|
}
|
|
346
|
+
export function readRawConfig() {
|
|
347
|
+
return readRawConfigState().data;
|
|
348
|
+
}
|
|
349
|
+
function invalidateRawConfigCache() {
|
|
350
|
+
cachedRawConfig = null;
|
|
351
|
+
cachedRawConfigFile = null;
|
|
352
|
+
cachedRawConfigMtimeMs = null;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Atomically persist the raw config with the HMAC embedded as a top-level
|
|
356
|
+
* `_sig` field, computed over `canonicalBodyForSig` (the object WITHOUT
|
|
357
|
+
* `_sig`). Because the signature lives inside the same file and the file is
|
|
358
|
+
* swapped in via `renameSync` (atomic on POSIX), a concurrent reader can never
|
|
359
|
+
* observe a half-written file or a config/signature pair that are out of step
|
|
360
|
+
* — the two torn-read cases the audit found.
|
|
361
|
+
*/
|
|
245
362
|
function writeRawConfig(raw) {
|
|
246
363
|
const configDir = getConfigDir();
|
|
247
364
|
const configFile = getConfigFile();
|
|
248
365
|
mkdirSync(configDir, { recursive: true });
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
366
|
+
// Never carry an inbound `_sig` through; we always recompute it.
|
|
367
|
+
const { _sig: _ignored, ...rest } = raw;
|
|
368
|
+
const body = canonicalBodyForSig(rest);
|
|
369
|
+
const sig = signConfig(body);
|
|
370
|
+
const out = JSON.stringify({ ...rest, _sig: sig }, null, 2) + '\n';
|
|
371
|
+
// Write to a unique temp file in the same dir, then atomically rename over
|
|
372
|
+
// the target. The original config survives a failed/partial write — we never
|
|
373
|
+
// truncate it. Same-dir tmp guarantees rename is a same-filesystem move.
|
|
374
|
+
const tmpFile = `${configFile}.tmp-${process.pid}-${randomBytes(6).toString('hex')}`;
|
|
375
|
+
try {
|
|
376
|
+
writeFileSync(tmpFile, out, { mode: 0o600 });
|
|
377
|
+
// On Windows, renameSync over an EXISTING target can throw EPERM if another
|
|
378
|
+
// process has the destination open concurrently. This is non-corrupting:
|
|
379
|
+
// the original config is left intact and the catch below removes the tmp
|
|
380
|
+
// file and rethrows, so a write may FAIL rather than damage the config.
|
|
381
|
+
renameSync(tmpFile, configFile);
|
|
382
|
+
}
|
|
383
|
+
catch (err) {
|
|
384
|
+
try {
|
|
385
|
+
if (existsSync(tmpFile))
|
|
386
|
+
rmSync(tmpFile, { force: true });
|
|
387
|
+
}
|
|
388
|
+
catch { /* best-effort */ }
|
|
389
|
+
throw err;
|
|
390
|
+
}
|
|
391
|
+
// The embedded `_sig` is now the source of truth. Delete the stale legacy
|
|
392
|
+
// `.config-sig` only after a successful embedded write, only if it exists —
|
|
393
|
+
// this completes the one-way upgrade from the legacy format.
|
|
394
|
+
try {
|
|
395
|
+
const sigFile = getSigFile();
|
|
396
|
+
if (existsSync(sigFile))
|
|
397
|
+
rmSync(sigFile, { force: true });
|
|
398
|
+
}
|
|
399
|
+
catch { /* best-effort */ }
|
|
253
400
|
cachedConfig = null;
|
|
254
401
|
cachedConfigFile = null;
|
|
402
|
+
invalidateRawConfigCache();
|
|
255
403
|
// Clear tamper flag on legitimate write
|
|
256
404
|
configTampered = false;
|
|
257
405
|
}
|
|
406
|
+
/**
|
|
407
|
+
* The ONLY sanctioned read-modify-write path for config.json.
|
|
408
|
+
*
|
|
409
|
+
* EVERY mutating accessor MUST go through this helper. Do NOT call
|
|
410
|
+
* `writeRawConfig` with a `raw` object obtained from a bare `readRawConfig()`:
|
|
411
|
+
* `readRawConfig()` collapses a parse failure to `{}`, discarding the
|
|
412
|
+
* `parseFailed` flag, so mutating that `{}` and writing it back atomically
|
|
413
|
+
* persists a near-empty, validly-signed config — wiping `cloudApiKey` and every
|
|
414
|
+
* other setting the corrupt file still holds, with no error signal. This helper
|
|
415
|
+
* reads through `readRawConfigState()` (which preserves `parseFailed`) and
|
|
416
|
+
* refuses to write when the on-disk config is unreadable.
|
|
417
|
+
*
|
|
418
|
+
* `onParseFail`:
|
|
419
|
+
* - `'throw'` (default): surface corruption to user-facing/explicit setters so
|
|
420
|
+
* the caller sees the problem rather than silently losing credentials.
|
|
421
|
+
* - `'skip'`: silently no-op for automatic / hot-path writes that must NEVER
|
|
422
|
+
* throw (background sync, read-with-migration). Returns false so the caller
|
|
423
|
+
* can tell the write was skipped.
|
|
424
|
+
*
|
|
425
|
+
* Returns true if the mutation was applied and persisted, false if it was
|
|
426
|
+
* skipped because the config was unparseable (`onParseFail: 'skip'`).
|
|
427
|
+
*/
|
|
428
|
+
function mutateRawConfig(fn, onParseFail = 'throw') {
|
|
429
|
+
const { data, parseFailed } = readRawConfigState();
|
|
430
|
+
if (parseFailed) {
|
|
431
|
+
console.error('[ShieldCortex] config.json is unreadable — refusing to overwrite (would wipe settings incl. cloudApiKey). Fix or remove the file.');
|
|
432
|
+
if (onParseFail === 'throw') {
|
|
433
|
+
throw new Error('ShieldCortex config.json is corrupt/unparseable; refusing to overwrite to avoid losing settings. ' +
|
|
434
|
+
'Fix or remove ~/.shieldcortex/config.json and retry.');
|
|
435
|
+
}
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
fn(data);
|
|
439
|
+
writeRawConfig(data);
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
258
442
|
export function getTrustedSkills() {
|
|
259
443
|
const raw = readRawConfig();
|
|
260
444
|
return Array.isArray(raw.trustedSkills) ? raw.trustedSkills : [];
|
|
261
445
|
}
|
|
262
446
|
export function addTrustedSkill(path) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
447
|
+
// Throw-policy explicit setter: on a corrupt config mutateRawConfig throws
|
|
448
|
+
// (surfacing the corruption) rather than wiping it. The closure is itself a
|
|
449
|
+
// no-op write when the skill is already present, which is acceptable churn on
|
|
450
|
+
// a VALID config and avoids a second bare read just to skip a write — the one
|
|
451
|
+
// case we must not silently swallow is the corrupt one, which the helper
|
|
452
|
+
// handles by throwing.
|
|
453
|
+
mutateRawConfig((raw) => {
|
|
454
|
+
const list = Array.isArray(raw.trustedSkills) ? raw.trustedSkills : [];
|
|
455
|
+
if (!list.includes(path))
|
|
456
|
+
list.push(path);
|
|
267
457
|
raw.trustedSkills = list;
|
|
268
|
-
|
|
269
|
-
}
|
|
458
|
+
});
|
|
270
459
|
}
|
|
271
460
|
export function removeTrustedSkill(path) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
461
|
+
mutateRawConfig((raw) => {
|
|
462
|
+
const list = Array.isArray(raw.trustedSkills) ? raw.trustedSkills : [];
|
|
463
|
+
const idx = list.indexOf(path);
|
|
464
|
+
if (idx !== -1)
|
|
465
|
+
list.splice(idx, 1);
|
|
277
466
|
raw.trustedSkills = list;
|
|
278
|
-
|
|
279
|
-
}
|
|
467
|
+
});
|
|
280
468
|
}
|
|
281
469
|
// ── Cloud Iron Dome Cache ─────────────────────────────
|
|
282
470
|
/**
|
|
283
471
|
* Persist cloud Iron Dome data (patterns + policy) to config.json with HMAC integrity.
|
|
284
472
|
*/
|
|
285
473
|
export function setCloudIronDomeCache(data) {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
474
|
+
mutateRawConfig((raw) => {
|
|
475
|
+
raw.cloudIronDome = data;
|
|
476
|
+
});
|
|
289
477
|
}
|
|
290
478
|
/**
|
|
291
479
|
* Read cached cloud Iron Dome data from config.json.
|
|
@@ -298,13 +486,66 @@ export function getCloudIronDomeCache() {
|
|
|
298
486
|
return null;
|
|
299
487
|
}
|
|
300
488
|
// ── Sync Timestamp ────────────────────────────────────
|
|
489
|
+
// Debounce state for lastSyncAt. The sync queue, graph-sync and memory-sync all
|
|
490
|
+
// call updateLastSyncAt() after EVERY successful upload — a busy agent fired a
|
|
491
|
+
// full read-modify-write of config.json per record. We keep the latest value in
|
|
492
|
+
// memory and only persist at most once per LAST_SYNC_PERSIST_INTERVAL_MS, which
|
|
493
|
+
// collapses a burst to a single write while readers still see a fresh value.
|
|
494
|
+
const LAST_SYNC_PERSIST_INTERVAL_MS = 60_000;
|
|
495
|
+
let pendingLastSyncAt = null;
|
|
496
|
+
let lastSyncPersistedAtMs = 0;
|
|
301
497
|
/**
|
|
302
|
-
*
|
|
498
|
+
* Record a successful cloud sync. Always updates the in-memory timestamp;
|
|
499
|
+
* persists to config.json at most once per 60s (time-debounced). Callers
|
|
500
|
+
* (sync.ts / sync-queue.ts / memory-sync.ts) are unchanged — the debounce is
|
|
501
|
+
* internal.
|
|
303
502
|
*/
|
|
304
503
|
export function updateLastSyncAt() {
|
|
504
|
+
const now = Date.now();
|
|
505
|
+
pendingLastSyncAt = new Date(now).toISOString();
|
|
506
|
+
if (now - lastSyncPersistedAtMs < LAST_SYNC_PERSIST_INTERVAL_MS) {
|
|
507
|
+
return; // within the debounce window — memory updated, disk left alone
|
|
508
|
+
}
|
|
509
|
+
persistLastSyncAt(now);
|
|
510
|
+
}
|
|
511
|
+
function persistLastSyncAt(nowMs) {
|
|
512
|
+
if (pendingLastSyncAt === null)
|
|
513
|
+
return;
|
|
514
|
+
// Automatic / hot path (fires after cloud uploads): MUST NOT throw. The
|
|
515
|
+
// 'skip' policy makes mutateRawConfig a silent no-op on an unreadable config
|
|
516
|
+
// (returns false) instead of clobbering it. Only stamp the debounce clock
|
|
517
|
+
// when the write actually landed, so a skipped write is retried next call.
|
|
518
|
+
const written = mutateRawConfig((raw) => {
|
|
519
|
+
raw.lastSyncAt = pendingLastSyncAt;
|
|
520
|
+
}, 'skip');
|
|
521
|
+
if (written)
|
|
522
|
+
lastSyncPersistedAtMs = nowMs;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Force-persist the latest in-memory lastSyncAt regardless of the debounce
|
|
526
|
+
* window, so the final sync timestamp isn't lost if the process exits inside
|
|
527
|
+
* the debounce window. Safe no-op if nothing is pending.
|
|
528
|
+
*
|
|
529
|
+
* Available for a shutdown path (not currently wired): the existing
|
|
530
|
+
* SIGINT/SIGTERM/exit handlers (src/index.ts, src/api/visualization-server.ts)
|
|
531
|
+
* are MCP-server / dashboard lifecycle concerns and don't obviously own the
|
|
532
|
+
* cloud-sync clock, so wiring is left to whichever shutdown owner adopts it
|
|
533
|
+
* rather than bolted onto an unrelated handler. Losing at most one debounced
|
|
534
|
+
* timestamp on abrupt exit is non-critical (the next sync re-stamps it).
|
|
535
|
+
*/
|
|
536
|
+
export function flushLastSyncAt() {
|
|
537
|
+
persistLastSyncAt(Date.now());
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Returns the most recent sync timestamp, preferring the in-memory value (which
|
|
541
|
+
* may be ahead of disk inside the debounce window) and falling back to the
|
|
542
|
+
* persisted config value.
|
|
543
|
+
*/
|
|
544
|
+
export function getLastSyncAt() {
|
|
545
|
+
if (pendingLastSyncAt !== null)
|
|
546
|
+
return pendingLastSyncAt;
|
|
305
547
|
const raw = readRawConfig();
|
|
306
|
-
raw.lastSyncAt
|
|
307
|
-
writeRawConfig(raw);
|
|
548
|
+
return typeof raw.lastSyncAt === 'string' ? raw.lastSyncAt : null;
|
|
308
549
|
}
|
|
309
550
|
const VALID_MODES = ['strict', 'balanced', 'permissive'];
|
|
310
551
|
/**
|
|
@@ -325,9 +566,9 @@ export function setDefenceMode(mode) {
|
|
|
325
566
|
if (!VALID_MODES.includes(mode)) {
|
|
326
567
|
throw new Error(`Invalid defence mode: ${mode}. Must be one of: ${VALID_MODES.join(', ')}`);
|
|
327
568
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
569
|
+
mutateRawConfig((raw) => {
|
|
570
|
+
raw.defenceMode = mode;
|
|
571
|
+
});
|
|
331
572
|
}
|
|
332
573
|
const DEFAULT_VERIFY_CONFIG = {
|
|
333
574
|
verifyEnabled: false,
|
|
@@ -352,16 +593,16 @@ export function getVerifyConfig() {
|
|
|
352
593
|
* Persists LLM verification config to ~/.shieldcortex/config.json.
|
|
353
594
|
*/
|
|
354
595
|
export function setVerifyConfig(updates) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
596
|
+
mutateRawConfig((raw) => {
|
|
597
|
+
if (updates.verifyEnabled !== undefined)
|
|
598
|
+
raw.verifyEnabled = updates.verifyEnabled;
|
|
599
|
+
if (updates.verifyMode !== undefined)
|
|
600
|
+
raw.verifyMode = updates.verifyMode;
|
|
601
|
+
if (updates.verifyTriggers !== undefined)
|
|
602
|
+
raw.verifyTriggers = updates.verifyTriggers;
|
|
603
|
+
if (updates.verifyTimeoutMs !== undefined)
|
|
604
|
+
raw.verifyTimeoutMs = updates.verifyTimeoutMs;
|
|
605
|
+
});
|
|
365
606
|
}
|
|
366
607
|
// ── Review Copilot Config ───────────────────────────────
|
|
367
608
|
function readReviewCopilotRaw() {
|
|
@@ -400,15 +641,15 @@ export function getReviewCopilotConfig() {
|
|
|
400
641
|
* Persists local Review Copilot config.
|
|
401
642
|
*/
|
|
402
643
|
export function setReviewCopilotConfig(updates) {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
644
|
+
mutateRawConfig((raw) => {
|
|
645
|
+
const existing = raw.reviewCopilot && typeof raw.reviewCopilot === 'object'
|
|
646
|
+
? raw.reviewCopilot
|
|
647
|
+
: {};
|
|
648
|
+
raw.reviewCopilot = {
|
|
649
|
+
...existing,
|
|
650
|
+
...updates,
|
|
651
|
+
};
|
|
652
|
+
});
|
|
412
653
|
}
|
|
413
654
|
// ── Ranker Config ─────────────────────────────────────
|
|
414
655
|
const DEFAULT_RANKER_CONFIG = {
|
|
@@ -469,18 +710,18 @@ export function getRankerConfig() {
|
|
|
469
710
|
* fields supplied in `updates` are written; other fields are preserved.
|
|
470
711
|
*/
|
|
471
712
|
export function setRankerConfig(updates) {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
713
|
+
mutateRawConfig((raw) => {
|
|
714
|
+
const existing = raw.ranker && typeof raw.ranker === 'object'
|
|
715
|
+
? raw.ranker
|
|
716
|
+
: {};
|
|
717
|
+
if (updates.engine !== undefined)
|
|
718
|
+
existing.engine = updates.engine;
|
|
719
|
+
if (updates.rrfK !== undefined)
|
|
720
|
+
existing.rrfK = updates.rrfK;
|
|
721
|
+
if (updates.weights !== undefined)
|
|
722
|
+
existing.weights = updates.weights;
|
|
723
|
+
raw.ranker = existing;
|
|
724
|
+
});
|
|
484
725
|
}
|
|
485
726
|
const DEFAULT_OPENCLAW_MEMORY_CONFIG = {
|
|
486
727
|
autoMemory: true,
|
|
@@ -513,18 +754,18 @@ export function getOpenClawMemoryConfig() {
|
|
|
513
754
|
* Persists OpenClaw memory integration config.
|
|
514
755
|
*/
|
|
515
756
|
export function setOpenClawMemoryConfig(updates) {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
757
|
+
mutateRawConfig((raw) => {
|
|
758
|
+
if (updates.autoMemory !== undefined)
|
|
759
|
+
raw.openclawAutoMemory = updates.autoMemory;
|
|
760
|
+
if (updates.dedupe !== undefined)
|
|
761
|
+
raw.openclawAutoMemoryDedupe = updates.dedupe;
|
|
762
|
+
if (updates.noveltyThreshold !== undefined) {
|
|
763
|
+
raw.openclawAutoMemoryNoveltyThreshold = clamp(updates.noveltyThreshold, 0.6, 0.99);
|
|
764
|
+
}
|
|
765
|
+
if (updates.maxRecent !== undefined) {
|
|
766
|
+
raw.openclawAutoMemoryMaxRecent = Math.floor(clamp(updates.maxRecent, 50, 1000));
|
|
767
|
+
}
|
|
768
|
+
});
|
|
528
769
|
}
|
|
529
770
|
/**
|
|
530
771
|
* Returns whether OpenClaw auto-memory extraction is enabled.
|
|
@@ -554,9 +795,9 @@ export function isProactiveRecallEnabled() {
|
|
|
554
795
|
* Persists proactive recall preference to ~/.shieldcortex/config.json.
|
|
555
796
|
*/
|
|
556
797
|
export function setProactiveRecall(enabled) {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
798
|
+
mutateRawConfig((raw) => {
|
|
799
|
+
raw.proactiveRecall = enabled;
|
|
800
|
+
});
|
|
560
801
|
}
|
|
561
802
|
/**
|
|
562
803
|
* Restores the v4.10.x defaults for users who preferred the old behaviour.
|
|
@@ -564,32 +805,32 @@ export function setProactiveRecall(enabled) {
|
|
|
564
805
|
* so the flip is a one-command undo.
|
|
565
806
|
*/
|
|
566
807
|
export function restore410Defaults() {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
808
|
+
mutateRawConfig((raw) => {
|
|
809
|
+
raw.proactiveRecall = true;
|
|
810
|
+
const existingInterceptor = (raw.interceptor && typeof raw.interceptor === 'object')
|
|
811
|
+
? raw.interceptor
|
|
812
|
+
: {};
|
|
813
|
+
const existingSeverity = (existingInterceptor.severityActions && typeof existingInterceptor.severityActions === 'object')
|
|
814
|
+
? existingInterceptor.severityActions
|
|
815
|
+
: {};
|
|
816
|
+
raw.interceptor = {
|
|
817
|
+
...existingInterceptor,
|
|
818
|
+
severityActions: {
|
|
819
|
+
...existingSeverity,
|
|
820
|
+
low: 'log',
|
|
821
|
+
medium: 'warn',
|
|
822
|
+
high: 'require_approval',
|
|
823
|
+
critical: 'require_approval',
|
|
824
|
+
},
|
|
825
|
+
};
|
|
826
|
+
const existingSessionStart = (raw.sessionStart && typeof raw.sessionStart === 'object')
|
|
827
|
+
? raw.sessionStart
|
|
828
|
+
: {};
|
|
829
|
+
raw.sessionStart = {
|
|
830
|
+
...existingSessionStart,
|
|
831
|
+
preamble: 'minimal',
|
|
832
|
+
};
|
|
833
|
+
});
|
|
593
834
|
}
|
|
594
835
|
/**
|
|
595
836
|
* Returns the resolved on/off state of the opt-in auto-memory hooks.
|
|
@@ -618,16 +859,16 @@ export function getAutoMemoryEnableConfig() {
|
|
|
618
859
|
* runtime gate cannot disagree.
|
|
619
860
|
*/
|
|
620
861
|
export function setAutoMemoryEnableConfig(updates) {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
862
|
+
mutateRawConfig((raw) => {
|
|
863
|
+
const existing = raw.autoMemory && typeof raw.autoMemory === 'object'
|
|
864
|
+
? raw.autoMemory
|
|
865
|
+
: {};
|
|
866
|
+
if (updates.enableStop !== undefined)
|
|
867
|
+
existing.enableStop = updates.enableStop;
|
|
868
|
+
if (updates.enableSessionEnd !== undefined)
|
|
869
|
+
existing.enableSessionEnd = updates.enableSessionEnd;
|
|
870
|
+
raw.autoMemory = existing;
|
|
871
|
+
});
|
|
631
872
|
}
|
|
632
873
|
const DEFAULT_TOOL_RESPONSE_SCAN_CONFIG = {
|
|
633
874
|
scanToolResponses: true,
|
|
@@ -647,12 +888,12 @@ export function getToolResponseScanConfig() {
|
|
|
647
888
|
* Persists tool response scanning config.
|
|
648
889
|
*/
|
|
649
890
|
export function setToolResponseScanConfig(updates) {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
891
|
+
mutateRawConfig((raw) => {
|
|
892
|
+
if (updates.scanToolResponses !== undefined)
|
|
893
|
+
raw.scanToolResponses = updates.scanToolResponses;
|
|
894
|
+
if (updates.toolResponseMode !== undefined)
|
|
895
|
+
raw.toolResponseMode = updates.toolResponseMode;
|
|
896
|
+
});
|
|
656
897
|
}
|
|
657
898
|
// ── Device Identity ────────────────────────────────────
|
|
658
899
|
/**
|
|
@@ -660,13 +901,26 @@ export function setToolResponseScanConfig(updates) {
|
|
|
660
901
|
* Generates and persists on first call; reads from config thereafter.
|
|
661
902
|
*/
|
|
662
903
|
export function getDeviceId() {
|
|
663
|
-
const raw =
|
|
904
|
+
const { data: raw, parseFailed } = readRawConfigState();
|
|
664
905
|
if (typeof raw.deviceId === 'string' && raw.deviceId) {
|
|
665
906
|
return raw.deviceId;
|
|
666
907
|
}
|
|
667
908
|
const id = randomUUID();
|
|
668
|
-
|
|
669
|
-
|
|
909
|
+
if (parseFailed) {
|
|
910
|
+
// The config file exists but is unreadable. Persisting now would write
|
|
911
|
+
// `{ deviceId }` over the corrupt bytes and destroy cloudApiKey and every
|
|
912
|
+
// other setting the file still holds. Return an ephemeral id this run and
|
|
913
|
+
// leave the file untouched — identity persists once the file is readable.
|
|
914
|
+
// (mutateRawConfig('skip') would no-op here too, but we want this specific
|
|
915
|
+
// diagnostic, so we short-circuit before calling it.)
|
|
916
|
+
console.error('[ShieldCortex] config.json unreadable — using an ephemeral device id this run; identity was NOT persisted.');
|
|
917
|
+
return id;
|
|
918
|
+
}
|
|
919
|
+
// Persist through the guarded helper so no bare writeRawConfig exists outside
|
|
920
|
+
// mutateRawConfig; skip-policy keeps this read-then-persist path non-throwing.
|
|
921
|
+
mutateRawConfig((m) => {
|
|
922
|
+
m.deviceId = id;
|
|
923
|
+
}, 'skip');
|
|
670
924
|
return id;
|
|
671
925
|
}
|
|
672
926
|
/**
|
|
@@ -674,12 +928,18 @@ export function getDeviceId() {
|
|
|
674
928
|
* Stores in config on first call; reads from config thereafter.
|
|
675
929
|
*/
|
|
676
930
|
export function getDeviceName() {
|
|
677
|
-
const raw =
|
|
931
|
+
const { data: raw, parseFailed } = readRawConfigState();
|
|
678
932
|
if (typeof raw.deviceName === 'string' && raw.deviceName) {
|
|
679
933
|
return raw.deviceName;
|
|
680
934
|
}
|
|
681
935
|
const name = hostname();
|
|
682
|
-
|
|
683
|
-
|
|
936
|
+
if (parseFailed) {
|
|
937
|
+
// See getDeviceId — never overwrite an unreadable config.
|
|
938
|
+
console.error('[ShieldCortex] config.json unreadable — using the live hostname this run; device name was NOT persisted.');
|
|
939
|
+
return name;
|
|
940
|
+
}
|
|
941
|
+
mutateRawConfig((m) => {
|
|
942
|
+
m.deviceName = name;
|
|
943
|
+
}, 'skip');
|
|
684
944
|
return name;
|
|
685
945
|
}
|