failproofai 1.0.0-beta.4 → 1.0.0-beta.6
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/app-path-routes-manifest.json +2 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/routes-manifest.json +6 -0
- 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 +3 -1
- 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 +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- 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 +3 -1
- 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 +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
- 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 -3
- 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 +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 +2 -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 +3 -2
- 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 +3 -2
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -2
- 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/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +5 -3
- 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 +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +3 -1
- 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/server-reference-manifest.json +10 -10
- package/.next/standalone/.next/server/app/policies/page.js +4 -2
- 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/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +4 -2
- 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/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 +6 -4
- 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/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +4 -2
- 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/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/settings/page/build-manifest.json +18 -0
- package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +6 -0
- package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +53 -0
- package/.next/standalone/.next/server/app/settings/page.js +18 -0
- package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app-paths-manifest.json +2 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ky_n0r._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0lk_935._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p7s8-d._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0v2fpxe._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-dh5jy._.js → [root-of-the-server]__1_ps0jw._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__1a7i2q4._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1iny24d._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tyedjt._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +5 -0
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +5 -0
- package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +5 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +10 -10
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_a_gwn._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1a2hcb_._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1e7-pa7._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rtdfof._.js → [root-of-the-server]__1rdruw4._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tyf0tc._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rag5dg._.js → [root-of-the-server]__1yw368c._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_04mekdy._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0igtyj5._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0r-dsre._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0se484m._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0uz7o9y._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1fahg9e._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1gmzkwj._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/_1w8sola._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1w_6p9g._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
- 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 +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +5 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware.js +2 -2
- 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 +54 -12
- package/.next/standalone/.next/static/chunks/{2c90h1g6x8z8y.js → 0_2n13i94rnw5.js} +1 -1
- package/.next/standalone/.next/static/chunks/1bm0q12hgs22l.css +2 -0
- package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
- package/.next/standalone/.next/static/chunks/23557z4e6dtsr.js +1 -0
- package/.next/standalone/.next/static/chunks/28zkwzu50puki.js +1 -0
- package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
- package/.next/standalone/.next/static/chunks/3081gykdubv8b.js +1 -0
- package/.next/standalone/.next/static/chunks/{06jakt_xhoolk.js → 34fa65k0cldkj.js} +1 -1
- package/.next/standalone/.next/static/chunks/35v07j9gc1pvr.js +6 -0
- package/.next/standalone/.next/static/chunks/{2zb3m1tzq6h98.js → 3f_939wi3kfnx.js} +22 -22
- package/.next/standalone/.next/static/chunks/{31cqiv_cpgj9m.js → 3p1vpm2_-tfwm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0ue9ak_xexiqh.js → 3rbpw_waieafm.js} +1 -1
- package/.next/standalone/.next/static/chunks/40ffnfpkeyvb0.js +1 -0
- package/.next/standalone/app/actions/get-hooks-config.ts +2 -1
- package/.next/standalone/app/actions/get-scheduled-audit.ts +70 -0
- package/.next/standalone/app/actions/update-scheduled-audit.ts +44 -0
- package/.next/standalone/app/api/audit/run/route.ts +28 -0
- package/.next/standalone/app/api/audit/status/route.ts +13 -1
- package/.next/standalone/app/api/auth/status/route.ts +1 -1
- package/.next/standalone/app/audit/_components/auth-dialog.css +159 -0
- package/.next/standalone/app/audit/_components/auth-dialog.tsx +3 -0
- package/.next/standalone/app/audit/audit-styles.css +6 -149
- package/.next/standalone/app/settings/page.tsx +31 -0
- package/.next/standalone/app/settings/settings-client.tsx +488 -0
- package/.next/standalone/components/navbar.tsx +2 -0
- package/.next/standalone/lib/auth/api-server-client.ts +2 -3
- package/.next/standalone/lib/auth/auth-store.ts +4 -4
- package/.next/standalone/lib/codex-sessions.ts +2 -1
- package/.next/standalone/lib/dashboard-host.ts +101 -0
- package/.next/standalone/lib/install-check.ts +22 -7
- package/.next/standalone/lib/pi-sessions.ts +99 -14
- package/.next/standalone/lib/telemetry-enabled.ts +54 -0
- package/.next/standalone/lib/telemetry-id.ts +52 -1
- package/.next/standalone/lib/telemetry.ts +8 -4
- package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
- package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
- package/.next/standalone/node_modules/react/package.json +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/package.json +4 -4
- package/.next/standalone/package.json +7 -6
- package/.next/standalone/proxy.ts +89 -1
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +197 -80
- package/dist/cli.mjs +17699 -15345
- package/dist/worker.mjs +1620 -257
- package/lib/auth/api-server-client.ts +2 -3
- package/lib/auth/auth-store.ts +4 -4
- package/lib/codex-sessions.ts +2 -1
- package/lib/dashboard-host.ts +101 -0
- package/lib/install-check.ts +22 -7
- package/lib/pi-sessions.ts +99 -14
- package/lib/telemetry-enabled.ts +54 -0
- package/lib/telemetry-id.ts +52 -1
- package/lib/telemetry.ts +8 -4
- package/package.json +7 -6
- package/scripts/install-telemetry.mjs +8 -1
- package/scripts/launch.ts +25 -3
- package/scripts/parse-script-args.ts +17 -1
- package/scripts/prune-standalone.mjs +20 -0
- package/scripts/translate-docs/readme-translator.ts +163 -4
- package/scripts/validate-mdx.ts +153 -13
- package/src/audit/audit-lock.ts +285 -0
- package/src/audit/audit-schedule.ts +111 -0
- package/src/audit/cache.ts +18 -4
- package/src/audit/cli-adapters/goose.ts +2 -1
- package/src/audit/cli-adapters/hermes.ts +28 -10
- package/src/audit/cli.ts +191 -35
- package/src/audit/dashboard-cache.ts +2 -1
- package/src/hooks/builtin-policies.ts +100 -3
- package/src/hooks/cloud-connection.ts +163 -14
- package/src/hooks/cloud-enrollment-cli.ts +119 -21
- package/src/hooks/cloud-enrollment.ts +87 -17
- package/src/hooks/cloud-introspect.ts +147 -0
- package/src/hooks/cloud-managed-policies.ts +33 -2
- package/src/hooks/collector-config.ts +32 -40
- package/src/hooks/configure-wizard.ts +696 -318
- package/src/hooks/custom-hooks-loader.ts +125 -11
- package/src/hooks/daemon-client.ts +83 -17
- package/src/hooks/daemon-download.ts +54 -2
- package/src/hooks/daemon-service.ts +595 -27
- package/src/hooks/first-run-gate.ts +39 -0
- package/src/hooks/fp-config.ts +497 -0
- package/src/hooks/fp-home.ts +342 -0
- package/src/hooks/fp-reset.ts +332 -0
- package/src/hooks/handler.ts +42 -1
- package/src/hooks/hook-activity-store.ts +35 -20
- package/src/hooks/hook-logger.ts +2 -1
- package/src/hooks/hook-telemetry.ts +5 -1
- package/src/hooks/hooks-config.ts +4 -3
- package/src/hooks/loader-utils.ts +166 -5
- package/src/hooks/manager.ts +5 -4
- package/src/hooks/onboarding-lock.ts +119 -0
- package/src/hooks/policy-types.ts +0 -7
- package/src/hooks/session-pause-cli.ts +15 -13
- package/src/hooks/session-pause.ts +36 -4
- package/src/hooks/setup-state.ts +256 -0
- package/src/hooks/tui.ts +4 -4
- package/src/hooks/worker-server.ts +53 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0f8nu8d._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0szqzl8._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cmdyn_._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ls0gsr._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n_uvv0._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0sdrz-j._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1j4u3yd._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1q9xxca.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
- package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
- package/.next/standalone/.next/static/chunks/0oe2-hp0_2tzs.js +0 -6
- package/.next/standalone/.next/static/chunks/1fw3pxobk_otl.js +0 -1
- package/.next/standalone/.next/static/chunks/3h5_3u46gu9jx.js +0 -1
- package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
- package/.next/standalone/.next/static/chunks/3zdwnnthucs_1.css +0 -2
- package/.next/standalone/.next/static/chunks/43a1ywqsg9bq9.js +0 -1
- package/.next/standalone/Cargo.lock +0 -2222
- package/.next/standalone/Cargo.toml +0 -9
- package/.next/standalone/crates/CLOUD_POLICIES.md +0 -136
- package/.next/standalone/crates/PROTOCOL.md +0 -122
- package/.next/standalone/crates/failproofaid/Cargo.toml +0 -25
- package/.next/standalone/crates/failproofaid/src/cloud_client.rs +0 -484
- package/.next/standalone/crates/failproofaid/src/cloud_policies.rs +0 -921
- package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
- package/.next/standalone/crates/failproofaid/src/main.rs +0 -532
- package/.next/standalone/crates/failproofaid/src/paths.rs +0 -201
- package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
- package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
- package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
- package/.next/standalone/crates/fpai-collect/Cargo.toml +0 -43
- package/.next/standalone/crates/fpai-collect/src/config.rs +0 -412
- package/.next/standalone/crates/fpai-collect/src/cursor.rs +0 -520
- package/.next/standalone/crates/fpai-collect/src/delivery.rs +0 -472
- package/.next/standalone/crates/fpai-collect/src/filetail.rs +0 -484
- package/.next/standalone/crates/fpai-collect/src/health.rs +0 -369
- package/.next/standalone/crates/fpai-collect/src/lib.rs +0 -34
- package/.next/standalone/crates/fpai-collect/src/redact.rs +0 -516
- package/.next/standalone/crates/fpai-collect/src/sources/claude/mod.rs +0 -279
- package/.next/standalone/crates/fpai-collect/src/sources/claude/transform.rs +0 -619
- package/.next/standalone/crates/fpai-collect/src/sources/codex/mod.rs +0 -156
- package/.next/standalone/crates/fpai-collect/src/sources/codex/transform.rs +0 -715
- package/.next/standalone/crates/fpai-collect/src/sources/copilot/mod.rs +0 -158
- package/.next/standalone/crates/fpai-collect/src/sources/copilot/transform.rs +0 -512
- package/.next/standalone/crates/fpai-collect/src/sources/goose/mod.rs +0 -511
- package/.next/standalone/crates/fpai-collect/src/sources/goose/transform.rs +0 -586
- package/.next/standalone/crates/fpai-collect/src/sources/hermes/mod.rs +0 -318
- package/.next/standalone/crates/fpai-collect/src/sources/hermes/transform.rs +0 -579
- package/.next/standalone/crates/fpai-collect/src/sources/hooks/mod.rs +0 -310
- package/.next/standalone/crates/fpai-collect/src/sources/hooks/transform.rs +0 -448
- package/.next/standalone/crates/fpai-collect/src/sources/mod.rs +0 -23
- package/.next/standalone/crates/fpai-collect/src/sources/openclaw/mod.rs +0 -176
- package/.next/standalone/crates/fpai-collect/src/sources/openclaw/transform.rs +0 -500
- package/.next/standalone/crates/fpai-collect/src/sources/opencode/mod.rs +0 -315
- package/.next/standalone/crates/fpai-collect/src/sources/opencode/transform.rs +0 -493
- package/.next/standalone/crates/fpai-collect/src/sources/pi/mod.rs +0 -201
- package/.next/standalone/crates/fpai-collect/src/sources/pi/transform.rs +0 -533
- package/.next/standalone/crates/fpai-collect/src/spool.rs +0 -458
- package/.next/standalone/crates/fpai-collect/src/sqlitepoll.rs +0 -387
- package/.next/standalone/crates/fpai-collect/src/supervisor.rs +0 -423
- package/.next/standalone/crates/fpai-collect/src/uploader.rs +0 -600
- package/.next/standalone/crates/fpai-collect/tests/claude_source.rs +0 -738
- package/.next/standalone/crates/fpai-collect/tests/claude_subagents.rs +0 -393
- package/.next/standalone/crates/fpai-collect/tests/codex_source.rs +0 -975
- package/.next/standalone/crates/fpai-collect/tests/config.rs +0 -286
- package/.next/standalone/crates/fpai-collect/tests/copilot_source.rs +0 -1019
- package/.next/standalone/crates/fpai-collect/tests/delivery.rs +0 -281
- package/.next/standalone/crates/fpai-collect/tests/goose_source.rs +0 -1008
- package/.next/standalone/crates/fpai-collect/tests/hermes_source.rs +0 -1129
- package/.next/standalone/crates/fpai-collect/tests/hooks_source.rs +0 -621
- package/.next/standalone/crates/fpai-collect/tests/openclaw_source.rs +0 -854
- package/.next/standalone/crates/fpai-collect/tests/opencode_source.rs +0 -1478
- package/.next/standalone/crates/fpai-collect/tests/pi_source.rs +0 -1191
- package/.next/standalone/crates/fpai-collect/tests/supervisor.rs +0 -290
- package/.next/standalone/crates/fpai-collect/tests/uploader.rs +0 -506
- package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
- package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
- package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
- package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
- package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
- package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
- package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
- package/.next/standalone/integration-suite/Dockerfile +0 -47
- package/.next/standalone/integration-suite/README.md +0 -129
- package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
- package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
- package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
- package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
- package/.next/standalone/integration-suite/install-clis.sh +0 -136
- package/.next/standalone/integration-suite/probe-cli.sh +0 -248
- package/.next/standalone/integration-suite/report.js +0 -120
- package/.next/standalone/integration-suite/run.sh +0 -159
- package/.next/standalone/openclaw-plugin/index.js +0 -223
- package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
- package/.next/standalone/openclaw-plugin/package.json +0 -12
- package/.next/standalone/osv-scanner.toml +0 -17
- package/.next/standalone/pi-extension/index.ts +0 -524
- package/.next/standalone/pi-extension/package.json +0 -12
- package/.next/standalone/postcss.config.mjs +0 -8
- package/.next/standalone/readme-arch-hq.gif +0 -0
- package/.next/standalone/rust-toolchain.toml +0 -4
- package/src/auth/cli.ts +0 -371
- /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_ssgManifest.js +0 -0
package/src/audit/cli.ts
CHANGED
|
@@ -14,8 +14,14 @@
|
|
|
14
14
|
* No arguments yet — a bare `failproofai audit` does a full scan (all CLIs, all
|
|
15
15
|
* history). Flags (--since, --cli, --project, --port, --no-open) are easy
|
|
16
16
|
* follow-ups against `RunAuditOptions`.
|
|
17
|
+
*
|
|
18
|
+
* `--scheduled` is the one exception, and it is not a flag on the interactive
|
|
19
|
+
* command so much as a second entry point sharing its name: it runs the same
|
|
20
|
+
* scan with no TTY, no browser and no server, and reports its outcome through
|
|
21
|
+
* an exit code. See `runScheduledAudit`.
|
|
17
22
|
*/
|
|
18
23
|
import { runAudit } from "./index";
|
|
24
|
+
import { acquireAuditLock, type AuditLockInfo } from "./audit-lock";
|
|
19
25
|
import { writeDashboardCache } from "./dashboard-cache";
|
|
20
26
|
import type { AuditResult, RunAuditOptions } from "./types";
|
|
21
27
|
import { trackHookEvent } from "../hooks/hook-telemetry";
|
|
@@ -28,6 +34,16 @@ import { brandAnsi, ANSI_RESET, ANSI_BOLD, ANSI_DIM } from "../hooks/tui";
|
|
|
28
34
|
* for `start` mode, which `failproofai` (bare) already uses. */
|
|
29
35
|
const DASHBOARD_PORT = 8020;
|
|
30
36
|
|
|
37
|
+
/**
|
|
38
|
+
* `EX_TEMPFAIL` from sysexits.h — "another audit already holds the lock".
|
|
39
|
+
*
|
|
40
|
+
* A distinct code rather than 1, because the scheduler that spawns the headless
|
|
41
|
+
* run has to tell the two apart: a failure deserves a report, while losing the
|
|
42
|
+
* lock means only "come back in fifteen minutes". Collapsing them would make a
|
|
43
|
+
* healthy machine that simply ran two audits close together look broken.
|
|
44
|
+
*/
|
|
45
|
+
export const EXIT_AUDIT_ALREADY_RUNNING = 75;
|
|
46
|
+
|
|
31
47
|
/**
|
|
32
48
|
* Mirror of `app/audit/_components/run-progress.tsx`'s `STAGES`. Kept identical
|
|
33
49
|
* so the CLI and the dashboard's in-progress view tell the same story — the
|
|
@@ -49,6 +65,12 @@ USAGE
|
|
|
49
65
|
wasteful patterns, then open the audit dashboard.
|
|
50
66
|
failproofai audit --help Show this help.
|
|
51
67
|
|
|
68
|
+
failproofai audit --scheduled
|
|
69
|
+
Headless run: scan, refresh the dashboard's cached
|
|
70
|
+
result, print one line, exit. No browser, no
|
|
71
|
+
server. This is what a scheduled audit runs; exit
|
|
72
|
+
75 means another audit already had the lock.
|
|
73
|
+
|
|
52
74
|
WHAT IT DOES
|
|
53
75
|
1. Scans past sessions from every installed agent CLI (Claude, Codex, Cursor,
|
|
54
76
|
Copilot, OpenCode, Pi) — entirely on your machine.
|
|
@@ -247,10 +269,13 @@ function printSummary(result: AuditResult): void {
|
|
|
247
269
|
|
|
248
270
|
/**
|
|
249
271
|
* Which entry point ran the audit. `onboarding` is the automatic post-setup run;
|
|
250
|
-
* `cli` is an explicit `failproofai audit
|
|
251
|
-
*
|
|
272
|
+
* `cli` is an explicit `failproofai audit`; `scheduled` is the headless run the
|
|
273
|
+
* daemon spawns. Carried on every cli_audit_* event so the first audit a user
|
|
274
|
+
* ever runs, a deliberate one, and one nobody was present for stay distinct —
|
|
275
|
+
* without which an opt-in scheduled scan would silently inflate the counts that
|
|
276
|
+
* describe what people actually do by hand.
|
|
252
277
|
*/
|
|
253
|
-
type AuditSource = "cli" | "onboarding";
|
|
278
|
+
type AuditSource = "cli" | "onboarding" | "scheduled";
|
|
254
279
|
|
|
255
280
|
/** Shared so both entry points report cli_audit_completed identically. */
|
|
256
281
|
function auditCompletedProps(source: AuditSource, result: AuditResult) {
|
|
@@ -263,6 +288,89 @@ function auditCompletedProps(source: AuditSource, result: AuditResult) {
|
|
|
263
288
|
};
|
|
264
289
|
}
|
|
265
290
|
|
|
291
|
+
/** One line naming whoever holds the lock, for a refusal message. */
|
|
292
|
+
function heldByLine(held: AuditLockInfo | null): string {
|
|
293
|
+
if (!held) return "another audit is already running";
|
|
294
|
+
const ageS = Math.max(0, Math.round((Date.now() - held.startedAt) / 1000));
|
|
295
|
+
return `another audit is already running (pid ${held.pid}, started by ${held.source} ${ageS}s ago)`;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ── Headless (scheduled) audit ───────────────────────────────────────────────
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Run the audit with nobody watching: no TTY animation, no browser, no prompts,
|
|
302
|
+
* no dashboard server left behind.
|
|
303
|
+
*
|
|
304
|
+
* This is the entry point the scheduler spawns, and it must be a SEPARATE
|
|
305
|
+
* short-lived process. `src/hooks/worker-server.ts` serialises every request
|
|
306
|
+
* through one promise chain that `crates/failproofaid/src/worker.rs` caps at
|
|
307
|
+
* 30s, and `daemon-client.ts` turns that timeout into a DENY — so a ~104-second
|
|
308
|
+
* audit on the warm worker would be a machine-wide fail-closed denial across
|
|
309
|
+
* all 12 CLIs for as long as it ran.
|
|
310
|
+
*
|
|
311
|
+
* Returns the exit code instead of exiting, so the caller owns the exit and
|
|
312
|
+
* this stays callable from a test:
|
|
313
|
+
* 0 the scan completed (whether or not it found anything)
|
|
314
|
+
* 1 the scan failed, or its result could not be persisted
|
|
315
|
+
* 75 another audit holds the lock — not an error, come back later
|
|
316
|
+
*/
|
|
317
|
+
export async function runScheduledAudit(): Promise<number> {
|
|
318
|
+
const attempt = acquireAuditLock("scheduled");
|
|
319
|
+
if (!attempt.ok) {
|
|
320
|
+
// Deliberately ahead of cli_audit_started: a run that never started must
|
|
321
|
+
// not be counted as one that did.
|
|
322
|
+
process.stderr.write(`failproofai: ${heldByLine(attempt.heldBy)}; skipping this scheduled run\n`);
|
|
323
|
+
return EXIT_AUDIT_ALREADY_RUNNING;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const instanceId = getInstanceId();
|
|
327
|
+
try {
|
|
328
|
+
// Every event here is awaited, unlike runAuditCli's fire-and-forget
|
|
329
|
+
// `started`. Nothing keeps this process alive once the audit settles — no
|
|
330
|
+
// dashboard server, no user — and there is nobody waiting on latency
|
|
331
|
+
// either, so the bounded (5s, never-throws) send costs nothing that matters.
|
|
332
|
+
await trackHookEvent(instanceId, "cli_audit_started", { source: "scheduled" });
|
|
333
|
+
|
|
334
|
+
let result: AuditResult;
|
|
335
|
+
try {
|
|
336
|
+
result = await runAudit({});
|
|
337
|
+
} catch (err) {
|
|
338
|
+
await trackHookEvent(instanceId, "cli_audit_failed", {
|
|
339
|
+
source: "scheduled",
|
|
340
|
+
error_type: err instanceof Error ? err.name : "unknown",
|
|
341
|
+
error_message: sanitizeErrorMessage(err),
|
|
342
|
+
});
|
|
343
|
+
process.stderr.write(
|
|
344
|
+
`failproofai: scheduled audit failed: ${err instanceof Error ? err.message : String(err)}\n`,
|
|
345
|
+
);
|
|
346
|
+
return 1;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
await trackHookEvent(instanceId, "cli_audit_completed", auditCompletedProps("scheduled", result));
|
|
350
|
+
|
|
351
|
+
// No cache write for an empty scan, matching both interactive paths — and it
|
|
352
|
+
// matters more here. This run is unattended, so overwriting a real earlier
|
|
353
|
+
// audit's cached result with an empty one (after a history rotation, or on a
|
|
354
|
+
// service unit whose HOME resolves somewhere else) would blank the dashboard
|
|
355
|
+
// with nobody present to notice why.
|
|
356
|
+
if (result.eventsScanned > 0 && !writeDashboardCache({}, result)) {
|
|
357
|
+
// The cache is the only channel by which this run's result reaches
|
|
358
|
+
// anyone, so failing to write it is a failed run, not a footnote.
|
|
359
|
+
process.stderr.write("failproofai: scheduled audit ran but its result could not be saved\n");
|
|
360
|
+
return 1;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
process.stdout.write(
|
|
364
|
+
`failproofai: audit complete — ${num(result.eventsScanned)} tool calls across ` +
|
|
365
|
+
`${num(result.transcripts.scanned)} sessions, ${num(result.totals.hits)} hits\n`,
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
return 0;
|
|
369
|
+
} finally {
|
|
370
|
+
attempt.lock.release();
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
266
374
|
// ── Post-setup background audit ────────────────────────────────────────────────
|
|
267
375
|
|
|
268
376
|
/**
|
|
@@ -279,47 +387,67 @@ function auditCompletedProps(source: AuditSource, result: AuditResult) {
|
|
|
279
387
|
export async function runPostSetupAudit(): Promise<void> {
|
|
280
388
|
if (process.env.FAILPROOFAI_NO_AUTO_AUDIT === "1") return;
|
|
281
389
|
|
|
282
|
-
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
let result: AuditResult;
|
|
293
|
-
try {
|
|
294
|
-
result = await runWithProgress({});
|
|
295
|
-
} catch (err) {
|
|
296
|
-
// Awaited: this function returns straight into the dashboard boot, and a
|
|
297
|
-
// fire-and-forget fetch would race it.
|
|
298
|
-
await trackHookEvent(instanceId, "cli_audit_failed", {
|
|
299
|
-
source: "onboarding",
|
|
300
|
-
error_type: err instanceof Error ? err.name : "unknown",
|
|
301
|
-
error_message: sanitizeErrorMessage(err),
|
|
302
|
-
});
|
|
390
|
+
// Take the same cross-process cache lock the scheduled run, `failproofai
|
|
391
|
+
// audit` and the dashboard re-run take. This onboarding scan writes the very
|
|
392
|
+
// same sha1-keyed per-transcript cache and single-slot dashboard cache, so it
|
|
393
|
+
// is the fourth writer and must serialise with the other three — a scheduled
|
|
394
|
+
// daemon child can already be mid-scan when setup finishes. Held ⇒ skip, best
|
|
395
|
+
// effort: the dashboard boots on whatever the holder's run leaves behind, and
|
|
396
|
+
// this is pre-warming, not a result anyone is waiting on. `onboarding` matches
|
|
397
|
+
// the telemetry source below and the lock source declared in audit-lock.ts.
|
|
398
|
+
const attempt = acquireAuditLock("onboarding");
|
|
399
|
+
if (!attempt.ok) {
|
|
303
400
|
process.stdout.write(
|
|
304
|
-
|
|
401
|
+
`\n ${c(DIM, "an audit is already running — the dashboard will show its result.")}\n\n`,
|
|
305
402
|
);
|
|
306
403
|
return;
|
|
307
404
|
}
|
|
308
405
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
406
|
+
try {
|
|
407
|
+
const instanceId = getInstanceId();
|
|
408
|
+
// Fire-and-forget, as in runAuditCli: the multi-second scan below keeps the
|
|
409
|
+
// process alive long enough for this to land, and the completed/failed event
|
|
410
|
+
// that follows is awaited.
|
|
411
|
+
void trackHookEvent(instanceId, "cli_audit_started", { source: "onboarding" });
|
|
313
412
|
|
|
314
|
-
if (result.eventsScanned === 0) {
|
|
315
413
|
process.stdout.write(
|
|
316
|
-
`\n ${c(
|
|
414
|
+
`\n ${c(PINK, "✦")} ${c(BOLD, "failproofai audit now running")} ${c(DIM, "· ctrl+c to stop")}\n\n`,
|
|
317
415
|
);
|
|
318
|
-
|
|
416
|
+
|
|
417
|
+
let result: AuditResult;
|
|
418
|
+
try {
|
|
419
|
+
result = await runWithProgress({});
|
|
420
|
+
} catch (err) {
|
|
421
|
+
// Awaited: this function returns straight into the dashboard boot, and a
|
|
422
|
+
// fire-and-forget fetch would race it.
|
|
423
|
+
await trackHookEvent(instanceId, "cli_audit_failed", {
|
|
424
|
+
source: "onboarding",
|
|
425
|
+
error_type: err instanceof Error ? err.name : "unknown",
|
|
426
|
+
error_message: sanitizeErrorMessage(err),
|
|
427
|
+
});
|
|
428
|
+
process.stdout.write(
|
|
429
|
+
` ${c(PINK, "!")} ${c(DIM, "audit couldn't finish — run")} ${c(CYAN, "failproofai audit")} ${c(DIM, "later.")}\n\n`,
|
|
430
|
+
);
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Reported before the empty-history return below, so an onboarding audit that
|
|
435
|
+
// finds nothing is still counted — matching runAuditCli, which reports
|
|
436
|
+
// completed regardless of what the scan turned up.
|
|
437
|
+
await trackHookEvent(instanceId, "cli_audit_completed", auditCompletedProps("onboarding", result));
|
|
438
|
+
|
|
439
|
+
if (result.eventsScanned === 0) {
|
|
440
|
+
process.stdout.write(
|
|
441
|
+
`\n ${c(DIM, "no agent sessions to audit yet — come back after using your agent.")}\n\n`,
|
|
442
|
+
);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
writeDashboardCache({}, result);
|
|
446
|
+
printSummary(result);
|
|
447
|
+
process.stdout.write("\n");
|
|
448
|
+
} finally {
|
|
449
|
+
attempt.lock.release();
|
|
319
450
|
}
|
|
320
|
-
writeDashboardCache({}, result);
|
|
321
|
-
printSummary(result);
|
|
322
|
-
process.stdout.write("\n");
|
|
323
451
|
}
|
|
324
452
|
|
|
325
453
|
// ── Entry point ──────────────────────────────────────────────────────────────
|
|
@@ -329,6 +457,15 @@ export async function runAuditCli(args: string[]): Promise<void> {
|
|
|
329
457
|
process.stdout.write(HELP);
|
|
330
458
|
process.exit(0);
|
|
331
459
|
}
|
|
460
|
+
// The headless path, spawned rather than typed. Handled ahead of the
|
|
461
|
+
// rejection below so that adding it costs the interactive path nothing: it
|
|
462
|
+
// still refuses every argument it has always refused.
|
|
463
|
+
if (args.includes("--scheduled")) {
|
|
464
|
+
const extra = args.find((a) => a !== "--scheduled");
|
|
465
|
+
if (extra) die(`\`audit --scheduled\` takes no other arguments (got: ${extra}).`);
|
|
466
|
+
process.exit(await runScheduledAudit());
|
|
467
|
+
}
|
|
468
|
+
|
|
332
469
|
// No arguments supported yet — reject typos rather than silently doing a bare
|
|
333
470
|
// audit, so a future `failproofai audit --since 7d` doesn't quietly no-op.
|
|
334
471
|
const stray = args.find((a) => a !== "--help" && a !== "-h");
|
|
@@ -339,6 +476,18 @@ export async function runAuditCli(args: string[]): Promise<void> {
|
|
|
339
476
|
);
|
|
340
477
|
}
|
|
341
478
|
|
|
479
|
+
// Taken before any telemetry so a refused run is not counted as a started
|
|
480
|
+
// one, and released the moment the SCAN is done — see below, well before
|
|
481
|
+
// launch() parks this process on the dashboard.
|
|
482
|
+
const attempt = acquireAuditLock("cli");
|
|
483
|
+
if (!attempt.ok) {
|
|
484
|
+
process.stderr.write(
|
|
485
|
+
`Error: ${heldByLine(attempt.heldBy)}.\n` +
|
|
486
|
+
`Two audits write the same cache files, so this one won't start. Try again shortly.\n`,
|
|
487
|
+
);
|
|
488
|
+
process.exit(EXIT_AUDIT_ALREADY_RUNNING);
|
|
489
|
+
}
|
|
490
|
+
|
|
342
491
|
const instanceId = getInstanceId();
|
|
343
492
|
// Fire-and-forget is safe for `started`: the multi-second audit below (and the
|
|
344
493
|
// awaited cli_audit_completed / cli_audit_failed) keep the process alive long
|
|
@@ -393,6 +542,13 @@ export async function runAuditCli(args: string[]): Promise<void> {
|
|
|
393
542
|
);
|
|
394
543
|
}
|
|
395
544
|
|
|
545
|
+
// Released here, not in a `finally`: the lock covers the SCAN, and launch()
|
|
546
|
+
// below keeps this process alive for as long as the user leaves the dashboard
|
|
547
|
+
// open. Holding it that long would block every scheduled run until the
|
|
548
|
+
// one-hour stale ceiling expired. The exit paths above (die(), the
|
|
549
|
+
// empty-history exit) are covered by the handle's own process-exit hook.
|
|
550
|
+
attempt.lock.release();
|
|
551
|
+
|
|
396
552
|
const url = `http://localhost:${DASHBOARD_PORT}/audit`;
|
|
397
553
|
process.stdout.write(
|
|
398
554
|
`\n ${c(DIM, "starting the dashboard…")}\n` +
|
|
@@ -15,6 +15,7 @@ import { join } from "node:path";
|
|
|
15
15
|
import { homedir } from "node:os";
|
|
16
16
|
import { writeJsonAtomically } from "../../lib/atomic-write";
|
|
17
17
|
import type { AuditResult, RunAuditOptions } from "./types";
|
|
18
|
+
import { auditDashboardFile } from "../hooks/fp-home";
|
|
18
19
|
|
|
19
20
|
const DEFAULT_MAX_AGE_MINUTES = 30;
|
|
20
21
|
|
|
@@ -50,7 +51,7 @@ export interface DashboardCacheEntry {
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
function getCachePath(): string {
|
|
53
|
-
return
|
|
54
|
+
return auditDashboardFile();
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
/** Read the cache file. Returns null on missing/corrupt/unreadable file —
|
|
@@ -195,9 +195,106 @@ const CURL_PIPE_SH_RE = /(?:curl|wget)\s.*\|\s*(?:sh|bash|zsh|dash|ksh|csh|tcsh|
|
|
|
195
195
|
* Matched against BOTH the raw command and its shell-unescaped form (see
|
|
196
196
|
* `stripShellQuoting`), because a shell reconstructs the binary name from
|
|
197
197
|
* fragments a regex over the raw string cannot see.
|
|
198
|
+
*
|
|
199
|
+
* ANCHORED ON COMMAND POSITION, which is the third thing an earlier version got
|
|
200
|
+
* wrong and the most expensive: with no anchor, the pattern fired on any string
|
|
201
|
+
* that merely CONTAINED the invocation, so `grep -rn "failproofai config
|
|
202
|
+
* --pause" docs/`, `git commit -m "docs: explain failproofai config --pause"`,
|
|
203
|
+
* `gh pr create --body "...failproofai config --pause"` and `git log --grep`
|
|
204
|
+
* were all denied. This policy is `defaultEnabled`, and this repo's own
|
|
205
|
+
* CHANGELOG and `docs/built-in-policies.mdx` contain that literal string, so
|
|
206
|
+
* the first thing it did on a real machine was block someone reading the
|
|
207
|
+
* documentation for it. Its sibling `FAILPROOFAI_CLI_RE` was anchored from the
|
|
208
|
+
* start; this one was not.
|
|
209
|
+
*
|
|
210
|
+
* The distinction that matters is structural, not textual: in a real
|
|
211
|
+
* invocation the binary sits where the shell will look for a COMMAND — first in
|
|
212
|
+
* a segment, or behind a runner (`npx`, `pnpm dlx`, `node`, `sh -c`) and its
|
|
213
|
+
* flags. In every false positive above it sits where an ARGUMENT goes.
|
|
198
214
|
*/
|
|
199
|
-
|
|
200
|
-
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Split a command into the pieces a shell would treat as separate commands.
|
|
218
|
+
*
|
|
219
|
+
* Includes `(`, `)` and backtick alongside the ordinary operators so that
|
|
220
|
+
* command substitution — `echo $(failproofai config --pause)` — puts the inner
|
|
221
|
+
* invocation in command position of its own segment rather than hiding it as an
|
|
222
|
+
* argument of `echo`.
|
|
223
|
+
*/
|
|
224
|
+
const SEGMENT_SEPARATORS = /[;&|\n\r(){}`]+/;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Tokens that stand in front of the real binary without being it: package
|
|
228
|
+
* runners, interpreters, and the `exec`-alikes. Compared by basename, so
|
|
229
|
+
* `/usr/bin/env` and `env` behave identically.
|
|
230
|
+
*/
|
|
231
|
+
const COMMAND_PREFIX_TOKENS = new Set([
|
|
232
|
+
"npx", "bunx", "pnpx", "npm", "pnpm", "yarn", "dlx", "exec", "run",
|
|
233
|
+
"node", "bun", "deno", "env", "command", "builtin", "nohup", "setsid",
|
|
234
|
+
"time", "timeout", "nice", "stdbuf", "xargs", "sudo", "doas",
|
|
235
|
+
"sh", "bash", "zsh", "dash", "ksh", "fish", "ash",
|
|
236
|
+
]);
|
|
237
|
+
|
|
238
|
+
/** The binary itself, however it is pinned or pathed: `failproofai`,
|
|
239
|
+
* `failproofai@latest`, `/usr/local/bin/failproofai`,
|
|
240
|
+
* `./node_modules/.bin/failproofai`, `.../failproofai/bin/failproofai.mjs`. */
|
|
241
|
+
const SELF_BINARY_TOKEN_RE = /(?:^|\/)failproofai[^/]*$/;
|
|
242
|
+
|
|
243
|
+
/** `config`, and the two aliases the entrypoint normalizes to it. */
|
|
244
|
+
const CONFIG_SUBCOMMAND_RE = /^(?:config|configure|setup)$/;
|
|
245
|
+
|
|
246
|
+
/** `--pause`, with or without an `=value` tail. */
|
|
247
|
+
const PAUSE_FLAG_RE = /^--pause(?:=|$)/;
|
|
248
|
+
|
|
249
|
+
/** An `FOO=bar` prefix assignment, which a shell consumes before the command. */
|
|
250
|
+
const ENV_ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* A bare operand belonging to the runner in front of it — `timeout 30 …`,
|
|
254
|
+
* `nice -n 5 …`. Skipped so the runner's argument is not mistaken for the
|
|
255
|
+
* command. Safe to skip unconditionally: no binary is named `30` or `5m`.
|
|
256
|
+
*/
|
|
257
|
+
const RUNNER_OPERAND_RE = /^\d+[a-z]*$/i;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* True when `command` runs `failproofai config --pause` in command position.
|
|
261
|
+
*
|
|
262
|
+
* Residual false positive, deliberately accepted: a quoted argument that itself
|
|
263
|
+
* contains a shell operator AND the whole invocation — `git commit -m "x;
|
|
264
|
+
* failproofai config --pause"` — survives `stripShellQuoting` as two segments
|
|
265
|
+
* and denies. That is a far narrower miss than matching every mention, and it
|
|
266
|
+
* errs toward refusing rather than toward silently suspending enforcement.
|
|
267
|
+
*/
|
|
268
|
+
function namesSelfPause(command: string): boolean {
|
|
269
|
+
for (const segment of command.split(SEGMENT_SEPARATORS)) {
|
|
270
|
+
const tokens = segment.split(/\s+/).filter(Boolean);
|
|
271
|
+
let i = 0;
|
|
272
|
+
// Walk off everything a shell resolves before it settles on the command:
|
|
273
|
+
// prefix assignments, redirections, runner flags, and the runners.
|
|
274
|
+
while (i < tokens.length) {
|
|
275
|
+
const token = tokens[i];
|
|
276
|
+
const isSkippable =
|
|
277
|
+
ENV_ASSIGNMENT_RE.test(token) ||
|
|
278
|
+
token.startsWith("-") ||
|
|
279
|
+
token.startsWith(">") ||
|
|
280
|
+
token.startsWith("<") ||
|
|
281
|
+
RUNNER_OPERAND_RE.test(token) ||
|
|
282
|
+
COMMAND_PREFIX_TOKENS.has(token.slice(token.lastIndexOf("/") + 1));
|
|
283
|
+
if (!isSkippable) break;
|
|
284
|
+
i++;
|
|
285
|
+
}
|
|
286
|
+
if (i >= tokens.length) continue;
|
|
287
|
+
if (!SELF_BINARY_TOKEN_RE.test(tokens[i])) continue;
|
|
288
|
+
|
|
289
|
+
// The binary IS the command here. Now require `config … --pause` in the
|
|
290
|
+
// argument order the CLI actually accepts.
|
|
291
|
+
const args = tokens.slice(i + 1);
|
|
292
|
+
const configAt = args.findIndex((a) => CONFIG_SUBCOMMAND_RE.test(a));
|
|
293
|
+
if (configAt === -1) continue;
|
|
294
|
+
if (args.slice(configAt + 1).some((a) => PAUSE_FLAG_RE.test(a))) return true;
|
|
295
|
+
}
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
201
298
|
|
|
202
299
|
/**
|
|
203
300
|
* Decode one ANSI-C `$'...'` body (the text between the quotes) to the bytes a
|
|
@@ -699,7 +796,7 @@ function blockSelfPause(ctx: PolicyContext): PolicyResult {
|
|
|
699
796
|
// The raw command AND its shell-unescaped form: a shell strips quotes and
|
|
700
797
|
// backslashes before running the binary, so `fail\proofai config --pause`
|
|
701
798
|
// reaches the pause CLI even though the literal name is broken.
|
|
702
|
-
if (
|
|
799
|
+
if (namesSelfPause(cmd) || namesSelfPause(stripShellQuoting(cmd))) {
|
|
703
800
|
return deny(
|
|
704
801
|
"Pausing failproofai enforcement is a human action, not an agent one. " +
|
|
705
802
|
"If a policy is blocking legitimate work, say so and let the operator decide.",
|