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
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* and results in an empty hook list for that file. Builtins continue normally.
|
|
12
12
|
*/
|
|
13
13
|
import { resolve, isAbsolute, basename } from "node:path";
|
|
14
|
+
import { randomUUID } from "crypto";
|
|
14
15
|
import { existsSync, readdirSync } from "node:fs";
|
|
15
16
|
import { readFile } from "node:fs/promises";
|
|
16
17
|
import { pathToFileURL } from "node:url";
|
|
@@ -18,12 +19,20 @@ import { homedir } from "node:os";
|
|
|
18
19
|
import { createHash } from "node:crypto";
|
|
19
20
|
import { hookLogWarn, hookLogError, hookLogInfo } from "./hook-logger";
|
|
20
21
|
import { customPolicies, getCustomHooks, clearCustomHooks } from "./custom-hooks-registry";
|
|
21
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
findDistIndex,
|
|
24
|
+
rewriteFileTree,
|
|
25
|
+
TMP_SUFFIX,
|
|
26
|
+
cleanupTmpFiles,
|
|
27
|
+
isTmpArtifact,
|
|
28
|
+
sweepStaleTmpArtifacts,
|
|
29
|
+
} from "./loader-utils";
|
|
22
30
|
import { findProjectConfigDir } from "./hooks-config";
|
|
23
31
|
import { trackHookEvent } from "./hook-telemetry";
|
|
24
32
|
import { getInstanceId } from "../../lib/telemetry-id";
|
|
25
33
|
import type { CustomHook } from "./policy-types";
|
|
26
34
|
import type { CloudManagedPolicyArtifact } from "./cloud-managed-policies";
|
|
35
|
+
import { customPoliciesDir, shimsDir } from "./fp-home";
|
|
27
36
|
|
|
28
37
|
const LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__";
|
|
29
38
|
|
|
@@ -45,6 +54,15 @@ interface CachedPolicyModule {
|
|
|
45
54
|
* functions prevents an unbounded ESM module-cache leak in the resident worker.
|
|
46
55
|
* A source-tree change replaces this entry and imports one new module. */
|
|
47
56
|
const policyModuleCache = new Map<string, CachedPolicyModule>();
|
|
57
|
+
/**
|
|
58
|
+
* Cap, for the reason `gitBranchCache` states and this cache did not carry
|
|
59
|
+
* over: a warm worker touching many projects over its lifetime must not grow
|
|
60
|
+
* this unboundedly. Keyed by absolute policy-file path and holding cloned hook
|
|
61
|
+
* closures, so the entries are not small. Cleared wholesale rather than evicted
|
|
62
|
+
* one at a time — the next load simply re-imports, which is the same cost the
|
|
63
|
+
* cache exists to avoid paying twice, not a correctness change.
|
|
64
|
+
*/
|
|
65
|
+
const POLICY_MODULE_CACHE_MAX_ENTRIES = 500;
|
|
48
66
|
|
|
49
67
|
/** Script extensions we could load, used to spot near-miss filenames. */
|
|
50
68
|
const LOADABLE_EXT_RE = /\.(js|mjs|ts)$/;
|
|
@@ -87,7 +105,11 @@ export function findSkippedPolicyFiles(dir: string): string[] {
|
|
|
87
105
|
e.isFile() &&
|
|
88
106
|
LOADABLE_EXT_RE.test(e.name) &&
|
|
89
107
|
!CONVENTION_FILE_RE.test(e.name) &&
|
|
90
|
-
!e.name.endsWith(".d.ts")
|
|
108
|
+
!e.name.endsWith(".d.ts") &&
|
|
109
|
+
// Never our own generated files. They are `.mjs` and never match the
|
|
110
|
+
// convention, so each one a killed load left behind was reported to
|
|
111
|
+
// the user as a policy file that would not load.
|
|
112
|
+
!isTmpArtifact(e.name),
|
|
91
113
|
)
|
|
92
114
|
.map((e) => e.name)
|
|
93
115
|
.sort((a, b) => a.localeCompare(b));
|
|
@@ -96,13 +118,69 @@ export function findSkippedPolicyFiles(dir: string): string[] {
|
|
|
96
118
|
}
|
|
97
119
|
}
|
|
98
120
|
|
|
121
|
+
/**
|
|
122
|
+
* How long a policy module gets to finish evaluating its top level.
|
|
123
|
+
*
|
|
124
|
+
* Matches the 10s `handler.ts` gives a policy's `fn` to run, for the same
|
|
125
|
+
* reason: a policy file is user code, and user code that hangs must cost one
|
|
126
|
+
* skipped policy rather than the process.
|
|
127
|
+
*
|
|
128
|
+
* `FAILPROOFAI_POLICY_LOAD_TIMEOUT_MS` shortens it for tests — a suite proving
|
|
129
|
+
* the deadline fires should not have to sit out the real one.
|
|
130
|
+
*/
|
|
131
|
+
function moduleLoadTimeoutMs(): number {
|
|
132
|
+
const override = Number(process.env.FAILPROOFAI_POLICY_LOAD_TIMEOUT_MS);
|
|
133
|
+
return Number.isFinite(override) && override > 0 ? override : 10_000;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* `import()`, but it cannot hang forever.
|
|
138
|
+
*
|
|
139
|
+
* A bare `await import(...)` on a module whose top level contains an `await`
|
|
140
|
+
* that never resolves never settles — it does not reject, so no `catch` and no
|
|
141
|
+
* `finally` anywhere above it ever runs. In the one-shot hook path that costs
|
|
142
|
+
* one hook process, which the agent CLI's own timeout reaps. In the warm daemon
|
|
143
|
+
* worker it holds `worker-server.ts`'s serialization chain forever, and every
|
|
144
|
+
* subsequent hook on the machine queues behind it and fail-closed denies.
|
|
145
|
+
*
|
|
146
|
+
* Losing the race does NOT cancel the import — nothing can. It stops us
|
|
147
|
+
* *waiting* on it, which is the part that matters, and the caller's `catch`
|
|
148
|
+
* below reports the file as failed to load. The abandoned module keeps its
|
|
149
|
+
* half-initialized entry in the ESM cache, but each load writes a uniquely
|
|
150
|
+
* named temporary tree, so a retry imports a different specifier and is never
|
|
151
|
+
* poisoned by it.
|
|
152
|
+
*/
|
|
153
|
+
async function importWithDeadline(fileUrl: string): Promise<void> {
|
|
154
|
+
const budget = moduleLoadTimeoutMs();
|
|
155
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
156
|
+
try {
|
|
157
|
+
await Promise.race([
|
|
158
|
+
import(/* webpackIgnore: true */ fileUrl),
|
|
159
|
+
new Promise<never>((_, reject) => {
|
|
160
|
+
timer = setTimeout(
|
|
161
|
+
() => reject(new Error(`timed out loading module after ${budget}ms`)),
|
|
162
|
+
budget,
|
|
163
|
+
);
|
|
164
|
+
timer.unref?.();
|
|
165
|
+
}),
|
|
166
|
+
]);
|
|
167
|
+
} finally {
|
|
168
|
+
if (timer) clearTimeout(timer);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
99
172
|
/**
|
|
100
173
|
* Load a single policy file into the globalThis custom hooks registry.
|
|
101
174
|
* Does NOT clear the registry — caller is responsible for that.
|
|
102
175
|
*/
|
|
103
176
|
async function loadSingleFile(
|
|
104
177
|
absPath: string,
|
|
105
|
-
opts?: {
|
|
178
|
+
opts?: {
|
|
179
|
+
strict?: boolean;
|
|
180
|
+
conventionScope?: "project" | "user";
|
|
181
|
+
/** Cloud-managed policies pass their pinned digest for load-time re-verification. */
|
|
182
|
+
verifyEntrySha?: string;
|
|
183
|
+
},
|
|
106
184
|
): Promise<void> {
|
|
107
185
|
const g = globalThis as Record<string, unknown>;
|
|
108
186
|
g[LOADING_KEY] = true;
|
|
@@ -113,8 +191,19 @@ async function loadSingleFile(
|
|
|
113
191
|
const distUrl = distIndex ? pathToFileURL(distIndex).href : null;
|
|
114
192
|
|
|
115
193
|
const sequence = ++loadSequence;
|
|
116
|
-
|
|
117
|
-
|
|
194
|
+
// pid + sequence is not unique enough on its own: two containers sharing a
|
|
195
|
+
// mounted HOME, or two pid namespaces, can collide — and on the shim's
|
|
196
|
+
// fallback path a collision means EEXIST, which fails the load OPEN. A
|
|
197
|
+
// random id removes the class. It costs nothing: `fingerprintTemporaryTree`
|
|
198
|
+
// normalises the whole suffix away, so the module cache still hits.
|
|
199
|
+
const tmpSuffix = `${TMP_SUFFIX}.${process.pid}.${sequence}.${randomUUID()}.mjs`;
|
|
200
|
+
tmpFiles = await rewriteFileTree(
|
|
201
|
+
absPath,
|
|
202
|
+
distUrl,
|
|
203
|
+
distIndex,
|
|
204
|
+
tmpSuffix,
|
|
205
|
+
opts?.verifyEntrySha,
|
|
206
|
+
);
|
|
118
207
|
|
|
119
208
|
const fingerprint = await fingerprintTemporaryTree(tmpFiles, tmpSuffix);
|
|
120
209
|
const cached = policyModuleCache.get(absPath);
|
|
@@ -126,7 +215,8 @@ async function loadSingleFile(
|
|
|
126
215
|
const entryTmp = absPath + tmpSuffix;
|
|
127
216
|
const fileUrl = pathToFileURL(entryTmp).href;
|
|
128
217
|
const hooksBefore = getCustomHooks().length;
|
|
129
|
-
await
|
|
218
|
+
await importWithDeadline(fileUrl);
|
|
219
|
+
if (policyModuleCache.size >= POLICY_MODULE_CACHE_MAX_ENTRIES) policyModuleCache.clear();
|
|
130
220
|
policyModuleCache.set(absPath, {
|
|
131
221
|
fingerprint,
|
|
132
222
|
hooks: getCustomHooks()
|
|
@@ -139,7 +229,9 @@ async function loadSingleFile(
|
|
|
139
229
|
? "module_not_found"
|
|
140
230
|
: /SyntaxError|Unexpected token/i.test(msg)
|
|
141
231
|
? "syntax_error"
|
|
142
|
-
:
|
|
232
|
+
: /timed out loading module/i.test(msg)
|
|
233
|
+
? "load_timeout"
|
|
234
|
+
: "runtime_error";
|
|
143
235
|
void trackHookEvent(getInstanceId(), "custom_hooks_load_error", {
|
|
144
236
|
error_type: errorType,
|
|
145
237
|
is_convention: !!opts?.conventionScope,
|
|
@@ -282,7 +374,11 @@ export async function loadAllCustomHooks(
|
|
|
282
374
|
if (!loadedPaths.has(absPath)) {
|
|
283
375
|
loadedPaths.add(absPath);
|
|
284
376
|
const hooksBefore = getCustomHooks().length;
|
|
285
|
-
|
|
377
|
+
// A cloud-managed policy re-verifies its pinned digest at load, binding
|
|
378
|
+
// the imported bytes to what desired-state promised.
|
|
379
|
+
await loadSingleFile(absPath, {
|
|
380
|
+
verifyEntrySha: cloudManagedByPath.get(absPath)?.sha256,
|
|
381
|
+
});
|
|
286
382
|
for (const hook of getCustomHooks().slice(hooksBefore)) {
|
|
287
383
|
const cloudManaged = cloudManagedByPath.get(absPath);
|
|
288
384
|
const tagged = hook as CustomHook & {
|
|
@@ -312,7 +408,22 @@ export async function loadAllCustomHooks(
|
|
|
312
408
|
// exactly that — and it is discovered here as well. Skip what step 1 loaded,
|
|
313
409
|
// or the file is imported twice and every hook in it fires twice per event.
|
|
314
410
|
const projectDir = resolve(projectRoot, ".failproofai", "policies");
|
|
315
|
-
|
|
411
|
+
// Clear out generated files a killed load left behind, before anything scans
|
|
412
|
+
// this directory. The temporary tree has to be written beside the sources for
|
|
413
|
+
// a rewritten relative import to resolve, and its name now carries a pid and
|
|
414
|
+
// sequence number — so unlike the old fixed name, each abnormal termination
|
|
415
|
+
// leaks one file permanently rather than leaving one that the next load
|
|
416
|
+
// overwrites. Best-effort and age-gated, so it can never remove a tree
|
|
417
|
+
// another process is still importing.
|
|
418
|
+
if (conventionEnabled) {
|
|
419
|
+
void sweepStaleTmpArtifacts(projectDir);
|
|
420
|
+
warnSkippedPolicyFiles(projectDir, "project");
|
|
421
|
+
}
|
|
422
|
+
// The shim directory leaks the same way and is swept on the same terms. It is
|
|
423
|
+
// ours alone and outside the policy tree, so nothing else would ever reap it;
|
|
424
|
+
// the age gate is what keeps this from removing a shim another process is
|
|
425
|
+
// still importing.
|
|
426
|
+
void sweepStaleTmpArtifacts(shimsDir());
|
|
316
427
|
const projectFiles = conventionEnabled
|
|
317
428
|
? discoverPolicyFiles(projectDir).filter((f) => !loadedPaths.has(f))
|
|
318
429
|
: [];
|
|
@@ -347,8 +458,11 @@ export async function loadAllCustomHooks(
|
|
|
347
458
|
// so the second import is a no-op — but Node honours the query and would
|
|
348
459
|
// double-register. The binary runs under Bun and the tests under Node, so the
|
|
349
460
|
// bug is invisible from both sides. Do not rely on that; dedupe the paths.
|
|
350
|
-
const userDir =
|
|
351
|
-
if (conventionEnabled && userDir !== projectDir)
|
|
461
|
+
const userDir = customPoliciesDir();
|
|
462
|
+
if (conventionEnabled && userDir !== projectDir) {
|
|
463
|
+
void sweepStaleTmpArtifacts(userDir);
|
|
464
|
+
warnSkippedPolicyFiles(userDir, "user");
|
|
465
|
+
}
|
|
352
466
|
const userFiles =
|
|
353
467
|
conventionEnabled && userDir !== projectDir
|
|
354
468
|
? discoverPolicyFiles(userDir).filter((f) => !loadedPaths.has(f))
|
|
@@ -18,6 +18,8 @@ import { homedir } from "node:os";
|
|
|
18
18
|
import type { IntegrationType } from "./types";
|
|
19
19
|
import { readHooksConfig } from "./hooks-config";
|
|
20
20
|
import { existsSync } from "node:fs";
|
|
21
|
+
import { daemonSocket as daemonSocketPath } from "./fp-home";
|
|
22
|
+
import { readConfig } from "./fp-config";
|
|
21
23
|
|
|
22
24
|
const PROTOCOL_VERSION = 1;
|
|
23
25
|
|
|
@@ -67,6 +69,27 @@ export interface DaemonHookResponse {
|
|
|
67
69
|
stderr: string;
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Why an attempt failed — and it matters, because the two cases deserve
|
|
74
|
+
* opposite answers.
|
|
75
|
+
*
|
|
76
|
+
* `protocol-mismatch` means a daemon answered and is demonstrably HEALTHY; we
|
|
77
|
+
* simply cannot speak its wire format. The cause is known and benign: the CLI
|
|
78
|
+
* upgraded via npm and the daemon has not been reinstalled yet. Falling back to
|
|
79
|
+
* in-process evaluation there loses no enforcement at all — it runs the
|
|
80
|
+
* identical policy engine, just slower — so denying every tool call over it
|
|
81
|
+
* would take a working machine offline to protect nothing.
|
|
82
|
+
*
|
|
83
|
+
* `unreachable` means nothing answered. That could be a stopped service, a
|
|
84
|
+
* deleted socket, or tampering, and we cannot tell which — so it keeps failing
|
|
85
|
+
* closed, which is the whole point of `daemonConfigured`.
|
|
86
|
+
*/
|
|
87
|
+
export type DaemonFailure = "unreachable" | "protocol-mismatch";
|
|
88
|
+
|
|
89
|
+
export type DaemonAttempt =
|
|
90
|
+
| { ok: true; response: DaemonHookResponse }
|
|
91
|
+
| { ok: false; failure: DaemonFailure };
|
|
92
|
+
|
|
70
93
|
/**
|
|
71
94
|
* Whether a daemon is listening, regardless of how it was started.
|
|
72
95
|
*
|
|
@@ -86,7 +109,7 @@ export function daemonSocketPresent(): boolean {
|
|
|
86
109
|
|
|
87
110
|
function socketPath(): string {
|
|
88
111
|
if (process.env.FAILPROOFAI_DAEMON_SOCKET) return process.env.FAILPROOFAI_DAEMON_SOCKET;
|
|
89
|
-
return
|
|
112
|
+
return daemonSocketPath();
|
|
90
113
|
}
|
|
91
114
|
|
|
92
115
|
/**
|
|
@@ -99,8 +122,11 @@ function socketPath(): string {
|
|
|
99
122
|
*/
|
|
100
123
|
export function isDaemonConfigured(): boolean {
|
|
101
124
|
try {
|
|
102
|
-
return
|
|
125
|
+
return readConfig().daemon.configured === true;
|
|
103
126
|
} catch {
|
|
127
|
+
// Unreadable config reads as NOT daemon-configured. The failure direction
|
|
128
|
+
// is deliberate: the alternative is a machine that fails closed on every
|
|
129
|
+
// tool call because a config file got truncated.
|
|
104
130
|
return false;
|
|
105
131
|
}
|
|
106
132
|
}
|
|
@@ -120,24 +146,52 @@ function encodeFrame(value: unknown): Buffer {
|
|
|
120
146
|
* falls through to whatever its own fallback policy is.
|
|
121
147
|
*/
|
|
122
148
|
export async function tryDaemonHook(req: DaemonHookRequest): Promise<DaemonHookResponse | null> {
|
|
149
|
+
const attempt = await attemptDaemonHook(req);
|
|
150
|
+
return attempt.ok ? attempt.response : null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The same attempt, but reporting WHY it failed.
|
|
155
|
+
*
|
|
156
|
+
* `tryDaemonHook` above collapses every failure into `null`, which was fine
|
|
157
|
+
* while the caller had one answer for all of them. It no longer does: a
|
|
158
|
+
* protocol mismatch must fall back rather than deny (see `DaemonFailure`), and
|
|
159
|
+
* a caller cannot make that distinction from `null`.
|
|
160
|
+
*/
|
|
161
|
+
export async function attemptDaemonHook(
|
|
162
|
+
req: DaemonHookRequest,
|
|
163
|
+
opts?: {
|
|
164
|
+
/**
|
|
165
|
+
* Override the RESPONSE budget only — the connect budget is never
|
|
166
|
+
* relaxed. Used by the health probe (`probeDaemonEndToEnd`), which runs
|
|
167
|
+
* from an interactive command rather than a hook and must not make a
|
|
168
|
+
* person wait out the full 30s hook budget to be told their daemon is
|
|
169
|
+
* broken. Never set this on the hook path: 30s is matched to the
|
|
170
|
+
* daemon's own read timeout so this side never gives up on a request the
|
|
171
|
+
* daemon is still honestly working on.
|
|
172
|
+
*/
|
|
173
|
+
responseTimeoutMs?: number;
|
|
174
|
+
},
|
|
175
|
+
): Promise<DaemonAttempt> {
|
|
123
176
|
// Windows never has a daemon in this phase (see the plan's platform
|
|
124
177
|
// scope) — skip the attempt outright rather than depending on however
|
|
125
178
|
// Node happens to behave when handed a POSIX socket path on Windows.
|
|
126
|
-
if (process.platform === "win32") return
|
|
179
|
+
if (process.platform === "win32") return { ok: false, failure: "unreachable" };
|
|
180
|
+
const responseBudget = opts?.responseTimeoutMs ?? DAEMON_RESPONSE_TIMEOUT_MS;
|
|
127
181
|
|
|
128
|
-
return new Promise<
|
|
182
|
+
return new Promise<DaemonAttempt>((resolvePromise) => {
|
|
129
183
|
let settled = false;
|
|
130
184
|
let timer: ReturnType<typeof setTimeout>;
|
|
131
185
|
const arm = (ms: number) => {
|
|
132
186
|
clearTimeout(timer);
|
|
133
|
-
timer = setTimeout(() =>
|
|
187
|
+
timer = setTimeout(() => fail("unreachable"), ms);
|
|
134
188
|
// Don't let this timer alone keep the process alive if everything else
|
|
135
189
|
// has already finished — it's always cleared on the success/failure
|
|
136
190
|
// paths below, this just avoids it being the sole reason a --hook
|
|
137
191
|
// process lingers if something upstream forgets to await us.
|
|
138
192
|
timer.unref?.();
|
|
139
193
|
};
|
|
140
|
-
const finish = (result:
|
|
194
|
+
const finish = (result: DaemonAttempt) => {
|
|
141
195
|
if (settled) return;
|
|
142
196
|
settled = true;
|
|
143
197
|
clearTimeout(timer);
|
|
@@ -145,6 +199,7 @@ export async function tryDaemonHook(req: DaemonHookRequest): Promise<DaemonHookR
|
|
|
145
199
|
socket.destroy();
|
|
146
200
|
resolvePromise(result);
|
|
147
201
|
};
|
|
202
|
+
const fail = (failure: DaemonFailure) => finish({ ok: false, failure });
|
|
148
203
|
|
|
149
204
|
const socket = createConnection({ path: socketPath() });
|
|
150
205
|
arm(DAEMON_CONNECT_TIMEOUT_MS);
|
|
@@ -155,7 +210,7 @@ export async function tryDaemonHook(req: DaemonHookRequest): Promise<DaemonHookR
|
|
|
155
210
|
socket.on("connect", () => {
|
|
156
211
|
// Connected: the daemon is demonstrably reachable, so the question is
|
|
157
212
|
// no longer "is it there" but "how long does this evaluation take".
|
|
158
|
-
arm(
|
|
213
|
+
arm(responseBudget);
|
|
159
214
|
socket.write(
|
|
160
215
|
encodeFrame({
|
|
161
216
|
type: "hook",
|
|
@@ -175,7 +230,7 @@ export async function tryDaemonHook(req: DaemonHookRequest): Promise<DaemonHookR
|
|
|
175
230
|
if (recvBuf.length < 4) return;
|
|
176
231
|
declaredLen = recvBuf.readUInt32BE(0);
|
|
177
232
|
if (declaredLen > MAX_FRAME_LEN) {
|
|
178
|
-
|
|
233
|
+
fail("unreachable");
|
|
179
234
|
return;
|
|
180
235
|
}
|
|
181
236
|
recvBuf = recvBuf.subarray(4);
|
|
@@ -188,12 +243,16 @@ export async function tryDaemonHook(req: DaemonHookRequest): Promise<DaemonHookR
|
|
|
188
243
|
try {
|
|
189
244
|
message = JSON.parse(body.toString("utf8")) as Record<string, unknown>;
|
|
190
245
|
} catch {
|
|
191
|
-
|
|
246
|
+
fail("unreachable");
|
|
192
247
|
return;
|
|
193
248
|
}
|
|
194
249
|
|
|
195
250
|
if (message.protocolVersion !== PROTOCOL_VERSION) {
|
|
196
|
-
|
|
251
|
+
// Catches BOTH directions. A newer CLI sends v2 and the daemon replies
|
|
252
|
+
// with an error stamped v1; an older CLI sends v1 and a newer daemon
|
|
253
|
+
// replies stamped v2. Either way the versions disagree here, and either
|
|
254
|
+
// way a daemon answered — so this is skew, not absence.
|
|
255
|
+
fail("protocol-mismatch");
|
|
197
256
|
return;
|
|
198
257
|
}
|
|
199
258
|
if (
|
|
@@ -202,16 +261,23 @@ export async function tryDaemonHook(req: DaemonHookRequest): Promise<DaemonHookR
|
|
|
202
261
|
typeof message.stdout === "string" &&
|
|
203
262
|
typeof message.stderr === "string"
|
|
204
263
|
) {
|
|
205
|
-
finish({
|
|
264
|
+
finish({
|
|
265
|
+
ok: true,
|
|
266
|
+
response: {
|
|
267
|
+
exitCode: message.exitCode,
|
|
268
|
+
stdout: message.stdout,
|
|
269
|
+
stderr: message.stderr,
|
|
270
|
+
},
|
|
271
|
+
});
|
|
206
272
|
return;
|
|
207
273
|
}
|
|
208
|
-
// Anything else — an explicit `error` message
|
|
209
|
-
// confusion), or a well-formed-but-wrong-shape
|
|
210
|
-
// identically to a connection failure: no partial trust.
|
|
211
|
-
|
|
274
|
+
// Anything else — an explicit `error` message at a MATCHING protocol
|
|
275
|
+
// version, a `pong` (protocol confusion), or a well-formed-but-wrong-shape
|
|
276
|
+
// body — is treated identically to a connection failure: no partial trust.
|
|
277
|
+
fail("unreachable");
|
|
212
278
|
});
|
|
213
279
|
|
|
214
|
-
socket.on("error", () =>
|
|
215
|
-
socket.on("close", () =>
|
|
280
|
+
socket.on("error", () => fail("unreachable"));
|
|
281
|
+
socket.on("close", () => fail("unreachable"));
|
|
216
282
|
});
|
|
217
283
|
}
|
|
@@ -33,13 +33,24 @@
|
|
|
33
33
|
* this writes an executable that a service manager will then run at login.
|
|
34
34
|
*/
|
|
35
35
|
import { createHash } from "node:crypto";
|
|
36
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
chmodSync,
|
|
38
|
+
existsSync,
|
|
39
|
+
mkdirSync,
|
|
40
|
+
readdirSync,
|
|
41
|
+
readFileSync,
|
|
42
|
+
renameSync,
|
|
43
|
+
rmSync,
|
|
44
|
+
statSync,
|
|
45
|
+
writeFileSync,
|
|
46
|
+
} from "node:fs";
|
|
37
47
|
import { createRequire } from "node:module";
|
|
38
48
|
import { homedir } from "node:os";
|
|
39
49
|
import { dirname, resolve } from "node:path";
|
|
40
50
|
import { gunzipSync } from "node:zlib";
|
|
41
51
|
import { version } from "../../package.json";
|
|
42
52
|
import type { PlatformKey } from "./daemon-service";
|
|
53
|
+
import { binDir as layoutBinDir } from "./fp-home";
|
|
43
54
|
|
|
44
55
|
/**
|
|
45
56
|
* Where release assets live. Overridable so an air-gapped or proxied site can
|
|
@@ -62,7 +73,7 @@ function baseUrl(): string {
|
|
|
62
73
|
|
|
63
74
|
/** Where downloaded binaries live. One directory, one file per version. */
|
|
64
75
|
export function daemonBinaryDir(): string {
|
|
65
|
-
return
|
|
76
|
+
return layoutBinDir();
|
|
66
77
|
}
|
|
67
78
|
|
|
68
79
|
/**
|
|
@@ -165,6 +176,47 @@ function platformPackageBinary(dir: string): string | null {
|
|
|
165
176
|
* manager could pick up), mode 0755, pid-suffixed so two concurrent wizards
|
|
166
177
|
* stay off each other's file.
|
|
167
178
|
*/
|
|
179
|
+
/**
|
|
180
|
+
* Delete daemon binaries older than the current and previous versions.
|
|
181
|
+
*
|
|
182
|
+
* Called AFTER the new binary is in place and the unit repointed, never
|
|
183
|
+
* before: a binary a running service points at must not be removed out from
|
|
184
|
+
* under it. One previous version is kept deliberately — a rollback is then a
|
|
185
|
+
* local file rather than a re-download, which is the difference between
|
|
186
|
+
* working and not on a machine that is offline or behind a proxy.
|
|
187
|
+
*
|
|
188
|
+
* Sorted by mtime rather than by parsing versions out of filenames: a
|
|
189
|
+
* prerelease ordering (`1.0.0-beta.10` vs `1.0.0-beta.9`) is easy to get
|
|
190
|
+
* subtly wrong, and "which did we install most recently" is the question that
|
|
191
|
+
* actually matters here.
|
|
192
|
+
*/
|
|
193
|
+
export function pruneOldDaemonBinaries(keep = 2): string[] {
|
|
194
|
+
const removed: string[] = [];
|
|
195
|
+
try {
|
|
196
|
+
const dir = daemonBinaryDir();
|
|
197
|
+
const entries = readdirSync(dir)
|
|
198
|
+
.filter((f) => f.startsWith("failproofaid-") && !f.endsWith(".tmp"))
|
|
199
|
+
.map((f) => {
|
|
200
|
+
const full = resolve(dir, f);
|
|
201
|
+
return { full, mtime: statSync(full).mtimeMs };
|
|
202
|
+
})
|
|
203
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
204
|
+
|
|
205
|
+
for (const stale of entries.slice(keep)) {
|
|
206
|
+
try {
|
|
207
|
+
rmSync(stale.full, { force: true });
|
|
208
|
+
removed.push(stale.full);
|
|
209
|
+
} catch {
|
|
210
|
+
// In use, or not ours to delete. Leaving it costs disk; removing it by
|
|
211
|
+
// force could break a running service.
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} catch {
|
|
215
|
+
// No directory yet, or unreadable — nothing to prune.
|
|
216
|
+
}
|
|
217
|
+
return removed;
|
|
218
|
+
}
|
|
219
|
+
|
|
168
220
|
function installBinaryBytes(binary: Buffer): string {
|
|
169
221
|
const target = installedBinaryPath();
|
|
170
222
|
const tempPath = `${target}.${process.pid}.tmp`;
|