failproofai 1.0.0-beta.4 → 1.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +2 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/routes-manifest.json +6 -0
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +3 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +3 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -3
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +5 -3
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +3 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +10 -10
- package/.next/standalone/.next/server/app/policies/page.js +4 -2
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +4 -2
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +4 -2
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/settings/page/build-manifest.json +18 -0
- package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +6 -0
- package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +53 -0
- package/.next/standalone/.next/server/app/settings/page.js +18 -0
- package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app-paths-manifest.json +2 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ky_n0r._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0lk_935._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p7s8-d._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0v2fpxe._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-dh5jy._.js → [root-of-the-server]__1_ps0jw._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__1a7i2q4._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1iny24d._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tyedjt._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +5 -0
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +5 -0
- package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +5 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +10 -10
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_a_gwn._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1a2hcb_._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1e7-pa7._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rtdfof._.js → [root-of-the-server]__1rdruw4._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tyf0tc._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rag5dg._.js → [root-of-the-server]__1yw368c._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_04mekdy._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0igtyj5._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0r-dsre._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0se484m._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0uz7o9y._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1fahg9e._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1gmzkwj._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/_1w8sola._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1w_6p9g._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +5 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware.js +2 -2
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +54 -12
- package/.next/standalone/.next/static/chunks/{2c90h1g6x8z8y.js → 0_2n13i94rnw5.js} +1 -1
- package/.next/standalone/.next/static/chunks/1bm0q12hgs22l.css +2 -0
- package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
- package/.next/standalone/.next/static/chunks/23557z4e6dtsr.js +1 -0
- package/.next/standalone/.next/static/chunks/28zkwzu50puki.js +1 -0
- package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
- package/.next/standalone/.next/static/chunks/3081gykdubv8b.js +1 -0
- package/.next/standalone/.next/static/chunks/{06jakt_xhoolk.js → 34fa65k0cldkj.js} +1 -1
- package/.next/standalone/.next/static/chunks/35v07j9gc1pvr.js +6 -0
- package/.next/standalone/.next/static/chunks/{2zb3m1tzq6h98.js → 3f_939wi3kfnx.js} +22 -22
- package/.next/standalone/.next/static/chunks/{31cqiv_cpgj9m.js → 3p1vpm2_-tfwm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0ue9ak_xexiqh.js → 3rbpw_waieafm.js} +1 -1
- package/.next/standalone/.next/static/chunks/40ffnfpkeyvb0.js +1 -0
- package/.next/standalone/app/actions/get-hooks-config.ts +2 -1
- package/.next/standalone/app/actions/get-scheduled-audit.ts +70 -0
- package/.next/standalone/app/actions/update-scheduled-audit.ts +44 -0
- package/.next/standalone/app/api/audit/run/route.ts +28 -0
- package/.next/standalone/app/api/audit/status/route.ts +13 -1
- package/.next/standalone/app/api/auth/status/route.ts +1 -1
- package/.next/standalone/app/audit/_components/auth-dialog.css +159 -0
- package/.next/standalone/app/audit/_components/auth-dialog.tsx +3 -0
- package/.next/standalone/app/audit/audit-styles.css +6 -149
- package/.next/standalone/app/settings/page.tsx +31 -0
- package/.next/standalone/app/settings/settings-client.tsx +488 -0
- package/.next/standalone/components/navbar.tsx +2 -0
- package/.next/standalone/lib/auth/api-server-client.ts +2 -3
- package/.next/standalone/lib/auth/auth-store.ts +4 -4
- package/.next/standalone/lib/codex-sessions.ts +2 -1
- package/.next/standalone/lib/dashboard-host.ts +101 -0
- package/.next/standalone/lib/install-check.ts +22 -7
- package/.next/standalone/lib/pi-sessions.ts +99 -14
- package/.next/standalone/lib/telemetry-enabled.ts +54 -0
- package/.next/standalone/lib/telemetry-id.ts +52 -1
- package/.next/standalone/lib/telemetry.ts +8 -4
- package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
- package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
- package/.next/standalone/node_modules/react/package.json +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/package.json +4 -4
- package/.next/standalone/package.json +7 -6
- package/.next/standalone/proxy.ts +89 -1
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +197 -80
- package/dist/cli.mjs +17699 -15345
- package/dist/worker.mjs +1620 -257
- package/lib/auth/api-server-client.ts +2 -3
- package/lib/auth/auth-store.ts +4 -4
- package/lib/codex-sessions.ts +2 -1
- package/lib/dashboard-host.ts +101 -0
- package/lib/install-check.ts +22 -7
- package/lib/pi-sessions.ts +99 -14
- package/lib/telemetry-enabled.ts +54 -0
- package/lib/telemetry-id.ts +52 -1
- package/lib/telemetry.ts +8 -4
- package/package.json +7 -6
- package/scripts/install-telemetry.mjs +8 -1
- package/scripts/launch.ts +25 -3
- package/scripts/parse-script-args.ts +17 -1
- package/scripts/prune-standalone.mjs +20 -0
- package/scripts/translate-docs/readme-translator.ts +163 -4
- package/scripts/validate-mdx.ts +153 -13
- package/src/audit/audit-lock.ts +285 -0
- package/src/audit/audit-schedule.ts +111 -0
- package/src/audit/cache.ts +18 -4
- package/src/audit/cli-adapters/goose.ts +2 -1
- package/src/audit/cli-adapters/hermes.ts +28 -10
- package/src/audit/cli.ts +191 -35
- package/src/audit/dashboard-cache.ts +2 -1
- package/src/hooks/builtin-policies.ts +100 -3
- package/src/hooks/cloud-connection.ts +163 -14
- package/src/hooks/cloud-enrollment-cli.ts +119 -21
- package/src/hooks/cloud-enrollment.ts +87 -17
- package/src/hooks/cloud-introspect.ts +147 -0
- package/src/hooks/cloud-managed-policies.ts +33 -2
- package/src/hooks/collector-config.ts +32 -40
- package/src/hooks/configure-wizard.ts +696 -318
- package/src/hooks/custom-hooks-loader.ts +125 -11
- package/src/hooks/daemon-client.ts +83 -17
- package/src/hooks/daemon-download.ts +54 -2
- package/src/hooks/daemon-service.ts +595 -27
- package/src/hooks/first-run-gate.ts +39 -0
- package/src/hooks/fp-config.ts +497 -0
- package/src/hooks/fp-home.ts +342 -0
- package/src/hooks/fp-reset.ts +332 -0
- package/src/hooks/handler.ts +42 -1
- package/src/hooks/hook-activity-store.ts +35 -20
- package/src/hooks/hook-logger.ts +2 -1
- package/src/hooks/hook-telemetry.ts +5 -1
- package/src/hooks/hooks-config.ts +4 -3
- package/src/hooks/loader-utils.ts +166 -5
- package/src/hooks/manager.ts +5 -4
- package/src/hooks/onboarding-lock.ts +119 -0
- package/src/hooks/policy-types.ts +0 -7
- package/src/hooks/session-pause-cli.ts +15 -13
- package/src/hooks/session-pause.ts +36 -4
- package/src/hooks/setup-state.ts +256 -0
- package/src/hooks/tui.ts +4 -4
- package/src/hooks/worker-server.ts +53 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0f8nu8d._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0szqzl8._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cmdyn_._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ls0gsr._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n_uvv0._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0sdrz-j._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1j4u3yd._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1q9xxca.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
- package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
- package/.next/standalone/.next/static/chunks/0oe2-hp0_2tzs.js +0 -6
- package/.next/standalone/.next/static/chunks/1fw3pxobk_otl.js +0 -1
- package/.next/standalone/.next/static/chunks/3h5_3u46gu9jx.js +0 -1
- package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
- package/.next/standalone/.next/static/chunks/3zdwnnthucs_1.css +0 -2
- package/.next/standalone/.next/static/chunks/43a1ywqsg9bq9.js +0 -1
- package/.next/standalone/Cargo.lock +0 -2222
- package/.next/standalone/Cargo.toml +0 -9
- package/.next/standalone/crates/CLOUD_POLICIES.md +0 -136
- package/.next/standalone/crates/PROTOCOL.md +0 -122
- package/.next/standalone/crates/failproofaid/Cargo.toml +0 -25
- package/.next/standalone/crates/failproofaid/src/cloud_client.rs +0 -484
- package/.next/standalone/crates/failproofaid/src/cloud_policies.rs +0 -921
- package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
- package/.next/standalone/crates/failproofaid/src/main.rs +0 -532
- package/.next/standalone/crates/failproofaid/src/paths.rs +0 -201
- package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
- package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
- package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
- package/.next/standalone/crates/fpai-collect/Cargo.toml +0 -43
- package/.next/standalone/crates/fpai-collect/src/config.rs +0 -412
- package/.next/standalone/crates/fpai-collect/src/cursor.rs +0 -520
- package/.next/standalone/crates/fpai-collect/src/delivery.rs +0 -472
- package/.next/standalone/crates/fpai-collect/src/filetail.rs +0 -484
- package/.next/standalone/crates/fpai-collect/src/health.rs +0 -369
- package/.next/standalone/crates/fpai-collect/src/lib.rs +0 -34
- package/.next/standalone/crates/fpai-collect/src/redact.rs +0 -516
- package/.next/standalone/crates/fpai-collect/src/sources/claude/mod.rs +0 -279
- package/.next/standalone/crates/fpai-collect/src/sources/claude/transform.rs +0 -619
- package/.next/standalone/crates/fpai-collect/src/sources/codex/mod.rs +0 -156
- package/.next/standalone/crates/fpai-collect/src/sources/codex/transform.rs +0 -715
- package/.next/standalone/crates/fpai-collect/src/sources/copilot/mod.rs +0 -158
- package/.next/standalone/crates/fpai-collect/src/sources/copilot/transform.rs +0 -512
- package/.next/standalone/crates/fpai-collect/src/sources/goose/mod.rs +0 -511
- package/.next/standalone/crates/fpai-collect/src/sources/goose/transform.rs +0 -586
- package/.next/standalone/crates/fpai-collect/src/sources/hermes/mod.rs +0 -318
- package/.next/standalone/crates/fpai-collect/src/sources/hermes/transform.rs +0 -579
- package/.next/standalone/crates/fpai-collect/src/sources/hooks/mod.rs +0 -310
- package/.next/standalone/crates/fpai-collect/src/sources/hooks/transform.rs +0 -448
- package/.next/standalone/crates/fpai-collect/src/sources/mod.rs +0 -23
- package/.next/standalone/crates/fpai-collect/src/sources/openclaw/mod.rs +0 -176
- package/.next/standalone/crates/fpai-collect/src/sources/openclaw/transform.rs +0 -500
- package/.next/standalone/crates/fpai-collect/src/sources/opencode/mod.rs +0 -315
- package/.next/standalone/crates/fpai-collect/src/sources/opencode/transform.rs +0 -493
- package/.next/standalone/crates/fpai-collect/src/sources/pi/mod.rs +0 -201
- package/.next/standalone/crates/fpai-collect/src/sources/pi/transform.rs +0 -533
- package/.next/standalone/crates/fpai-collect/src/spool.rs +0 -458
- package/.next/standalone/crates/fpai-collect/src/sqlitepoll.rs +0 -387
- package/.next/standalone/crates/fpai-collect/src/supervisor.rs +0 -423
- package/.next/standalone/crates/fpai-collect/src/uploader.rs +0 -600
- package/.next/standalone/crates/fpai-collect/tests/claude_source.rs +0 -738
- package/.next/standalone/crates/fpai-collect/tests/claude_subagents.rs +0 -393
- package/.next/standalone/crates/fpai-collect/tests/codex_source.rs +0 -975
- package/.next/standalone/crates/fpai-collect/tests/config.rs +0 -286
- package/.next/standalone/crates/fpai-collect/tests/copilot_source.rs +0 -1019
- package/.next/standalone/crates/fpai-collect/tests/delivery.rs +0 -281
- package/.next/standalone/crates/fpai-collect/tests/goose_source.rs +0 -1008
- package/.next/standalone/crates/fpai-collect/tests/hermes_source.rs +0 -1129
- package/.next/standalone/crates/fpai-collect/tests/hooks_source.rs +0 -621
- package/.next/standalone/crates/fpai-collect/tests/openclaw_source.rs +0 -854
- package/.next/standalone/crates/fpai-collect/tests/opencode_source.rs +0 -1478
- package/.next/standalone/crates/fpai-collect/tests/pi_source.rs +0 -1191
- package/.next/standalone/crates/fpai-collect/tests/supervisor.rs +0 -290
- package/.next/standalone/crates/fpai-collect/tests/uploader.rs +0 -506
- package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
- package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
- package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
- package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
- package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
- package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
- package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
- package/.next/standalone/integration-suite/Dockerfile +0 -47
- package/.next/standalone/integration-suite/README.md +0 -129
- package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
- package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
- package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
- package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
- package/.next/standalone/integration-suite/install-clis.sh +0 -136
- package/.next/standalone/integration-suite/probe-cli.sh +0 -248
- package/.next/standalone/integration-suite/report.js +0 -120
- package/.next/standalone/integration-suite/run.sh +0 -159
- package/.next/standalone/openclaw-plugin/index.js +0 -223
- package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
- package/.next/standalone/openclaw-plugin/package.json +0 -12
- package/.next/standalone/osv-scanner.toml +0 -17
- package/.next/standalone/pi-extension/index.ts +0 -524
- package/.next/standalone/pi-extension/package.json +0 -12
- package/.next/standalone/postcss.config.mjs +0 -8
- package/.next/standalone/readme-arch-hq.gif +0 -0
- package/.next/standalone/rust-toolchain.toml +0 -4
- package/src/auth/cli.ts +0 -371
- /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every path inside `~/.failproofai`, in one place.
|
|
3
|
+
*
|
|
4
|
+
* Before this module the layout was ~40 hardcoded `resolve(homedir(),
|
|
5
|
+
* ".failproofai", …)` calls in TypeScript and ~10 more in Rust, and the two
|
|
6
|
+
* sides had drifted into three different ideas of where things lived
|
|
7
|
+
* (`cache/hook-activity` vs `hook-activity`, `spool` at the root vs under
|
|
8
|
+
* state). Moving anything meant finding every one of them, and missing one
|
|
9
|
+
* meant the daemon wrote where the dashboard did not read — silently, because
|
|
10
|
+
* an absent directory is indistinguishable from an idle one.
|
|
11
|
+
*
|
|
12
|
+
* So: nothing outside this file may join a path onto the failproofai home.
|
|
13
|
+
* The Rust side mirrors it in `crates/failproofaid/src/paths.rs`. The test that
|
|
14
|
+
* asserts the two agree lives THERE, not here —
|
|
15
|
+
* `paths::tests::every_mirrored_path_agrees_with_fp_home_ts` imports this module
|
|
16
|
+
* in a child process and compares every mirrored path against its own.
|
|
17
|
+
*
|
|
18
|
+
* This line used to cite `__tests__/hooks/fp-home.test.ts`, which contains no
|
|
19
|
+
* reference to `crates/` and never checked anything cross-language. While both
|
|
20
|
+
* files claimed the guard existed, three paths were wrong at once: the daemon's
|
|
21
|
+
* `run/` ignored `FAILPROOFAI_HOME` (so a healthy daemon on a fail-closed
|
|
22
|
+
* machine denied every tool call), its cloud policy directory still said
|
|
23
|
+
* `cloud-managed`, and its credential still said `cloud.json`.
|
|
24
|
+
*
|
|
25
|
+
* ## Layout 2
|
|
26
|
+
*
|
|
27
|
+
* ```
|
|
28
|
+
* ~/.failproofai/
|
|
29
|
+
* VERSION layout / cli / daemon versions
|
|
30
|
+
* config.toml 0644 non-secret: mode, daemon, collector prefs
|
|
31
|
+
* credentials.toml 0600 every token
|
|
32
|
+
* bin/ downloaded daemon binaries, one per version
|
|
33
|
+
* policies/
|
|
34
|
+
* local-policies/ the builtin enable/disable set
|
|
35
|
+
* cloud-policies/ content-addressed artifacts + active generation
|
|
36
|
+
* custom-policies/ user convention policies (*.mjs)
|
|
37
|
+
* cursors/<source>/ per-source collector watermarks
|
|
38
|
+
* audit/ audit report + per-session cache
|
|
39
|
+
* hook-activity/ decision log the dashboard reads
|
|
40
|
+
* custom-agents/ SDK spool (events/ + failed/)
|
|
41
|
+
* run/ sockets + flock — MUST stay shallow, see below
|
|
42
|
+
* state/ daemon scratch: spool, failed, health, pauses, shims
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* **`run/` is deliberately NOT under `state/`.** A Unix socket path must fit
|
|
46
|
+
* in `sockaddr_un.sun_path` (~108 bytes) and we have hit that ceiling twice
|
|
47
|
+
* already during development. Every directory level spends part of a budget
|
|
48
|
+
* that a long `$HOME` has already eaten into, so the socket stays as close to
|
|
49
|
+
* the root as it can.
|
|
50
|
+
*
|
|
51
|
+
* ## Project scope is NOT here
|
|
52
|
+
*
|
|
53
|
+
* `<project>/.failproofai/` keeps its old shape — `policies-config.json` and
|
|
54
|
+
* `policies/*.mjs` at the top. Those files are committed to users' git repos,
|
|
55
|
+
* so reorganising them would rewrite history in every repo that adopted
|
|
56
|
+
* failproofai. `hooks-config.ts` still owns those paths.
|
|
57
|
+
*/
|
|
58
|
+
import { homedir } from "node:os";
|
|
59
|
+
import { resolve } from "node:path";
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The on-disk layout this build speaks.
|
|
63
|
+
*
|
|
64
|
+
* Bumped whenever a path in this file moves. Recorded in `VERSION`, and what
|
|
65
|
+
* `detectLayout()` compares against — a home written by a different layout is
|
|
66
|
+
* refused rather than half-read, because reading a moved directory silently
|
|
67
|
+
* yields "no data" instead of an error.
|
|
68
|
+
*
|
|
69
|
+
* 1 — the original flat/`cache`-based layout, through 1.0.0-beta.5.
|
|
70
|
+
* 2 — this file.
|
|
71
|
+
*/
|
|
72
|
+
export const LAYOUT_VERSION = 2;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* `~/.failproofai`, or `FAILPROOFAI_HOME`.
|
|
76
|
+
*
|
|
77
|
+
* The override is load-bearing for tests and containers: everything below is
|
|
78
|
+
* derived from it, so pointing it elsewhere relocates the entire layout
|
|
79
|
+
* atomically rather than per-path.
|
|
80
|
+
*/
|
|
81
|
+
export function failproofaiHome(home?: string): string {
|
|
82
|
+
if (home) return resolve(home, ".failproofai");
|
|
83
|
+
return process.env.FAILPROOFAI_HOME || resolve(homedir(), ".failproofai");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const at = (...parts: string[]): string => resolve(failproofaiHome(), ...parts);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Same, against an explicitly supplied HOME (not the failproofai home).
|
|
90
|
+
*
|
|
91
|
+
* `setup-state` and `onboarding-lock` take a `home` parameter purely so their
|
|
92
|
+
* tests can point at a temp directory, and a helper that silently ignored it
|
|
93
|
+
* would send those tests at the developer's real home — which is precisely the
|
|
94
|
+
* bug this overload exists to make impossible.
|
|
95
|
+
*/
|
|
96
|
+
const atHome = (home: string | undefined, ...parts: string[]): string =>
|
|
97
|
+
home ? resolve(failproofaiHome(home), ...parts) : at(...parts);
|
|
98
|
+
|
|
99
|
+
// ── Top level ────────────────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
/** Layout / CLI / daemon versions. Also the layout marker. */
|
|
102
|
+
export const versionFile = (home?: string) => atHome(home, "VERSION");
|
|
103
|
+
|
|
104
|
+
/** Non-secret configuration. World-readable by design; never holds a token. */
|
|
105
|
+
export const configFile = (home?: string) => atHome(home, "config.toml");
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Every credential, owner-only.
|
|
109
|
+
*
|
|
110
|
+
* Split from `config.toml` because that file is written with a bare
|
|
111
|
+
* `writeFileSync` and inherits the umask (0664 on a normal machine). A token
|
|
112
|
+
* there would be readable by every local user on the box — which is exactly
|
|
113
|
+
* why `ingest.json` and `cloud.json` were separate files before this.
|
|
114
|
+
*/
|
|
115
|
+
export const credentialsFile = (home?: string) => atHome(home, "credentials.toml");
|
|
116
|
+
|
|
117
|
+
// ── Daemon binaries ──────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
export const binDir = (home?: string) => atHome(home, "bin");
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* One file per version, never overwritten in place: an in-place write hits
|
|
123
|
+
* `ETXTBSY` against the running daemon on Linux, and would silently repoint a
|
|
124
|
+
* live service unit at a binary built from different source.
|
|
125
|
+
*/
|
|
126
|
+
export const daemonBinary = (version: string, home?: string) =>
|
|
127
|
+
resolve(binDir(home), `failproofaid-${version}`);
|
|
128
|
+
|
|
129
|
+
// ── Policies ─────────────────────────────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
export const policiesDir = (home?: string) => atHome(home, "policies");
|
|
132
|
+
|
|
133
|
+
/** The builtin enable/disable set and per-policy params, GLOBAL scope only. */
|
|
134
|
+
export const localPoliciesDir = (home?: string) => atHome(home, "policies", "local-policies");
|
|
135
|
+
export const globalPolicyConfigFile = (home?: string) =>
|
|
136
|
+
resolve(localPoliciesDir(home), "policies-config.json");
|
|
137
|
+
|
|
138
|
+
/** Cloud-managed generations: `active.json` plus content-addressed artifacts. */
|
|
139
|
+
export const cloudPoliciesDir = (home?: string) => resolve(policiesDir(home), "cloud-policies");
|
|
140
|
+
|
|
141
|
+
/** User convention policies (`*.mjs`) that load without any flag. */
|
|
142
|
+
export const customPoliciesDir = (home?: string) => resolve(policiesDir(home), "custom-policies");
|
|
143
|
+
|
|
144
|
+
// ── Collector ────────────────────────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
/** Per-source watermarks. One directory per source — never shared: the cursor
|
|
147
|
+
* store rewrites its whole map atomically, so two sources sharing a file
|
|
148
|
+
* clobber each other and the loser re-reads from zero after every restart. */
|
|
149
|
+
export const cursorsDir = (source?: string, home?: string) =>
|
|
150
|
+
source ? resolve(atHome(home, "cursors"), source) : atHome(home, "cursors");
|
|
151
|
+
|
|
152
|
+
/** The SDK spool root. Mirrors `~/.agenteye/`, which stays supported. */
|
|
153
|
+
export const customAgentsDir = (home?: string) => atHome(home, "custom-agents");
|
|
154
|
+
export const customAgentsEventsDir = (home?: string) => resolve(customAgentsDir(home), "events");
|
|
155
|
+
export const customAgentsFailedDir = (home?: string) => resolve(customAgentsDir(home), "failed");
|
|
156
|
+
|
|
157
|
+
// ── Audit ────────────────────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
export const auditDir = (home?: string) => atHome(home, "audit");
|
|
160
|
+
export const auditDashboardFile = (home?: string) => resolve(auditDir(home), "dashboard.json");
|
|
161
|
+
export const auditCacheDir = (home?: string) => resolve(auditDir(home), "cache");
|
|
162
|
+
|
|
163
|
+
// ── Hook activity ────────────────────────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
/** The decision log: page-sized JSONL the dashboard's activity tab reads. */
|
|
166
|
+
export const hookActivityDir = (home?: string) => atHome(home, "hook-activity");
|
|
167
|
+
|
|
168
|
+
// ── Runtime ──────────────────────────────────────────────────────────────────
|
|
169
|
+
|
|
170
|
+
/** Sockets and the singleton flock. Shallow on purpose — see the header note. */
|
|
171
|
+
export const runDir = (home?: string) => atHome(home, "run");
|
|
172
|
+
export const daemonSocket = () =>
|
|
173
|
+
process.env.FAILPROOFAI_DAEMON_SOCKET || resolve(runDir(), "failproofaid.sock");
|
|
174
|
+
export const workerSocket = (home?: string) => resolve(runDir(home), "worker.sock");
|
|
175
|
+
export const daemonLock = (home?: string) => resolve(runDir(home), "failproofaid.lock");
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Single-flight lock for the audit.
|
|
179
|
+
*
|
|
180
|
+
* Three separate processes can start one — a scheduled run, `failproofai
|
|
181
|
+
* audit`, and the dashboard's re-run — and all three write the same
|
|
182
|
+
* sha1-keyed cache files, so the lock has to be visible across processes.
|
|
183
|
+
* It sits in `run/` with the other runtime files rather than under `audit/`,
|
|
184
|
+
* which holds results a human might keep and a reset is expected to clear.
|
|
185
|
+
*
|
|
186
|
+
* NOT mirrored in `crates/failproofaid/src/paths.rs` yet: nothing in Rust
|
|
187
|
+
* takes this lock until the daemon grows its audit lane, and a `pub fn` no
|
|
188
|
+
* caller uses is a dead_code failure under `-D warnings`.
|
|
189
|
+
*/
|
|
190
|
+
export const auditLockFile = (home?: string) => resolve(runDir(home), "audit.lock");
|
|
191
|
+
|
|
192
|
+
// ── Daemon scratch state ─────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
/** Everything the daemon writes that a human would never open by hand. */
|
|
195
|
+
export const stateDir = (home?: string) => atHome(home, "state");
|
|
196
|
+
/**
|
|
197
|
+
* Where the policy loader writes its ESM shim.
|
|
198
|
+
*
|
|
199
|
+
* Under the user's own home rather than beside the installed package: that
|
|
200
|
+
* directory belongs to whoever installed failproofai, and on a system-wide
|
|
201
|
+
* install it is root-owned, which made every non-root hook fail to load
|
|
202
|
+
* cloud-managed and custom policies — silently, and open.
|
|
203
|
+
*/
|
|
204
|
+
export const shimsDir = (home?: string) => resolve(stateDir(home), "shims");
|
|
205
|
+
export const spoolDir = (home?: string) => resolve(stateDir(home), "spool");
|
|
206
|
+
export const failedDir = (home?: string) => resolve(stateDir(home), "failed");
|
|
207
|
+
export const collectorHealthFile = (home?: string) => resolve(stateDir(home), "collector-health.json");
|
|
208
|
+
/** Per-session enforcement pauses, keyed by a hash of the session id. */
|
|
209
|
+
export const sessionPauseDir = () => resolve(stateDir(), "sessions");
|
|
210
|
+
/**
|
|
211
|
+
* When the scheduled audit last ran, and when the next one is due.
|
|
212
|
+
*
|
|
213
|
+
* The DAEMON is the sole writer (`crates/failproofaid/src/audit_lane.rs`, which
|
|
214
|
+
* mirrors this path in `paths.rs`) — it owns the schedule, and a second writer
|
|
215
|
+
* racing it could hand a machine two full scans back to back. Everything on this
|
|
216
|
+
* side reads it: the interval itself lives in `config.toml`'s `[audit]` table,
|
|
217
|
+
* which a human edits, while this file is derived state a human never opens,
|
|
218
|
+
* which is why it sits under `state/` rather than beside the audit results.
|
|
219
|
+
*/
|
|
220
|
+
export const auditScheduleFile = (home?: string) => resolve(stateDir(home), "audit-schedule.json");
|
|
221
|
+
/**
|
|
222
|
+
* The anonymous instance id this machine reports telemetry under.
|
|
223
|
+
*
|
|
224
|
+
* The mirror image of `auditScheduleFile()` above: here the CLI is the sole
|
|
225
|
+
* writer (`getInstanceId()` in `lib/telemetry-id.ts`, which persists whatever it
|
|
226
|
+
* resolved) and **failproofaid** only reads, through `telemetry_id_path()` in
|
|
227
|
+
* `crates/failproofaid/src/paths.rs`. The daemon cannot re-derive the value —
|
|
228
|
+
* `getInstanceId`'s middle tier hashes Node-formatted strings (`os.arch()` is
|
|
229
|
+
* `x64` where Rust says `x86_64`), and a near-miss there does not fail, it
|
|
230
|
+
* silently files one machine under two PostHog persons — so this file is how the
|
|
231
|
+
* two agree. Derived state a human never opens, hence `state/` rather than
|
|
232
|
+
* beside `config.toml`.
|
|
233
|
+
*/
|
|
234
|
+
export const telemetryIdFile = (home?: string) => resolve(stateDir(home), "telemetry-id");
|
|
235
|
+
export const launcherMarker = (home?: string) => resolve(stateDir(home), "launcher-configured");
|
|
236
|
+
/**
|
|
237
|
+
* The version that last reported an install, so an upgrade is reported once.
|
|
238
|
+
*
|
|
239
|
+
* Under `state/` in layout 2 like every other piece of derived state, and that
|
|
240
|
+
* placement fixes a real bug rather than being tidiness: at the root it was
|
|
241
|
+
* `~/.failproofai/last-version`, which `detectLayout()` lists as a LAYOUT-1
|
|
242
|
+
* MARKER — while the code that writes it still runs under layout 2, and runs
|
|
243
|
+
* BEFORE the layout check. So on a genuinely fresh machine the CLI created the
|
|
244
|
+
* file, then immediately read it back as evidence of an old layout, classified
|
|
245
|
+
* a brand-new home as stale, and opened every new user's first command with
|
|
246
|
+
* "failproofai reorganised … Removed 1 item(s) from the old layout."
|
|
247
|
+
*/
|
|
248
|
+
export const lastVersionFile = (home?: string) => resolve(stateDir(home), "last-version");
|
|
249
|
+
export const onboardingLockFile = (home?: string) => resolve(stateDir(home), "onboarding.lock");
|
|
250
|
+
export const codexSessionPathsFile = (home?: string) => resolve(stateDir(home), "codex-session-paths.json");
|
|
251
|
+
|
|
252
|
+
export const logsDir = (home?: string) => atHome(home, "logs");
|
|
253
|
+
|
|
254
|
+
// ── Layout 1 (legacy) ────────────────────────────────────────────────────────
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Where layout 1 kept things.
|
|
258
|
+
*
|
|
259
|
+
* Retained ONLY so `detectLayout()` can recognise an old home and so the reset
|
|
260
|
+
* can delete it. Nothing reads data from these paths: the decision was
|
|
261
|
+
* wipe-and-re-setup rather than migrate, because a half-migrated home that
|
|
262
|
+
* silently reads "no data" is worse than one that says so.
|
|
263
|
+
*/
|
|
264
|
+
export const legacy = {
|
|
265
|
+
policyConfig: () => at("policies-config.json"),
|
|
266
|
+
policyConfigLocal: () => at("policies-config.local.json"),
|
|
267
|
+
cloudCredentials: () => at("cloud.json"),
|
|
268
|
+
ingestCredentials: () => at("ingest.json"),
|
|
269
|
+
launcherMarker: () => at(".launcher-configured"),
|
|
270
|
+
lastVersion: () => at("last-version"),
|
|
271
|
+
auditDashboard: () => at("audit-dashboard.json"),
|
|
272
|
+
cacheDir: () => at("cache"),
|
|
273
|
+
hookActivityDir: () => at("cache", "hook-activity"),
|
|
274
|
+
auditCacheDir: () => at("cache", "audit"),
|
|
275
|
+
codexSessionPaths: () => at("cache", "codex-session-paths.json"),
|
|
276
|
+
spoolDir: () => at("spool"),
|
|
277
|
+
failedDir: () => at("failed"),
|
|
278
|
+
collectorHealth: () => at("collector-health.json"),
|
|
279
|
+
sessionPauseDir: () => at("state", "sessions"),
|
|
280
|
+
cloudManagedPolicies: () => at("policies", "cloud-managed"),
|
|
281
|
+
onboardingLock: () => at(".onboarding.lock"),
|
|
282
|
+
} as const;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Every path a reset must remove, in the order it should remove them.
|
|
286
|
+
*
|
|
287
|
+
* Enumerated rather than "delete the home directory" so a reset never takes
|
|
288
|
+
* out something a future layout adds and this list has not been taught about
|
|
289
|
+
* — and so the function is auditable at a glance, which matters for anything
|
|
290
|
+
* that deletes user data.
|
|
291
|
+
*/
|
|
292
|
+
export function resettablePaths(): string[] {
|
|
293
|
+
return [
|
|
294
|
+
// Layout 1
|
|
295
|
+
legacy.policyConfig(),
|
|
296
|
+
legacy.policyConfigLocal(),
|
|
297
|
+
legacy.cloudCredentials(),
|
|
298
|
+
legacy.ingestCredentials(),
|
|
299
|
+
legacy.launcherMarker(),
|
|
300
|
+
legacy.lastVersion(),
|
|
301
|
+
legacy.auditDashboard(),
|
|
302
|
+
legacy.cacheDir(),
|
|
303
|
+
legacy.spoolDir(),
|
|
304
|
+
legacy.failedDir(),
|
|
305
|
+
legacy.collectorHealth(),
|
|
306
|
+
legacy.onboardingLock(),
|
|
307
|
+
legacy.cloudManagedPolicies(),
|
|
308
|
+
// The MACHINE-OWNED children of `policies/`, never the parent.
|
|
309
|
+
//
|
|
310
|
+
// `at("policies")` stood here, and on layout 1 that directory IS the
|
|
311
|
+
// documented home for hand-written personal policies — `docs/
|
|
312
|
+
// configuration.mdx` names `~/.failproofai/policies/` as the user scope,
|
|
313
|
+
// and `manager.ts` read it. Those are source files a person wrote; nothing
|
|
314
|
+
// regenerates them, nothing backed them up, and the reset message named
|
|
315
|
+
// only "policy config, activity history and audit cache". Worse, the
|
|
316
|
+
// machine then reported itself configured — `isConfigured()` is a union
|
|
317
|
+
// that still sees the agent CLIs' untouched settings files — so the wizard
|
|
318
|
+
// was skipped and hooks kept firing against an empty policy set.
|
|
319
|
+
//
|
|
320
|
+
// `custom-policies/` is deliberately absent from this list for the same
|
|
321
|
+
// reason. Both directories below are re-derived: `local-policies/` by
|
|
322
|
+
// setup, `cloud-policies/` by the next daemon poll.
|
|
323
|
+
localPoliciesDir(),
|
|
324
|
+
cloudPoliciesDir(),
|
|
325
|
+
at("cursors"),
|
|
326
|
+
at("state"),
|
|
327
|
+
// Layout 2.
|
|
328
|
+
// NOT versionFile() — it is the layout marker, not user data, and the
|
|
329
|
+
// reset rewrites it immediately afterwards. Listing it made a no-op reset
|
|
330
|
+
// report "removed 1 item" for a file it had just recreated.
|
|
331
|
+
configFile(),
|
|
332
|
+
credentialsFile(),
|
|
333
|
+
auditDir(),
|
|
334
|
+
hookActivityDir(),
|
|
335
|
+
customAgentsDir(),
|
|
336
|
+
logsDir(),
|
|
337
|
+
// NOT bin/ — a downloaded daemon binary is large, version-pinned and
|
|
338
|
+
// re-verified on use, so deleting it only forces a needless re-download.
|
|
339
|
+
// NOT run/ — sockets belong to a running daemon; removing them out from
|
|
340
|
+
// under it breaks a live process rather than resetting configuration.
|
|
341
|
+
];
|
|
342
|
+
}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What happens when this CLI meets a home directory written by a different
|
|
3
|
+
* layout.
|
|
4
|
+
*
|
|
5
|
+
* The decision was wipe-and-re-setup rather than migrate. A migration has to
|
|
6
|
+
* be right on every path or it half-moves a home, and a half-moved home fails
|
|
7
|
+
* in the worst available way: the daemon writes where the dashboard does not
|
|
8
|
+
* read, and an absent directory is indistinguishable from an idle one. A reset
|
|
9
|
+
* is one destructive operation that is either done or not done.
|
|
10
|
+
*
|
|
11
|
+
* ## Where the deletion happens, and where it deliberately does not
|
|
12
|
+
*
|
|
13
|
+
* Only a real CLI command resets. A HOOK never deletes anything, because a
|
|
14
|
+
* hook runs unattended, once per tool call, with an agent waiting on stdout —
|
|
15
|
+
* removing a user's audit history and cursors from inside one would be a
|
|
16
|
+
* destructive act nobody asked for and nobody saw.
|
|
17
|
+
*
|
|
18
|
+
* But a hook must not stay silent either. On a stale layout the merged config
|
|
19
|
+
* resolves to nothing, so every builtin policy quietly stops firing: the
|
|
20
|
+
* machine looks protected and is not. That is the single worst outcome here,
|
|
21
|
+
* so the hook path says so on stderr, every time, until setup is re-run.
|
|
22
|
+
*
|
|
23
|
+
* ## Why the hook does not simply deny
|
|
24
|
+
*
|
|
25
|
+
* Failing closed is this product's instinct, and it is the wrong instinct
|
|
26
|
+
* here. A stale layout is the result of an upgrade the user did not ask to be
|
|
27
|
+
* interrupted by, and this branch has already demonstrated — on this very
|
|
28
|
+
* machine — that a blanket deny takes `UserPromptSubmit` with it and locks the
|
|
29
|
+
* user out of their agent entirely, with no way back except hand-editing JSON.
|
|
30
|
+
* A loud warning that survives until setup runs is the proportionate answer.
|
|
31
|
+
*/
|
|
32
|
+
import { existsSync, mkdirSync, readdirSync, renameSync, rmSync } from "node:fs";
|
|
33
|
+
import { resolve } from "node:path";
|
|
34
|
+
import {
|
|
35
|
+
LAYOUT_VERSION,
|
|
36
|
+
customPoliciesDir,
|
|
37
|
+
failproofaiHome,
|
|
38
|
+
policiesDir,
|
|
39
|
+
resettablePaths,
|
|
40
|
+
} from "./fp-home";
|
|
41
|
+
import { detectLayout, readConfig, updateConfig, writeVersionFile, type LayoutState } from "./fp-config";
|
|
42
|
+
import {
|
|
43
|
+
daemonServiceStatus,
|
|
44
|
+
daemonVersionSkew,
|
|
45
|
+
isDaemonSupportedPlatform,
|
|
46
|
+
probeDaemonEndToEnd,
|
|
47
|
+
} from "./daemon-service";
|
|
48
|
+
|
|
49
|
+
export interface ResetOutcome {
|
|
50
|
+
/** Paths that existed and were removed. */
|
|
51
|
+
removed: string[];
|
|
52
|
+
/** Basenames of user-authored policy files moved into `custom-policies/`. */
|
|
53
|
+
migrated: string[];
|
|
54
|
+
/** The layout that was found before the reset. */
|
|
55
|
+
from: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Script files a person could have written by hand. `.d.ts` is a type stub. */
|
|
59
|
+
const USER_SOURCE_RE = /\.(js|mjs|ts)$/;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Move hand-written policies from layout 1's `policies/` into layout 2's
|
|
63
|
+
* `policies/custom-policies/`, where the loader actually reads them.
|
|
64
|
+
*
|
|
65
|
+
* Layout 1 documented `~/.failproofai/policies/` as the user scope for personal
|
|
66
|
+
* convention policies, and layout 2 moved the directory the loader opens down a
|
|
67
|
+
* level (`customPoliciesDir()`). Left alone, those files survive the reset —
|
|
68
|
+
* that part is handled by keeping the parent out of `resettablePaths()` — but
|
|
69
|
+
* nothing would ever load them again, which is the same silent enforcement gap
|
|
70
|
+
* by a slower route.
|
|
71
|
+
*
|
|
72
|
+
* Every loadable extension moves, not just the `*policies.{js,mjs,ts}` names
|
|
73
|
+
* the convention loads: a file that misses the naming convention is still
|
|
74
|
+
* source somebody wrote, and `findSkippedPolicyFiles` exists precisely to tell
|
|
75
|
+
* them so. Leaving it behind to be reported against an empty directory helps
|
|
76
|
+
* nobody.
|
|
77
|
+
*
|
|
78
|
+
* A destination that already exists is never overwritten — the source is left
|
|
79
|
+
* in place instead. The parent directory is no longer deleted, so "left in
|
|
80
|
+
* place" means "still there to look at", which is the right outcome for the
|
|
81
|
+
* only case that reaches it (a half-finished earlier migration).
|
|
82
|
+
*/
|
|
83
|
+
export function migrateConventionPolicies(): string[] {
|
|
84
|
+
const from = policiesDir();
|
|
85
|
+
if (!existsSync(from)) return [];
|
|
86
|
+
const moved: string[] = [];
|
|
87
|
+
try {
|
|
88
|
+
const entries = readdirSync(from, { withFileTypes: true }).filter(
|
|
89
|
+
(e) => e.isFile() && USER_SOURCE_RE.test(e.name) && !e.name.endsWith(".d.ts"),
|
|
90
|
+
);
|
|
91
|
+
if (entries.length === 0) return [];
|
|
92
|
+
|
|
93
|
+
const to = customPoliciesDir();
|
|
94
|
+
mkdirSync(to, { recursive: true });
|
|
95
|
+
for (const entry of entries) {
|
|
96
|
+
const target = resolve(to, entry.name);
|
|
97
|
+
if (existsSync(target)) continue;
|
|
98
|
+
try {
|
|
99
|
+
renameSync(resolve(from, entry.name), target);
|
|
100
|
+
moved.push(entry.name);
|
|
101
|
+
} catch {
|
|
102
|
+
// One unmovable file must not strand the rest. It stays where it is,
|
|
103
|
+
// and the reset no longer deletes that directory.
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
// A policies directory we cannot read is not worth aborting a reset over.
|
|
108
|
+
}
|
|
109
|
+
return moved.sort((a, b) => a.localeCompare(b));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Delete every path layout 1 or layout 2 could have written, then stamp
|
|
114
|
+
* VERSION so the next run reads as current.
|
|
115
|
+
*
|
|
116
|
+
* Enumerated rather than "remove the home directory": a reset must never take
|
|
117
|
+
* out something a future layout adds that this list has not been taught about,
|
|
118
|
+
* and `bin/` and `run/` are excluded on purpose — a downloaded daemon binary is
|
|
119
|
+
* large, version-pinned and re-verified on use, and `run/` holds sockets
|
|
120
|
+
* belonging to a process that may be alive right now.
|
|
121
|
+
*/
|
|
122
|
+
export function resetHome(from: number): ResetOutcome {
|
|
123
|
+
// BEFORE the deletions, so a file that is mid-move is never one the reset
|
|
124
|
+
// then walks over.
|
|
125
|
+
const migrated = migrateConventionPolicies();
|
|
126
|
+
const removed: string[] = [];
|
|
127
|
+
for (const path of resettablePaths()) {
|
|
128
|
+
if (!existsSync(path)) continue;
|
|
129
|
+
try {
|
|
130
|
+
rmSync(path, { recursive: true, force: true });
|
|
131
|
+
removed.push(path);
|
|
132
|
+
} catch {
|
|
133
|
+
// A path we cannot remove is not worth aborting the reset over; the
|
|
134
|
+
// VERSION stamp below is what stops this repeating, and a leftover
|
|
135
|
+
// directory from an old layout is inert once nothing reads it.
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
writeVersionFile();
|
|
139
|
+
return { removed, migrated, from };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface LayoutCheck {
|
|
143
|
+
state: LayoutState;
|
|
144
|
+
/** Lines to print. Empty when there is nothing to say. */
|
|
145
|
+
lines: string[];
|
|
146
|
+
/** True when the caller should stop rather than continue. */
|
|
147
|
+
fatal: boolean;
|
|
148
|
+
/**
|
|
149
|
+
* True when this call actually reset the home.
|
|
150
|
+
*
|
|
151
|
+
* The caller must force setup when it is set. A reset removes the global
|
|
152
|
+
* policy config, but `isConfigured()` is a union that also counts the agent
|
|
153
|
+
* CLIs' settings files — which the reset deliberately leaves alone — so the
|
|
154
|
+
* machine still reads as configured, the wizard is skipped, and
|
|
155
|
+
* `markLauncherSeen()` back-fills the marker so every later run skips it too.
|
|
156
|
+
* The user is left with hooks firing on every tool call against no policies
|
|
157
|
+
* at all, and nothing ever says so again.
|
|
158
|
+
*/
|
|
159
|
+
didReset: boolean;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The interactive path: reset a stale home, refuse a future one.
|
|
164
|
+
*
|
|
165
|
+
* A future layout is refused rather than reset because the two failures are
|
|
166
|
+
* not the same. An older home can be rebuilt by re-running setup; a home
|
|
167
|
+
* written by a NEWER CLI holds data this build cannot read but a simple
|
|
168
|
+
* upgrade could, and deleting it would destroy something recoverable.
|
|
169
|
+
*/
|
|
170
|
+
export async function checkLayoutForCli(): Promise<LayoutCheck> {
|
|
171
|
+
const state = detectLayout();
|
|
172
|
+
|
|
173
|
+
if (state.kind === "future") {
|
|
174
|
+
return {
|
|
175
|
+
state,
|
|
176
|
+
fatal: true,
|
|
177
|
+
didReset: false,
|
|
178
|
+
lines: [
|
|
179
|
+
`This machine's failproofai directory was written by a newer version`,
|
|
180
|
+
`(layout ${state.found}; this build speaks ${LAYOUT_VERSION}).`,
|
|
181
|
+
``,
|
|
182
|
+
`Upgrade rather than reset — the data is fine, this build just cannot read it:`,
|
|
183
|
+
` npm install -g failproofai@latest`,
|
|
184
|
+
],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (state.kind === "stale") {
|
|
189
|
+
const { removed, migrated } = resetHome(state.found);
|
|
190
|
+
return {
|
|
191
|
+
state,
|
|
192
|
+
fatal: false,
|
|
193
|
+
didReset: true,
|
|
194
|
+
lines: [
|
|
195
|
+
`failproofai reorganised ${failproofaiHome()} in this version.`,
|
|
196
|
+
`Removed ${removed.length} item(s) from the old layout — policy config, activity`,
|
|
197
|
+
`history and audit cache. Your downloaded daemon binary was kept.`,
|
|
198
|
+
// Named individually rather than counted. These are files a person
|
|
199
|
+
// wrote; "moved 3 items" is not something you can check at a glance,
|
|
200
|
+
// and the whole point of saying it is that they can.
|
|
201
|
+
...(migrated.length > 0
|
|
202
|
+
? [
|
|
203
|
+
``,
|
|
204
|
+
`Kept your own policy file(s) and moved them to where this version`,
|
|
205
|
+
`loads them (${customPoliciesDir()}):`,
|
|
206
|
+
...migrated.map((name) => ` ${name}`),
|
|
207
|
+
]
|
|
208
|
+
: []),
|
|
209
|
+
``,
|
|
210
|
+
`Run \`failproofai config\` to set up again.`,
|
|
211
|
+
],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (state.kind === "absent") writeVersionFile();
|
|
216
|
+
return {
|
|
217
|
+
state,
|
|
218
|
+
fatal: false,
|
|
219
|
+
didReset: false,
|
|
220
|
+
lines: [...(await healDaemonFlag()), ...staleDaemonHint()],
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Clear `daemonConfigured` when the service it points at is provably gone.
|
|
226
|
+
*
|
|
227
|
+
* This exists because the combination it repairs bricked a real machine during
|
|
228
|
+
* development. `daemonConfigured` makes every hook route through failproofaid
|
|
229
|
+
* and FAIL CLOSED when it cannot be reached — so removing the service without
|
|
230
|
+
* first clearing the flag denies every tool call on the box, including
|
|
231
|
+
* `UserPromptSubmit`, which locks the user out of their agent entirely. There
|
|
232
|
+
* was no command to undo it: the only recovery was hand-editing JSON.
|
|
233
|
+
*
|
|
234
|
+
* Deliberately keyed on "not-installed", never on "stopped". A stopped service
|
|
235
|
+
* is usually a restart in progress, and clearing the flag there would silently
|
|
236
|
+
* downgrade a healthy machine to the in-process path — trading a loud, correct
|
|
237
|
+
* failure for a quiet, wrong one.
|
|
238
|
+
*/
|
|
239
|
+
async function healDaemonFlag(): Promise<string[]> {
|
|
240
|
+
try {
|
|
241
|
+
const cfg = readConfig();
|
|
242
|
+
if (!cfg.daemon.configured) return [];
|
|
243
|
+
if (!isDaemonSupportedPlatform()) return [];
|
|
244
|
+
|
|
245
|
+
const status = daemonServiceStatus();
|
|
246
|
+
if (status === "not-installed") {
|
|
247
|
+
updateConfig({ daemon: { configured: false } });
|
|
248
|
+
return [
|
|
249
|
+
`failproofaid is no longer installed, but this machine was still configured`,
|
|
250
|
+
`to require it — which denies every tool call. Cleared that flag; policies`,
|
|
251
|
+
`now evaluate in-process. Run \`failproofai config\` to reinstall the daemon.`,
|
|
252
|
+
``,
|
|
253
|
+
];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Installed and RUNNING is not the same as working, and the difference is
|
|
257
|
+
// a total lockout. `ExecStart` bakes in `process.execPath`, so an
|
|
258
|
+
// `nvm uninstall 20` months later leaves a unit systemd still calls active
|
|
259
|
+
// whose worker dies on every spawn. Nothing else catches it: the install
|
|
260
|
+
// probe cannot run retroactively, and the not-installed branch above never
|
|
261
|
+
// fires because the unit is very much installed.
|
|
262
|
+
//
|
|
263
|
+
// Clearing the flag is the whole repair, and it is deliberately NOT
|
|
264
|
+
// accompanied by an uninstall: this runs unprompted at the top of whatever
|
|
265
|
+
// command the user typed, and tearing down a root-owned service from there
|
|
266
|
+
// is not a decision to make on their behalf. Removing and reinstalling the
|
|
267
|
+
// unit is the wizard's job, where a person is present — see the
|
|
268
|
+
// `daemonAlreadyHealthy` probe in `configure-wizard.ts`.
|
|
269
|
+
if (status === "running" && !(await probeDaemonEndToEnd())) {
|
|
270
|
+
updateConfig({ daemon: { configured: false } });
|
|
271
|
+
return [
|
|
272
|
+
`failproofaid is running but cannot evaluate policies — its worker process`,
|
|
273
|
+
`will not start (most often because the Node install its service was built`,
|
|
274
|
+
`against is gone). This machine was configured to require it, which denies`,
|
|
275
|
+
`every tool call, so that flag is cleared; policies now evaluate in-process.`,
|
|
276
|
+
`Run \`failproofai config\` to rebuild the service.`,
|
|
277
|
+
``,
|
|
278
|
+
];
|
|
279
|
+
}
|
|
280
|
+
return [];
|
|
281
|
+
} catch {
|
|
282
|
+
// Never let a self-heal attempt break the command the user actually typed.
|
|
283
|
+
return [];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* One line when the daemon is older than the CLI.
|
|
289
|
+
*
|
|
290
|
+
* Deliberately NOT on the hook path. A stale daemon still enforces every policy
|
|
291
|
+
* correctly — it is slower to notice an upgrade, not broken — so a warning once
|
|
292
|
+
* per tool call would be noise about something that is working. CLI commands
|
|
293
|
+
* are where a person is present to act on it.
|
|
294
|
+
*/
|
|
295
|
+
function staleDaemonHint(): string[] {
|
|
296
|
+
try {
|
|
297
|
+
const skew = daemonVersionSkew();
|
|
298
|
+
if (!skew) return [];
|
|
299
|
+
return [
|
|
300
|
+
`[failproofai] daemon is ${skew.installed}, CLI is ${skew.expected} — ` +
|
|
301
|
+
`run \`failproofai config\` to update it.`,
|
|
302
|
+
``,
|
|
303
|
+
];
|
|
304
|
+
} catch {
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* The hook path: never delete, never deny, but never stay quiet either.
|
|
311
|
+
*
|
|
312
|
+
* Returns a single stderr line when the layout is not current. Hooks run once
|
|
313
|
+
* per tool call, so this repeats — deliberately. A warning that appears once
|
|
314
|
+
* and then stops is a warning nobody sees, and the state it describes (a
|
|
315
|
+
* machine whose global policies have silently stopped firing) persists until
|
|
316
|
+
* somebody acts on it.
|
|
317
|
+
*/
|
|
318
|
+
export function layoutWarningForHook(): string | null {
|
|
319
|
+
const state = detectLayout();
|
|
320
|
+
if (state.kind === "current" || state.kind === "absent") return null;
|
|
321
|
+
if (state.kind === "future") {
|
|
322
|
+
return (
|
|
323
|
+
`[failproofai] this directory was written by a newer version ` +
|
|
324
|
+
`(layout ${state.found} vs ${LAYOUT_VERSION}) — policies are NOT being enforced. ` +
|
|
325
|
+
`Upgrade failproofai.`
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
return (
|
|
329
|
+
`[failproofai] setup predates this version — global policies are NOT being enforced. ` +
|
|
330
|
+
`Run \`failproofai config\` to re-create it.`
|
|
331
|
+
);
|
|
332
|
+
}
|