failproofai 1.0.0-beta.0 → 1.0.0-beta.10
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} +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} +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]__04pwh4t._.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]__10butu6._.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]__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/_0-yi74u._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_04mekdy._.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/_1y4qr7u._.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/00adubtgov21s.js +1 -0
- package/.next/standalone/.next/static/chunks/{03fmihek9n986.js → 06zgzyh3wv1ig.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1hdhteui932bq.js → 0qmo2t50kpb30.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1grd7y94-igz9.js → 0yudogeqddpzr.js} +1 -1
- package/.next/standalone/.next/static/chunks/16p68cybetr31.js +1 -0
- package/.next/standalone/.next/static/chunks/1bm0q12hgs22l.css +2 -0
- package/.next/standalone/.next/static/chunks/{1d6ncspjagm53.js → 1fu90ch80qngv.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2vr7aazw6k2-n.js → 1m416lie3qwsj.js} +1 -1
- package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
- package/.next/standalone/.next/static/chunks/1ypqzvoal8j56.js +6 -0
- package/.next/standalone/.next/static/chunks/23kev26by1e4f.js +1 -0
- package/.next/standalone/.next/static/chunks/{29k6ef49zppxj.js → 2h4sbmp0ad78w.js} +1 -1
- package/.next/standalone/.next/static/chunks/2obwg4tterjpa.js +1 -0
- package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
- package/.next/standalone/.next/static/chunks/{29ydr-8q0zis6.js → 30ufy4_e2n78e.js} +22 -22
- package/.next/standalone/.next/static/chunks/31coz11ia5vc4.js +1 -0
- package/.next/standalone/.next/static/chunks/{1s-_swlc80y43.js → 3p4j0l1o_p-nm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0ip1n8xmgxarv.js → 3uxnxwxcctyj0.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 +17 -10
- package/.next/standalone/proxy.ts +89 -1
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +518 -79
- package/dist/cli.mjs +11484 -6046
- package/dist/worker.mjs +2019 -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 +17 -10
- package/scripts/build-daemon-packages.mjs +210 -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 +17 -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 +915 -158
- package/src/hooks/custom-hooks-loader.ts +191 -33
- package/src/hooks/daemon-client.ts +136 -17
- package/src/hooks/daemon-download.ts +192 -30
- package/src/hooks/daemon-service.ts +1190 -92
- 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 +345 -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]__0259h38._.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]__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]__1zj-87g._.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_1-upb14.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/0u9hc6oxx-gpj.js +0 -6
- package/.next/standalone/.next/static/chunks/0zbxssxh53n-3.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/.next/static/chunks/3wrgbih-516uj.js +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/{7Lxtd352rSaHB-pmm6G8M → pBBcTvE6_VueYGbacy1kg}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{7Lxtd352rSaHB-pmm6G8M → pBBcTvE6_VueYGbacy1kg}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{7Lxtd352rSaHB-pmm6G8M → pBBcTvE6_VueYGbacy1kg}/_ssgManifest.js +0 -0
package/bin/failproofai.mjs
CHANGED
|
@@ -124,13 +124,13 @@ if (hookIdx >= 0) {
|
|
|
124
124
|
// machine until `failproofai config` has installed failproofaid AND
|
|
125
125
|
// written the daemonConfigured marker (Stage 4). Until then this is
|
|
126
126
|
// byte-for-byte the same handleHookEvent(...) call below.
|
|
127
|
-
const { isDaemonConfigured,
|
|
127
|
+
const { isDaemonConfigured, attemptDaemonHook } = await import("../src/hooks/daemon-client");
|
|
128
128
|
if (isDaemonConfigured()) {
|
|
129
129
|
const { readStdinPayload } = await import("../src/hooks/read-stdin");
|
|
130
130
|
const { evaluateHookEvent } = await import("../src/hooks/handler");
|
|
131
131
|
const stdinRead = await readStdinPayload();
|
|
132
132
|
|
|
133
|
-
const
|
|
133
|
+
const attempt = await attemptDaemonHook({
|
|
134
134
|
hookEvent: eventType,
|
|
135
135
|
cli,
|
|
136
136
|
stdin: stdinRead.payload,
|
|
@@ -140,21 +140,40 @@ if (hookIdx >= 0) {
|
|
|
140
140
|
cwd: process.cwd(),
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
143
|
+
// On a daemon-configured machine the daemon is the ONLY evaluator. Every
|
|
144
|
+
// way of not getting an answer from it denies, and in-process evaluation
|
|
145
|
+
// is never reached from this branch — that is what "all enforcement is
|
|
146
|
+
// routed through the daemon" means, and a fallback here would be a second
|
|
147
|
+
// policy engine reachable by breaking the first.
|
|
148
|
+
//
|
|
149
|
+
// The two failures still differ in what the USER has to do, so they are
|
|
150
|
+
// told apart in the message and nowhere else:
|
|
151
|
+
//
|
|
152
|
+
// protocol-mismatch: a daemon answered, so it is alive; the CLI and the
|
|
153
|
+
// daemon are different versions, which is what an `npm update` that
|
|
154
|
+
// has not been followed by `failproofai config` looks like. The remedy
|
|
155
|
+
// is an upgrade, and naming it is the difference between a one-command
|
|
156
|
+
// fix and a support ticket. `daemonVersionSkew()` has already been
|
|
157
|
+
// hinting this on every CLI command.
|
|
158
|
+
//
|
|
159
|
+
// unreachable: nothing answered. A stopped service, a deleted socket and
|
|
160
|
+
// deliberate tampering are indistinguishable from here — and a machine
|
|
161
|
+
// where stopping one service silently disables every guardrail is not
|
|
162
|
+
// a guarded machine.
|
|
163
|
+
let result;
|
|
164
|
+
if (attempt.ok) {
|
|
165
|
+
result = attempt.response;
|
|
166
|
+
} else {
|
|
167
|
+
const reason =
|
|
168
|
+
attempt.failure === "protocol-mismatch"
|
|
169
|
+
? "failproofaid is running a different protocol version than this CLI, so it " +
|
|
170
|
+
"cannot evaluate this call. Run `failproofai config` to update the daemon."
|
|
171
|
+
: "failproofaid could not be reached. This machine is configured to run hooks through it " +
|
|
172
|
+
"— check the daemon (see `failproofai config`) rather than retrying blindly.";
|
|
173
|
+
result = await evaluateHookEvent(eventType, cli, stdinRead.payload, {
|
|
174
|
+
forceDecision: { decision: "deny", reason },
|
|
175
|
+
});
|
|
176
|
+
}
|
|
158
177
|
|
|
159
178
|
if (result.stdout) process.stdout.write(result.stdout);
|
|
160
179
|
if (result.stderr) process.stderr.write(result.stderr);
|
|
@@ -191,18 +210,8 @@ if (hookIdx >= 0) {
|
|
|
191
210
|
* Error → unexpected; shows message only, exits 2
|
|
192
211
|
*/
|
|
193
212
|
async function runCli() {
|
|
194
|
-
// Report a fresh install / upgrade. Deliberately here rather than at module
|
|
195
|
-
// scope: everything above this point is the --hook fast path, which runs on
|
|
196
|
-
// every tool call. No-ops after the first run on a given version.
|
|
197
|
-
try {
|
|
198
|
-
const { maybeReportInstall } = await import("../lib/install-check");
|
|
199
|
-
await maybeReportInstall(version);
|
|
200
|
-
} catch {
|
|
201
|
-
// never block a command on reporting
|
|
202
|
-
}
|
|
203
|
-
|
|
204
213
|
// --help / -h (only when not inside a subcommand that handles its own --help)
|
|
205
|
-
const SUBCOMMANDS = ["policies", "policy", "
|
|
214
|
+
const SUBCOMMANDS = ["policies", "policy", "audit", "config", "uninstall", "backfill"];
|
|
206
215
|
if ((args.includes("--help") || args.includes("-h")) && !SUBCOMMANDS.includes(args[0])) {
|
|
207
216
|
const extraArgs = args.filter((a) => a !== "--help" && a !== "-h");
|
|
208
217
|
if (extraArgs.length > 0) {
|
|
@@ -243,16 +252,20 @@ COMMANDS
|
|
|
243
252
|
|
|
244
253
|
policies --help, -h Show this help for the policies command
|
|
245
254
|
|
|
246
|
-
auth Sign in / out of FailproofAI from the CLI.
|
|
247
|
-
login Email + OTP flow; writes ~/.failproofai/auth.json
|
|
248
|
-
logout Revoke this session and remove auth.json
|
|
249
|
-
whoami Print the currently authenticated identity
|
|
250
|
-
auth --help, -h Show this help for the auth command
|
|
251
|
-
|
|
252
255
|
audit Audit your agent's behavior, then open the
|
|
253
256
|
dashboard at http://localhost:8020/audit
|
|
254
257
|
audit --help, -h Show this help for the audit command
|
|
255
258
|
|
|
259
|
+
uninstall Remove failproofai from this machine: hook
|
|
260
|
+
entries from every agent CLI, and the daemon
|
|
261
|
+
service. Run this BEFORE \`npm rm -g failproofai\`
|
|
262
|
+
— npm runs no uninstall script, so removing the
|
|
263
|
+
package alone leaves both behind.
|
|
264
|
+
--purge Also delete ~/.failproofai (settings,
|
|
265
|
+
credentials, audit history, daemon binary)
|
|
266
|
+
--dry-run Show what would be removed, change nothing
|
|
267
|
+
--yes, -y Skip the confirmation prompt
|
|
268
|
+
|
|
256
269
|
--version, -v Print version and exit
|
|
257
270
|
--help, -h Show this help message
|
|
258
271
|
|
|
@@ -301,6 +314,296 @@ LINKS
|
|
|
301
314
|
process.exit(0);
|
|
302
315
|
}
|
|
303
316
|
|
|
317
|
+
// First-run onboarding — before any subcommand runs its own work.
|
|
318
|
+
//
|
|
319
|
+
// On a machine that has never been set up, the first thing the user typed is
|
|
320
|
+
// almost never the thing they need first, so we run the wizard and then let
|
|
321
|
+
// their original command proceed. Exemptions matter more than the rule:
|
|
322
|
+
//
|
|
323
|
+
// --hook never reaches here (it exits above) — it runs on every
|
|
324
|
+
// tool call, and a wizard on that path would hang an agent
|
|
325
|
+
// --version/--help answering "what is this" must not require setup
|
|
326
|
+
// config IS the wizard
|
|
327
|
+
// policies/policy explicit configuration actions. Intercepting these would
|
|
328
|
+
// fight the intent the user just stated, and would break
|
|
329
|
+
// non-interactive scripts that call them to do setup.
|
|
330
|
+
//
|
|
331
|
+
// `maybeFirstRunConfigure` is itself a no-op on a configured machine, on a
|
|
332
|
+
// non-TTY, and under sudo — so this is a cheap check, not a second gate.
|
|
333
|
+
// Layout check, before onboarding decides anything. A home written by an
|
|
334
|
+
// older layout is reset here — visibly, in a real command the user typed —
|
|
335
|
+
// rather than from a hook, which runs unattended once per tool call. A home
|
|
336
|
+
// written by a NEWER layout stops the command instead: that data is fine and
|
|
337
|
+
// an upgrade would read it, so deleting it would destroy something
|
|
338
|
+
// recoverable.
|
|
339
|
+
//
|
|
340
|
+
// `audit --scheduled` is the exception, and it takes the HOOK's branch: it is
|
|
341
|
+
// spawned by failproofaid on a timer, so "visibly, in a real command the user
|
|
342
|
+
// typed" is exactly what it is not. `checkLayoutForCli()` deletes
|
|
343
|
+
// config.toml and credentials.toml (see `resettablePaths`), so letting a
|
|
344
|
+
// background process reach it would silently revoke a user's
|
|
345
|
+
// `[telemetry] enabled = false`, erase their cloud enrolment, and switch off
|
|
346
|
+
// `[audit] auto` — the setting that scheduled the run — with the explanation
|
|
347
|
+
// going only to the service journal. Reachable on every machine at the next
|
|
348
|
+
// LAYOUT_VERSION bump, when a home carrying `auto = true` is by definition
|
|
349
|
+
// stale. Verified live: one scheduled tick took a home's whole config.
|
|
350
|
+
//
|
|
351
|
+
// `--help` / `--version` take the same exemption `first-run-gate.ts` gives
|
|
352
|
+
// them, and for a stronger reason. Those two answer "what is this / how do I
|
|
353
|
+
// use it"; the subcommands that parse their own help (`policies --help`) fall
|
|
354
|
+
// past the help block above and reach here, so without this a user typing
|
|
355
|
+
// `failproofai policies --help` had their home reset by a question. The
|
|
356
|
+
// adjacent, far less destructive first-run gate exempted help from the start.
|
|
357
|
+
let layoutWasReset = false;
|
|
358
|
+
{
|
|
359
|
+
const isHelpOrVersion =
|
|
360
|
+
args.includes("--help") || args.includes("-h") || args.includes("--version") || args.includes("-v");
|
|
361
|
+
if (args[0] === "audit" && args.includes("--scheduled")) {
|
|
362
|
+
const { layoutWarningForHook } = await import("../src/hooks/fp-reset");
|
|
363
|
+
const warning = layoutWarningForHook();
|
|
364
|
+
if (warning) {
|
|
365
|
+
// Exit 1, not 75: 75 means "another audit holds the lock" and is retried
|
|
366
|
+
// in fifteen minutes, which here would just re-warn four times an hour
|
|
367
|
+
// forever. A stale home is a real failure that `failproofai config`
|
|
368
|
+
// fixes, so it is reported and retried at the ordinary cadence.
|
|
369
|
+
console.error(warning);
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
} else if (!isHelpOrVersion) {
|
|
373
|
+
const { checkLayoutForCli } = await import("../src/hooks/fp-reset");
|
|
374
|
+
const check = await checkLayoutForCli();
|
|
375
|
+
for (const line of check.lines) console.error(line);
|
|
376
|
+
if (check.fatal) process.exit(1);
|
|
377
|
+
layoutWasReset = check.didReset;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Report a fresh install / upgrade. AFTER the layout check above, never
|
|
382
|
+
// before: this writes the `last-version` marker, and while that file lived at
|
|
383
|
+
// the root of the home it was one of the landmarks `detectLayout()` reads as
|
|
384
|
+
// "layout 1". Running first meant the CLI created the file and then read it
|
|
385
|
+
// back as evidence of an old layout, so every genuinely fresh machine had its
|
|
386
|
+
// very first command open with "failproofai reorganised … Removed 1 item(s)
|
|
387
|
+
// from the old layout." Nothing was lost — there was nothing there — but
|
|
388
|
+
// training every new user to ignore that banner is expensive given what it
|
|
389
|
+
// says on a real layout-1 home. The file has also moved under `state/`, so
|
|
390
|
+
// the two are independent now; the order is kept because it is the correct
|
|
391
|
+
// one regardless.
|
|
392
|
+
//
|
|
393
|
+
// Deliberately not at module scope: everything above the CLI entry is the
|
|
394
|
+
// --hook fast path, which runs on every tool call. No-ops after the first run
|
|
395
|
+
// on a given version.
|
|
396
|
+
try {
|
|
397
|
+
const { maybeReportInstall } = await import("../lib/install-check");
|
|
398
|
+
await maybeReportInstall(version);
|
|
399
|
+
} catch {
|
|
400
|
+
// never block a command on reporting
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const { shouldOfferFirstRun } = await import("../src/hooks/first-run-gate");
|
|
404
|
+
if (shouldOfferFirstRun(args) || layoutWasReset) {
|
|
405
|
+
try {
|
|
406
|
+
const { maybeFirstRunConfigure } = await import("../src/hooks/configure-wizard");
|
|
407
|
+
// `audit` runs its own scan immediately after this returns; firing the
|
|
408
|
+
// post-setup audit too would scan the whole history twice in a row.
|
|
409
|
+
//
|
|
410
|
+
// `force` after a reset: the home's policy config is gone, but the agent
|
|
411
|
+
// CLIs' settings files were deliberately left alone, so `isConfigured()`
|
|
412
|
+
// still reads true off `hasGlobalHooks` and setup would be skipped —
|
|
413
|
+
// leaving hooks firing against no policies, silently and permanently.
|
|
414
|
+
await maybeFirstRunConfigure(
|
|
415
|
+
{},
|
|
416
|
+
{ postSetupAudit: args[0] !== "audit", force: layoutWasReset },
|
|
417
|
+
);
|
|
418
|
+
} catch {
|
|
419
|
+
// Onboarding is never allowed to block the command the user actually typed.
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// backfill [--since <YYYY-MM-DD|Nd>] [--dry-run]
|
|
424
|
+
//
|
|
425
|
+
// Hands off to the daemon rather than doing the work: the cursors it rewinds
|
|
426
|
+
// are held in memory by the RUNNING collector, which would write them back
|
|
427
|
+
// over. Every precondition a person can get wrong is still checked HERE,
|
|
428
|
+
// synchronously, because reporting success and leaving the real failure in the
|
|
429
|
+
// journal is what already cost twenty minutes on a live machine.
|
|
430
|
+
if (args[0] === "backfill") {
|
|
431
|
+
const subArgs = args.slice(1);
|
|
432
|
+
if (subArgs.includes("--help") || subArgs.includes("-h")) {
|
|
433
|
+
console.log(`
|
|
434
|
+
failproofai backfill — re-send history the collector has already read past
|
|
435
|
+
|
|
436
|
+
USAGE
|
|
437
|
+
failproofai backfill [--since <when>] [--dry-run]
|
|
438
|
+
|
|
439
|
+
WHY
|
|
440
|
+
The collector never re-reads a file it has a cursor for, which is right until
|
|
441
|
+
the dashboard's data is cleared, a machine is re-enrolled, or cursors advanced
|
|
442
|
+
before there was anywhere to send. Then the history exists on disk and nowhere
|
|
443
|
+
else, with no way to ask for it again.
|
|
444
|
+
|
|
445
|
+
Re-sending is safe: redaction is deterministic, so a re-sent event hashes
|
|
446
|
+
identically to its first send and collapses into the row already there.
|
|
447
|
+
|
|
448
|
+
OPTIONS
|
|
449
|
+
--since <when> How far back. \`30d\`, \`6m\`, or \`YYYY-MM-DD\`.
|
|
450
|
+
Default: 30 days.
|
|
451
|
+
--dry-run Report what would be re-read and change nothing.
|
|
452
|
+
|
|
453
|
+
Which streams are sent follows [collector] in ~/.failproofai/config.toml —
|
|
454
|
+
a backfill never sends something your config says you do not want.
|
|
455
|
+
`);
|
|
456
|
+
process.exit(0);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const KNOWN = new Set(["--since", "--dry-run"]);
|
|
460
|
+
const unknown = subArgs.find((a, i) => a.startsWith("-") && !KNOWN.has(a) && subArgs[i - 1] !== "--since");
|
|
461
|
+
if (unknown) {
|
|
462
|
+
throw new CliError(`Unexpected argument: ${unknown}\nRun \`failproofai backfill --help\` for usage.`);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
let sinceMs;
|
|
466
|
+
const sinceIdx = subArgs.indexOf("--since");
|
|
467
|
+
if (sinceIdx >= 0) {
|
|
468
|
+
const raw = subArgs[sinceIdx + 1];
|
|
469
|
+
if (!raw || raw.startsWith("-")) throw new CliError("Missing value after --since.");
|
|
470
|
+
// `30d` / `6m` / an ISO date. Rejected rather than guessed at: silently
|
|
471
|
+
// reading an unparseable window as "the default" would send a different
|
|
472
|
+
// amount of history than was asked for, and nothing would say so.
|
|
473
|
+
const rel = /^(\d+)([dmy])$/.exec(raw);
|
|
474
|
+
if (rel) {
|
|
475
|
+
const n = Number(rel[1]);
|
|
476
|
+
const days = rel[2] === "d" ? n : rel[2] === "m" ? n * 30 : n * 365;
|
|
477
|
+
sinceMs = Date.now() - days * 24 * 60 * 60 * 1000;
|
|
478
|
+
} else {
|
|
479
|
+
const t = Date.parse(raw);
|
|
480
|
+
if (Number.isNaN(t)) {
|
|
481
|
+
throw new CliError(`Could not read --since ${raw}. Use 30d, 6m, or YYYY-MM-DD.`);
|
|
482
|
+
}
|
|
483
|
+
sinceMs = t;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
lastSubcommand = "backfill";
|
|
488
|
+
const { runBackfillCommand } = await import("../src/hooks/backfill-cli");
|
|
489
|
+
const result = runBackfillCommand({ sinceMs, dryRun: subArgs.includes("--dry-run") });
|
|
490
|
+
for (const line of result.lines) {
|
|
491
|
+
if (result.exitCode === 0) console.log(line);
|
|
492
|
+
else console.error(line);
|
|
493
|
+
}
|
|
494
|
+
await track("cli_backfill", { ok: result.exitCode === 0, dry_run: subArgs.includes("--dry-run"), explicit_since: sinceIdx >= 0 });
|
|
495
|
+
lastSubcommand = null;
|
|
496
|
+
await exitAfterFlush(result.exitCode);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// uninstall [--purge] [--dry-run] [--yes|-y]
|
|
501
|
+
//
|
|
502
|
+
// Top-level, and deliberately NOT a flag on `policies`. `policies --uninstall`
|
|
503
|
+
// disables policies; this removes the product — hook entries across every
|
|
504
|
+
// agent CLI plus the root-owned daemon service — and the two must not be one
|
|
505
|
+
// keystroke apart.
|
|
506
|
+
if (args[0] === "uninstall") {
|
|
507
|
+
const subArgs = args.slice(1);
|
|
508
|
+
if (subArgs.includes("--help") || subArgs.includes("-h")) {
|
|
509
|
+
console.log(`
|
|
510
|
+
failproofai uninstall — remove failproofai from this machine
|
|
511
|
+
|
|
512
|
+
USAGE
|
|
513
|
+
failproofai uninstall [--purge] [--dry-run] [--yes]
|
|
514
|
+
|
|
515
|
+
WHAT IT REMOVES
|
|
516
|
+
• failproofai hook entries from every agent CLI that has them
|
|
517
|
+
• the failproofaid daemon service (needs sudo)
|
|
518
|
+
• the "require the daemon" flag — cleared FIRST, so a partial uninstall can
|
|
519
|
+
never leave this machine denying every tool call
|
|
520
|
+
|
|
521
|
+
OPTIONS
|
|
522
|
+
--purge Also delete ~/.failproofai — settings, credentials, audit
|
|
523
|
+
history and the downloaded daemon binary. Off by default so a
|
|
524
|
+
reinstall keeps your history.
|
|
525
|
+
--dry-run Print what would be removed and change nothing.
|
|
526
|
+
--yes, -y Skip the confirmation prompt. Required when there is no TTY.
|
|
527
|
+
|
|
528
|
+
WHY THIS EXISTS
|
|
529
|
+
npm runs no uninstall script, so \`npm rm -g failproofai\` removes the package
|
|
530
|
+
and leaves the hook entries and the service behind. Run this first, then:
|
|
531
|
+
npm rm -g failproofai
|
|
532
|
+
`);
|
|
533
|
+
process.exit(0);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const KNOWN = new Set(["--purge", "--dry-run", "--yes", "-y"]);
|
|
537
|
+
const unknown = subArgs.find((a) => !KNOWN.has(a));
|
|
538
|
+
if (unknown) {
|
|
539
|
+
throw new CliError(
|
|
540
|
+
`Unexpected argument: ${unknown}\nRun \`failproofai uninstall --help\` for usage.`,
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
lastSubcommand = "uninstall_command";
|
|
545
|
+
const { runUninstallCommand } = await import("../src/hooks/uninstall-cli");
|
|
546
|
+
const purge = subArgs.includes("--purge");
|
|
547
|
+
const dryRun = subArgs.includes("--dry-run");
|
|
548
|
+
const yes = subArgs.includes("--yes") || subArgs.includes("-y");
|
|
549
|
+
|
|
550
|
+
// Set only when the prompt actually rendered the plan, which is the one
|
|
551
|
+
// case where printing it again would duplicate it.
|
|
552
|
+
let planWasShown = false;
|
|
553
|
+
const result = await runUninstallCommand({
|
|
554
|
+
purge,
|
|
555
|
+
dryRun,
|
|
556
|
+
yes,
|
|
557
|
+
cwd: process.cwd(),
|
|
558
|
+
// Only offered when a person can actually answer. Without a TTY the
|
|
559
|
+
// command requires --yes rather than assuming consent — see the module.
|
|
560
|
+
confirm: process.stdin.isTTY
|
|
561
|
+
? async (planLines) => {
|
|
562
|
+
planWasShown = true;
|
|
563
|
+
const { selectOne } = await import("../src/hooks/tui");
|
|
564
|
+
// "No" first, so the default landing position on Enter is the
|
|
565
|
+
// non-destructive one.
|
|
566
|
+
const answer = await selectOne({
|
|
567
|
+
message: purge
|
|
568
|
+
? "Remove failproofai and DELETE ~/.failproofai?"
|
|
569
|
+
: "Remove failproofai from this machine?",
|
|
570
|
+
body: planLines,
|
|
571
|
+
choices: [
|
|
572
|
+
{ label: "No, cancel", value: false },
|
|
573
|
+
{ label: purge ? "Yes, remove and purge" : "Yes, remove it", value: true },
|
|
574
|
+
],
|
|
575
|
+
});
|
|
576
|
+
return answer === true;
|
|
577
|
+
}
|
|
578
|
+
: undefined,
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
// The prompt already rendered the plan as its body; re-printing it would
|
|
582
|
+
// show the same block twice. `planLines` is reported by the command rather
|
|
583
|
+
// than guessed from the text — see UninstallResult.
|
|
584
|
+
const skip = planWasShown ? result.planLines : 0;
|
|
585
|
+
for (const line of result.lines.slice(skip)) {
|
|
586
|
+
if (result.exitCode === 0) console.log(line);
|
|
587
|
+
else console.error(line);
|
|
588
|
+
}
|
|
589
|
+
// NOT after a purge. `track` resolves the instance id, and `getInstanceId()`
|
|
590
|
+
// lazily WRITES ~/.failproofai/state/telemetry-id — which re-created the
|
|
591
|
+
// whole directory seconds after the purge deleted it, leaving a machine the
|
|
592
|
+
// user had just wiped holding a brand-new tracking identifier and making
|
|
593
|
+
// the command's own "✓ deleted" line false. A purge means gone; nothing
|
|
594
|
+
// gets to touch the home afterwards, least of all telemetry.
|
|
595
|
+
if (!result.purged) {
|
|
596
|
+
await track("cli_uninstall_command", {
|
|
597
|
+
ok: result.exitCode === 0,
|
|
598
|
+
purge,
|
|
599
|
+
dry_run: dryRun,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
lastSubcommand = null;
|
|
603
|
+
await exitAfterFlush(result.exitCode);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
|
|
304
607
|
// policies [--install|-i|--uninstall|-u|--help|-h] [names...] [--scope] [--beta] [--custom|-c <path>]
|
|
305
608
|
if (args[0] === "policies") {
|
|
306
609
|
const subArgs = args.slice(1);
|
|
@@ -566,19 +869,6 @@ EXAMPLES
|
|
|
566
869
|
process.exit(0);
|
|
567
870
|
}
|
|
568
871
|
|
|
569
|
-
// auth — email-OTP login flow against the FailproofAI api-server.
|
|
570
|
-
if (args[0] === "auth") {
|
|
571
|
-
lastSubcommand = "auth";
|
|
572
|
-
const { runAuthCli } = await import("../src/auth/cli");
|
|
573
|
-
await runAuthCli(args.slice(1));
|
|
574
|
-
await track("cli_auth_invoked", {
|
|
575
|
-
args_count: args.length - 1,
|
|
576
|
-
subcommand: args[1] ?? "help",
|
|
577
|
-
exit_code: process.exitCode ?? 0,
|
|
578
|
-
});
|
|
579
|
-
process.exit(process.exitCode ?? 0);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
872
|
// audit — scan local agent-CLI history, then launch the dashboard at /audit.
|
|
583
873
|
if (args[0] === "audit") {
|
|
584
874
|
lastSubcommand = "audit";
|
|
@@ -769,19 +1059,183 @@ WHAT IT DOES
|
|
|
769
1059
|
3. Policies — presets (combine any), Everything, or a custom pick
|
|
770
1060
|
4. Review — confirms the exact files it will change, then applies
|
|
771
1061
|
|
|
1062
|
+
FAILPROOF CLOUD
|
|
1063
|
+
failproofai config --connect <url> --token <key> [--machine-id <id>]
|
|
1064
|
+
Connect this machine to Failproof Cloud
|
|
1065
|
+
[--no-transcripts] decisions only, no transcripts
|
|
1066
|
+
failproofai config --disconnect Stop pulling policy and sending activity
|
|
1067
|
+
failproofai config --status Show connection and pause state
|
|
1068
|
+
|
|
1069
|
+
One connection, two capabilities: this machine PULLS centrally-managed
|
|
1070
|
+
policies and SENDS what its hooks decided, so the dashboard shows the fleet
|
|
1071
|
+
it is enforcing on. Both are checked against the server before anything is
|
|
1072
|
+
written, and reported separately — a key carrying policies:pull but not
|
|
1073
|
+
events:add connects for policy and says exactly why the dashboard is empty.
|
|
1074
|
+
|
|
1075
|
+
Tokens are stored owner-only in ~/.failproofai/, never in the service unit —
|
|
1076
|
+
that file is world-readable. Connecting needs no sudo, and the machine id
|
|
1077
|
+
defaults to this host's name.
|
|
1078
|
+
|
|
1079
|
+
Connecting sends BOTH policy decisions and full session transcripts. A
|
|
1080
|
+
transcript carries prompts, file contents and whatever was pasted into a
|
|
1081
|
+
terminal — that is the point of connecting, and it is stated here rather than
|
|
1082
|
+
buried behind a flag nobody finds. Use --no-transcripts for decisions only.
|
|
1083
|
+
|
|
1084
|
+
PAUSING ENFORCEMENT (one session, always time-boxed)
|
|
1085
|
+
failproofai config --pause Pause this directory's newest agent session (30m)
|
|
1086
|
+
failproofai config --pause 10m Pause for a given time (max 8h; s/m/h, bare = minutes)
|
|
1087
|
+
failproofai config --resume End the pause early
|
|
1088
|
+
failproofai config --status Show what is paused and when it lifts
|
|
1089
|
+
--session <id> Target a specific session
|
|
1090
|
+
--all With --resume, end every active pause
|
|
1091
|
+
|
|
1092
|
+
A pause suspends builtin, custom and convention policies for that session
|
|
1093
|
+
only, and always expires on its own. Cloud-managed policies keep enforcing.
|
|
1094
|
+
|
|
772
1095
|
Prefer flags? See \`failproofai policies --help\`.
|
|
773
1096
|
`.trimStart());
|
|
774
1097
|
process.exit(0);
|
|
775
1098
|
}
|
|
776
1099
|
lastSubcommand = "config";
|
|
1100
|
+
|
|
1101
|
+
// --pause / --resume / --status are non-interactive session actions that
|
|
1102
|
+
// share `config`'s surface but not the wizard. They write session state,
|
|
1103
|
+
// never the config file — a pause that reached policies-config.json would
|
|
1104
|
+
// be committed and outlive the session that asked for it.
|
|
1105
|
+
// Cloud enrolment. Deliberately writes a credential file the daemon reads
|
|
1106
|
+
// rather than an Environment= line in the service unit: that unit is
|
|
1107
|
+
// installed world-readable (0644, /etc/systemd/system), so a token there
|
|
1108
|
+
// would be readable by every local user. Keeping it out also means no
|
|
1109
|
+
// sudo, and lets an already-installed daemon be connected.
|
|
1110
|
+
const connectIdx = args.indexOf("--connect");
|
|
1111
|
+
const wantsDisconnect = args.includes("--disconnect");
|
|
1112
|
+
if (connectIdx >= 0 || wantsDisconnect) {
|
|
1113
|
+
if (connectIdx >= 0 && wantsDisconnect) {
|
|
1114
|
+
throw new CliError("--connect and --disconnect cannot be combined.");
|
|
1115
|
+
}
|
|
1116
|
+
const valueAfter = (flag) => {
|
|
1117
|
+
const i = args.indexOf(flag);
|
|
1118
|
+
if (i < 0) return undefined;
|
|
1119
|
+
const v = args[i + 1];
|
|
1120
|
+
if (!v || v.startsWith("-")) throw new CliError(`Missing value after ${flag}.`);
|
|
1121
|
+
return v;
|
|
1122
|
+
};
|
|
1123
|
+
let result;
|
|
1124
|
+
if (wantsDisconnect) {
|
|
1125
|
+
const { runDisconnectCommand } = await import("../src/hooks/cloud-enrollment-cli");
|
|
1126
|
+
result = runDisconnectCommand();
|
|
1127
|
+
} else {
|
|
1128
|
+
const { hostname } = await import("node:os");
|
|
1129
|
+
const { runConnectCommand } = await import("../src/hooks/cloud-enrollment-cli");
|
|
1130
|
+
result = await runConnectCommand({
|
|
1131
|
+
url: valueAfter("--connect"),
|
|
1132
|
+
token: valueAfter("--token"),
|
|
1133
|
+
machineId: valueAfter("--machine-id"),
|
|
1134
|
+
machineLabel: valueAfter("--machine-label"),
|
|
1135
|
+
defaultMachineId: hostname(),
|
|
1136
|
+
// Transcripts are what connecting is FOR, so they default on and the
|
|
1137
|
+
// disclosure is made at the point of connection rather than hidden
|
|
1138
|
+
// behind an opt-in flag most people never discover — a dashboard
|
|
1139
|
+
// showing only decisions is the empty-dashboard problem in a
|
|
1140
|
+
// different costume. --no-transcripts is the explicit way out, and
|
|
1141
|
+
// `failproofai config --status` always says which is in effect.
|
|
1142
|
+
sessions: !args.includes("--no-transcripts"),
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
for (const line of result.lines) {
|
|
1146
|
+
if (result.exitCode === 0) console.log(line);
|
|
1147
|
+
else console.error(line);
|
|
1148
|
+
}
|
|
1149
|
+
await track("cli_cloud_enrollment", {
|
|
1150
|
+
action: wantsDisconnect ? "disconnect" : "connect",
|
|
1151
|
+
ok: result.exitCode === 0,
|
|
1152
|
+
});
|
|
1153
|
+
await exitAfterFlush(result.exitCode);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
const pauseIdx = args.indexOf("--pause");
|
|
1158
|
+
const wantsResume = args.includes("--resume");
|
|
1159
|
+
const wantsStatus = args.includes("--status");
|
|
1160
|
+
if (pauseIdx >= 0 || wantsResume || wantsStatus) {
|
|
1161
|
+
const chosen = [pauseIdx >= 0 && "--pause", wantsResume && "--resume", wantsStatus && "--status"].filter(Boolean);
|
|
1162
|
+
if (chosen.length > 1) {
|
|
1163
|
+
throw new CliError(`${chosen.join(" and ")} cannot be combined.`);
|
|
1164
|
+
}
|
|
1165
|
+
const sessionIdx = args.indexOf("--session");
|
|
1166
|
+
if (sessionIdx >= 0 && !args[sessionIdx + 1]) {
|
|
1167
|
+
throw new CliError("Missing session id after --session.");
|
|
1168
|
+
}
|
|
1169
|
+
// A bare `--pause` takes the default duration, so only treat the next
|
|
1170
|
+
// token as a duration when it isn't another flag.
|
|
1171
|
+
const next = pauseIdx >= 0 ? args[pauseIdx + 1] : undefined;
|
|
1172
|
+
const duration = next && !next.startsWith("-") ? next : undefined;
|
|
1173
|
+
|
|
1174
|
+
const { runPauseCommand } = await import("../src/hooks/session-pause-cli");
|
|
1175
|
+
const result = runPauseCommand({
|
|
1176
|
+
action: pauseIdx >= 0 ? "pause" : wantsResume ? "resume" : "status",
|
|
1177
|
+
duration,
|
|
1178
|
+
sessionId: sessionIdx >= 0 ? args[sessionIdx + 1] : undefined,
|
|
1179
|
+
all: args.includes("--all"),
|
|
1180
|
+
cwd: process.cwd(),
|
|
1181
|
+
});
|
|
1182
|
+
// `--status` answers "what is this machine's state?", which is both
|
|
1183
|
+
// halves: whether enforcement is paused AND whether cloud is connected.
|
|
1184
|
+
if (wantsStatus) {
|
|
1185
|
+
const { connectionStatusLines } = await import("../src/hooks/cloud-enrollment-cli");
|
|
1186
|
+
for (const line of connectionStatusLines()) console.log(line);
|
|
1187
|
+
// Always printed, including where reports can never work: "why am I
|
|
1188
|
+
// not getting them?" is the question --status exists to answer, and an
|
|
1189
|
+
// omitted line answers it with silence.
|
|
1190
|
+
console.log("");
|
|
1191
|
+
}
|
|
1192
|
+
for (const line of result.lines) {
|
|
1193
|
+
if (result.exitCode === 0) console.log(line);
|
|
1194
|
+
else console.error(line);
|
|
1195
|
+
}
|
|
1196
|
+
await track("cli_pause_invoked", {
|
|
1197
|
+
action: pauseIdx >= 0 ? "pause" : wantsResume ? "resume" : "status",
|
|
1198
|
+
ok: result.exitCode === 0,
|
|
1199
|
+
affected: result.affected,
|
|
1200
|
+
});
|
|
1201
|
+
await exitAfterFlush(result.exitCode);
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
777
1205
|
const { runConfigureWizard } = await import("../src/hooks/configure-wizard");
|
|
778
1206
|
const result = await runConfigureWizard();
|
|
779
1207
|
await track("cli_configure_invoked", {
|
|
780
1208
|
applied: result.applied,
|
|
781
|
-
|
|
1209
|
+
// `target` and `scopes`, not `scope`: the wizard rework replaced that
|
|
1210
|
+
// field and nothing caught it, because `.mjs` is outside the tsconfig
|
|
1211
|
+
// include so `tsc --noEmit` never type-checks this file. Every
|
|
1212
|
+
// `cli_configure_invoked` since has reported `scope: null`.
|
|
1213
|
+
target: result.target ?? null,
|
|
1214
|
+
scopes: result.scopes ?? [],
|
|
782
1215
|
cli_count: result.clis?.length ?? 0,
|
|
1216
|
+
abort: result.abort ?? null,
|
|
783
1217
|
});
|
|
784
|
-
|
|
1218
|
+
// `abort` is the field `WizardAbort` exists to expose, and exiting 0
|
|
1219
|
+
// regardless discarded it: a fleet script could not tell "the user pressed
|
|
1220
|
+
// Esc" from "this machine could not install the required daemon and is
|
|
1221
|
+
// unconfigured". Cancelling is not a failure; the other two are.
|
|
1222
|
+
await exitAfterFlush(!result.applied && result.abort && result.abort !== "cancelled" ? 1 : 0);
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// Shared by both "unknown thing" guards below, so a mistyped SUBCOMMAND gets
|
|
1227
|
+
// the same nearest-match treatment a mistyped flag already got.
|
|
1228
|
+
function levenshtein(a, b) {
|
|
1229
|
+
const m = a.length, n = b.length;
|
|
1230
|
+
const dp = Array.from({ length: m + 1 }, (_, i) =>
|
|
1231
|
+
Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0))
|
|
1232
|
+
);
|
|
1233
|
+
for (let i = 1; i <= m; i++)
|
|
1234
|
+
for (let j = 1; j <= n; j++)
|
|
1235
|
+
dp[i][j] = a[i - 1] === b[j - 1]
|
|
1236
|
+
? dp[i - 1][j - 1]
|
|
1237
|
+
: 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
1238
|
+
return dp[m][n];
|
|
785
1239
|
}
|
|
786
1240
|
|
|
787
1241
|
// Unknown flag guard — must appear after all known-flag branches
|
|
@@ -789,20 +1243,7 @@ WHAT IT DOES
|
|
|
789
1243
|
const unknownFlag = args.find(a => a.startsWith("-") && !knownFlags.includes(a));
|
|
790
1244
|
|
|
791
1245
|
if (unknownFlag) {
|
|
792
|
-
|
|
793
|
-
const m = a.length, n = b.length;
|
|
794
|
-
const dp = Array.from({ length: m + 1 }, (_, i) =>
|
|
795
|
-
Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0))
|
|
796
|
-
);
|
|
797
|
-
for (let i = 1; i <= m; i++)
|
|
798
|
-
for (let j = 1; j <= n; j++)
|
|
799
|
-
dp[i][j] = a[i - 1] === b[j - 1]
|
|
800
|
-
? dp[i - 1][j - 1]
|
|
801
|
-
: 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
802
|
-
return dp[m][n];
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
const primary = ["--version", "--help", "--hook", "policies", "policy", "auth", "audit"];
|
|
1246
|
+
const primary = ["--version", "--help", "--hook", "policies", "policy", "audit"];
|
|
806
1247
|
const closest = primary.reduce((best, flag) => {
|
|
807
1248
|
const dist = levenshtein(unknownFlag, flag);
|
|
808
1249
|
return dist < best.dist ? { flag, dist } : best;
|
|
@@ -818,29 +1259,27 @@ WHAT IT DOES
|
|
|
818
1259
|
// Unknown subcommand guard (non-flag args that aren't a known subcommand)
|
|
819
1260
|
const unknownSubcommand = args.find(a => !a.startsWith("-") && !SUBCOMMANDS.includes(a));
|
|
820
1261
|
if (unknownSubcommand) {
|
|
1262
|
+
// Nearest match rather than a hardcoded "policies", which was wrong for
|
|
1263
|
+
// every input that was not a typo of it. `auth` made that concrete: it was
|
|
1264
|
+
// a real subcommand until this release, so an old script or plain muscle
|
|
1265
|
+
// memory lands here, and answering "did you mean policies?" sends someone
|
|
1266
|
+
// to the one command that has nothing to do with what they typed.
|
|
1267
|
+
const nearest = SUBCOMMANDS.reduce(
|
|
1268
|
+
(best, name) => {
|
|
1269
|
+
const dist = levenshtein(unknownSubcommand, name);
|
|
1270
|
+
return dist < best.dist ? { name, dist } : best;
|
|
1271
|
+
},
|
|
1272
|
+
{ name: SUBCOMMANDS[0], dist: Infinity },
|
|
1273
|
+
);
|
|
821
1274
|
throw new CliError(
|
|
822
1275
|
`Unknown command: ${unknownSubcommand}\n` +
|
|
823
|
-
`Did you mean: failproofai
|
|
1276
|
+
`Did you mean: failproofai ${nearest.name}?\n` +
|
|
824
1277
|
`Run \`failproofai --help\` for usage details.`
|
|
825
1278
|
);
|
|
826
1279
|
}
|
|
827
1280
|
|
|
828
|
-
// First-run onboarding — on the first bare `failproofai` invocation, run the
|
|
829
|
-
// configure wizard (which also fires the post-setup audit) BEFORE the
|
|
830
|
-
// dashboard. Unlike before, we then fall through to launch the dashboard, so a
|
|
831
|
-
// fresh user gets: setup → audit → dashboard, and every later `failproofai`
|
|
832
|
-
// goes straight to the dashboard. Best-effort: any error must not block launch.
|
|
833
|
-
if (args.length === 0) {
|
|
834
|
-
try {
|
|
835
|
-
const { maybeFirstRunConfigure } = await import("../src/hooks/configure-wizard");
|
|
836
|
-
await maybeFirstRunConfigure();
|
|
837
|
-
} catch {
|
|
838
|
-
// First-run onboarding is non-critical; fall through to the dashboard.
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
|
|
842
1281
|
// Dashboard launch — always production mode. Runs on every bare `failproofai`
|
|
843
|
-
// (
|
|
1282
|
+
// (first-run onboarding, if any, already ran above).
|
|
844
1283
|
const { launch } = await import("../scripts/launch");
|
|
845
1284
|
launch("start");
|
|
846
1285
|
}
|