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
|
@@ -16,11 +16,14 @@
|
|
|
16
16
|
* Environment variables still win over this file, so CI, containers and the
|
|
17
17
|
* existing tests are unaffected.
|
|
18
18
|
*/
|
|
19
|
+
import { randomUUID } from "node:crypto";
|
|
19
20
|
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
20
|
-
import { homedir } from "node:os";
|
|
21
|
+
import { homedir, hostname } from "node:os";
|
|
21
22
|
import { join } from "node:path";
|
|
22
23
|
import { writeJsonAtomically } from "../../lib/atomic-write";
|
|
23
24
|
import { fetchWithTimeout, isAbortError } from "../../lib/fetch-with-timeout";
|
|
25
|
+
import { credentialsFile } from "./fp-home";
|
|
26
|
+
import { readCredentials, writeCredentials } from "./fp-config";
|
|
24
27
|
|
|
25
28
|
const SCHEMA_VERSION = 1;
|
|
26
29
|
const VERIFY_TIMEOUT_MS = 10_000;
|
|
@@ -29,17 +32,34 @@ export interface CloudCredentials {
|
|
|
29
32
|
url: string;
|
|
30
33
|
machineId: string;
|
|
31
34
|
token: string;
|
|
35
|
+
/**
|
|
36
|
+
* A human-facing name for this machine (e.g. the hostname, or "Chetan's
|
|
37
|
+
* laptop"). Display only — the identity is `machineId`. Optional: absent on
|
|
38
|
+
* credentials written before labels existed, and never required to connect.
|
|
39
|
+
*/
|
|
40
|
+
machineLabel?: string;
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
interface StoredCredentials extends CloudCredentials {
|
|
35
44
|
schemaVersion: number;
|
|
36
45
|
}
|
|
37
46
|
|
|
38
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Where the cloud credential lives.
|
|
49
|
+
*
|
|
50
|
+
* Layout 2 moved it out of `cloud.json` and into the `[cloud]` table of
|
|
51
|
+
* `credentials.toml`, alongside every other token, so there is exactly one
|
|
52
|
+
* owner-only file to protect rather than three. The env override still names a
|
|
53
|
+
* standalone JSON file, because the daemon and CI both use it that way and it
|
|
54
|
+
* predates the consolidation.
|
|
55
|
+
*/
|
|
39
56
|
export function cloudCredentialPath(): string {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
return process.env.FAILPROOFAI_CLOUD_CREDENTIALS ?? credentialsFile();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** True when the credential is coming from the JSON override, not the TOML. */
|
|
61
|
+
function usingJsonOverride(): boolean {
|
|
62
|
+
return Boolean(process.env.FAILPROOFAI_CLOUD_CREDENTIALS);
|
|
43
63
|
}
|
|
44
64
|
|
|
45
65
|
/**
|
|
@@ -81,6 +101,7 @@ export function maskToken(token: string): string {
|
|
|
81
101
|
}
|
|
82
102
|
|
|
83
103
|
export function readCloudCredentials(): CloudCredentials | null {
|
|
104
|
+
if (!usingJsonOverride()) return readCredentials().cloud ?? null;
|
|
84
105
|
const path = cloudCredentialPath();
|
|
85
106
|
if (!existsSync(path)) return null;
|
|
86
107
|
try {
|
|
@@ -90,7 +111,8 @@ export function readCloudCredentials(): CloudCredentials | null {
|
|
|
90
111
|
return null;
|
|
91
112
|
}
|
|
92
113
|
if (!raw.url || !raw.machineId || !raw.token) return null;
|
|
93
|
-
|
|
114
|
+
const label = typeof raw.machineLabel === "string" && raw.machineLabel ? raw.machineLabel : undefined;
|
|
115
|
+
return { url: raw.url, machineId: raw.machineId, token: raw.token, machineLabel: label };
|
|
94
116
|
} catch {
|
|
95
117
|
// Unreadable or malformed reads as "not connected" rather than throwing:
|
|
96
118
|
// this is consulted by `--status`, and a corrupt file should be reported
|
|
@@ -100,21 +122,63 @@ export function readCloudCredentials(): CloudCredentials | null {
|
|
|
100
122
|
}
|
|
101
123
|
|
|
102
124
|
export function writeCloudCredentials(creds: CloudCredentials): void {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
125
|
+
if (usingJsonOverride()) {
|
|
126
|
+
// 0600 inside a 0700 dir — the default for writeJsonAtomically.
|
|
127
|
+
writeJsonAtomically(cloudCredentialPath(), { schemaVersion: SCHEMA_VERSION, ...creds });
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Merge, never replace: credentials.toml also carries the ingest key and the
|
|
131
|
+
// auth session, and rewriting the file from one caller must not drop the
|
|
132
|
+
// others.
|
|
133
|
+
writeCredentials({ ...readCredentials(), cloud: creds });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The stable key that identifies this machine to the cloud.
|
|
138
|
+
*
|
|
139
|
+
* An explicit `--machine-id` always wins. Otherwise the id already enrolled on
|
|
140
|
+
* this machine is reused, so re-running `--connect` is idempotent and never
|
|
141
|
+
* "moves" the machine. Only a machine that has none mints a fresh one — and it
|
|
142
|
+
* mints a random id, NOT the hostname, because two hosts sharing a hostname
|
|
143
|
+
* (fresh cloud VMs, cloned images) would otherwise silently merge into one
|
|
144
|
+
* machine on the server. The hostname becomes the human label instead.
|
|
145
|
+
*/
|
|
146
|
+
export function resolveMachineId(explicit?: string): string {
|
|
147
|
+
const trimmed = explicit?.trim();
|
|
148
|
+
if (trimmed) return trimmed;
|
|
149
|
+
const existing = readCloudCredentials();
|
|
150
|
+
if (existing?.machineId) return existing.machineId;
|
|
151
|
+
return randomUUID();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The human-facing name for this machine. Defaults to the hostname as a
|
|
156
|
+
* *suggestion* — mutable, not the identity, and free to collide with another
|
|
157
|
+
* machine's label because `machineId` keeps them apart.
|
|
158
|
+
*/
|
|
159
|
+
export function resolveMachineLabel(explicit?: string): string {
|
|
160
|
+
return explicit?.trim() || hostname();
|
|
106
161
|
}
|
|
107
162
|
|
|
108
163
|
/** True if there was something to remove. */
|
|
109
164
|
export function clearCloudCredentials(): boolean {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
165
|
+
if (usingJsonOverride()) {
|
|
166
|
+
const path = cloudCredentialPath();
|
|
167
|
+
if (!existsSync(path)) return false;
|
|
168
|
+
try {
|
|
169
|
+
rmSync(path);
|
|
170
|
+
return true;
|
|
171
|
+
} catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
117
174
|
}
|
|
175
|
+
const current = readCredentials();
|
|
176
|
+
if (!current.cloud) return false;
|
|
177
|
+
// Drop only the cloud table — disconnecting policy must not also revoke the
|
|
178
|
+
// ingest key or the dashboard session.
|
|
179
|
+
const { cloud: _dropped, ...rest } = current;
|
|
180
|
+
writeCredentials(rest);
|
|
181
|
+
return true;
|
|
118
182
|
}
|
|
119
183
|
|
|
120
184
|
export type VerifyResult =
|
|
@@ -131,7 +195,13 @@ export type VerifyResult =
|
|
|
131
195
|
* came back.
|
|
132
196
|
*/
|
|
133
197
|
export async function verifyCloudCredentials(creds: CloudCredentials): Promise<VerifyResult> {
|
|
134
|
-
|
|
198
|
+
// The label rides the enrolment request so the server can record a
|
|
199
|
+
// human-facing name alongside the id. An older server simply ignores the
|
|
200
|
+
// extra query param, so sending it is always safe.
|
|
201
|
+
const labelParam = creds.machineLabel
|
|
202
|
+
? `&label=${encodeURIComponent(creds.machineLabel)}`
|
|
203
|
+
: "";
|
|
204
|
+
const url = `${creds.url}/enforcement/v1/desired-state?machineId=${encodeURIComponent(creds.machineId)}${labelParam}`;
|
|
135
205
|
let response: Response;
|
|
136
206
|
try {
|
|
137
207
|
response = await fetchWithTimeout(
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /v1/auth/introspect` — asking the server what a key actually is.
|
|
3
|
+
*
|
|
4
|
+
* Before this endpoint existed a machine could not describe its own
|
|
5
|
+
* credential: every other `/keys*` route needs a `keys:*` permission that a
|
|
6
|
+
* machine credential must never hold. So a revoked key, a valid key missing
|
|
7
|
+
* one permission, and a valid key pointed at the WRONG ORG all failed
|
|
8
|
+
* identically — at the point of use, later, as an empty dashboard.
|
|
9
|
+
*
|
|
10
|
+
* Introspect answers all three at connect time, in one request:
|
|
11
|
+
*
|
|
12
|
+
* is the key accepted at all? 401 vs 200
|
|
13
|
+
* which org will this machine report into? org_slug / org_name
|
|
14
|
+
* can it do the job it was provisioned for? permissions
|
|
15
|
+
*
|
|
16
|
+
* ## Effective, not granted — the subtle part
|
|
17
|
+
*
|
|
18
|
+
* Permissions imply other permissions, and the server widens a grant at
|
|
19
|
+
* authentication time (`expand_implied`): `alerts:read` also carries
|
|
20
|
+
* `issues:read`, and so on. Authorization is checked against the WIDENED set.
|
|
21
|
+
*
|
|
22
|
+
* The dashboard's key page and the CLI both display the *stored* grant, so both
|
|
23
|
+
* understate what a key can do. Introspect returns what the server actually
|
|
24
|
+
* enforces — which is precisely why a local permission check is only correct
|
|
25
|
+
* when it is built against this response and not against the displayed list.
|
|
26
|
+
*
|
|
27
|
+
* ## Do not poll this
|
|
28
|
+
*
|
|
29
|
+
* `/v1` has no rate limiting and failed auth is not negatively cached: one bad
|
|
30
|
+
* token is one database lookup per request. This runs ONCE, when a machine is
|
|
31
|
+
* provisioned. Revocation and expiry are cached server-side for up to a minute,
|
|
32
|
+
* so treat a positive answer as current to within a minute — never to the
|
|
33
|
+
* second.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/** The path introspect is served on, appended to the cloud origin. */
|
|
37
|
+
export const INTROSPECT_PATH = "/v1/auth/introspect";
|
|
38
|
+
|
|
39
|
+
export interface KeyIdentity {
|
|
40
|
+
keyId?: string;
|
|
41
|
+
orgId?: string;
|
|
42
|
+
orgSlug?: string;
|
|
43
|
+
orgName?: string;
|
|
44
|
+
/** The EFFECTIVE permission set — what the server enforces. */
|
|
45
|
+
permissions: string[];
|
|
46
|
+
expiresAt?: string | null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type IntrospectResult =
|
|
50
|
+
| { kind: "ok"; identity: KeyIdentity }
|
|
51
|
+
/** The key was refused. Nothing more is knowable — the body carries no detail. */
|
|
52
|
+
| { kind: "rejected" }
|
|
53
|
+
/**
|
|
54
|
+
* This server has no introspect endpoint, i.e. it predates the endpoint.
|
|
55
|
+
* The caller must fall back to probing rather than treating it as a bad key:
|
|
56
|
+
* the CLI ships independently of whatever AgentEye a customer runs, so a
|
|
57
|
+
* perfectly good key on an older server must still connect.
|
|
58
|
+
*/
|
|
59
|
+
| { kind: "unsupported" }
|
|
60
|
+
/** Network/DNS/TLS — indistinguishable from a typo'd host, so reported as-is. */
|
|
61
|
+
| { kind: "unreachable"; reason: string };
|
|
62
|
+
|
|
63
|
+
export function introspectUrlFor(origin: string): string {
|
|
64
|
+
return `${origin.replace(/\/$/, "")}${INTROSPECT_PATH}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function introspectKey(
|
|
68
|
+
origin: string,
|
|
69
|
+
token: string,
|
|
70
|
+
fetchImpl: typeof fetch = fetch,
|
|
71
|
+
timeoutMs = 10_000,
|
|
72
|
+
): Promise<IntrospectResult> {
|
|
73
|
+
// AbortController rather than the shared fetchWithTimeout helper: this needs
|
|
74
|
+
// an injectable `fetch` so tests never reach the network, and that helper
|
|
75
|
+
// closes over the global one.
|
|
76
|
+
const controller = new AbortController();
|
|
77
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
78
|
+
let res: Response;
|
|
79
|
+
try {
|
|
80
|
+
res = await fetchImpl(introspectUrlFor(origin), {
|
|
81
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
82
|
+
// Never follow a redirect. The dashboard app answers unrouted paths and
|
|
83
|
+
// will happily 307 to a login page that then returns 200 — which would
|
|
84
|
+
// read as a valid key against an endpoint that authenticated nothing.
|
|
85
|
+
redirect: "manual",
|
|
86
|
+
signal: controller.signal,
|
|
87
|
+
});
|
|
88
|
+
} catch (err) {
|
|
89
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
90
|
+
// Deliberately does not echo the URL: setup is routinely run while
|
|
91
|
+
// screen-sharing, and the host may be one the user would rather not show.
|
|
92
|
+
return { kind: "unreachable", reason: message };
|
|
93
|
+
} finally {
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (res.status === 401 || res.status === 403) return { kind: "rejected" };
|
|
98
|
+
// 404 is "this server has no such route"; a redirect means we reached the web
|
|
99
|
+
// app rather than the API, which on an older deployment means the same thing.
|
|
100
|
+
if (res.status === 404 || (res.status >= 300 && res.status < 400)) {
|
|
101
|
+
return { kind: "unsupported" };
|
|
102
|
+
}
|
|
103
|
+
if (!res.ok) return { kind: "unreachable", reason: `the server answered ${res.status}` };
|
|
104
|
+
|
|
105
|
+
let body: unknown;
|
|
106
|
+
try {
|
|
107
|
+
body = await res.json();
|
|
108
|
+
} catch {
|
|
109
|
+
// A 200 that is not JSON is a proxy or a static host, not introspect.
|
|
110
|
+
return { kind: "unsupported" };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const b = body as Record<string, unknown>;
|
|
114
|
+
// `valid: false` is not documented as a 200 shape, but treating a negative
|
|
115
|
+
// assertion as success would be the worst possible reading of it.
|
|
116
|
+
if (b.valid === false) return { kind: "rejected" };
|
|
117
|
+
if (!Array.isArray(b.permissions)) {
|
|
118
|
+
// Shaped like something else entirely — an unrelated 200 from a proxy.
|
|
119
|
+
return { kind: "unsupported" };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
kind: "ok",
|
|
124
|
+
identity: {
|
|
125
|
+
keyId: typeof b.key_id === "string" ? b.key_id : undefined,
|
|
126
|
+
orgId: typeof b.org_id === "string" ? b.org_id : undefined,
|
|
127
|
+
orgSlug: typeof b.org_slug === "string" ? b.org_slug : undefined,
|
|
128
|
+
orgName: typeof b.org_name === "string" ? b.org_name : undefined,
|
|
129
|
+
permissions: b.permissions.filter((p): p is string => typeof p === "string"),
|
|
130
|
+
expiresAt: typeof b.expires_at === "string" ? b.expires_at : null,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** The permission each capability needs. Named so the messages can quote them. */
|
|
136
|
+
export const PERMISSION_EVENTS = "events:add";
|
|
137
|
+
export const PERMISSION_POLICIES = "policies:pull";
|
|
138
|
+
|
|
139
|
+
export function hasPermission(identity: KeyIdentity, permission: string): boolean {
|
|
140
|
+
return identity.permissions.includes(permission);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** "Acme Inc (acme)", or the slug/id alone when the server sent less. */
|
|
144
|
+
export function describeOrg(identity: KeyIdentity): string {
|
|
145
|
+
if (identity.orgName && identity.orgSlug) return `${identity.orgName} (${identity.orgSlug})`;
|
|
146
|
+
return identity.orgName ?? identity.orgSlug ?? identity.orgId ?? "an unknown organisation";
|
|
147
|
+
}
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* before import so the worker never knowingly executes modified bytes.
|
|
6
6
|
*/
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
|
-
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
8
|
+
import { existsSync, readFileSync, realpathSync, rmSync } from "node:fs";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
10
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
11
|
+
import { cloudPoliciesDir } from "./fp-home";
|
|
11
12
|
|
|
12
13
|
const ACTIVE_SCHEMA_VERSION = 1;
|
|
13
14
|
const SHA256_RE = /^[a-f0-9]{64}$/;
|
|
@@ -47,7 +48,7 @@ interface ActiveManifest {
|
|
|
47
48
|
export function cloudManagedPolicyRoot(): string {
|
|
48
49
|
return (
|
|
49
50
|
process.env.FAILPROOFAI_CLOUD_POLICY_DIR ??
|
|
50
|
-
|
|
51
|
+
cloudPoliciesDir()
|
|
51
52
|
);
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -85,6 +86,36 @@ function resolveManagedPath(root: string, candidate: string): string {
|
|
|
85
86
|
return realFile;
|
|
86
87
|
}
|
|
87
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Stop enforcing cloud-managed policies on this machine.
|
|
91
|
+
*
|
|
92
|
+
* Removes `active.json` only — the manifest that says which generation is
|
|
93
|
+
* live. The generation directories and content-addressed artifacts are left
|
|
94
|
+
* alone: they are large, hash-verified on use, and inert once nothing points
|
|
95
|
+
* at them, so keeping them makes a reconnect cheap and offline-safe.
|
|
96
|
+
*
|
|
97
|
+
* Called by `--disconnect`, which without it did not disconnect. Clearing the
|
|
98
|
+
* credential stops the daemon REFRESHING policy; every artifact already on disk
|
|
99
|
+
* kept being loaded and enforced on every tool call, so a user who had
|
|
100
|
+
* deliberately left their organisation's cloud went on being governed by
|
|
101
|
+
* whatever generation happened to be current when they left — indefinitely,
|
|
102
|
+
* with `--status` reporting the machine as unconnected.
|
|
103
|
+
*
|
|
104
|
+
* Returns true when a manifest was actually removed.
|
|
105
|
+
*/
|
|
106
|
+
export function clearActiveCloudManagedPolicies(): boolean {
|
|
107
|
+
const activePath = resolve(cloudManagedPolicyRoot(), "active.json");
|
|
108
|
+
if (!existsSync(activePath)) return false;
|
|
109
|
+
try {
|
|
110
|
+
rmSync(activePath, { force: true });
|
|
111
|
+
return true;
|
|
112
|
+
} catch {
|
|
113
|
+
// Best-effort: `--disconnect` still clears the credentials, and reporting
|
|
114
|
+
// a failure to remove one file would obscure that the rest succeeded.
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
88
119
|
export function readActiveCloudManagedPolicies(): CloudManagedPolicyArtifact[] {
|
|
89
120
|
const root = cloudManagedPolicyRoot();
|
|
90
121
|
const activePath = resolve(root, "active.json");
|
|
@@ -28,9 +28,11 @@ import { join } from "node:path";
|
|
|
28
28
|
import { homedir } from "node:os";
|
|
29
29
|
|
|
30
30
|
import { readHooksConfig, writeHooksConfig } from "./hooks-config";
|
|
31
|
+
import { credentialsFile, failproofaiHome as layoutHome } from "./fp-home";
|
|
32
|
+
import { readCredentials, writeCredentials, updateConfig } from "./fp-config";
|
|
31
33
|
|
|
32
34
|
/** The hosted ingest endpoint — a COMPLETE endpoint, not a base to join onto. */
|
|
33
|
-
export const DEFAULT_INGEST_URL = "https://server.befailproof.ai/events";
|
|
35
|
+
export const DEFAULT_INGEST_URL = "https://server.befailproof.ai/v1/events";
|
|
34
36
|
|
|
35
37
|
export interface IngestCredential {
|
|
36
38
|
url: string;
|
|
@@ -45,19 +47,26 @@ export interface CollectorSettings {
|
|
|
45
47
|
hooksVerbosity?: "all" | "decisions" | "off";
|
|
46
48
|
redact?: "minimal" | "off";
|
|
47
49
|
environment?: string;
|
|
50
|
+
/**
|
|
51
|
+
* The machine's id, so the daemon can stamp it on every collected event and
|
|
52
|
+
* the dashboard groups by machine rather than by `agent_id` (a per-project
|
|
53
|
+
* identity). Written by `--connect --machine-id`; the Rust collector reads it
|
|
54
|
+
* as `collector.machineId`.
|
|
55
|
+
*/
|
|
56
|
+
machineId?: string;
|
|
48
57
|
}
|
|
49
58
|
|
|
50
59
|
export function failproofaiHome(): string {
|
|
51
|
-
return
|
|
60
|
+
return layoutHome();
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
export function ingestPath(): string {
|
|
55
|
-
return
|
|
64
|
+
return credentialsFile();
|
|
56
65
|
}
|
|
57
66
|
|
|
58
67
|
/** Whether an ingest credential is already configured on this machine. */
|
|
59
68
|
export function hasIngestCredential(): boolean {
|
|
60
|
-
return
|
|
69
|
+
return Boolean(readCredentials().ingest);
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
/**
|
|
@@ -69,24 +78,19 @@ export function hasIngestCredential(): boolean {
|
|
|
69
78
|
* cannot read it either, is the truth.
|
|
70
79
|
*/
|
|
71
80
|
export function readIngestCredential(): IngestCredential | null {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (typeof raw.url !== "string" || typeof raw.key !== "string") return null;
|
|
75
|
-
if (!raw.url || !raw.key) return null;
|
|
76
|
-
return { url: raw.url, key: raw.key };
|
|
77
|
-
} catch {
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
81
|
+
const ingest = readCredentials().ingest;
|
|
82
|
+
return ingest ? { url: ingest.url, key: ingest.key } : null;
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
/** Remove the ingest credential. Returns whether a file was actually removed. */
|
|
83
86
|
export function clearIngestCredential(): boolean {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
const current = readCredentials();
|
|
88
|
+
if (!current.ingest) return false;
|
|
89
|
+
// Drop only the ingest table: stopping event reporting must not also revoke
|
|
90
|
+
// the policy-pull credential or the dashboard session.
|
|
91
|
+
const { ingest: _dropped, ...rest } = current;
|
|
92
|
+
writeCredentials(rest);
|
|
93
|
+
return true;
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
/**
|
|
@@ -98,38 +102,26 @@ export function clearIngestCredential(): boolean {
|
|
|
98
102
|
* without POSIX modes; the write itself is not.
|
|
99
103
|
*/
|
|
100
104
|
export function writeIngestCredential(cred: IngestCredential): string {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
} catch {
|
|
107
|
-
// Not fatal: the file's own 0600 is the primary protection.
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const path = ingestPath();
|
|
111
|
-
// `mode` on writeFileSync applies only when the file is CREATED, so an
|
|
112
|
-
// existing over-permissive file would keep its mode without the chmod.
|
|
113
|
-
writeFileSync(path, JSON.stringify(cred, null, 2) + "\n", { mode: 0o600 });
|
|
114
|
-
try {
|
|
115
|
-
chmodSync(path, 0o600);
|
|
116
|
-
} catch {
|
|
117
|
-
/* best effort */
|
|
118
|
-
}
|
|
119
|
-
return path;
|
|
105
|
+
// Merge, never replace — credentials.toml also holds the cloud token and the
|
|
106
|
+
// auth session, and one caller rewriting the file must not drop the others.
|
|
107
|
+
// writeCredentials owns the 0600 mode and tightens the home around it.
|
|
108
|
+
writeCredentials({ ...readCredentials(), ingest: cred });
|
|
109
|
+
return credentialsFile();
|
|
120
110
|
}
|
|
121
111
|
|
|
122
112
|
/** Merge the collector block into the shared (non-secret) config. */
|
|
123
113
|
export function writeCollectorSettings(settings: CollectorSettings): void {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
114
|
+
// Collector preferences are NOT secret, so they live in config.toml. The key
|
|
115
|
+
// that makes them meaningful lives in credentials.toml; that split is the
|
|
116
|
+
// whole point of layout 2.
|
|
117
|
+
updateConfig({
|
|
127
118
|
collector: {
|
|
128
119
|
sessions: settings.sessions,
|
|
129
120
|
hooks: settings.hooks,
|
|
130
121
|
hooksVerbosity: settings.hooksVerbosity ?? "decisions",
|
|
131
122
|
redact: settings.redact ?? "minimal",
|
|
132
123
|
environment: settings.environment ?? "local",
|
|
124
|
+
machineId: settings.machineId,
|
|
133
125
|
},
|
|
134
126
|
});
|
|
135
127
|
}
|