failproofai 1.0.0-beta.2 → 1.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +2 -1
- package/.next/standalone/.next/build-manifest.json +6 -6
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/routes-manifest.json +6 -0
- package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +3 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +3 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -3
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js +3 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +5 -3
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +3 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +30 -18
- package/.next/standalone/.next/server/app/policies/page.js +5 -3
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +4 -2
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +4 -2
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/settings/page/build-manifest.json +18 -0
- package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +6 -0
- package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +53 -0
- package/.next/standalone/.next/server/app/settings/page.js +18 -0
- package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app-paths-manifest.json +2 -1
- package/.next/standalone/.next/server/chunks/[externals]__0h9_83k._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[externals]__1ifpkx-._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__00ni9za._.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]__08y94xv._.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]__0-dh5jy._.js → [root-of-the-server]__0effm9m._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0ix8gm7._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ji11a-._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0ojwjxl._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__16le-kd._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vb5fl2._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +4 -4
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.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]__20d06rh._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +3 -0
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[externals]__1aq139v._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-wfhrp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1apb7k_._.js → [root-of-the-server]__0_1tiya._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__170799-._.js → [root-of-the-server]__0spkm68._.js} +11 -11
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0t281d4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17itmx-._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__01od0m6._.js → [root-of-the-server]__1n2vcv4._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tlvv9a._.js +3 -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/_0-yi74u._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0vo2qyt._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0xej-0e._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_10399ge._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_149k9oy._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1_hwp06._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1ctse6u._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1e19atl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1mnhi_r._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1ror7k9._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1sdgprl._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +4 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
- package/.next/standalone/.next/server/middleware.js +2 -2
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +74 -20
- package/.next/standalone/.next/static/chunks/{03fmihek9n986.js → 06zgzyh3wv1ig.js} +1 -1
- package/.next/standalone/.next/static/chunks/0km-jn9o5xyqs.css +2 -0
- package/.next/standalone/.next/static/chunks/{1hdhteui932bq.js → 0qmo2t50kpb30.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2sbelanzvxvv0.js → 113g8wtal6_c4.js} +22 -22
- package/.next/standalone/.next/static/chunks/16p68cybetr31.js +1 -0
- package/.next/standalone/.next/static/chunks/18wqn0_3mzylf.js +1 -0
- package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
- package/.next/standalone/.next/static/chunks/{0fi645si-uiiq.js → 1xq1m8h14tnlj.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3hp8ulqknybil.js → 1y3vwogqb88dx.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3vs4ay36tjds_.js → 1zmh-b0ulrqu7.js} +1 -1
- package/.next/standalone/.next/static/chunks/23kev26by1e4f.js +1 -0
- package/.next/standalone/.next/static/chunks/299lhw60e8gr6.js +1 -0
- package/.next/standalone/.next/static/chunks/{18rvdw5v72364.js → 2adbpdaxp5ygq.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2j94uwkb4s3qr.js → 2kbqcw-6swmav.js} +1 -1
- package/.next/standalone/.next/static/chunks/2n9n5m0963pdg.js +6 -0
- package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
- package/.next/standalone/.next/static/chunks/{31feojxhjyjpl.js → 30nrrpwymht5m.js} +1 -1
- package/.next/standalone/.next/static/chunks/31coz11ia5vc4.js +1 -0
- package/.next/standalone/.next/static/chunks/{turbopack-1lsv92x7klwrn.js → turbopack-3diub4ctvn1n0.js} +1 -1
- package/.next/standalone/SECURITY.md +8 -2
- package/.next/standalone/app/actions/get-active-pauses.ts +16 -0
- package/.next/standalone/app/actions/get-hooks-config.ts +2 -2
- 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/audit-dashboard.tsx +1 -3
- package/.next/standalone/app/audit/_components/audit-progress-strip.tsx +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/_components/come-back-better-section.tsx +1 -1
- package/.next/standalone/app/audit/_components/empty-state.tsx +0 -1
- package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +1 -1
- package/.next/standalone/app/audit/_components/quirks-section.tsx +0 -1
- package/.next/standalone/app/audit/_components/report-footer.tsx +0 -2
- package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
- package/.next/standalone/app/audit/_components/strengths-section.tsx +0 -1
- package/.next/standalone/app/audit/audit-styles.css +6 -149
- package/.next/standalone/app/components/pause-notices.tsx +111 -0
- package/.next/standalone/app/components/raw-log-viewer.tsx +1 -1
- package/.next/standalone/app/components/toast.tsx +1 -1
- package/.next/standalone/app/policies/hooks-client.tsx +84 -8
- package/.next/standalone/app/settings/page.tsx +31 -0
- package/.next/standalone/app/settings/settings-client.tsx +487 -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 -5
- package/.next/standalone/lib/codex-sessions.ts +2 -1
- package/.next/standalone/lib/dashboard-host.ts +101 -0
- package/.next/standalone/lib/install-check.ts +22 -7
- package/.next/standalone/lib/pi-sessions.ts +99 -14
- package/.next/standalone/lib/telemetry-enabled.ts +54 -0
- package/.next/standalone/lib/telemetry-id.ts +52 -1
- package/.next/standalone/lib/telemetry.ts +8 -4
- package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
- package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
- package/.next/standalone/node_modules/react/package.json +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/package.json +4 -4
- package/.next/standalone/package.json +23 -12
- package/.next/standalone/proxy.ts +111 -1
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai-worker.mjs +1 -1
- package/bin/failproofai.mjs +1041 -81
- package/bin/failproofaid-shim.mjs +4 -4
- package/dist/cli.mjs +18513 -10474
- package/dist/worker.mjs +1223 -299
- package/lib/auth/api-server-client.ts +2 -3
- package/lib/auth/auth-store.ts +4 -5
- package/lib/codex-sessions.ts +2 -1
- package/lib/dashboard-host.ts +101 -0
- package/lib/install-check.ts +22 -7
- package/lib/pi-sessions.ts +99 -14
- package/lib/telemetry-enabled.ts +54 -0
- package/lib/telemetry-id.ts +52 -1
- package/lib/telemetry.ts +8 -4
- package/package.json +23 -12
- package/scripts/build-daemon-packages.mjs +247 -0
- package/scripts/daemon-platforms.mjs +42 -0
- package/scripts/dev-hook.mjs +46 -5
- package/scripts/install-telemetry.mjs +8 -1
- package/scripts/launch.ts +25 -3
- package/scripts/parse-script-args.ts +27 -1
- package/scripts/prune-standalone.mjs +26 -0
- package/scripts/publish-aliases.mjs +62 -41
- package/scripts/repro-npm-install.sh +155 -0
- package/scripts/translate-docs/cache.ts +1 -1
- package/scripts/translate-docs/cli.ts +2 -2
- package/scripts/translate-docs/mintlify-nav.ts +1 -1
- 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 -5
- 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 -3
- package/src/audit/report.ts +0 -1
- package/src/hooks/backfill-cli.ts +247 -0
- package/src/hooks/builtin-policies.ts +391 -3
- package/src/hooks/cloud-connection.ts +354 -0
- package/src/hooks/cloud-enrollment-cli.ts +439 -0
- package/src/hooks/cloud-enrollment.ts +284 -0
- package/src/hooks/cloud-introspect.ts +147 -0
- package/src/hooks/cloud-managed-policies.ts +206 -0
- package/src/hooks/collector-config.ts +218 -0
- package/src/hooks/configure-wizard.ts +1211 -233
- package/src/hooks/custom-hooks-loader.ts +243 -37
- package/src/hooks/daemon-client.ts +136 -33
- package/src/hooks/daemon-download.ts +264 -31
- package/src/hooks/daemon-service.ts +1124 -76
- package/src/hooks/first-run-gate.ts +50 -0
- package/src/hooks/flush-cli.ts +180 -0
- package/src/hooks/fp-config.ts +835 -0
- package/src/hooks/fp-home.ts +714 -0
- package/src/hooks/fp-reset.ts +1363 -0
- package/src/hooks/handler.ts +185 -6
- package/src/hooks/harness-cli.ts +386 -0
- package/src/hooks/hook-activity-store.ts +171 -24
- package/src/hooks/hook-logger.ts +2 -2
- package/src/hooks/hook-telemetry.ts +5 -1
- package/src/hooks/hooks-config.ts +4 -3
- package/src/hooks/integrations.ts +0 -1
- package/src/hooks/loader-utils.ts +172 -9
- package/src/hooks/manager.ts +41 -5
- package/src/hooks/migrations.ts +450 -0
- package/src/hooks/onboarding-attempt.ts +195 -0
- package/src/hooks/onboarding-lock.ts +118 -0
- package/src/hooks/policy-types.ts +28 -0
- package/src/hooks/session-pause-cli.ts +159 -0
- package/src/hooks/session-pause.ts +267 -0
- package/src/hooks/setup-state.ts +256 -0
- package/src/hooks/tui.ts +209 -9
- package/src/hooks/uninstall-cli.ts +418 -0
- package/src/hooks/worker-server.ts +134 -15
- package/.next/standalone/.next/server/chunks/[externals]__14odj07._.js +0 -3
- package/.next/standalone/.next/server/chunks/[externals]__1s61mel._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0id_xf7._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0longh-._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[externals]__1_g_b3t._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0114ewg._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0nb0syg._.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]__0u02miy._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0uno39i._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ath6v_._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1psjybo._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_0oy_-36.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/0zbxssxh53n-3.js +0 -1
- package/.next/standalone/.next/static/chunks/2mazwb1qmur1p.js +0 -1
- package/.next/standalone/.next/static/chunks/2pujpnfx4pws6.css +0 -2
- package/.next/standalone/.next/static/chunks/33u59vf_8xpd-.js +0 -1
- package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
- package/.next/standalone/.next/static/chunks/3s7myk_co0gbh.js +0 -1
- package/.next/standalone/.next/static/chunks/40k1_v4os4cw6.js +0 -6
- package/.next/standalone/Cargo.lock +0 -428
- package/.next/standalone/Cargo.toml +0 -9
- package/.next/standalone/crates/PROTOCOL.md +0 -122
- package/.next/standalone/crates/failproofaid/Cargo.toml +0 -18
- package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
- package/.next/standalone/crates/failproofaid/src/main.rs +0 -83
- package/.next/standalone/crates/failproofaid/src/paths.rs +0 -172
- package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
- package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
- package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
- package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
- package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
- package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
- package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
- package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
- package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
- package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
- package/.next/standalone/integration-suite/Dockerfile +0 -47
- package/.next/standalone/integration-suite/README.md +0 -129
- package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
- package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
- package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
- package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
- package/.next/standalone/integration-suite/install-clis.sh +0 -136
- package/.next/standalone/integration-suite/probe-cli.sh +0 -248
- package/.next/standalone/integration-suite/report.js +0 -120
- package/.next/standalone/integration-suite/run.sh +0 -159
- package/.next/standalone/openclaw-plugin/index.js +0 -223
- package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
- package/.next/standalone/openclaw-plugin/package.json +0 -12
- package/.next/standalone/osv-scanner.toml +0 -17
- package/.next/standalone/pi-extension/index.ts +0 -524
- package/.next/standalone/pi-extension/package.json +0 -12
- package/.next/standalone/postcss.config.mjs +0 -8
- package/.next/standalone/readme-arch-hq.gif +0 -0
- package/.next/standalone/rust-toolchain.toml +0 -4
- package/src/auth/cli.ts +0 -371
- /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_ssgManifest.js +0 -0
|
@@ -1,502 +0,0 @@
|
|
|
1
|
-
//! The Unix-socket server: bind, accept, verify the peer, dispatch one
|
|
2
|
-
//! request per connection.
|
|
3
|
-
//!
|
|
4
|
-
//! `Hook` requests are relayed to the warm worker (see `worker.rs`); `Ping`
|
|
5
|
-
//! is answered directly. Any failure to reach/use the worker becomes a
|
|
6
|
-
//! client-facing `Error` response — never a hang, never a crash of the
|
|
7
|
-
//! connection-handling thread.
|
|
8
|
-
|
|
9
|
-
use crate::worker::Worker;
|
|
10
|
-
use fpai_ipc::{ClientMessage, PROTOCOL_VERSION, ServerMessage, peer, read_message, write_message};
|
|
11
|
-
use std::fs;
|
|
12
|
-
use std::io;
|
|
13
|
-
use std::os::unix::fs::PermissionsExt;
|
|
14
|
-
use std::os::unix::net::{UnixListener, UnixStream};
|
|
15
|
-
use std::path::{Path, PathBuf};
|
|
16
|
-
use std::sync::Arc;
|
|
17
|
-
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
|
18
|
-
use std::time::Duration;
|
|
19
|
-
|
|
20
|
-
/// Bounds how long a single connection may hold its handler thread waiting
|
|
21
|
-
/// on the peer. A client that connects and then sends nothing would
|
|
22
|
-
/// otherwise park a thread for the daemon's entire lifetime, and those
|
|
23
|
-
/// accumulate — the opposite of the "never a hang" promise above. Generous
|
|
24
|
-
/// for a real client (which writes its request immediately after connect,
|
|
25
|
-
/// over a local socket) and still bounded.
|
|
26
|
-
const CONNECTION_IO_TIMEOUT: Duration = Duration::from_secs(10);
|
|
27
|
-
|
|
28
|
-
/// Hard ceiling on connection-handling threads alive at once. The worker
|
|
29
|
-
/// serializes evaluation anyway, so more than a handful in flight already
|
|
30
|
-
/// means the daemon is badly backed up; refusing past this point turns
|
|
31
|
-
/// "spawn threads until the process dies" into a bounded, logged overload
|
|
32
|
-
/// that the client sees as an unreachable daemon (i.e. its own fail-closed
|
|
33
|
-
/// path), which is the correct outcome for a daemon in that state.
|
|
34
|
-
const MAX_INFLIGHT_CONNECTIONS: usize = 64;
|
|
35
|
-
|
|
36
|
-
pub struct Server {
|
|
37
|
-
listener: UnixListener,
|
|
38
|
-
socket_path: PathBuf,
|
|
39
|
-
worker: Arc<Worker>,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/// Decrements the in-flight connection count on scope exit, including
|
|
43
|
-
/// during an unwind — a leaked count would permanently shrink the budget in
|
|
44
|
-
/// [`MAX_INFLIGHT_CONNECTIONS`] and eventually wedge the daemon.
|
|
45
|
-
struct InflightGuard(Arc<AtomicUsize>);
|
|
46
|
-
|
|
47
|
-
impl Drop for InflightGuard {
|
|
48
|
-
fn drop(&mut self) {
|
|
49
|
-
self.0.fetch_sub(1, Ordering::Relaxed);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
impl Server {
|
|
54
|
-
/// Binds a fresh listener at `socket_path`, replacing a stale socket
|
|
55
|
-
/// file left behind by a process that didn't shut down cleanly (a live
|
|
56
|
-
/// daemon is never listening on a leftover file — the singleton lock in
|
|
57
|
-
/// `lock.rs` is what actually prevents two daemons; this only clears
|
|
58
|
-
/// the debris of one that's already gone).
|
|
59
|
-
pub fn bind(socket_path: &Path, worker: Arc<Worker>) -> io::Result<Self> {
|
|
60
|
-
if socket_path.exists() {
|
|
61
|
-
fs::remove_file(socket_path)?;
|
|
62
|
-
}
|
|
63
|
-
let listener = UnixListener::bind(socket_path)?;
|
|
64
|
-
fs::set_permissions(socket_path, fs::Permissions::from_mode(0o600))?;
|
|
65
|
-
Ok(Server {
|
|
66
|
-
listener,
|
|
67
|
-
socket_path: socket_path.to_path_buf(),
|
|
68
|
-
worker,
|
|
69
|
-
})
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/// Accepts and handles connections, one thread per connection, until
|
|
73
|
-
/// `shutdown` is set to `true`. A short accept timeout keeps the loop
|
|
74
|
-
/// polling `shutdown` instead of blocking forever in `accept()`, which
|
|
75
|
-
/// is what lets tests stop a server cleanly instead of leaking a
|
|
76
|
-
/// blocked thread for the rest of the test process's life.
|
|
77
|
-
pub fn run_until(&self, shutdown: Arc<AtomicBool>) -> io::Result<()> {
|
|
78
|
-
self.listener.set_nonblocking(true)?;
|
|
79
|
-
let inflight = Arc::new(AtomicUsize::new(0));
|
|
80
|
-
while !shutdown.load(Ordering::Relaxed) {
|
|
81
|
-
match self.listener.accept() {
|
|
82
|
-
Ok((stream, _addr)) => {
|
|
83
|
-
if inflight.load(Ordering::Relaxed) >= MAX_INFLIGHT_CONNECTIONS {
|
|
84
|
-
log_connection_error(&io::Error::other(format!(
|
|
85
|
-
"refusing connection: {MAX_INFLIGHT_CONNECTIONS} handlers already in flight"
|
|
86
|
-
)));
|
|
87
|
-
drop(stream);
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
inflight.fetch_add(1, Ordering::Relaxed);
|
|
91
|
-
let guard = InflightGuard(inflight.clone());
|
|
92
|
-
let worker = self.worker.clone();
|
|
93
|
-
std::thread::spawn(move || {
|
|
94
|
-
let _guard = guard;
|
|
95
|
-
if let Err(err) = handle_connection(stream, &worker) {
|
|
96
|
-
log_connection_error(&err);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {
|
|
101
|
-
std::thread::sleep(std::time::Duration::from_millis(20));
|
|
102
|
-
}
|
|
103
|
-
Err(err) => return Err(err),
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
Ok(())
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
impl Drop for Server {
|
|
111
|
-
fn drop(&mut self) {
|
|
112
|
-
let _ = fs::remove_file(&self.socket_path);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
fn log_connection_error(err: &io::Error) {
|
|
117
|
-
eprintln!("[failproofaid] connection error: {err}");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/// Handles exactly one request on `stream`: verify the peer is the same OS
|
|
121
|
-
/// user, read one framed [`ClientMessage`], dispatch it, write back one
|
|
122
|
-
/// framed [`ServerMessage`], then let the connection close.
|
|
123
|
-
pub fn handle_connection(stream: UnixStream, worker: &Worker) -> io::Result<()> {
|
|
124
|
-
match peer::is_same_user(&stream) {
|
|
125
|
-
Ok(true) => {}
|
|
126
|
-
Ok(false) => {
|
|
127
|
-
// Different OS user: drop the connection with no response at
|
|
128
|
-
// all, rather than an Error message that would confirm a
|
|
129
|
-
// daemon is listening here to a peer that has no business
|
|
130
|
-
// asking.
|
|
131
|
-
return Ok(());
|
|
132
|
-
}
|
|
133
|
-
Err(err) => return Err(err),
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// `run_until` puts the *listener* in non-blocking mode. On Linux that
|
|
137
|
-
// doesn't reach accepted sockets (std uses `accept4`), but on
|
|
138
|
-
// BSD-derived systems — macOS, which this daemon supports via launchd —
|
|
139
|
-
// the accepted socket inherits `O_NONBLOCK` from the listener. Left
|
|
140
|
-
// inherited, `read_message` below returns `WouldBlock` the instant the
|
|
141
|
-
// client's bytes haven't landed yet, which this function would treat as
|
|
142
|
-
// a malformed frame and answer with silence: every hook call on macOS
|
|
143
|
-
// would fail closed. Set the mode explicitly rather than relying on
|
|
144
|
-
// per-platform accept semantics.
|
|
145
|
-
stream.set_nonblocking(false)?;
|
|
146
|
-
// Bound the peer's share of this thread's life in both directions (see
|
|
147
|
-
// CONNECTION_IO_TIMEOUT).
|
|
148
|
-
stream.set_read_timeout(Some(CONNECTION_IO_TIMEOUT))?;
|
|
149
|
-
stream.set_write_timeout(Some(CONNECTION_IO_TIMEOUT))?;
|
|
150
|
-
|
|
151
|
-
let mut reader = stream.try_clone()?;
|
|
152
|
-
let mut writer = stream;
|
|
153
|
-
|
|
154
|
-
let request: ClientMessage = match read_message(&mut reader) {
|
|
155
|
-
Ok(msg) => msg,
|
|
156
|
-
Err(_) => return Ok(()), // malformed frame: nothing to respond to, nothing to act on
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
let response = dispatch(request, worker);
|
|
160
|
-
write_message(&mut writer, &response)
|
|
161
|
-
.map_err(|e| io::Error::other(format!("failed to write response: {e}")))
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
fn dispatch(request: ClientMessage, worker: &Worker) -> ServerMessage {
|
|
165
|
-
if request.protocol_version() != PROTOCOL_VERSION {
|
|
166
|
-
return ServerMessage::Error {
|
|
167
|
-
protocol_version: PROTOCOL_VERSION,
|
|
168
|
-
message: format!(
|
|
169
|
-
"protocol version mismatch: daemon speaks {PROTOCOL_VERSION}, client sent {}",
|
|
170
|
-
request.protocol_version()
|
|
171
|
-
),
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
match request {
|
|
176
|
-
ClientMessage::Ping { .. } => ServerMessage::Pong {
|
|
177
|
-
protocol_version: PROTOCOL_VERSION,
|
|
178
|
-
},
|
|
179
|
-
ClientMessage::Hook {
|
|
180
|
-
hook_event,
|
|
181
|
-
cli,
|
|
182
|
-
stdin,
|
|
183
|
-
cwd,
|
|
184
|
-
..
|
|
185
|
-
} => match worker.call(&hook_event, &cli, &stdin, cwd.as_deref()) {
|
|
186
|
-
Ok(outcome) => ServerMessage::HookResult {
|
|
187
|
-
protocol_version: PROTOCOL_VERSION,
|
|
188
|
-
exit_code: outcome.exit_code,
|
|
189
|
-
stdout: outcome.stdout,
|
|
190
|
-
stderr: outcome.stderr,
|
|
191
|
-
},
|
|
192
|
-
Err(err) => ServerMessage::Error {
|
|
193
|
-
protocol_version: PROTOCOL_VERSION,
|
|
194
|
-
message: format!("worker call failed: {err}"),
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
#[cfg(test)]
|
|
201
|
-
mod tests {
|
|
202
|
-
use super::*;
|
|
203
|
-
use crate::worker::WorkerCommand;
|
|
204
|
-
use std::sync::atomic::AtomicBool;
|
|
205
|
-
use std::time::Duration;
|
|
206
|
-
|
|
207
|
-
fn temp_socket_path(name: &str) -> PathBuf {
|
|
208
|
-
std::env::temp_dir().join(format!(
|
|
209
|
-
"failproofaid-server-test-{}-{}-{}",
|
|
210
|
-
std::process::id(),
|
|
211
|
-
name,
|
|
212
|
-
fastrand_ish()
|
|
213
|
-
))
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Avoids pulling in a `rand` dependency just to de-collide temp socket
|
|
217
|
-
// paths across tests running in parallel threads.
|
|
218
|
-
fn fastrand_ish() -> u64 {
|
|
219
|
-
use std::time::{SystemTime, UNIX_EPOCH};
|
|
220
|
-
SystemTime::now()
|
|
221
|
-
.duration_since(UNIX_EPOCH)
|
|
222
|
-
.unwrap()
|
|
223
|
-
.as_nanos() as u64
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/// A `WorkerCommand` that never produces a working worker — for tests
|
|
227
|
-
/// that don't care about Hook responses and just need *a* command
|
|
228
|
-
/// (Ping never touches the worker at all).
|
|
229
|
-
fn broken_worker_cmd() -> WorkerCommand {
|
|
230
|
-
WorkerCommand::shell("true")
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/// Owns a running test server + the worker it supervises. Shuts the
|
|
234
|
-
/// server down and joins its thread on drop — including during an
|
|
235
|
-
/// unwind from a failed `assert!`/`.unwrap()` — so a failing test
|
|
236
|
-
/// cleans up its spawned worker process (via `Worker::drop`'s
|
|
237
|
-
/// process-group kill, see worker.rs) exactly as reliably as a passing
|
|
238
|
-
/// one. Before this guard existed, a panic between `start_test_server`
|
|
239
|
-
/// and the manual `shutdown.store` + `handle.join()` at the end of a
|
|
240
|
-
/// test permanently orphaned the server thread and its live `bun`
|
|
241
|
-
/// worker subprocess — reproduced live: three orphaned
|
|
242
|
-
/// `failproofai-worker.mjs` processes were still running, minutes
|
|
243
|
-
/// later, from earlier iterations of this very test file.
|
|
244
|
-
struct TestServerGuard {
|
|
245
|
-
shutdown: Arc<AtomicBool>,
|
|
246
|
-
handle: Option<std::thread::JoinHandle<()>>,
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
impl Drop for TestServerGuard {
|
|
250
|
-
fn drop(&mut self) {
|
|
251
|
-
self.shutdown.store(true, Ordering::Relaxed);
|
|
252
|
-
if let Some(handle) = self.handle.take() {
|
|
253
|
-
let _ = handle.join();
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
fn start_test_server_with_worker(
|
|
259
|
-
socket_path: PathBuf,
|
|
260
|
-
worker_cmd: WorkerCommand,
|
|
261
|
-
) -> TestServerGuard {
|
|
262
|
-
let shutdown = Arc::new(AtomicBool::new(false));
|
|
263
|
-
let shutdown_clone = shutdown.clone();
|
|
264
|
-
let worker_socket_path = temp_socket_path("worker-internal");
|
|
265
|
-
let handle = std::thread::spawn(move || {
|
|
266
|
-
let worker = Arc::new(crate::worker::Worker::new(worker_socket_path, worker_cmd));
|
|
267
|
-
let server = Server::bind(&socket_path, worker).expect("bind should succeed");
|
|
268
|
-
server
|
|
269
|
-
.run_until(shutdown_clone)
|
|
270
|
-
.expect("run_until should not error");
|
|
271
|
-
});
|
|
272
|
-
// Give the background thread a moment to actually bind before the
|
|
273
|
-
// test tries to connect.
|
|
274
|
-
std::thread::sleep(Duration::from_millis(50));
|
|
275
|
-
TestServerGuard {
|
|
276
|
-
shutdown,
|
|
277
|
-
handle: Some(handle),
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
fn start_test_server(socket_path: PathBuf) -> TestServerGuard {
|
|
282
|
-
start_test_server_with_worker(socket_path, broken_worker_cmd())
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
#[test]
|
|
286
|
-
fn ping_gets_pong() {
|
|
287
|
-
let socket_path = temp_socket_path("ping");
|
|
288
|
-
let _guard = start_test_server(socket_path.clone());
|
|
289
|
-
|
|
290
|
-
let mut stream = UnixStream::connect(&socket_path).unwrap();
|
|
291
|
-
write_message(
|
|
292
|
-
&mut stream,
|
|
293
|
-
&ClientMessage::Ping {
|
|
294
|
-
protocol_version: PROTOCOL_VERSION,
|
|
295
|
-
},
|
|
296
|
-
)
|
|
297
|
-
.unwrap();
|
|
298
|
-
let response: ServerMessage = read_message(&mut stream).unwrap();
|
|
299
|
-
assert_eq!(
|
|
300
|
-
response,
|
|
301
|
-
ServerMessage::Pong {
|
|
302
|
-
protocol_version: PROTOCOL_VERSION
|
|
303
|
-
}
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
#[test]
|
|
308
|
-
fn hook_request_gets_an_error_when_the_worker_cannot_be_reached() {
|
|
309
|
-
let socket_path = temp_socket_path("hook-broken-worker");
|
|
310
|
-
let _guard = start_test_server(socket_path.clone());
|
|
311
|
-
|
|
312
|
-
let mut stream = UnixStream::connect(&socket_path).unwrap();
|
|
313
|
-
write_message(
|
|
314
|
-
&mut stream,
|
|
315
|
-
&ClientMessage::Hook {
|
|
316
|
-
protocol_version: PROTOCOL_VERSION,
|
|
317
|
-
hook_event: "PreToolUse".to_string(),
|
|
318
|
-
cli: "claude".to_string(),
|
|
319
|
-
stdin: "{}".to_string(),
|
|
320
|
-
cwd: None,
|
|
321
|
-
},
|
|
322
|
-
)
|
|
323
|
-
.unwrap();
|
|
324
|
-
let response: ServerMessage = read_message(&mut stream).unwrap();
|
|
325
|
-
match response {
|
|
326
|
-
ServerMessage::Error { .. } => {}
|
|
327
|
-
other => panic!("expected Error, got {other:?}"),
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/// The real end-to-end path: a real `failproofaid` server relaying a
|
|
332
|
-
/// real Hook request to the real TypeScript worker (spawned via `bun`
|
|
333
|
-
/// against this repo's own `bin/failproofai-worker.mjs`), which runs
|
|
334
|
-
/// the actual, unmodified policy-evaluation engine. Proves Rust and TS
|
|
335
|
-
/// sides of the wire protocol actually agree, not just that each side's
|
|
336
|
-
/// own unit tests pass in isolation.
|
|
337
|
-
#[test]
|
|
338
|
-
fn relays_a_hook_request_to_the_real_typescript_worker_end_to_end() {
|
|
339
|
-
let repo_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
|
340
|
-
.parent()
|
|
341
|
-
.and_then(|p| p.parent())
|
|
342
|
-
.expect("crates/failproofaid should be two levels under the repo root")
|
|
343
|
-
.to_path_buf();
|
|
344
|
-
let worker_script = repo_root.join("bin").join("failproofai-worker.mjs");
|
|
345
|
-
assert!(
|
|
346
|
-
worker_script.exists(),
|
|
347
|
-
"expected {worker_script:?} to exist"
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
// A real project dir with block-sudo enabled, so the response
|
|
351
|
-
// proves real policy evaluation ran, not just that SOME response
|
|
352
|
-
// came back.
|
|
353
|
-
let project_dir = std::env::temp_dir().join(format!(
|
|
354
|
-
"failproofaid-e2e-project-{}-{}",
|
|
355
|
-
std::process::id(),
|
|
356
|
-
fastrand_ish()
|
|
357
|
-
));
|
|
358
|
-
std::fs::create_dir_all(project_dir.join(".failproofai")).unwrap();
|
|
359
|
-
std::fs::write(
|
|
360
|
-
project_dir
|
|
361
|
-
.join(".failproofai")
|
|
362
|
-
.join("policies-config.json"),
|
|
363
|
-
r#"{"enabledPolicies":["block-sudo"]}"#,
|
|
364
|
-
)
|
|
365
|
-
.unwrap();
|
|
366
|
-
|
|
367
|
-
let socket_path = temp_socket_path("hook-real-worker");
|
|
368
|
-
let worker_cmd = WorkerCommand::shell(format!("bun {}", worker_script.display()));
|
|
369
|
-
let _guard = start_test_server_with_worker(socket_path.clone(), worker_cmd);
|
|
370
|
-
|
|
371
|
-
let stdin = serde_json::json!({
|
|
372
|
-
"cwd": project_dir.to_string_lossy(),
|
|
373
|
-
"tool_name": "Bash",
|
|
374
|
-
"tool_input": { "command": "sudo rm -rf /" },
|
|
375
|
-
})
|
|
376
|
-
.to_string();
|
|
377
|
-
|
|
378
|
-
let mut stream = UnixStream::connect(&socket_path).unwrap();
|
|
379
|
-
stream
|
|
380
|
-
.set_read_timeout(Some(Duration::from_secs(15)))
|
|
381
|
-
.unwrap();
|
|
382
|
-
write_message(
|
|
383
|
-
&mut stream,
|
|
384
|
-
&ClientMessage::Hook {
|
|
385
|
-
protocol_version: PROTOCOL_VERSION,
|
|
386
|
-
hook_event: "PreToolUse".to_string(),
|
|
387
|
-
cli: "claude".to_string(),
|
|
388
|
-
stdin,
|
|
389
|
-
cwd: Some(project_dir.to_string_lossy().to_string()),
|
|
390
|
-
},
|
|
391
|
-
)
|
|
392
|
-
.unwrap();
|
|
393
|
-
let response: ServerMessage = read_message(&mut stream).unwrap();
|
|
394
|
-
match response {
|
|
395
|
-
ServerMessage::HookResult {
|
|
396
|
-
exit_code, stdout, ..
|
|
397
|
-
} => {
|
|
398
|
-
// Claude's PreToolUse deny contract is JSON on stdout at
|
|
399
|
-
// exit 0 (hookSpecificOutput.permissionDecision), not a
|
|
400
|
-
// nonzero exit code.
|
|
401
|
-
assert_eq!(exit_code, 0);
|
|
402
|
-
assert!(
|
|
403
|
-
stdout.contains("\"permissionDecision\":\"deny\""),
|
|
404
|
-
"expected a real deny from block-sudo, got stdout: {stdout}"
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
other => panic!("expected HookResult, got {other:?}"),
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
std::fs::remove_dir_all(&project_dir).ok();
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
#[test]
|
|
414
|
-
fn mismatched_protocol_version_gets_an_explicit_error() {
|
|
415
|
-
let socket_path = temp_socket_path("version-mismatch");
|
|
416
|
-
let _guard = start_test_server(socket_path.clone());
|
|
417
|
-
|
|
418
|
-
let mut stream = UnixStream::connect(&socket_path).unwrap();
|
|
419
|
-
write_message(
|
|
420
|
-
&mut stream,
|
|
421
|
-
&ClientMessage::Ping {
|
|
422
|
-
protocol_version: PROTOCOL_VERSION + 999,
|
|
423
|
-
},
|
|
424
|
-
)
|
|
425
|
-
.unwrap();
|
|
426
|
-
let response: ServerMessage = read_message(&mut stream).unwrap();
|
|
427
|
-
match response {
|
|
428
|
-
ServerMessage::Error { message, .. } => {
|
|
429
|
-
assert!(message.contains("version"));
|
|
430
|
-
}
|
|
431
|
-
other => panic!("expected Error, got {other:?}"),
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
#[test]
|
|
436
|
-
fn bind_replaces_a_stale_socket_file() {
|
|
437
|
-
let socket_path = temp_socket_path("stale");
|
|
438
|
-
// Simulate a leftover file from a crashed daemon: not even a valid
|
|
439
|
-
// socket, just a regular file at that path.
|
|
440
|
-
std::fs::write(&socket_path, b"not a socket").unwrap();
|
|
441
|
-
|
|
442
|
-
let worker = Arc::new(crate::worker::Worker::new(
|
|
443
|
-
temp_socket_path("stale-worker"),
|
|
444
|
-
broken_worker_cmd(),
|
|
445
|
-
));
|
|
446
|
-
let server = Server::bind(&socket_path, worker).expect("bind should clear the stale file");
|
|
447
|
-
drop(server);
|
|
448
|
-
assert!(
|
|
449
|
-
!socket_path.exists(),
|
|
450
|
-
"Drop should clean up the socket file"
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
#[test]
|
|
455
|
-
fn bound_socket_file_is_owner_only() {
|
|
456
|
-
let socket_path = temp_socket_path("perms");
|
|
457
|
-
let worker = Arc::new(crate::worker::Worker::new(
|
|
458
|
-
temp_socket_path("perms-worker"),
|
|
459
|
-
broken_worker_cmd(),
|
|
460
|
-
));
|
|
461
|
-
let server = Server::bind(&socket_path, worker).unwrap();
|
|
462
|
-
let mode = std::fs::metadata(&socket_path)
|
|
463
|
-
.unwrap()
|
|
464
|
-
.permissions()
|
|
465
|
-
.mode()
|
|
466
|
-
& 0o777;
|
|
467
|
-
assert_eq!(mode, 0o600);
|
|
468
|
-
drop(server);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
#[test]
|
|
472
|
-
fn multiple_concurrent_pings_all_get_answered() {
|
|
473
|
-
let socket_path = temp_socket_path("concurrent");
|
|
474
|
-
let _guard = start_test_server(socket_path.clone());
|
|
475
|
-
|
|
476
|
-
let clients: Vec<_> = (0..8)
|
|
477
|
-
.map(|_| {
|
|
478
|
-
let path = socket_path.clone();
|
|
479
|
-
std::thread::spawn(move || {
|
|
480
|
-
let mut stream = UnixStream::connect(&path).unwrap();
|
|
481
|
-
write_message(
|
|
482
|
-
&mut stream,
|
|
483
|
-
&ClientMessage::Ping {
|
|
484
|
-
protocol_version: PROTOCOL_VERSION,
|
|
485
|
-
},
|
|
486
|
-
)
|
|
487
|
-
.unwrap();
|
|
488
|
-
let response: ServerMessage = read_message(&mut stream).unwrap();
|
|
489
|
-
assert_eq!(
|
|
490
|
-
response,
|
|
491
|
-
ServerMessage::Pong {
|
|
492
|
-
protocol_version: PROTOCOL_VERSION
|
|
493
|
-
}
|
|
494
|
-
);
|
|
495
|
-
})
|
|
496
|
-
})
|
|
497
|
-
.collect();
|
|
498
|
-
for c in clients {
|
|
499
|
-
c.join().unwrap();
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
}
|