failproofai 1.0.0-beta.1 → 1.0.0-beta.11
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 +6 -6
- 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/build-manifest.json +3 -3
- 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/build-manifest.json +3 -3
- 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/build-manifest.json +3 -3
- 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/build-manifest.json +3 -3
- 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/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +30 -18
- package/.next/standalone/.next/server/app/policies/page.js +5 -3
- 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 +3 -3
- 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/build-manifest.json +3 -3
- 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/build-manifest.json +3 -3
- 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]__0h9_83k._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[externals]__1ifpkx-._.js +3 -0
- 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]__16le-kd._.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} +4 -4
- 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} +2 -2
- 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/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[externals]__1aq139v._.js +3 -0
- 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]__170799-._.js → [root-of-the-server]__0spkm68._.js} +11 -11
- 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]__1gwr8l-._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1apb7k_._.js → [root-of-the-server]__1rdruw4._.js} +2 -2
- 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]__01od0m6._.js → [root-of-the-server]__1yw368c._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__20ie7me._.js +3 -0
- 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/_0n4bc3i._.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/_1e19atl._.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/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -1
- 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 +6 -6
- 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 +74 -20
- package/.next/standalone/.next/static/chunks/{2ovmv4esqalpb.js → 0-m1vctj2jny6.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1b3a1scouf40o.js → 04y6igmx5olt6.js} +1 -1
- package/.next/standalone/.next/static/chunks/{35584kn409ad1.js → 06abmgw7cvecc.js} +22 -22
- package/.next/standalone/.next/static/chunks/{03fmihek9n986.js → 06zgzyh3wv1ig.js} +1 -1
- package/.next/standalone/.next/static/chunks/0on899o88y54u.js +1 -0
- package/.next/standalone/.next/static/chunks/0p_y4ndalc68e.js +6 -0
- package/.next/standalone/.next/static/chunks/{1hdhteui932bq.js → 0qmo2t50kpb30.js} +1 -1
- package/.next/standalone/.next/static/chunks/0ug1y_6-uwu5t.js +1 -0
- package/.next/standalone/.next/static/chunks/13vrxdsdihqd_.css +2 -0
- package/.next/standalone/.next/static/chunks/16p68cybetr31.js +1 -0
- package/.next/standalone/.next/static/chunks/19lrnqxn7ni7i.js +1 -0
- package/.next/standalone/.next/static/chunks/{30is-6r00sbd7.js → 1a9qlmo5s678i.js} +1 -1
- package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
- package/.next/standalone/.next/static/chunks/23kev26by1e4f.js +1 -0
- package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
- package/.next/standalone/.next/static/chunks/31coz11ia5vc4.js +1 -0
- package/.next/standalone/.next/static/chunks/{07ix3lb4gib0x.js → 37nu_nos28w1x.js} +1 -1
- package/.next/standalone/.next/static/chunks/{191njgoik03fm.js → 43ylh_2r15k-w.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-1lsv92x7klwrn.js → turbopack-3diub4ctvn1n0.js} +1 -1
- package/.next/standalone/app/actions/get-active-pauses.ts +16 -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/components/pause-notices.tsx +111 -0
- package/.next/standalone/app/policies/hooks-client.tsx +81 -6
- 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 +23 -10
- package/.next/standalone/proxy.ts +111 -1
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +580 -81
- package/dist/cli.mjs +12974 -7447
- package/dist/worker.mjs +2020 -272
- 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 +23 -10
- package/scripts/build-daemon-packages.mjs +247 -0
- package/scripts/daemon-platforms.mjs +42 -0
- package/scripts/install-telemetry.mjs +8 -1
- package/scripts/launch.ts +25 -3
- package/scripts/parse-script-args.ts +27 -1
- package/scripts/prune-standalone.mjs +26 -0
- package/scripts/publish-aliases.mjs +62 -41
- package/scripts/repro-npm-install.sh +155 -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/backfill-cli.ts +247 -0
- package/src/hooks/builtin-policies.ts +390 -2
- package/src/hooks/cloud-connection.ts +354 -0
- package/src/hooks/cloud-enrollment-cli.ts +325 -0
- package/src/hooks/cloud-enrollment.ts +243 -0
- package/src/hooks/cloud-introspect.ts +147 -0
- package/src/hooks/cloud-managed-policies.ts +169 -0
- package/src/hooks/collector-config.ts +234 -0
- package/src/hooks/configure-wizard.ts +930 -158
- package/src/hooks/custom-hooks-loader.ts +191 -33
- package/src/hooks/daemon-client.ts +144 -17
- package/src/hooks/daemon-download.ts +264 -30
- package/src/hooks/daemon-service.ts +1030 -72
- package/src/hooks/first-run-gate.ts +50 -0
- package/src/hooks/fp-config.ts +497 -0
- package/src/hooks/fp-home.ts +375 -0
- package/src/hooks/fp-reset.ts +464 -0
- package/src/hooks/handler.ts +185 -6
- package/src/hooks/hook-activity-store.ts +85 -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 +171 -8
- package/src/hooks/manager.ts +5 -4
- package/src/hooks/onboarding-attempt.ts +179 -0
- package/src/hooks/onboarding-lock.ts +119 -0
- package/src/hooks/policy-types.ts +28 -0
- package/src/hooks/session-pause-cli.ts +159 -0
- package/src/hooks/session-pause.ts +268 -0
- package/src/hooks/setup-state.ts +256 -0
- package/src/hooks/tui.ts +119 -4
- package/src/hooks/uninstall-cli.ts +349 -0
- package/src/hooks/worker-server.ts +65 -3
- package/.next/standalone/.next/server/chunks/[externals]__14odj07._.js +0 -3
- package/.next/standalone/.next/server/chunks/[externals]__1s61mel._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0id_xf7._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0longh-._.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/[externals]__1_g_b3t._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0114ewg._.js +0 -4
- 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]__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]__0qucxyj._.js +0 -4
- 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]__0uno39i._.js +0 -3
- 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]__1ath6v_._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ig795d._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1yo1gyz._.js +0 -3
- 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_14q3-7b.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/0fdx7ikab08-g.js +0 -6
- package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
- package/.next/standalone/.next/static/chunks/0zbxssxh53n-3.js +0 -1
- package/.next/standalone/.next/static/chunks/1d71o1_tav721.js +0 -1
- package/.next/standalone/.next/static/chunks/24gs8ip6_spcb.js +0 -1
- package/.next/standalone/.next/static/chunks/2mazwb1qmur1p.js +0 -1
- package/.next/standalone/.next/static/chunks/2pujpnfx4pws6.css +0 -2
- package/.next/standalone/.next/static/chunks/33u59vf_8xpd-.js +0 -1
- package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
- package/.next/standalone/Cargo.lock +0 -428
- package/.next/standalone/Cargo.toml +0 -9
- package/.next/standalone/crates/PROTOCOL.md +0 -122
- package/.next/standalone/crates/failproofaid/Cargo.toml +0 -18
- package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
- package/.next/standalone/crates/failproofaid/src/main.rs +0 -83
- package/.next/standalone/crates/failproofaid/src/paths.rs +0 -172
- 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-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/{avJMC2AXvrg13Pbm4QH8F → BZDo0aAHzDwh5ntxgIgpc}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{avJMC2AXvrg13Pbm4QH8F → BZDo0aAHzDwh5ntxgIgpc}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{avJMC2AXvrg13Pbm4QH8F → BZDo0aAHzDwh5ntxgIgpc}/_ssgManifest.js +0 -0
|
@@ -7,6 +7,9 @@ import { Check, ChevronDown, Code, Copy, Settings, Shield, ShieldAlert, ShieldCh
|
|
|
7
7
|
import PaginationControls from "@/app/components/pagination-controls";
|
|
8
8
|
import { getHookActivityAction, searchHookActivityAction } from "@/app/actions/get-hook-activity";
|
|
9
9
|
import type { HookActivityPayload } from "@/app/actions/get-hook-activity";
|
|
10
|
+
import { getActivePausesAction } from "@/app/actions/get-active-pauses";
|
|
11
|
+
import type { ActivePause } from "@/src/hooks/session-pause";
|
|
12
|
+
import { PausedBanner, PausedNote, PausedPill } from "@/app/components/pause-notices";
|
|
10
13
|
import { getHooksConfigAction } from "@/app/actions/get-hooks-config";
|
|
11
14
|
import type { HooksConfigPayload, PolicyInfo, CustomPolicyInfo } from "@/app/actions/get-hooks-config";
|
|
12
15
|
import type { IntegrationType } from "@/src/hooks/types";
|
|
@@ -386,7 +389,27 @@ function DetailPanel({
|
|
|
386
389
|
event detail
|
|
387
390
|
</span>
|
|
388
391
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-2">
|
|
392
|
+
<PausedNote item={item} />
|
|
389
393
|
<EnforcementNote item={item} />
|
|
394
|
+
{item.policySource && (
|
|
395
|
+
<div>
|
|
396
|
+
<span className="text-muted-foreground">Decided by: </span>
|
|
397
|
+
<span className="font-mono text-foreground">
|
|
398
|
+
{item.policySource === "cloud" && item.cloudPolicyId
|
|
399
|
+
? `cloud · ${item.cloudPolicyId} rev ${item.cloudRevision}`
|
|
400
|
+
: item.policySource}
|
|
401
|
+
</span>
|
|
402
|
+
</div>
|
|
403
|
+
)}
|
|
404
|
+
{item.cloudGeneration !== undefined && (
|
|
405
|
+
<div>
|
|
406
|
+
{/* Present on every row of a managed machine, not just cloud
|
|
407
|
+
decisions — it is what separates a rollout that changed no
|
|
408
|
+
outcomes from one that never arrived. */}
|
|
409
|
+
<span className="text-muted-foreground">Cloud generation: </span>
|
|
410
|
+
<span className="font-mono text-foreground">{item.cloudGeneration}</span>
|
|
411
|
+
</div>
|
|
412
|
+
)}
|
|
390
413
|
<div>
|
|
391
414
|
<span className="text-muted-foreground">Session ID: </span>
|
|
392
415
|
<span className="font-mono text-foreground">
|
|
@@ -443,6 +466,7 @@ function ActivityTab({
|
|
|
443
466
|
|
|
444
467
|
const [page, setPage] = useState(() => paramToPage(url.get("page")));
|
|
445
468
|
const [data, setData] = useState<HookActivityPayload | null>(null);
|
|
469
|
+
const [activePauses, setActivePauses] = useState<ActivePause[]>([]);
|
|
446
470
|
const [expandedRow, setExpandedRow] = useState<number | null>(null);
|
|
447
471
|
|
|
448
472
|
const [filterDecision, setFilterDecision] = useState<"" | "allow" | "deny" | "instruct">(() => {
|
|
@@ -456,10 +480,14 @@ function ActivityTab({
|
|
|
456
480
|
const v = url.get("cli");
|
|
457
481
|
return isKnownCli(v) ? v : "";
|
|
458
482
|
});
|
|
483
|
+
const [filterSource, setFilterSource] = useState<"" | "builtin" | "custom" | "convention" | "cloud">(() => {
|
|
484
|
+
const v = url.get("source");
|
|
485
|
+
return v === "builtin" || v === "custom" || v === "convention" || v === "cloud" ? v : "";
|
|
486
|
+
});
|
|
459
487
|
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
460
488
|
const filterTelemetryFirstRunRef = useRef(true);
|
|
461
|
-
const filtersRef = useRef({ filterDecision, filterEventType, filterPolicy, filterSessionId, filterCli });
|
|
462
|
-
filtersRef.current = { filterDecision, filterEventType, filterPolicy, filterSessionId, filterCli };
|
|
489
|
+
const filtersRef = useRef({ filterDecision, filterEventType, filterPolicy, filterSessionId, filterCli, filterSource });
|
|
490
|
+
filtersRef.current = { filterDecision, filterEventType, filterPolicy, filterSessionId, filterCli, filterSource };
|
|
463
491
|
|
|
464
492
|
useEffect(() => {
|
|
465
493
|
if (!mountedRef.current) {
|
|
@@ -472,17 +500,18 @@ function ActivityTab({
|
|
|
472
500
|
policy: filterPolicy || undefined,
|
|
473
501
|
session: filterSessionId || undefined,
|
|
474
502
|
cli: filterCli || undefined,
|
|
503
|
+
source: filterSource || undefined,
|
|
475
504
|
page: pageToParam(page),
|
|
476
505
|
});
|
|
477
506
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
478
|
-
}, [filterDecision, filterEventType, filterPolicy, filterSessionId, filterCli, page]);
|
|
507
|
+
}, [filterDecision, filterEventType, filterPolicy, filterSessionId, filterCli, filterSource, page]);
|
|
479
508
|
|
|
480
|
-
const hasActiveFilters = filterDecision !== "" || filterEventType !== "" || filterPolicy !== "" || filterSessionId !== "" || filterCli !== "";
|
|
509
|
+
const hasActiveFilters = filterDecision !== "" || filterEventType !== "" || filterPolicy !== "" || filterSessionId !== "" || filterCli !== "" || filterSource !== "";
|
|
481
510
|
|
|
482
511
|
const fetchData = useCallback(async (p: number) => {
|
|
483
512
|
try {
|
|
484
|
-
const { filterDecision: fd, filterEventType: fe, filterPolicy: fp, filterSessionId: fs, filterCli: fc } = filtersRef.current;
|
|
485
|
-
const active = fd !== "" || fe !== "" || fp !== "" || fs !== "" || fc !== "";
|
|
513
|
+
const { filterDecision: fd, filterEventType: fe, filterPolicy: fp, filterSessionId: fs, filterCli: fc, filterSource: fsrc } = filtersRef.current;
|
|
514
|
+
const active = fd !== "" || fe !== "" || fp !== "" || fs !== "" || fc !== "" || fsrc !== "";
|
|
486
515
|
let result: HookActivityPayload;
|
|
487
516
|
if (active) {
|
|
488
517
|
result = await searchHookActivityAction(
|
|
@@ -492,6 +521,7 @@ function ActivityTab({
|
|
|
492
521
|
policyName: fp || undefined,
|
|
493
522
|
sessionId: fs || undefined,
|
|
494
523
|
integration: fc || undefined,
|
|
524
|
+
source: fsrc || undefined,
|
|
495
525
|
},
|
|
496
526
|
p,
|
|
497
527
|
);
|
|
@@ -511,6 +541,22 @@ function ActivityTab({
|
|
|
511
541
|
return () => clearInterval(id);
|
|
512
542
|
}, [page, fetchData, intervalSec]);
|
|
513
543
|
|
|
544
|
+
// Pause state is polled independently of the activity page: it is live
|
|
545
|
+
// machine state, not a property of whichever rows are on screen, and it must
|
|
546
|
+
// keep updating while the user sits on page 3 of history.
|
|
547
|
+
useEffect(() => {
|
|
548
|
+
let cancelled = false;
|
|
549
|
+
const load = () => {
|
|
550
|
+
getActivePausesAction()
|
|
551
|
+
.then((p) => { if (!cancelled) setActivePauses(p); })
|
|
552
|
+
.catch(() => { /* non-critical: the banner simply stays hidden */ });
|
|
553
|
+
};
|
|
554
|
+
load();
|
|
555
|
+
const ms = intervalSec > 0 ? intervalSec * 1000 : 5000;
|
|
556
|
+
const id = setInterval(load, ms);
|
|
557
|
+
return () => { cancelled = true; clearInterval(id); };
|
|
558
|
+
}, [intervalSec]);
|
|
559
|
+
|
|
514
560
|
useEffect(() => {
|
|
515
561
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
516
562
|
debounceRef.current = setTimeout(() => {
|
|
@@ -565,6 +611,10 @@ function ActivityTab({
|
|
|
565
611
|
|
|
566
612
|
return (
|
|
567
613
|
<>
|
|
614
|
+
{/* Above the stats, deliberately: a paused machine is the most important
|
|
615
|
+
thing on this screen, and the numbers below it are being produced with
|
|
616
|
+
local enforcement switched off. */}
|
|
617
|
+
<PausedBanner pauses={activePauses} />
|
|
568
618
|
{data?.stats && data.stats.totalEvents > 0 && (
|
|
569
619
|
<div style={{ marginBottom: 18 }}>
|
|
570
620
|
<StatsBar stats={data.stats} />
|
|
@@ -593,6 +643,29 @@ function ActivityTab({
|
|
|
593
643
|
<option value="PermissionRequest">PermissionRequest</option>
|
|
594
644
|
</select>
|
|
595
645
|
</div>
|
|
646
|
+
<div className="filter-group">
|
|
647
|
+
{/* "What did my organization's policies decide here?" is the
|
|
648
|
+
question cloud rollout reporting rests on, and it is
|
|
649
|
+
unanswerable while the source is only a prefix on a name. */}
|
|
650
|
+
<span className="filter-label">source</span>
|
|
651
|
+
<select
|
|
652
|
+
value={filterSource}
|
|
653
|
+
onChange={(e) => {
|
|
654
|
+
const v = e.target.value;
|
|
655
|
+
setFilterSource(
|
|
656
|
+
v === "builtin" || v === "custom" || v === "convention" || v === "cloud" ? v : "",
|
|
657
|
+
);
|
|
658
|
+
}}
|
|
659
|
+
className="filter-input"
|
|
660
|
+
aria-label="Filter by policy source"
|
|
661
|
+
>
|
|
662
|
+
<option value="">all sources</option>
|
|
663
|
+
<option value="builtin">builtin</option>
|
|
664
|
+
<option value="custom">custom</option>
|
|
665
|
+
<option value="convention">convention</option>
|
|
666
|
+
<option value="cloud">cloud</option>
|
|
667
|
+
</select>
|
|
668
|
+
</div>
|
|
596
669
|
<div className="filter-group">
|
|
597
670
|
<span className="filter-label">cli</span>
|
|
598
671
|
<select
|
|
@@ -642,6 +715,7 @@ function ActivityTab({
|
|
|
642
715
|
setFilterCli("");
|
|
643
716
|
setFilterPolicy("");
|
|
644
717
|
setFilterSessionId("");
|
|
718
|
+
setFilterSource("");
|
|
645
719
|
}}
|
|
646
720
|
aria-label="Clear all filters"
|
|
647
721
|
>
|
|
@@ -744,6 +818,7 @@ function ActivityTab({
|
|
|
744
818
|
</td>
|
|
745
819
|
<td className="px-3 py-2">
|
|
746
820
|
<DecisionBadge decision={item.decision} />
|
|
821
|
+
{item.pausedBy && <PausedPill />}
|
|
747
822
|
</td>
|
|
748
823
|
<td className="px-3 py-2">
|
|
749
824
|
<EventTypeBadge eventType={item.eventType} />
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /settings — one page, sections. The single home for the machine-level
|
|
3
|
+
* controls the design plan collected here: the scheduled local audit and
|
|
4
|
+
* emailed audit reports.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately NOT a home for telemetry: the product decision is that telemetry
|
|
7
|
+
* is documented but not advertised in-product, so there is no telemetry control
|
|
8
|
+
* or status on this page. `config.toml` plus the docs are the whole story.
|
|
9
|
+
*
|
|
10
|
+
* Thin server wrapper (Suspense boundary + the disabled-pages gate every route
|
|
11
|
+
* uses); all the reads/writes live in the client and its server actions.
|
|
12
|
+
*/
|
|
13
|
+
import { Suspense } from "react";
|
|
14
|
+
import { notFound } from "next/navigation";
|
|
15
|
+
import SettingsClient from "./settings-client";
|
|
16
|
+
|
|
17
|
+
export const dynamic = "force-dynamic";
|
|
18
|
+
|
|
19
|
+
export default async function SettingsPage() {
|
|
20
|
+
const disabled = (process.env.FAILPROOFAI_DISABLE_PAGES ?? "")
|
|
21
|
+
.split(",")
|
|
22
|
+
.map((s) => s.trim())
|
|
23
|
+
.filter(Boolean);
|
|
24
|
+
if (disabled.includes("settings")) notFound();
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Suspense>
|
|
28
|
+
<SettingsClient />
|
|
29
|
+
</Suspense>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* /settings client — two sections (scheduled audit, email reports) plus the
|
|
5
|
+
* degraded states that are most of the real screens: daemon not installed /
|
|
6
|
+
* stopped / unsupported, no scan ever run, a scan running now, a last run that
|
|
7
|
+
* failed, signed out, and not cloud-enrolled. Each is shown explicitly, because
|
|
8
|
+
* a missing control reads as a bug.
|
|
9
|
+
*
|
|
10
|
+
* Visual conventions are the site chrome's, matched to /policies: the brutalist
|
|
11
|
+
* `.report`/`.section`/`.panel`/`.btn` classes from globals.css, the same
|
|
12
|
+
* emerald switch /policies uses (PolicyToggle), inline `var(--…)` colours, and
|
|
13
|
+
* the shared `toast()`. No new design language, colour, or component library.
|
|
14
|
+
*
|
|
15
|
+
* All writes go through server actions that call `updateConfig` — never a raw
|
|
16
|
+
* file write — so the CLI and dashboard cannot diverge. The parity mapping is
|
|
17
|
+
* documented on each action module.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
21
|
+
import { getScheduledAuditAction, type ScheduledAuditView } from "@/app/actions/get-scheduled-audit";
|
|
22
|
+
import { setAutoAuditAction, setAuditIntervalAction } from "@/app/actions/update-scheduled-audit";
|
|
23
|
+
import { triggerRun, RerunError } from "@/app/audit/_components/rerun-button";
|
|
24
|
+
import { AuthDialog } from "@/app/audit/_components/auth-dialog";
|
|
25
|
+
import { toast } from "@/app/components/toast";
|
|
26
|
+
import { fetchWithTimeout } from "@/lib/fetch-with-timeout";
|
|
27
|
+
import { formatRelativeTime } from "@/lib/format-duration";
|
|
28
|
+
|
|
29
|
+
// ── formatting helpers ───────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
function fmtAbsolute(ms: number): string {
|
|
32
|
+
return new Date(ms).toLocaleString(undefined, {
|
|
33
|
+
month: "short",
|
|
34
|
+
day: "numeric",
|
|
35
|
+
hour: "2-digit",
|
|
36
|
+
minute: "2-digit",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** "in 6d" / "in 3h" / "in 12m" / "now". formatRelativeTime only speaks past. */
|
|
41
|
+
function fmtFuture(ms: number): string {
|
|
42
|
+
const diff = ms - Date.now();
|
|
43
|
+
if (diff <= 0) return "now";
|
|
44
|
+
if (diff < 3_600_000) return `in ${Math.max(1, Math.floor(diff / 60_000))}m`;
|
|
45
|
+
if (diff < 86_400_000) return `in ${Math.floor(diff / 3_600_000)}h`;
|
|
46
|
+
return `in ${Math.floor(diff / 86_400_000)}d`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── shared primitives (match /policies) ──────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
/** The exact switch /policies uses — copied shape, not a new control. */
|
|
52
|
+
function Toggle({
|
|
53
|
+
enabled,
|
|
54
|
+
onChange,
|
|
55
|
+
disabled,
|
|
56
|
+
label,
|
|
57
|
+
}: {
|
|
58
|
+
enabled: boolean;
|
|
59
|
+
onChange: () => void;
|
|
60
|
+
disabled?: boolean;
|
|
61
|
+
label: string;
|
|
62
|
+
}) {
|
|
63
|
+
return (
|
|
64
|
+
<button
|
|
65
|
+
type="button"
|
|
66
|
+
onClick={onChange}
|
|
67
|
+
disabled={disabled}
|
|
68
|
+
role="switch"
|
|
69
|
+
aria-checked={enabled}
|
|
70
|
+
aria-label={label}
|
|
71
|
+
className={`relative inline-flex h-4 w-7 shrink-0 items-center rounded-full transition-colors disabled:opacity-40 ${
|
|
72
|
+
enabled ? "bg-emerald-500" : "bg-muted-foreground/30"
|
|
73
|
+
}`}
|
|
74
|
+
>
|
|
75
|
+
<span
|
|
76
|
+
className={`inline-block h-3 w-3 rounded-full bg-white shadow transition-transform ${
|
|
77
|
+
enabled ? "translate-x-3.5" : "translate-x-0.5"
|
|
78
|
+
}`}
|
|
79
|
+
/>
|
|
80
|
+
</button>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type PillTone = "ok" | "warn" | "bad" | "muted";
|
|
85
|
+
const PILL_TONE: Record<PillTone, { fg: string; bg: string; bd: string }> = {
|
|
86
|
+
ok: { fg: "var(--accent-green)", bg: "rgba(102,209,181,0.10)", bd: "rgba(102,209,181,0.30)" },
|
|
87
|
+
warn: { fg: "var(--amber)", bg: "rgba(232,196,106,0.10)", bd: "rgba(232,196,106,0.30)" },
|
|
88
|
+
bad: { fg: "var(--accent-pink)", bg: "rgba(228,88,124,0.10)", bd: "rgba(228,88,124,0.30)" },
|
|
89
|
+
muted: { fg: "var(--ink-2)", bg: "transparent", bd: "var(--line-2)" },
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function Pill({ tone, children }: { tone: PillTone; children: React.ReactNode }) {
|
|
93
|
+
const t = PILL_TONE[tone];
|
|
94
|
+
return (
|
|
95
|
+
<span
|
|
96
|
+
style={{
|
|
97
|
+
display: "inline-flex",
|
|
98
|
+
alignItems: "center",
|
|
99
|
+
gap: 6,
|
|
100
|
+
padding: "3px 9px",
|
|
101
|
+
fontFamily: "var(--font-mono)",
|
|
102
|
+
fontSize: 11,
|
|
103
|
+
letterSpacing: "0.04em",
|
|
104
|
+
color: t.fg,
|
|
105
|
+
background: t.bg,
|
|
106
|
+
border: `1px solid ${t.bd}`,
|
|
107
|
+
textTransform: "uppercase",
|
|
108
|
+
}}
|
|
109
|
+
>
|
|
110
|
+
{children}
|
|
111
|
+
</span>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const SECTION_TITLE: React.CSSProperties = {
|
|
116
|
+
fontFamily: "var(--font-mono)",
|
|
117
|
+
fontSize: 16,
|
|
118
|
+
fontWeight: 600,
|
|
119
|
+
letterSpacing: "-0.01em",
|
|
120
|
+
color: "var(--ink)",
|
|
121
|
+
margin: "0 0 4px",
|
|
122
|
+
};
|
|
123
|
+
const BODY: React.CSSProperties = {
|
|
124
|
+
fontFamily: "var(--font-mono)",
|
|
125
|
+
fontSize: 13,
|
|
126
|
+
color: "var(--ink-2)",
|
|
127
|
+
lineHeight: 1.65,
|
|
128
|
+
margin: 0,
|
|
129
|
+
};
|
|
130
|
+
const MUTED: React.CSSProperties = { ...BODY, color: "var(--dim)", fontSize: 12 };
|
|
131
|
+
const CODE: React.CSSProperties = { color: "var(--ink)", fontVariantLigatures: "none" };
|
|
132
|
+
|
|
133
|
+
/** A monospace inline command the user can copy by eye. */
|
|
134
|
+
function Cmd({ children }: { children: React.ReactNode }) {
|
|
135
|
+
return (
|
|
136
|
+
<code
|
|
137
|
+
style={{
|
|
138
|
+
fontFamily: "var(--font-mono)",
|
|
139
|
+
fontSize: 12,
|
|
140
|
+
color: "var(--accent-green)",
|
|
141
|
+
background: "var(--bg-2)",
|
|
142
|
+
border: "1px solid var(--line-2)",
|
|
143
|
+
padding: "1px 6px",
|
|
144
|
+
whiteSpace: "nowrap",
|
|
145
|
+
}}
|
|
146
|
+
>
|
|
147
|
+
{children}
|
|
148
|
+
</code>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ── scheduled audit section ──────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
function ScheduledAuditSection({
|
|
155
|
+
view,
|
|
156
|
+
onReload,
|
|
157
|
+
}: {
|
|
158
|
+
view: ScheduledAuditView;
|
|
159
|
+
onReload: () => Promise<void>;
|
|
160
|
+
}) {
|
|
161
|
+
const [auto, setAuto] = useState(view.auto);
|
|
162
|
+
const [interval, setIntervalDays] = useState(view.intervalDays);
|
|
163
|
+
const [savingAuto, setSavingAuto] = useState(false);
|
|
164
|
+
const [savingInterval, setSavingInterval] = useState(false);
|
|
165
|
+
const [running, setRunning] = useState(false);
|
|
166
|
+
const [runningNow, setRunningNow] = useState(false);
|
|
167
|
+
|
|
168
|
+
// Keep local state honest if a background reload brought new server truth
|
|
169
|
+
// (e.g. someone toggled via CLI, or the interval clamp changed the value).
|
|
170
|
+
useEffect(() => setAuto(view.auto), [view.auto]);
|
|
171
|
+
useEffect(() => setIntervalDays(view.intervalDays), [view.intervalDays]);
|
|
172
|
+
|
|
173
|
+
// Reflect a scan already in flight (started here or from /audit) so the button
|
|
174
|
+
// and status line don't claim the machine is idle when it isn't.
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
let cancelled = false;
|
|
177
|
+
(async () => {
|
|
178
|
+
try {
|
|
179
|
+
const res = await fetchWithTimeout("/api/audit/status", { cache: "no-store" });
|
|
180
|
+
if (res.ok && !cancelled) {
|
|
181
|
+
const s = (await res.json()) as { running?: boolean };
|
|
182
|
+
setRunning(Boolean(s.running));
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
/* status is best-effort; a missing poll just means we assume idle */
|
|
186
|
+
}
|
|
187
|
+
})();
|
|
188
|
+
return () => {
|
|
189
|
+
cancelled = true;
|
|
190
|
+
};
|
|
191
|
+
}, []);
|
|
192
|
+
|
|
193
|
+
const daemonInactive = view.daemon !== "running";
|
|
194
|
+
const daemonUnsupported = view.daemon === "unsupported-platform";
|
|
195
|
+
|
|
196
|
+
const onToggleAuto = useCallback(async () => {
|
|
197
|
+
const next = !auto;
|
|
198
|
+
setAuto(next); // optimistic
|
|
199
|
+
setSavingAuto(true);
|
|
200
|
+
try {
|
|
201
|
+
const res = await setAutoAuditAction(next);
|
|
202
|
+
setAuto(res.auto);
|
|
203
|
+
toast(res.auto ? "Scheduled scanning on." : "Scheduled scanning off.");
|
|
204
|
+
await onReload();
|
|
205
|
+
} catch {
|
|
206
|
+
setAuto(!next); // revert
|
|
207
|
+
toast("Could not save that.");
|
|
208
|
+
} finally {
|
|
209
|
+
setSavingAuto(false);
|
|
210
|
+
}
|
|
211
|
+
}, [auto, onReload]);
|
|
212
|
+
|
|
213
|
+
const commitInterval = useCallback(
|
|
214
|
+
async (raw: number) => {
|
|
215
|
+
setSavingInterval(true);
|
|
216
|
+
try {
|
|
217
|
+
// The config owns the 1..90 clamp; we reflect whatever it stored.
|
|
218
|
+
const res = await setAuditIntervalAction(raw);
|
|
219
|
+
setIntervalDays(res.intervalDays);
|
|
220
|
+
toast(`Scanning every ${res.intervalDays} day${res.intervalDays === 1 ? "" : "s"}.`);
|
|
221
|
+
} catch {
|
|
222
|
+
setIntervalDays(view.intervalDays);
|
|
223
|
+
toast("Could not save that.");
|
|
224
|
+
} finally {
|
|
225
|
+
setSavingInterval(false);
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
[view.intervalDays],
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const onRunNow = useCallback(async () => {
|
|
232
|
+
if (runningNow || running) return;
|
|
233
|
+
setRunningNow(true);
|
|
234
|
+
setRunning(true);
|
|
235
|
+
try {
|
|
236
|
+
await triggerRun({ cli: [], since: "all", noCache: false });
|
|
237
|
+
toast("Audit complete.");
|
|
238
|
+
await onReload();
|
|
239
|
+
} catch (err) {
|
|
240
|
+
const msg =
|
|
241
|
+
err instanceof RerunError && err.kind === "timeout"
|
|
242
|
+
? "The scan is taking a while — it will finish in the background."
|
|
243
|
+
: "The scan could not be completed.";
|
|
244
|
+
toast(msg);
|
|
245
|
+
} finally {
|
|
246
|
+
setRunningNow(false);
|
|
247
|
+
setRunning(false);
|
|
248
|
+
}
|
|
249
|
+
}, [runningNow, running, onReload]);
|
|
250
|
+
|
|
251
|
+
const sched = view.schedule;
|
|
252
|
+
const lastExitBad =
|
|
253
|
+
sched?.lastExitCode != null && sched.lastExitCode !== 0 && sched.lastExitCode !== 75;
|
|
254
|
+
|
|
255
|
+
return (
|
|
256
|
+
<div className="panel" style={{ padding: 20, marginBottom: 20 }}>
|
|
257
|
+
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, flexWrap: "wrap" }}>
|
|
258
|
+
<div>
|
|
259
|
+
<h3 style={SECTION_TITLE}>Scheduled audit</h3>
|
|
260
|
+
<p style={MUTED}>Scan this machine on a timer, in the background.</p>
|
|
261
|
+
</div>
|
|
262
|
+
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
263
|
+
{view.daemon === "running" && <Pill tone="ok">daemon running</Pill>}
|
|
264
|
+
{view.daemon === "stopped" && <Pill tone="warn">daemon stopped</Pill>}
|
|
265
|
+
{view.daemon === "not-installed" && <Pill tone="warn">daemon not installed</Pill>}
|
|
266
|
+
{view.daemon === "unsupported-platform" && <Pill tone="muted">daemon unavailable</Pill>}
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
{/* Enable toggle + the plain statement about what the scan reads. */}
|
|
271
|
+
<div style={{ display: "flex", alignItems: "flex-start", gap: 12, marginTop: 18 }}>
|
|
272
|
+
<div style={{ marginTop: 2 }}>
|
|
273
|
+
<Toggle
|
|
274
|
+
enabled={auto}
|
|
275
|
+
onChange={onToggleAuto}
|
|
276
|
+
disabled={savingAuto || daemonUnsupported}
|
|
277
|
+
label={auto ? "Turn off scheduled scanning" : "Turn on scheduled scanning"}
|
|
278
|
+
/>
|
|
279
|
+
</div>
|
|
280
|
+
<div style={{ minWidth: 0 }}>
|
|
281
|
+
<p style={{ ...BODY, color: "var(--ink)" }}>
|
|
282
|
+
{auto ? "Scanning this machine on a schedule." : "Scan this machine on a schedule."}
|
|
283
|
+
</p>
|
|
284
|
+
<p style={{ ...BODY, marginTop: 6 }}>
|
|
285
|
+
The scan reads the <span style={CODE}>contents</span> of every session transcript on
|
|
286
|
+
disk across all installed agent CLIs — your prompts, the files they read and wrote,
|
|
287
|
+
and command output. It runs entirely on this machine. Nothing is sent anywhere unless
|
|
288
|
+
you also turn on emailed reports below.
|
|
289
|
+
</p>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
{/* Interval. The number bounds mirror the config's own 1..90 clamp as a UX
|
|
294
|
+
hint; the config remains the authority and we reflect what it stored. */}
|
|
295
|
+
<div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 16, flexWrap: "wrap" }}>
|
|
296
|
+
<label htmlFor="scan-interval" style={{ ...BODY }}>
|
|
297
|
+
Scan every
|
|
298
|
+
</label>
|
|
299
|
+
<input
|
|
300
|
+
id="scan-interval"
|
|
301
|
+
type="number"
|
|
302
|
+
min={1}
|
|
303
|
+
max={90}
|
|
304
|
+
step={1}
|
|
305
|
+
value={interval}
|
|
306
|
+
disabled={savingInterval || daemonUnsupported}
|
|
307
|
+
onChange={(e) => setIntervalDays(Number(e.target.value))}
|
|
308
|
+
onBlur={(e) => {
|
|
309
|
+
const v = Number(e.target.value);
|
|
310
|
+
// A cleared/garbage field must not persist NaN — snap back to the
|
|
311
|
+
// stored value and let the config keep owning the real bounds.
|
|
312
|
+
if (!Number.isFinite(v)) {
|
|
313
|
+
setIntervalDays(view.intervalDays);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (v !== view.intervalDays) void commitInterval(v);
|
|
317
|
+
}}
|
|
318
|
+
style={{
|
|
319
|
+
width: 64,
|
|
320
|
+
padding: "6px 8px",
|
|
321
|
+
background: "var(--bg)",
|
|
322
|
+
border: "1px solid var(--line-2)",
|
|
323
|
+
color: "var(--ink)",
|
|
324
|
+
fontFamily: "var(--font-mono)",
|
|
325
|
+
fontSize: 13,
|
|
326
|
+
textAlign: "center",
|
|
327
|
+
}}
|
|
328
|
+
/>
|
|
329
|
+
<span style={BODY}>day{interval === 1 ? "" : "s"}.</span>
|
|
330
|
+
<span style={MUTED}>1–90; the config keeps it in range.</span>
|
|
331
|
+
</div>
|
|
332
|
+
|
|
333
|
+
{/* Last run / next due — read from the daemon-written schedule file. */}
|
|
334
|
+
<div
|
|
335
|
+
style={{
|
|
336
|
+
marginTop: 18,
|
|
337
|
+
paddingTop: 16,
|
|
338
|
+
borderTop: "1px dashed var(--line)",
|
|
339
|
+
display: "flex",
|
|
340
|
+
flexDirection: "column",
|
|
341
|
+
gap: 6,
|
|
342
|
+
}}
|
|
343
|
+
>
|
|
344
|
+
{running && (
|
|
345
|
+
<p style={{ ...BODY, color: "var(--accent-green)" }}>A scan is running now…</p>
|
|
346
|
+
)}
|
|
347
|
+
|
|
348
|
+
{/* Last run */}
|
|
349
|
+
{sched?.lastRunAtMs != null ? (
|
|
350
|
+
<p style={BODY}>
|
|
351
|
+
Last scheduled scan:{" "}
|
|
352
|
+
<span style={CODE}>{fmtAbsolute(sched.lastRunAtMs)}</span>{" "}
|
|
353
|
+
<span style={MUTED}>({formatRelativeTime(sched.lastRunAtMs)})</span>
|
|
354
|
+
</p>
|
|
355
|
+
) : view.lastResultAt ? (
|
|
356
|
+
<p style={BODY}>
|
|
357
|
+
Last audit result:{" "}
|
|
358
|
+
<span style={CODE}>{fmtAbsolute(new Date(view.lastResultAt).getTime())}</span>{" "}
|
|
359
|
+
<span style={MUTED}>(no scheduled scan has run yet)</span>
|
|
360
|
+
</p>
|
|
361
|
+
) : (
|
|
362
|
+
<p style={BODY}>No scan has run yet.</p>
|
|
363
|
+
)}
|
|
364
|
+
|
|
365
|
+
{/* Next due */}
|
|
366
|
+
{auto ? (
|
|
367
|
+
sched?.nextDueAtMs != null ? (
|
|
368
|
+
<p style={BODY}>
|
|
369
|
+
Next scan due:{" "}
|
|
370
|
+
<span style={CODE}>{fmtAbsolute(sched.nextDueAtMs)}</span>{" "}
|
|
371
|
+
<span style={MUTED}>({fmtFuture(sched.nextDueAtMs)})</span>
|
|
372
|
+
</p>
|
|
373
|
+
) : (
|
|
374
|
+
<p style={BODY}>
|
|
375
|
+
Next scan:{" "}
|
|
376
|
+
<span style={MUTED}>the daemon will schedule it shortly.</span>
|
|
377
|
+
</p>
|
|
378
|
+
)
|
|
379
|
+
) : (
|
|
380
|
+
<p style={MUTED}>Scheduled scanning is off — no scan is scheduled.</p>
|
|
381
|
+
)}
|
|
382
|
+
|
|
383
|
+
{lastExitBad && (
|
|
384
|
+
<p style={{ ...BODY, color: "var(--amber)" }}>
|
|
385
|
+
The last scheduled scan exited with code {sched?.lastExitCode}. It will retry on the
|
|
386
|
+
next tick.
|
|
387
|
+
</p>
|
|
388
|
+
)}
|
|
389
|
+
{sched?.schemaAhead && (
|
|
390
|
+
<p style={MUTED}>
|
|
391
|
+
A newer daemon wrote this schedule; some fields may not be shown.
|
|
392
|
+
</p>
|
|
393
|
+
)}
|
|
394
|
+
</div>
|
|
395
|
+
|
|
396
|
+
{/* Degraded daemon guidance — say plainly why "on" may still not run. */}
|
|
397
|
+
{auto && daemonInactive && (
|
|
398
|
+
<p style={{ ...BODY, color: "var(--amber)", marginTop: 14 }}>
|
|
399
|
+
{daemonUnsupported ? (
|
|
400
|
+
<>The background daemon isn't available on this platform, so scheduled scans
|
|
401
|
+
can't run here. You can still run one now, and use the audit page.</>
|
|
402
|
+
) : view.daemon === "not-installed" ? (
|
|
403
|
+
<>Scheduled scanning is on, but the background service isn't installed, so nothing
|
|
404
|
+
will run on the timer yet. Install it with <Cmd>failproofai config</Cmd>.</>
|
|
405
|
+
) : (
|
|
406
|
+
<>Scheduled scanning is on, but the background service is stopped, so nothing will run
|
|
407
|
+
until it starts. Reinstall or repair it with <Cmd>failproofai config</Cmd>.</>
|
|
408
|
+
)}
|
|
409
|
+
</p>
|
|
410
|
+
)}
|
|
411
|
+
|
|
412
|
+
{/* Run now — reuses the existing /api/audit/run route via triggerRun. */}
|
|
413
|
+
<div style={{ marginTop: 18 }}>
|
|
414
|
+
<button
|
|
415
|
+
type="button"
|
|
416
|
+
className="btn"
|
|
417
|
+
onClick={onRunNow}
|
|
418
|
+
disabled={runningNow || running}
|
|
419
|
+
>
|
|
420
|
+
{runningNow || running ? "[ scanning… ]" : "[ run a scan now ]"}
|
|
421
|
+
</button>
|
|
422
|
+
</div>
|
|
423
|
+
</div>
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// ── page ─────────────────────────────────────────────────────────────────────
|
|
428
|
+
|
|
429
|
+
export default function SettingsClient() {
|
|
430
|
+
const [scheduled, setScheduled] = useState<ScheduledAuditView | null>(null);
|
|
431
|
+
const [loading, setLoading] = useState(true);
|
|
432
|
+
const [error, setError] = useState(false);
|
|
433
|
+
const mounted = useRef(true);
|
|
434
|
+
|
|
435
|
+
const reload = useCallback(async () => {
|
|
436
|
+
const s = await getScheduledAuditAction();
|
|
437
|
+
if (!mounted.current) return;
|
|
438
|
+
setScheduled(s);
|
|
439
|
+
}, []);
|
|
440
|
+
|
|
441
|
+
useEffect(() => {
|
|
442
|
+
mounted.current = true;
|
|
443
|
+
(async () => {
|
|
444
|
+
try {
|
|
445
|
+
await reload();
|
|
446
|
+
} catch {
|
|
447
|
+
if (mounted.current) setError(true);
|
|
448
|
+
} finally {
|
|
449
|
+
if (mounted.current) setLoading(false);
|
|
450
|
+
}
|
|
451
|
+
})();
|
|
452
|
+
return () => {
|
|
453
|
+
mounted.current = false;
|
|
454
|
+
};
|
|
455
|
+
}, [reload]);
|
|
456
|
+
|
|
457
|
+
return (
|
|
458
|
+
<main className="report">
|
|
459
|
+
<section className="section" data-screen-label="settings">
|
|
460
|
+
<h2
|
|
461
|
+
style={{
|
|
462
|
+
fontFamily: "var(--font-mono)",
|
|
463
|
+
fontSize: 24,
|
|
464
|
+
fontWeight: 600,
|
|
465
|
+
letterSpacing: "-0.01em",
|
|
466
|
+
color: "var(--ink)",
|
|
467
|
+
margin: "0 0 8px",
|
|
468
|
+
}}
|
|
469
|
+
>
|
|
470
|
+
Settings
|
|
471
|
+
</h2>
|
|
472
|
+
<p style={{ ...BODY, maxWidth: 720, margin: "0 0 24px" }}>
|
|
473
|
+
Machine-level controls for scheduled scanning and emailed reports.
|
|
474
|
+
</p>
|
|
475
|
+
|
|
476
|
+
{loading ? (
|
|
477
|
+
<p style={MUTED}>Loading…</p>
|
|
478
|
+
) : error || !scheduled ? (
|
|
479
|
+
<p style={{ ...BODY, color: "var(--accent-pink)" }}>
|
|
480
|
+
Could not load settings. Refresh to try again.
|
|
481
|
+
</p>
|
|
482
|
+
) : (
|
|
483
|
+
<ScheduledAuditSection view={scheduled} onReload={reload} />
|
|
484
|
+
)}
|
|
485
|
+
</section>
|
|
486
|
+
</main>
|
|
487
|
+
);
|
|
488
|
+
}
|