failproofai 1.0.4-beta.8 → 1.0.5-beta.0
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +5 -5
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -4
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +4 -37
- package/.next/standalone/.next/server/app/audit/page.js +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +4 -4
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +4 -4
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +5 -5
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +3 -3
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -15
- package/.next/standalone/.next/server/app/settings/page.js +4 -4
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__0hmcwgg._.js → [externals]__0-9ti7z._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__14b7cc6._.js → [externals]__0u3wrvq._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__08dchfu._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +5 -0
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_1_eakl4._.js → _1-akg6w._.js} +2 -2
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +10 -10
- package/.next/standalone/.next/server/chunks/_1vslkoe._.js +1 -1
- package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__187jks6._.js → [root-of-the-server]__00-s7h8._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__04px77e._.js → [root-of-the-server]__013jr2b._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0i0l1di._.js → [root-of-the-server]__01wy8d-._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0r3c1_0._.js → [root-of-the-server]__02npjtd._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1ckpmkk._.js → [root-of-the-server]__0da85px._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__17xnxlx._.js → [root-of-the-server]__0ftmoxc._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0kokc8z._.js → [root-of-the-server]__0n_lxhg._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0-4ja6q._.js → [root-of-the-server]__0p-5p8u._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0egr5s1._.js → [root-of-the-server]__0s740oi._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1fa1jd9._.js → [root-of-the-server]__0x1aj1_._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1kpd1za._.js → [root-of-the-server]__190won8._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__00cr3xu._.js → [root-of-the-server]__1p2otjt._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__00y9ww2._.js → [root-of-the-server]__1sc84bb._.js} +2 -21
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0s-77v5._.js → _0h_douw._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_00bqkhj._.js → _0u3xxgc._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0zs_4uu._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1_ozgif._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1ddpc5g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +14 -51
- package/.next/standalone/.next/server/chunks/ssr/{_14c6q09._.js → _1if9ixi._.js} +2 -21
- package/.next/standalone/.next/server/chunks/ssr/{_07t3jol._.js → _1u8-lu2._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +69 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/lib_openclaw-projects_ts_0qxq3bp._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +21 -65
- package/.next/standalone/.next/static/chunks/0hw6-c206lsma.js +1 -0
- package/.next/standalone/.next/static/chunks/{2mizt96igig1y.js → 0m9sx3khjrl1q.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1302duobnz07l.js → 0oos8qpc1ugos.js} +1 -1
- package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +1 -0
- package/.next/standalone/.next/static/chunks/{2ukucf6g-kbs5.js → 0unrx9g_x0u7j.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0jpd8dv2vk930.css → 29-iqnp9v_axt.css} +1 -1
- package/.next/standalone/.next/static/chunks/{3uyhmm01g64k4.css → 2k9f4tyv04809.css} +0 -1
- package/.next/standalone/.next/static/chunks/{0vm4wdx3w-2_w.js → 2qjn-leir0x_s.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1chm9piluggc-.js → 2xbxkaszi1xnw.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2n85g-crgcx5j.js → 33umhy-x204qj.js} +2 -2
- package/.next/standalone/.next/static/chunks/3nux7kkrc3eps.js +69 -0
- package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +1 -0
- package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +1 -0
- package/.next/standalone/.next/static/chunks/{3a4gth64hwtwf.js → 40tpgs4fjct5p.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1_x7pqemw5n_w.js → 40uih96wnkowy.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-3s3c-u_u_l0u9.js → turbopack-3p73obe0xmrxm.js} +1 -1
- package/.next/standalone/.opencode/plugins/failproofai.mjs +27 -51
- package/.next/standalone/app/actions/get-scheduled-audit.ts +0 -5
- package/.next/standalone/app/actions/update-scheduled-audit.ts +57 -18
- package/.next/standalone/app/api/auth/status/route.ts +2 -3
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +34 -140
- package/.next/standalone/app/audit/_components/audit-poster.tsx +10 -29
- package/.next/standalone/app/audit/_components/come-back-better-section.tsx +1 -6
- package/.next/standalone/app/audit/_components/empty-state.tsx +1 -4
- package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +5 -19
- package/.next/standalone/app/audit/_components/share-templates.ts +44 -110
- package/.next/standalone/app/audit/audit-styles.css +1 -132
- package/.next/standalone/app/settings/page.tsx +1 -1
- package/.next/standalone/app/settings/settings-client.tsx +45 -50
- package/.next/standalone/lib/auth/api-server-client.ts +5 -33
- package/.next/standalone/lib/claude-sessions.ts +27 -101
- package/.next/standalone/lib/codex-sessions.ts +4 -20
- package/.next/standalone/lib/copilot-sessions.ts +1 -32
- package/.next/standalone/lib/cursor-sessions.ts +5 -62
- package/.next/standalone/lib/download-session.ts +13 -58
- package/.next/standalone/lib/openclaw-projects.ts +18 -54
- package/.next/standalone/lib/openclaw-sessions.ts +17 -75
- package/.next/standalone/lib/sqlite-reader.ts +1 -78
- package/.next/standalone/package.json +9 -9
- package/.next/standalone/sdk/python/CHANGELOG.md +32 -9
- package/.next/standalone/sdk/python/failproofai_sdk/_events.py +20 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_redact.py +233 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_writer.py +4 -1
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/llama_index.py +5 -5
- package/.next/standalone/sdk/python/tests/integrations/test_llama_index.py +2 -2
- package/.next/standalone/sdk/python/tests/test_redaction.py +140 -0
- package/.next/standalone/sdk/python/tests/test_sdk.py +87 -0
- package/.next/standalone/sdk/python/tests/test_server_contract.py +60 -0
- package/.next/standalone/sdk/python/tests/test_site_docs.py +2 -2
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +1 -1
- package/dist/cli.mjs +664 -2121
- package/dist/worker.mjs +105 -422
- package/lib/auth/api-server-client.ts +5 -33
- package/lib/claude-sessions.ts +27 -101
- package/lib/codex-sessions.ts +4 -20
- package/lib/copilot-sessions.ts +1 -32
- package/lib/cursor-sessions.ts +5 -62
- package/lib/download-session.ts +13 -58
- package/lib/openclaw-projects.ts +18 -54
- package/lib/openclaw-sessions.ts +17 -75
- package/lib/sqlite-reader.ts +1 -78
- package/package.json +9 -9
- package/pi-extension/index.ts +5 -50
- package/src/audit/cache.ts +0 -6
- package/src/audit/cli-adapters/claude.ts +0 -13
- package/src/audit/cli-login.ts +1 -4
- package/src/audit/cli.ts +7 -157
- package/src/audit/harm-report.ts +4 -111
- package/src/audit/index.ts +15 -286
- package/src/audit/machine-store.ts +9 -8
- package/src/audit/redact-example.ts +15 -324
- package/src/audit/report-harm.ts +37 -33
- package/src/audit/report.ts +3 -6
- package/src/audit/schedule-cli.ts +33 -54
- package/src/audit/scoring.ts +0 -49
- package/src/audit/types.ts +0 -52
- package/src/hooks/builtin-policies.ts +2 -67
- package/src/hooks/configure-wizard.ts +0 -55
- package/src/hooks/fp-config.ts +48 -83
- package/src/hooks/fp-home.ts +0 -27
- package/src/hooks/integrations.ts +14 -51
- package/src/hooks/uninstall-cli.ts +2 -17
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0faqz9x._.js +0 -22
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zyc_nm._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__112b4go._.js +0 -24
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1l71g3y._.js +0 -22
- package/.next/standalone/.next/server/chunks/lib_openclaw-projects_ts_0gc-p24._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n2txhk._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kaotix._.js +0 -22
- package/.next/standalone/.next/server/chunks/ssr/_04ng_6x._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0l2hi_d._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_rerun-button_tsx_0blihgw._.js +0 -3
- package/.next/standalone/.next/static/chunks/0igq884ziuv-8.js +0 -1
- package/.next/standalone/.next/static/chunks/349jkb79v5g6z.js +0 -1
- package/.next/standalone/app/actions/get-leaks.ts +0 -104
- package/.next/standalone/app/audit/_components/leak-section.tsx +0 -192
- package/.next/standalone/lib/openclaw-db.ts +0 -271
- package/lib/openclaw-db.ts +0 -271
- package/src/audit/desktop-notify.ts +0 -420
- package/src/audit/leak-fingerprint.ts +0 -198
- package/src/audit/leak-record.ts +0 -248
- package/src/audit/leak-scan.ts +0 -268
- package/src/audit/leak-store.ts +0 -220
- package/src/audit/macos-notifier.ts +0 -298
- package/src/hooks/notice.ts +0 -24
- /package/.next/standalone/.next/static/{DNhaAH8iPGH0tIuxee5_2 → 4_QeL7mSPYsuiSVJmBOTj}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{DNhaAH8iPGH0tIuxee5_2 → 4_QeL7mSPYsuiSVJmBOTj}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{DNhaAH8iPGH0tIuxee5_2 → 4_QeL7mSPYsuiSVJmBOTj}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,{otherChunks:["static/chunks/1c6aptmytnu6q.js","static/chunks/2yuzx8716maf0.js","static/chunks/
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,{otherChunks:["static/chunks/1c6aptmytnu6q.js","static/chunks/2yuzx8716maf0.js","static/chunks/3i8rg2fv9nd6e.js","static/chunks/2zafgs90r_leq.js"],runtimeModuleIds:[94553]}]),(()=>{let e;if(!Array.isArray(globalThis.TURBOPACK))return;var t,r=function(){if(null!=self.TURBOPACK_ASSET_SUFFIX)return self.TURBOPACK_ASSET_SUFFIX;let e=document?.currentScript?.getAttribute?.("src")??"",t=e.indexOf("?");return t>=0?e.slice(t):""}(),n=((t=n||{})[t.Runtime=0]="Runtime",t[t.Parent=1]="Parent",t[t.Update=2]="Update",t);let o=new WeakMap;function l(e,t){this.m=e,this.e=t}let i=l.prototype,u=Object.prototype.hasOwnProperty,s="u">typeof Symbol&&Symbol.toStringTag;function c(e,t,r){u.call(e,t)||Object.defineProperty(e,t,r)}function a(e,t){let r=e[t];return r||(r=f(t),e[t]=r),r}function f(e){return{exports:{},error:void 0,id:e,namespaceObject:void 0}}function p(e,t,r){c(e,"__esModule",{value:!0}),s&&c(e,s,{value:"Module"});let n=0;for(;n<t.length;){let r=t[n++],o=t[n++];if("number"==typeof o)if(0===o)c(e,r,{value:t[n++],enumerable:!0,writable:!1});else throw Error(`unexpected tag: ${o}`);else"function"==typeof t[n]?c(e,r,{get:o,set:t[n++],enumerable:!0}):c(e,r,{get:o,enumerable:!0})}r||Object.seal(e)}function h(e,t){(null!=t?a(this.c,t):this.m).exports=e}i.s=function(e,t,r){let n,o;null!=t?o=(n=a(this.c,t)).exports:(n=this.m,o=this.e),n.namespaceObject=o,p(o,e,r)},i.j=function(e,t){let r,n;null!=t?n=(r=a(this.c,t)).exports:(r=this.m,n=this.e);let l=function(e,t){let r=o.get(e);if(!r){o.set(e,r=[]);let n=e=>{if("default"!==e){for(let t of r)if(u.call(t,e))return t}};e.exports=e.namespaceObject=new Proxy(t,{get(e,t){if(u.call(e,t)||"default"===t||"__esModule"===t)return Reflect.get(e,t);let r=n(t);return r&&Reflect.get(r,t)},set:()=>!1,defineProperty:()=>!1,deleteProperty:()=>!1,has:(e,t)=>!!Reflect.has(e,t)||"default"!==t&&"__esModule"!==t&&void 0!==n(t),ownKeys(e){let t=Reflect.ownKeys(e);for(let e of r)for(let r of Reflect.ownKeys(e))"default"===r||t.includes(r)||t.push(r);return t},getOwnPropertyDescriptor(e,t){let r=Reflect.getOwnPropertyDescriptor(e,t);if(r||"default"===t||"__esModule"===t)return r;let o=n(t);if(o)return{enumerable:!0,configurable:!0,get:()=>Reflect.get(o,t)}}})}return r}(r,n);"object"==typeof e&&null!==e&&l.push(e)},i.v=h,i.n=function(e,t){let r;(r=null!=t?a(this.c,t):this.m).exports=r.namespaceObject=e};let d=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,m=[null,d({}),d([]),d(d)];function y(e,t,r){let n=[],o=-1;for(let t=e;("object"==typeof t||"function"==typeof t)&&!m.includes(t);t=d(t))for(let r of Object.getOwnPropertyNames(t))n.push(r,function(e,t){return()=>e[t]}(e,r)),-1===o&&"default"===r&&(o=n.length-1);return r&&o>=0||(o>=0?n.splice(o,1,0,e):n.push("default",0,e)),p(t,n),t}function g(e){let t=K(e,this.m);if(t.namespaceObject)return t.namespaceObject;let r=t.exports;return t.namespaceObject=y(r,"function"==typeof r?function(...e){return r.apply(this,e)}:Object.create(null),r&&r.__esModule)}function b(e){let t=e.indexOf("#");-1!==t&&(e=e.substring(0,t));let r=e.indexOf("?");return -1!==r&&(e=e.substring(0,r)),e}i.i=g,i.A=function(e){return this.r(e)(g.bind(this))},i.t="function"==typeof require?require:function(){throw Error("Unexpected use of runtime require")},i.r=function(e){return K(e,this.m).exports},i.f=function(e){function t(t){if(t=b(t),u.call(e,t))return e[t].module();let r=Error(`Cannot find module '${t}'`);throw r.code="MODULE_NOT_FOUND",r}return t.keys=()=>Object.keys(e),t.resolve=t=>{if(t=b(t),u.call(e,t))return e[t].id();let r=Error(`Cannot find module '${t}'`);throw r.code="MODULE_NOT_FOUND",r},t.import=async e=>await t(e),t};let O=function(e){let t=new URL(e,"x:/"),r={};for(let e in t)r[e]=t[e];for(let t in r.href=e,r.pathname=e.replace(/[?#].*/,""),r.origin=r.protocol="",r.toString=r.toJSON=(...t)=>e,r)Object.defineProperty(this,t,{enumerable:!0,configurable:!0,value:r[t]})};function w(e,t){throw Error(`Invariant: ${t(e)}`)}O.prototype=URL.prototype,i.U=O,i.z=function(e){throw Error("dynamic usage of require is not supported")},i.g=globalThis;let R=l.prototype,k="string"==typeof TURBOPACK_CHUNK_BASE_PATH?TURBOPACK_CHUNK_BASE_PATH:"/_next/",U=new Map;i.M=U;let _=new Map,v=new Map,P=new Map;async function C(e,t,r){let n;if("string"==typeof r)return function(e,t,r){return A(e,t,r)}(e,t,E(r));let o=r.included||[],l=o.map(e=>!!U.has(e)||_.get(e));if(l.length>0&&l.every(e=>e))return void await Promise.all(l);for(let l of(n=A(e,t,E(r.path)),o))_.has(l)||_.set(l,n);await n}R.l=function(e){return C(n.Parent,this.m.id,e)};let j=Promise.resolve(void 0),$=new WeakMap;function A(t,r,o){let l=e.loadChunkCached(t,o),i=$.get(l);if(void 0===i){let e=$.set.bind($,l,j);i=l.then(e).catch(e=>{let l;switch(t){case n.Runtime:l=`as a runtime dependency of chunk ${r}`;break;case n.Parent:l=`from module ${r}`;break;case n.Update:l="from an HMR update";break;default:w(t,e=>`Unknown source type: ${e}`)}let i=Error(`Failed to load chunk ${o} ${l}${e?`: ${e}`:""}`,e?{cause:e}:void 0);throw i.name="ChunkLoadError",i}),$.set(l,i)}return i}R.L=function(e){var t,r;return t=n.Parent,r=this.m.id,A(t,r,e)};R.R=function(e){let t=this.r(e);return t?.default??t},R.P=function(e){return`/ROOT/${e??""}`},R.F=function(e){return e?`file:///ROOT/${e.split("/").map(encodeURIComponent).join("/")}`:"file:///ROOT/"},R.q=function(e,t){h.call(this,`${e}${r}`,t)};let T=/[^A-Za-z0-9\-_.!~*'()/]/;function E(e,t=k){let n=T.test(e)?e.split("/").map(encodeURIComponent).join("/"):e;return`${t}${n}${r}`}function S(e,t){let r,n=e.indexOf("?");if(-1!==n)r=n;else{let t=e.indexOf("#");r=-1!==t?t:e.length}return r>=t.length&&e.startsWith(t,r-t.length)}R.b=k,R.X=r,R.h=E;function M(e){return S(e,".css")}let x={};i.c=x;let K=(e,t)=>{let r=x[e];if(r){if(r.error)throw r.error;return r}return N(e,n.Parent,t.id)};function N(e,t,r){let n=U.get(e);if("function"!=typeof n)throw Error(function(e,t,r){let n;switch(t){case 0:n=`as a runtime entry of chunk ${r}`;break;case 1:n=`because it was required from module ${r}`;break;case 2:n="because of an HMR update";break;default:w(t,e=>`Unknown source type: ${e}`)}return`Module ${e} was instantiated ${n}, but the module factory is not available.`}(e,t,r));let o=f(e),i=o.exports;x[e]=o;let u=new l(o,i);try{n(u,o,i)}catch(e){throw o.error=e,e}return o.namespaceObject&&o.exports!==o.namespaceObject&&y(o.exports,o.namespaceObject),o}function B(t){let r;if(!Array.isArray(t))return e.registerChunk(void 0,t);let n=function(e){if("string"==typeof e)return e;if(e)return{src:e.getAttribute("src")};if("u">typeof TURBOPACK_NEXT_CHUNK_URLS)return{src:TURBOPACK_NEXT_CHUNK_URLS.pop()};throw Error("chunk path empty but not in a worker")}(t[0]);return 2===t.length?r=t[1]:(r=void 0,!function(e,t){let r=1;for(;r<e.length;){let n,o=r+1;for(;o<e.length&&"function"!=typeof e[o];)o++;if(o===e.length)throw Error("malformed chunk format, expected a factory function");let l=e[o];for(let l=r;l<o;l++){let r=e[l],o=t.get(r);if(o){n=o;break}}let i=n??l,u=!1;for(let n=r;n<o;n++){let r=e[n];t.has(r)||(u||(i===l&&Object.defineProperty(l,"name",{value:"module evaluation"}),u=!0),t.set(r,i))}r=o+1}}(t,U)),e.registerChunk(n,r)}let L=new Map;function q(e){let t=L.get(e);if(!t){let r,n;t={resolved:!1,loadingStarted:!1,retryAttempts:0,promise:new Promise((e,t)=>{r=e,n=t}),resolve:()=>{t.resolved=!0,r()},reject:n},L.set(e,t)}return t}function I(e,t,r,n,o){!(null==n||n instanceof DOMException&&"NetworkError"===n.name)||r.retryAttempts>=1||L.get(t)!==r?(L.get(t)===r&&L.delete(t),r.reject(n)):(r.retryAttempts++,setTimeout(()=>{r.resolved||L.get(t)!==r||(o?o():(r.loadingStarted=!1,H(e,t)))},200+Math.floor(401*Math.random())))}function H(e,t){let r=q(t);if(r.loadingStarted)return r.promise;if(e===n.Runtime)return r.loadingStarted=!0,M(t)&&r.resolve(),r.promise;if("function"==typeof importScripts)if(M(t));else if(S(t,".js")){self.TURBOPACK_NEXT_CHUNK_URLS.push(t);try{importScripts(t)}catch(n){I(e,t,r,n)}}else throw Error(`can't infer type of chunk from URL ${t} in worker`);else{let n=decodeURI(t);if(M(t))if(document.querySelectorAll(`link[rel=stylesheet][href="${t}"],link[rel=stylesheet][href^="${t}?"],link[rel=stylesheet][href="${n}"],link[rel=stylesheet][href^="${n}?"]`).length>0)r.resolve();else{let n=()=>{let o=document.createElement("link");return o.rel="stylesheet",o.crossOrigin=null,o.href=t,o.onerror=()=>{let l=document.createComment("");o.replaceWith(l),I(e,t,r,void 0,()=>l.replaceWith(n()))},o.onload=()=>{r.resolve()},o};document.head.appendChild(n())}else if(S(t,".js")){let o=document.querySelectorAll(`script[src="${t}"],script[src^="${t}?"],script[src="${n}"],script[src^="${n}?"]`);if(o.length>0)for(let n of Array.from(o))n.addEventListener("error",()=>{n.remove(),I(e,t,r)},{once:!0});else{let n=document.createElement("script");n.crossOrigin=null,n.src=t,n.onerror=()=>{n.remove(),I(e,t,r)},document.head.appendChild(n)}}else throw Error(`can't infer type of chunk from URL ${t}`)}return r.loadingStarted=!0,r.promise}e={async registerChunk(e,t){let r;if(null!=e&&(r=function(e){if("string"==typeof e)return e;let t=decodeURIComponent(e.src.replace(/[?#].*$/,""));return t.startsWith(k)?t.slice(k.length):t}(e),q("string"==typeof e?E(e):e.src).resolve()),null!=t){for(let e of t.otherChunks)q(E("string"==typeof e?e:e.path));if(await Promise.all(t.otherChunks.map(e=>{var t;return t=r,C(n.Runtime,t,e)})),t.runtimeModuleIds.length>0)for(let e of t.runtimeModuleIds)!function(e,t){let r=x[t];if(r){if(r.error)throw r.error;return}N(t,n.Runtime,e)}(r,e)}},loadChunkCached:(e,t)=>H(e,t)};var F=globalThis.TURBOPACK;globalThis.TURBOPACK={push:B},F.forEach(B)})();
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// • src/hooks/integrations.ts (buildOpenCodePluginShim production template)
|
|
19
19
|
// When #337 landed, this dev shim drifted and `block-read-outside-cwd`
|
|
20
20
|
// silently no-op'd on every opencode `read` call inside this repo.
|
|
21
|
-
import {
|
|
21
|
+
import { spawnSync } from "node:child_process";
|
|
22
22
|
import { resolveDevSpawn } from "../../scripts/dev-hook.mjs";
|
|
23
23
|
|
|
24
24
|
const BUS_EVENT_MAP = {
|
|
@@ -70,58 +70,34 @@ function canonicalizeToolInput(canonicalToolName, args) {
|
|
|
70
70
|
return out;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
* `applyDecision`, so the deny still lands before the tool runs. What changes
|
|
80
|
-
* is that the wait is now a promise rather than a blocked thread, and the TUI
|
|
81
|
-
* keeps painting while it happens.
|
|
82
|
-
*
|
|
83
|
-
* Fail-open on spawn error, and on the timeout — a policy that never ran must
|
|
84
|
-
* not read as a deny.
|
|
85
|
-
*/
|
|
86
|
-
async function runFailproofai(eventName, payload, directory) {
|
|
87
|
-
const resolved = resolveDevSpawn();
|
|
88
|
-
if (!resolved) {
|
|
73
|
+
function runFailproofai(eventName, payload, directory) {
|
|
74
|
+
// Shared with the JSON configs' launcher: locates bun across PATH, ~/.bun/bin,
|
|
75
|
+
// $BUN_INSTALL, Homebrew and every nvm version dir. A bare spawnSync("bun")
|
|
76
|
+
// dies with ENOENT the moment bun is off the hook's PATH.
|
|
77
|
+
const spawn = resolveDevSpawn();
|
|
78
|
+
if (!spawn) {
|
|
89
79
|
process.stderr.write(
|
|
90
80
|
"[failproofai-dev] bun not found — opencode policies are NOT enforcing. " +
|
|
91
81
|
"Install bun: curl -fsSL https://bun.sh/install | bash\n",
|
|
92
82
|
);
|
|
93
83
|
return { exitCode: 0, stdout: "", stderr: "" };
|
|
94
84
|
}
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
child = spawn(cmd, args, { cwd: directory });
|
|
101
|
-
} catch {
|
|
102
|
-
resolveRun({ exitCode: 0, stdout: "", stderr: "" });
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
let stdout = "";
|
|
106
|
-
let stderr = "";
|
|
107
|
-
let settled = false;
|
|
108
|
-
const finish = (exitCode) => {
|
|
109
|
-
if (settled) return;
|
|
110
|
-
settled = true;
|
|
111
|
-
clearTimeout(timer);
|
|
112
|
-
resolveRun({ exitCode, stdout, stderr });
|
|
113
|
-
};
|
|
114
|
-
const timer = setTimeout(() => {
|
|
115
|
-
try { child.kill("SIGKILL"); } catch { /* already gone */ }
|
|
116
|
-
finish(0);
|
|
117
|
-
}, 60_000);
|
|
118
|
-
child.stdout.on("data", (d) => { stdout += d; });
|
|
119
|
-
child.stderr.on("data", (d) => { stderr += d; });
|
|
120
|
-
child.on("error", () => finish(0));
|
|
121
|
-
child.on("close", (code) => finish(code ?? 0));
|
|
122
|
-
child.stdin.on("error", () => {});
|
|
123
|
-
child.stdin.end(JSON.stringify(payload));
|
|
85
|
+
const r = spawnSync(spawn.cmd, [...spawn.args, "--hook", eventName, "--cli", "opencode"], {
|
|
86
|
+
input: JSON.stringify(payload),
|
|
87
|
+
encoding: "utf8",
|
|
88
|
+
timeout: 60_000,
|
|
89
|
+
cwd: directory,
|
|
124
90
|
});
|
|
91
|
+
// `r.status` is null when the spawn never ran (ENOENT) or the child was
|
|
92
|
+
// killed. Coercing that to 0 reports "allowed" for a policy that never got
|
|
93
|
+
// to run, so say so on stderr rather than failing open in silence.
|
|
94
|
+
if (r.status === null) {
|
|
95
|
+
process.stderr.write(
|
|
96
|
+
`[failproofai-dev] ${eventName} hook did not run (${r.error?.code ?? r.signal ?? "unknown"}) — not enforcing\n`,
|
|
97
|
+
);
|
|
98
|
+
return { exitCode: 0, stdout: "", stderr: "" };
|
|
99
|
+
}
|
|
100
|
+
return { exitCode: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
125
101
|
}
|
|
126
102
|
|
|
127
103
|
async function applyDecision(result, ctx, eventName) {
|
|
@@ -174,7 +150,7 @@ export default async function failproofaiPlugin({ client, directory }) {
|
|
|
174
150
|
}
|
|
175
151
|
}
|
|
176
152
|
if (!prompt) prompt = (info.text || info.content || props.text || "").toString();
|
|
177
|
-
const r =
|
|
153
|
+
const r = runFailproofai("UserPromptSubmit", {
|
|
178
154
|
session_id: sessionID, cwd: directory, hook_event_name: "UserPromptSubmit", prompt,
|
|
179
155
|
}, directory);
|
|
180
156
|
await applyDecision(r, { client, sessionID }, "UserPromptSubmit");
|
|
@@ -184,7 +160,7 @@ export default async function failproofaiPlugin({ client, directory }) {
|
|
|
184
160
|
if (!claudeEvent) return;
|
|
185
161
|
const props = event.properties || {};
|
|
186
162
|
const sessionID = props.sessionID || (props.session && props.session.id) || props.id;
|
|
187
|
-
const r =
|
|
163
|
+
const r = runFailproofai(claudeEvent, {
|
|
188
164
|
session_id: sessionID, cwd: directory, hook_event_name: claudeEvent,
|
|
189
165
|
}, directory);
|
|
190
166
|
await applyDecision(r, { client, sessionID }, claudeEvent);
|
|
@@ -192,7 +168,7 @@ export default async function failproofaiPlugin({ client, directory }) {
|
|
|
192
168
|
|
|
193
169
|
"tool.execute.before": async (input, output) => {
|
|
194
170
|
const canonicalTool = canonicalizeTool(input.tool);
|
|
195
|
-
const r =
|
|
171
|
+
const r = runFailproofai("PreToolUse", {
|
|
196
172
|
session_id: input.sessionID,
|
|
197
173
|
cwd: directory,
|
|
198
174
|
tool_name: canonicalTool,
|
|
@@ -204,7 +180,7 @@ export default async function failproofaiPlugin({ client, directory }) {
|
|
|
204
180
|
|
|
205
181
|
"tool.execute.after": async (input, output) => {
|
|
206
182
|
const canonicalTool = canonicalizeTool(input.tool);
|
|
207
|
-
const r =
|
|
183
|
+
const r = runFailproofai("PostToolUse", {
|
|
208
184
|
session_id: input.sessionID,
|
|
209
185
|
cwd: directory,
|
|
210
186
|
tool_name: canonicalTool,
|
|
@@ -217,7 +193,7 @@ export default async function failproofaiPlugin({ client, directory }) {
|
|
|
217
193
|
|
|
218
194
|
"permission.ask": async (input, output) => {
|
|
219
195
|
const canonicalTool = canonicalizeTool(input.tool);
|
|
220
|
-
const r =
|
|
196
|
+
const r = runFailproofai("PermissionRequest", {
|
|
221
197
|
session_id: input.sessionID,
|
|
222
198
|
cwd: directory,
|
|
223
199
|
tool_name: canonicalTool || input.command || "permission",
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
* - `auto` ⟷ `config.json [audit] auto` (readConfig / updateConfig —
|
|
12
12
|
* the same call `failproofai audit --schedule` makes, so
|
|
13
13
|
* the two cannot diverge)
|
|
14
|
-
* - `notify` ⟷ `config.json [audit] notify` (the same value changed by
|
|
15
|
-
* `failproofai audit --notify` / `--no-notify`)
|
|
16
14
|
* - `intervalDays` ⟷ `config.json [audit] interval_days` (readConfig owns the
|
|
17
15
|
* 1..90 clamp — see fp-config.readIntervalDays)
|
|
18
16
|
* - `daemon` ⟷ `systemctl status failproofaid@<user>` (daemonServiceStatus)
|
|
@@ -60,8 +58,6 @@ export interface ScheduledAuditLastScan {
|
|
|
60
58
|
export interface ScheduledAuditView {
|
|
61
59
|
/** `[audit] auto` — whether the daemon scans on a timer. */
|
|
62
60
|
auto: boolean;
|
|
63
|
-
/** `[audit] notify` — whether scheduled findings raise an OS notification. */
|
|
64
|
-
notify: boolean;
|
|
65
61
|
/** `[audit] interval_days`, already clamped to 1..90 by readConfig. */
|
|
66
62
|
intervalDays: number;
|
|
67
63
|
/**
|
|
@@ -104,7 +100,6 @@ export async function getScheduledAuditAction(): Promise<ScheduledAuditView> {
|
|
|
104
100
|
|
|
105
101
|
return {
|
|
106
102
|
auto: config.audit.auto,
|
|
107
|
-
notify: config.audit.notify,
|
|
108
103
|
intervalDays: config.audit.intervalDays,
|
|
109
104
|
signedInAs: auth ? { id: auth.user.id, email: auth.user.email } : null,
|
|
110
105
|
daemon,
|
|
@@ -8,10 +8,9 @@
|
|
|
8
8
|
*
|
|
9
9
|
* ## CLI ⟷ dashboard parity
|
|
10
10
|
* - `setAutoAuditAction(enabled)` ⟷ `[audit] auto`
|
|
11
|
-
* - `setAuditNotifyAction(enabled)` ⟷ `[audit] notify`
|
|
12
11
|
* - `setAuditIntervalAction(days)` ⟷ `[audit] interval_days`
|
|
13
12
|
*
|
|
14
|
-
*
|
|
13
|
+
* Both go through the same `updateConfig` the CLI uses — `failproofai audit
|
|
15
14
|
* --schedule` / `--no-schedule` call it too — so a value set on either side is
|
|
16
15
|
* byte-identical. That is the whole mechanism behind "the two
|
|
17
16
|
* surfaces are always in sync": there is one file, one writer function, and no
|
|
@@ -19,6 +18,7 @@
|
|
|
19
18
|
*/
|
|
20
19
|
|
|
21
20
|
import { readConfig, updateConfig } from "@/src/hooks/fp-config";
|
|
21
|
+
import { readAuth, whoAmI } from "@/lib/auth/auth-store";
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* The outcome of trying to turn scheduling on.
|
|
@@ -34,34 +34,73 @@ import { readConfig, updateConfig } from "@/src/hooks/fp-config";
|
|
|
34
34
|
* A returned discriminant survives the boundary, so the caller can open the
|
|
35
35
|
* sign-in dialog for the one failure that has an obvious next step.
|
|
36
36
|
*/
|
|
37
|
-
export type SetAutoAuditResult =
|
|
37
|
+
export type SetAutoAuditResult =
|
|
38
|
+
| { ok: true; auto: boolean }
|
|
39
|
+
| { ok: false; reason: "signed-out" }
|
|
40
|
+
/**
|
|
41
|
+
* The session is intact locally and the api-server could not be reached.
|
|
42
|
+
*
|
|
43
|
+
* Separated from `signed-out` because `whoAmI()` collapses them: it returns
|
|
44
|
+
* null for a 401 AND for every transport failure, so an offline machine, a
|
|
45
|
+
* proxy that blocks the host, and a genuinely expired token were one answer.
|
|
46
|
+
* The user was told "that sign-in expired" and handed a code prompt that
|
|
47
|
+
* cannot succeed either — the failure it names is not the failure they have,
|
|
48
|
+
* and following its advice costs them a real, working session.
|
|
49
|
+
*/
|
|
50
|
+
| { ok: false; reason: "unreachable" };
|
|
38
51
|
|
|
39
52
|
/**
|
|
40
53
|
* Turn the scheduled scan on or off.
|
|
41
54
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
55
|
+
* Turning it ON is refused without a session. Scheduling and mailing are ONE
|
|
56
|
+
* decision — the reason to put a scan on a timer is to be told what it found —
|
|
57
|
+
* so a machine with the timer set and nobody to tell is a switch that reads as
|
|
58
|
+
* on and produces nothing, discoverable only by noticing that no digest ever
|
|
59
|
+
* arrives. The caller signs the user in first and retries.
|
|
60
|
+
*
|
|
61
|
+
* `whoAmI()` asks the SERVER, so this refuses in a case the page cannot see: a
|
|
62
|
+
* session file that exists locally but whose refresh token the api-server has
|
|
63
|
+
* rejected. The local file is what the page reads to show "reports go to …", so
|
|
64
|
+
* the two disagree exactly when a session has expired or was minted against a
|
|
65
|
+
* different server — and that disagreement is the common case, not an edge one.
|
|
66
|
+
*
|
|
67
|
+
* Turning it OFF never checks. An expired session must not be able to trap
|
|
68
|
+
* somebody into keeping a feature they are trying to disable.
|
|
69
|
+
*
|
|
70
|
+
* Note this gates SETTING UP the timer, not the machine's ongoing work: a
|
|
71
|
+
* session that later expires leaves the timer running and the local scan
|
|
72
|
+
* working, and only the digest stops. See `report-harm.ts`.
|
|
44
73
|
*
|
|
45
74
|
* Returns the value actually stored (re-read), so an optimistic UI can confirm
|
|
46
75
|
* against the source of truth rather than assume its own guess landed.
|
|
47
76
|
*/
|
|
48
77
|
export async function setAutoAuditAction(enabled: boolean): Promise<SetAutoAuditResult> {
|
|
49
|
-
|
|
78
|
+
if (enabled) {
|
|
79
|
+
const who = await whoAmI();
|
|
80
|
+
if (!who) {
|
|
81
|
+
// `whoAmI()` deletes the session on a 401 and leaves it alone on a
|
|
82
|
+
// transport failure, so what is left ON DISK is the one thing that tells
|
|
83
|
+
// the two apart — no extra request, and nothing new that can fail.
|
|
84
|
+
const local = readAuth();
|
|
85
|
+
const stillWithinWindow = local && local.refresh_expires_at * 1000 > Date.now();
|
|
86
|
+
return { ok: false, reason: stillWithinWindow ? "unreachable" : "signed-out" };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Enabling stamps consent in the same write, because the `whoAmI()` above is
|
|
90
|
+
// exactly what makes this a consent record rather than a guess: a person was
|
|
91
|
+
// present, signed in, and looking at the panel that enumerates what gets
|
|
92
|
+
// sent. `reportHarm` gates sending on the stamp, not on `auto`, so a machine
|
|
93
|
+
// that inherited `auto` from a release where it meant "scan locally on a
|
|
94
|
+
// timer" mails nothing until somebody passes through here or the CLI.
|
|
95
|
+
//
|
|
96
|
+
// Disabling leaves the stamp alone. It is a record of something that did
|
|
97
|
+
// happen, and it grants nothing on its own — sending needs `auto` too.
|
|
98
|
+
const next = updateConfig({
|
|
99
|
+
audit: enabled ? { auto: true, reportsConsentedAt: Date.now() } : { auto: false },
|
|
100
|
+
});
|
|
50
101
|
return { ok: true, auto: next.audit.auto };
|
|
51
102
|
}
|
|
52
103
|
|
|
53
|
-
/**
|
|
54
|
-
* Turn OS notifications for scheduled credential findings on or off.
|
|
55
|
-
*
|
|
56
|
-
* This does not change the timer or email delivery. It writes the same setting
|
|
57
|
-
* as `failproofai audit --notify` / `--no-notify` and returns the value re-read
|
|
58
|
-
* from the shared config so the dashboard never maintains a second truth.
|
|
59
|
-
*/
|
|
60
|
-
export async function setAuditNotifyAction(enabled: boolean): Promise<{ notify: boolean }> {
|
|
61
|
-
const next = updateConfig({ audit: { notify: enabled } });
|
|
62
|
-
return { notify: next.audit.notify };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
104
|
/**
|
|
66
105
|
* Set the days between scheduled scans.
|
|
67
106
|
*
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
* This keeps the dashboard UI and the CLI consistent regardless of whether
|
|
8
8
|
* the api-server is reachable.
|
|
9
9
|
*
|
|
10
|
-
* Reminders are gone: the machine now audits itself on a timer and
|
|
11
|
-
*
|
|
12
|
-
* about. The
|
|
10
|
+
* Reminders are gone: the machine now audits itself on a timer and mails a
|
|
11
|
+
* digest when it finds harm, so there is nothing to nudge anyone about. The
|
|
13
12
|
* scheduled-scan state lives in `getScheduledAuditAction`, which reads it from
|
|
14
13
|
* the config and the daemon rather than from here.
|
|
15
14
|
*/
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Top-level client wrapper for /audit.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Composes the calm personality report: classify the agent into one of
|
|
7
|
+
* 8 archetypes, derive a score, and render the 5-section flow:
|
|
8
8
|
*
|
|
9
9
|
* 01 AuditPoster — single-screen shareable poster
|
|
10
10
|
* 02 StrengthsSection — what it's great at
|
|
@@ -12,36 +12,23 @@
|
|
|
12
12
|
* 04 HowToImproveSection — install / configure
|
|
13
13
|
* 05 ComeBackBetterSection — spread the audit (invite)
|
|
14
14
|
*
|
|
15
|
-
* Every one of those components, and the score / persona / strengths / findings
|
|
16
|
-
* modules behind them, is intact and still unit-tested — only the renders and
|
|
17
|
-
* the imports here are commented out, so restoring any of it is deleting a pair
|
|
18
|
-
* of comment markers. See the header of `src/audit/scoring.ts` for why.
|
|
19
|
-
*
|
|
20
|
-
* What this page renders now is `AuditReportPlaceholder`: the scan's own
|
|
21
|
-
* numbers. The scan, the 12-CLI transcript reader and the emailed digest all
|
|
22
|
-
* still run.
|
|
23
|
-
*
|
|
24
15
|
* Empty / running states fall back to EmptyState and RunProgress.
|
|
25
16
|
*/
|
|
26
17
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
27
18
|
import { getAuditResultAction } from "@/app/actions/get-audit-result";
|
|
28
19
|
import type { AuditResult, RunAuditOptions } from "@/src/audit/types";
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// import { deriveFindings } from "@/src/audit/findings";
|
|
20
|
+
import { classifyAgent } from "@/src/audit/archetypes";
|
|
21
|
+
import { deriveScore, gradeFor, projectedScore } from "@/src/audit/scoring";
|
|
22
|
+
import { deriveStrengths } from "@/src/audit/strengths";
|
|
23
|
+
import { deriveFindings } from "@/src/audit/findings";
|
|
34
24
|
import { usePostHog } from "@/contexts/PostHogContext";
|
|
35
25
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// import { HowToImproveSection } from "./how-to-improve-section";
|
|
42
|
-
// import { ComeBackBetterSection } from "./come-back-better-section";
|
|
26
|
+
import { AuditPoster } from "./audit-poster";
|
|
27
|
+
import { StrengthsSection } from "./strengths-section";
|
|
28
|
+
import { QuirksSection } from "./quirks-section";
|
|
29
|
+
import { HowToImproveSection } from "./how-to-improve-section";
|
|
30
|
+
import { ComeBackBetterSection } from "./come-back-better-section";
|
|
43
31
|
import { ReportFooter } from "./report-footer";
|
|
44
|
-
import { LeakSection } from "./leak-section";
|
|
45
32
|
import { EmptyState } from "./empty-state";
|
|
46
33
|
import { RunProgress } from "./run-progress";
|
|
47
34
|
import { AuditProgressStrip, type RerunStatus } from "./audit-progress-strip";
|
|
@@ -59,9 +46,7 @@ type Initial =
|
|
|
59
46
|
/** Tag passed to the shared `startRerun()` handler so PostHog can tell
|
|
60
47
|
* whether the click came from the bottom return-section button or the
|
|
61
48
|
* empty-state CTA. */
|
|
62
|
-
|
|
63
|
-
// page has now that the old report's sections are switched off.
|
|
64
|
-
export type RerunSource = "return_section" | "empty_state" | "scan_header";
|
|
49
|
+
export type RerunSource = "return_section" | "empty_state";
|
|
65
50
|
|
|
66
51
|
interface Props {
|
|
67
52
|
initial: Initial;
|
|
@@ -74,9 +59,6 @@ interface Props {
|
|
|
74
59
|
totalCatalogSize: number;
|
|
75
60
|
}
|
|
76
61
|
|
|
77
|
-
/* Switched off with the persona layer — this named the project that seeded the
|
|
78
|
-
archetype classifier and the leaderboard row. Restore alongside
|
|
79
|
-
`classifyAgent` in this file.
|
|
80
62
|
function inferProjectName(result: AuditResult, override?: string): string {
|
|
81
63
|
if (override && override.trim()) return override;
|
|
82
64
|
// Pick the cwd that appears in the most examples — proxy for "your
|
|
@@ -99,7 +81,6 @@ function inferProjectName(result: AuditResult, override?: string): string {
|
|
|
99
81
|
if (segs.length >= 2) return `${segs[segs.length - 2]} / ${segs[segs.length - 1]}`;
|
|
100
82
|
return segs[segs.length - 1] ?? "your agent";
|
|
101
83
|
}
|
|
102
|
-
*/
|
|
103
84
|
|
|
104
85
|
export function AuditDashboard({ initial, projectFromUrl, totalCatalogSize }: Props) {
|
|
105
86
|
const [cache, setCache] = useState<Initial>(initial);
|
|
@@ -298,22 +279,16 @@ function MainReport({
|
|
|
298
279
|
onDismissRerun,
|
|
299
280
|
}: MainReportProps) {
|
|
300
281
|
const { capture } = usePostHog();
|
|
301
|
-
|
|
302
|
-
// const project = useMemo(() => inferProjectName(result, projectFromUrl), [result, projectFromUrl]);
|
|
282
|
+
const project = useMemo(() => inferProjectName(result, projectFromUrl), [result, projectFromUrl]);
|
|
303
283
|
// Seed classification with the project name so the behaviour fingerprint
|
|
304
284
|
// (used for tie-breaks + copy variants) is stable per project.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
// const projected = useMemo(() => projectedScore(result, score), [result, score]);
|
|
313
|
-
// const grade = gradeFor(score);
|
|
314
|
-
// const projectedGrade = gradeFor(projected);
|
|
315
|
-
// const strengths = useMemo(() => deriveStrengths(result), [result]);
|
|
316
|
-
// const findings = useMemo(() => deriveFindings(result), [result]);
|
|
285
|
+
const classification = useMemo(() => classifyAgent(result, project), [result, project]);
|
|
286
|
+
const score = useMemo(() => deriveScore(result), [result]);
|
|
287
|
+
const projected = useMemo(() => projectedScore(result, score), [result, score]);
|
|
288
|
+
const grade = gradeFor(score);
|
|
289
|
+
const projectedGrade = gradeFor(projected);
|
|
290
|
+
const strengths = useMemo(() => deriveStrengths(result), [result]);
|
|
291
|
+
const findings = useMemo(() => deriveFindings(result), [result]);
|
|
317
292
|
|
|
318
293
|
// One pass over result.results: detectors triggered + missing prescribed
|
|
319
294
|
// policies. Both feed PostHog instrumentation; `missing` also feeds the
|
|
@@ -335,13 +310,10 @@ function MainReport({
|
|
|
335
310
|
if (dashboardViewedRef.current) return;
|
|
336
311
|
dashboardViewedRef.current = true;
|
|
337
312
|
capture("audit_dashboard_viewed", {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
// grade,
|
|
343
|
-
// archetype: classification.archetype,
|
|
344
|
-
// secondary: classification.secondary ?? null,
|
|
313
|
+
score,
|
|
314
|
+
grade,
|
|
315
|
+
archetype: classification.archetype,
|
|
316
|
+
secondary: classification.secondary ?? null,
|
|
345
317
|
missing,
|
|
346
318
|
transcripts_scanned: result.transcripts.scanned,
|
|
347
319
|
results_count: result.results.length,
|
|
@@ -349,6 +321,10 @@ function MainReport({
|
|
|
349
321
|
});
|
|
350
322
|
}, [
|
|
351
323
|
capture,
|
|
324
|
+
score,
|
|
325
|
+
grade,
|
|
326
|
+
classification.archetype,
|
|
327
|
+
classification.secondary,
|
|
352
328
|
missing,
|
|
353
329
|
result.transcripts.scanned,
|
|
354
330
|
result.results.length,
|
|
@@ -356,43 +332,19 @@ function MainReport({
|
|
|
356
332
|
]);
|
|
357
333
|
|
|
358
334
|
/** Poster ref — captured to PNG by the poster's share buttons. */
|
|
359
|
-
|
|
335
|
+
const posterRef = useRef<HTMLDivElement>(null);
|
|
360
336
|
|
|
361
337
|
return (
|
|
362
338
|
<div className="app">
|
|
363
339
|
<AuditProgressStrip status={rerunStatus} onDismiss={onDismissRerun} />
|
|
364
340
|
<div className="app-shell">
|
|
365
341
|
<div className="report">
|
|
366
|
-
<AuditReportPlaceholder
|
|
367
|
-
transcripts={result.transcripts.scanned}
|
|
368
|
-
events={result.eventsScanned ?? 0}
|
|
369
|
-
projects={result.projectsScanned?.length ?? 0}
|
|
370
|
-
isRunning={isRunning || rerunStatus.kind === "running"}
|
|
371
|
-
onRerun={() => onRerun("scan_header")}
|
|
372
|
-
/>
|
|
373
|
-
{/* The rebuilt report. It reads the leak record rather than this
|
|
374
|
-
scan's result, deliberately: a credential found last month and
|
|
375
|
-
not touched by today's transcripts is still a credential to
|
|
376
|
-
rotate, and a report that showed only the current scan's findings
|
|
377
|
-
would go quiet on exactly those. */}
|
|
378
|
-
<LeakSection />
|
|
379
|
-
{/* The whole old report is switched off — the persona poster, the
|
|
380
|
-
strengths and quirks sections, the punch-list with its install-all
|
|
381
|
-
funnel, and the invite section. It is being rebuilt bottom-up
|
|
382
|
-
around leak detection; see the header of `src/audit/scoring.ts`
|
|
383
|
-
for the reasoning and `~/Desktop/failproofai-leak-detection-
|
|
384
|
-
verdict-2026-09-07.md` for the measurements behind it.
|
|
385
|
-
|
|
386
|
-
Every component below is intact and still unit-tested — only the
|
|
387
|
-
render is commented — so restoring any one of them is deleting a
|
|
388
|
-
pair of comment markers.
|
|
389
|
-
|
|
390
342
|
<AuditPoster
|
|
391
343
|
ref={posterRef}
|
|
392
344
|
archetypeKey={classification.archetype}
|
|
393
345
|
seed={classification.variantSeed}
|
|
394
|
-
|
|
395
|
-
|
|
346
|
+
score={score}
|
|
347
|
+
grade={grade}
|
|
396
348
|
missing={missing}
|
|
397
349
|
auditedAt={cachedAt ?? new Date().toISOString()}
|
|
398
350
|
/>
|
|
@@ -400,11 +352,10 @@ function MainReport({
|
|
|
400
352
|
<QuirksSection findings={findings} />
|
|
401
353
|
<HowToImproveSection
|
|
402
354
|
result={result}
|
|
403
|
-
|
|
404
|
-
|
|
355
|
+
projected={projected}
|
|
356
|
+
projectedGrade={projectedGrade}
|
|
405
357
|
/>
|
|
406
|
-
<ComeBackBetterSection />
|
|
407
|
-
*/}
|
|
358
|
+
<ComeBackBetterSection score={score} />
|
|
408
359
|
</div>
|
|
409
360
|
<ReportFooter cachedAt={cachedAt} />
|
|
410
361
|
</div>
|
|
@@ -412,63 +363,6 @@ function MainReport({
|
|
|
412
363
|
);
|
|
413
364
|
}
|
|
414
365
|
|
|
415
|
-
/**
|
|
416
|
-
* What `/audit` shows while the report is being rebuilt.
|
|
417
|
-
*
|
|
418
|
-
* Deliberately states the scan's own numbers rather than nothing at all: the
|
|
419
|
-
* scan still runs, still walks every transcript across all 12 CLIs, and still
|
|
420
|
-
* feeds the emailed digest. Saying so is the difference between "this page is
|
|
421
|
-
* under construction" and "this product is broken".
|
|
422
|
-
*/
|
|
423
|
-
function AuditReportPlaceholder(
|
|
424
|
-
{
|
|
425
|
-
transcripts,
|
|
426
|
-
events,
|
|
427
|
-
projects,
|
|
428
|
-
isRunning,
|
|
429
|
-
onRerun,
|
|
430
|
-
}: {
|
|
431
|
-
transcripts: number;
|
|
432
|
-
events: number;
|
|
433
|
-
projects: number;
|
|
434
|
-
isRunning: boolean;
|
|
435
|
-
onRerun: () => void;
|
|
436
|
-
},
|
|
437
|
-
) {
|
|
438
|
-
const n = (x: number) => x.toLocaleString();
|
|
439
|
-
return (
|
|
440
|
-
<section className="audit-sec" data-screen-label="01 Scan">
|
|
441
|
-
<div className="audit-sec-head">
|
|
442
|
-
<span className="audit-sec-eyebrow">
|
|
443
|
-
<span className="ix">01</span>{"// scan"}
|
|
444
|
-
</span>
|
|
445
|
-
{/* The re-scan control. It lived in the old report's sections and went
|
|
446
|
-
dormant when they were commented out, which left the page with no way
|
|
447
|
-
to run an audit again at all — you had to go back to the terminal. */}
|
|
448
|
-
<button
|
|
449
|
-
type="button"
|
|
450
|
-
className="scan-rerun"
|
|
451
|
-
onClick={onRerun}
|
|
452
|
-
disabled={isRunning}
|
|
453
|
-
>
|
|
454
|
-
{isRunning ? "scanning…" : "run again"}
|
|
455
|
-
</button>
|
|
456
|
-
</div>
|
|
457
|
-
<h2 className="audit-sec-title">scan complete</h2>
|
|
458
|
-
<div className="audit-sec-sub">
|
|
459
|
-
{n(events)} tool call{events === 1 ? "" : "s"} across {n(transcripts)}{" "}
|
|
460
|
-
transcript{transcripts === 1 ? "" : "s"}
|
|
461
|
-
{projects > 0 ? ` · ${n(projects)} project${projects === 1 ? "" : "s"}` : ""}
|
|
462
|
-
</div>
|
|
463
|
-
<div className="audit-sec-sub">
|
|
464
|
-
{"// the report is being rebuilt around leak detection. the scan, the"}
|
|
465
|
-
<br />
|
|
466
|
-
{"// 12-CLI transcript reader and the emailed digest are unaffected."}
|
|
467
|
-
</div>
|
|
468
|
-
</section>
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
366
|
interface ShellEmptyProps {
|
|
473
367
|
running: boolean;
|
|
474
368
|
mode?: "no-cache" | "zero-sessions";
|