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,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Disk persistence for hook activity entries using page-sized JSONL files.
|
|
3
3
|
*
|
|
4
|
-
* Storage location: ~/.failproofai/
|
|
4
|
+
* Storage location: ~/.failproofai/hook-activity/ (see fp-home.ts)
|
|
5
5
|
*
|
|
6
6
|
* File structure:
|
|
7
7
|
* - current.jsonl — actively written to, 0–PAGE_SIZE entries
|
|
@@ -22,19 +22,32 @@ import {
|
|
|
22
22
|
unlinkSync,
|
|
23
23
|
} from "node:fs";
|
|
24
24
|
import { join } from "node:path";
|
|
25
|
-
import { homedir } from "node:os";
|
|
26
25
|
import type { IntegrationType } from "./types";
|
|
26
|
+
import { hookActivityDir } from "./fp-home";
|
|
27
27
|
|
|
28
28
|
export const PAGE_SIZE = 25;
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Layout 2 promoted this out of `cache/`. It was never a cache — nothing
|
|
32
|
+
* regenerates it, and deleting it loses the decision history the dashboard's
|
|
33
|
+
* activity tab is built on. Resolved lazily (not at module load) so
|
|
34
|
+
* FAILPROOFAI_HOME is honoured by tests and containers that set it after
|
|
35
|
+
* import.
|
|
36
|
+
*/
|
|
37
|
+
const defaultStoreDir = () => hookActivityDir();
|
|
31
38
|
const CURRENT_FILE = "current.jsonl";
|
|
32
39
|
const COUNT_FILE = "current.count"; // tracks line count; O(1) read/write vs rereading current.jsonl
|
|
33
40
|
const STATS_FILE = "stats.json";
|
|
34
41
|
const LOCK_FILE = "current.lock"; // advisory lock for concurrent hook processes
|
|
35
42
|
const LOCK_STALE_MS = 2000; // steal lock if older than 2 s (covers crashed processes)
|
|
36
43
|
|
|
37
|
-
let
|
|
44
|
+
let storeDirOverride: string | null = null;
|
|
45
|
+
/**
|
|
46
|
+
* A getter, not a module-load constant: `FAILPROOFAI_HOME` is routinely set by
|
|
47
|
+
* a test or a container AFTER this module is imported, and a value captured at
|
|
48
|
+
* import time would silently point every write at the real home.
|
|
49
|
+
*/
|
|
50
|
+
const storeDirValue = () => storeDirOverride ?? defaultStoreDir();
|
|
38
51
|
let rotateSeq = 0;
|
|
39
52
|
|
|
40
53
|
// ── Types ──
|
|
@@ -69,6 +82,45 @@ export interface HookActivityEntry {
|
|
|
69
82
|
cwd?: string;
|
|
70
83
|
permissionMode?: string;
|
|
71
84
|
hookEventName?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Set only on events evaluated while a session pause was active. Absent on
|
|
87
|
+
* every ordinary row and on every row written before pausing existed, so —
|
|
88
|
+
* like `matchedPolicies` — consumers must treat `undefined` as "not paused"
|
|
89
|
+
* rather than rendering it as unknown.
|
|
90
|
+
*/
|
|
91
|
+
pausedBy?: string;
|
|
92
|
+
/** Epoch ms the pause lifts. Present iff `pausedBy` is. */
|
|
93
|
+
pauseExpiresAt?: number;
|
|
94
|
+
/**
|
|
95
|
+
* Where the policy that DECIDED came from. Absent when nothing decided (a
|
|
96
|
+
* plain allow) and on rows written before this existed — so, like
|
|
97
|
+
* `matchedPolicies`, `undefined` means "unknown", not "builtin".
|
|
98
|
+
*/
|
|
99
|
+
policySource?: "builtin" | "custom" | "convention" | "cloud";
|
|
100
|
+
/** Cloud policy id of the decider. Present only when `policySource` is "cloud". */
|
|
101
|
+
cloudPolicyId?: string;
|
|
102
|
+
/** Immutable version of that policy — the half of attribution that identifies WHICH version ran. */
|
|
103
|
+
cloudVersion?: number;
|
|
104
|
+
/**
|
|
105
|
+
* The cloud deployment active when this event was evaluated, recorded on
|
|
106
|
+
* every row of a managed machine regardless of what decided. "What was
|
|
107
|
+
* deployed here" is a different question from "what decided", and only this
|
|
108
|
+
* distinguishes a rollout that changed no outcomes from one that never
|
|
109
|
+
* arrived.
|
|
110
|
+
*/
|
|
111
|
+
cloudDeployment?: number;
|
|
112
|
+
/**
|
|
113
|
+
* What observe-mode policies WOULD have done, had they been enforcing. This
|
|
114
|
+
* record is the entire point of observe mode: without it the row is
|
|
115
|
+
* indistinguishable from one where the policy never matched, and the rollout
|
|
116
|
+
* being trialled is unmeasurable.
|
|
117
|
+
*/
|
|
118
|
+
observed?: Array<{
|
|
119
|
+
policyId: string;
|
|
120
|
+
version: number;
|
|
121
|
+
decision: "deny" | "instruct";
|
|
122
|
+
reason: string | null;
|
|
123
|
+
}>;
|
|
72
124
|
}
|
|
73
125
|
|
|
74
126
|
export interface HookActivityFilters {
|
|
@@ -77,6 +129,12 @@ export interface HookActivityFilters {
|
|
|
77
129
|
policyName?: string;
|
|
78
130
|
sessionId?: string;
|
|
79
131
|
integration?: IntegrationType;
|
|
132
|
+
/**
|
|
133
|
+
* Filter by where the deciding policy came from. "Show me everything my
|
|
134
|
+
* organization's policies decided" is the question cloud rollout reporting
|
|
135
|
+
* is built on, and it is unanswerable without this.
|
|
136
|
+
*/
|
|
137
|
+
source?: "builtin" | "custom" | "convention" | "cloud";
|
|
80
138
|
}
|
|
81
139
|
|
|
82
140
|
export interface HookActivityStats {
|
|
@@ -89,8 +147,8 @@ export interface HookActivityStats {
|
|
|
89
147
|
// ── Directory setup ──
|
|
90
148
|
|
|
91
149
|
function ensureDir(): void {
|
|
92
|
-
if (!existsSync(
|
|
93
|
-
mkdirSync(
|
|
150
|
+
if (!existsSync(storeDirValue())) {
|
|
151
|
+
mkdirSync(storeDirValue(), { recursive: true });
|
|
94
152
|
}
|
|
95
153
|
}
|
|
96
154
|
|
|
@@ -98,7 +156,7 @@ function ensureDir(): void {
|
|
|
98
156
|
|
|
99
157
|
function acquireLock(): void {
|
|
100
158
|
ensureDir();
|
|
101
|
-
const lockPath = join(
|
|
159
|
+
const lockPath = join(storeDirValue(), LOCK_FILE);
|
|
102
160
|
const deadline = Date.now() + LOCK_STALE_MS;
|
|
103
161
|
while (Date.now() < deadline) {
|
|
104
162
|
try {
|
|
@@ -121,7 +179,7 @@ function acquireLock(): void {
|
|
|
121
179
|
}
|
|
122
180
|
|
|
123
181
|
function releaseLock(): void {
|
|
124
|
-
try { unlinkSync(join(
|
|
182
|
+
try { unlinkSync(join(storeDirValue(), LOCK_FILE)); } catch { /* ignore */ }
|
|
125
183
|
}
|
|
126
184
|
|
|
127
185
|
// ── Writing (synchronous — hook handler is short-lived) ──
|
|
@@ -130,8 +188,8 @@ export function persistHookActivity(entry: HookActivityEntry): void {
|
|
|
130
188
|
ensureDir();
|
|
131
189
|
acquireLock();
|
|
132
190
|
try {
|
|
133
|
-
const currentPath = join(
|
|
134
|
-
const countPath = join(
|
|
191
|
+
const currentPath = join(storeDirValue(), CURRENT_FILE);
|
|
192
|
+
const countPath = join(storeDirValue(), COUNT_FILE);
|
|
135
193
|
|
|
136
194
|
const lineCount = readCount(countPath);
|
|
137
195
|
if (lineCount >= PAGE_SIZE) {
|
|
@@ -153,7 +211,7 @@ export function persistHookActivity(entry: HookActivityEntry): void {
|
|
|
153
211
|
|
|
154
212
|
function rotate(currentPath: string, countPath: string): void {
|
|
155
213
|
const archiveName = `page-${Date.now()}-${rotateSeq++}.jsonl`;
|
|
156
|
-
const archivePath = join(
|
|
214
|
+
const archivePath = join(storeDirValue(), archiveName);
|
|
157
215
|
renameSync(currentPath, archivePath);
|
|
158
216
|
// Reset count for the fresh file (write 0 — next append will set it to 1)
|
|
159
217
|
writeCount(countPath, 0);
|
|
@@ -195,14 +253,21 @@ interface StoredStats {
|
|
|
195
253
|
|
|
196
254
|
function readStoredStats(): StoredStats {
|
|
197
255
|
try {
|
|
198
|
-
return JSON.parse(readFileSync(join(
|
|
256
|
+
return JSON.parse(readFileSync(join(storeDirValue(), STATS_FILE), "utf-8")) as StoredStats;
|
|
199
257
|
} catch {
|
|
200
258
|
return { totalEvents: 0, denyCount: 0, policyMap: {} };
|
|
201
259
|
}
|
|
202
260
|
}
|
|
203
261
|
|
|
204
|
-
|
|
205
|
-
|
|
262
|
+
/**
|
|
263
|
+
* Fold one entry into a running total, in place.
|
|
264
|
+
*
|
|
265
|
+
* Extracted so the incremental path and {@link rebuildHookActivityStats} cannot
|
|
266
|
+
* disagree: a rebuild that counted differently from the appends would produce
|
|
267
|
+
* numbers that silently drift from the log they claim to summarise, and nothing
|
|
268
|
+
* would catch it because both sides look plausible on their own.
|
|
269
|
+
*/
|
|
270
|
+
function foldEntry(s: StoredStats, entry: HookActivityEntry): void {
|
|
206
271
|
s.totalEvents += 1;
|
|
207
272
|
if (entry.decision === "deny") s.denyCount += 1;
|
|
208
273
|
if (entry.policyNames && entry.policyNames.length > 0) {
|
|
@@ -212,17 +277,64 @@ function updateStats(entry: HookActivityEntry): void {
|
|
|
212
277
|
} else if (entry.policyName) {
|
|
213
278
|
s.policyMap[entry.policyName] = (s.policyMap[entry.policyName] ?? 0) + 1;
|
|
214
279
|
}
|
|
215
|
-
|
|
216
|
-
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Write stats atomically: PID-unique temp then rename, so no reader sees a partial file. */
|
|
283
|
+
function writeStoredStats(s: StoredStats): boolean {
|
|
284
|
+
const tmpPath = join(storeDirValue(), `stats.json.${process.pid}.tmp`);
|
|
217
285
|
try {
|
|
218
286
|
writeFileSync(tmpPath, JSON.stringify(s), "utf-8");
|
|
219
|
-
renameSync(tmpPath, join(
|
|
287
|
+
renameSync(tmpPath, join(storeDirValue(), STATS_FILE));
|
|
288
|
+
return true;
|
|
220
289
|
} catch {
|
|
221
290
|
try { unlinkSync(tmpPath); } catch { /* ignore */ }
|
|
222
291
|
// Non-fatal: stats file write failure doesn't block the hook
|
|
292
|
+
return false;
|
|
223
293
|
}
|
|
224
294
|
}
|
|
225
295
|
|
|
296
|
+
function updateStats(entry: HookActivityEntry): void {
|
|
297
|
+
const s = readStoredStats();
|
|
298
|
+
foldEntry(s, entry);
|
|
299
|
+
writeStoredStats(s);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Recompute `stats.json` from every page on disk.
|
|
304
|
+
*
|
|
305
|
+
* WHY THIS EXISTS. `stats.json` is INCREMENTAL — `updateStats` adds one entry per
|
|
306
|
+
* append and nothing ever rescans — so it is the only part of the decision log
|
|
307
|
+
* that cannot survive being lost. The layout migration carries the pages (real
|
|
308
|
+
* records) and deliberately drops `stats.json` and `current.count` as derived
|
|
309
|
+
* state, on the stated grounds that "the store rebuilds them". It did not: there
|
|
310
|
+
* was no rebuild anywhere in the store, so `readStoredStats()` fell through its
|
|
311
|
+
* catch to zeroes and began re-accumulating from the next event. A user upgrading
|
|
312
|
+
* from a pre-daemon home kept every record and lost every total — the dashboard
|
|
313
|
+
* listed their history while reporting 0 events, 0 denies and no top policy.
|
|
314
|
+
*
|
|
315
|
+
* It is exactly recomputable, which is what makes dropping it the right call and
|
|
316
|
+
* this function the missing half: pages are never pruned (there is no retention
|
|
317
|
+
* anywhere in this module), so the files on disk ARE the whole history.
|
|
318
|
+
*
|
|
319
|
+
* Returns the totals it wrote, or null if there was nothing to read.
|
|
320
|
+
*/
|
|
321
|
+
export function rebuildHookActivityStats(): StoredStats | null {
|
|
322
|
+
ensureDir();
|
|
323
|
+
const files = [CURRENT_FILE, ...getArchiveFiles()];
|
|
324
|
+
const s: StoredStats = { totalEvents: 0, denyCount: 0, policyMap: {} };
|
|
325
|
+
let sawAny = false;
|
|
326
|
+
for (const file of files) {
|
|
327
|
+
const path = join(storeDirValue(), file);
|
|
328
|
+
if (!existsSync(path)) continue;
|
|
329
|
+
sawAny = true;
|
|
330
|
+
// `readJsonlFile` already skips malformed lines, so a truncated tail costs
|
|
331
|
+
// that line rather than the whole rebuild.
|
|
332
|
+
for (const entry of readJsonlFile(path)) foldEntry(s, entry);
|
|
333
|
+
}
|
|
334
|
+
if (!sawAny) return null;
|
|
335
|
+
return writeStoredStats(s) ? s : null;
|
|
336
|
+
}
|
|
337
|
+
|
|
226
338
|
function readStats(): HookActivityStats {
|
|
227
339
|
const s = readStoredStats();
|
|
228
340
|
let topPolicy: string | null = null;
|
|
@@ -243,7 +355,7 @@ export function getHookActivityPage(page: number): HookActivityEntry[] {
|
|
|
243
355
|
if (page < 1) return [];
|
|
244
356
|
|
|
245
357
|
if (page === 1) {
|
|
246
|
-
const currentPath = join(
|
|
358
|
+
const currentPath = join(storeDirValue(), CURRENT_FILE);
|
|
247
359
|
return readJsonlFile(currentPath).reverse();
|
|
248
360
|
}
|
|
249
361
|
|
|
@@ -251,7 +363,7 @@ export function getHookActivityPage(page: number): HookActivityEntry[] {
|
|
|
251
363
|
const archiveIndex = page - 2;
|
|
252
364
|
if (archiveIndex >= archives.length) return [];
|
|
253
365
|
|
|
254
|
-
return readJsonlFile(join(
|
|
366
|
+
return readJsonlFile(join(storeDirValue(), archives[archiveIndex])).reverse();
|
|
255
367
|
}
|
|
256
368
|
|
|
257
369
|
export function getHookActivityPageCount(): number {
|
|
@@ -262,13 +374,13 @@ export function getHookActivityPageCount(): number {
|
|
|
262
374
|
export function getAllHookActivityEntries(): HookActivityEntry[] {
|
|
263
375
|
ensureDir();
|
|
264
376
|
|
|
265
|
-
const currentPath = join(
|
|
377
|
+
const currentPath = join(storeDirValue(), CURRENT_FILE);
|
|
266
378
|
const currentEntries = readJsonlFile(currentPath).reverse();
|
|
267
379
|
|
|
268
380
|
const archives = getArchiveFiles();
|
|
269
381
|
const archiveEntries: HookActivityEntry[] = [];
|
|
270
382
|
for (const file of archives) {
|
|
271
|
-
const entries = readJsonlFile(join(
|
|
383
|
+
const entries = readJsonlFile(join(storeDirValue(), file));
|
|
272
384
|
archiveEntries.push(...entries.reverse());
|
|
273
385
|
}
|
|
274
386
|
|
|
@@ -298,6 +410,11 @@ export function searchHookActivity(
|
|
|
298
410
|
return false;
|
|
299
411
|
}
|
|
300
412
|
if (filters.integration && entry.integration !== filters.integration) return false;
|
|
413
|
+
// Rows written before attribution existed have no policySource. They are
|
|
414
|
+
// excluded from every source filter rather than guessed at — a wrong
|
|
415
|
+
// attribution is worse than a missing one when the point is proving which
|
|
416
|
+
// rollout decided something.
|
|
417
|
+
if (filters.source && entry.policySource !== filters.source) return false;
|
|
301
418
|
return true;
|
|
302
419
|
});
|
|
303
420
|
|
|
@@ -325,6 +442,34 @@ export function getHookActivityHistory(page: number): {
|
|
|
325
442
|
|
|
326
443
|
// ── Internal helpers ──
|
|
327
444
|
|
|
445
|
+
/**
|
|
446
|
+
* Accept the pre-rename spelling of the two cloud-attribution fields.
|
|
447
|
+
*
|
|
448
|
+
* These pages are written by the DAEMON, so a machine that was cloud-connected
|
|
449
|
+
* before `cloudRevision`→`cloudVersion` / `cloudGeneration`→`cloudDeployment` has
|
|
450
|
+
* real rows on disk naming the old keys. Nothing here validates the shape — the
|
|
451
|
+
* line is `JSON.parse`d and cast — so those rows do not error, they simply carry
|
|
452
|
+
* keys nothing reads: every pre-upgrade cloud-decided decision renders as
|
|
453
|
+
* unattributed, which is the exact question these fields were added to answer.
|
|
454
|
+
*
|
|
455
|
+
* The Rust writer takes the same two aliases (`transform.rs`). Renaming a symbol
|
|
456
|
+
* is safe; renaming the name of data an older build already wrote is not.
|
|
457
|
+
*
|
|
458
|
+
* One-directional and non-destructive: the new key wins if both somehow appear,
|
|
459
|
+
* and the old key is left in place rather than deleted, since nothing reads it and
|
|
460
|
+
* removing it would rewrite history to look like it was always current.
|
|
461
|
+
*/
|
|
462
|
+
function withRenamedCloudFields(raw: Record<string, unknown>): HookActivityEntry {
|
|
463
|
+
const entry = raw as HookActivityEntry & Record<string, unknown>;
|
|
464
|
+
if (entry.cloudVersion === undefined && typeof raw.cloudRevision === "number") {
|
|
465
|
+
entry.cloudVersion = raw.cloudRevision;
|
|
466
|
+
}
|
|
467
|
+
if (entry.cloudDeployment === undefined && typeof raw.cloudGeneration === "number") {
|
|
468
|
+
entry.cloudDeployment = raw.cloudGeneration;
|
|
469
|
+
}
|
|
470
|
+
return entry;
|
|
471
|
+
}
|
|
472
|
+
|
|
328
473
|
function readJsonlFile(filePath: string): HookActivityEntry[] {
|
|
329
474
|
const content = readFileSafe(filePath);
|
|
330
475
|
if (!content.trim()) return [];
|
|
@@ -332,7 +477,7 @@ function readJsonlFile(filePath: string): HookActivityEntry[] {
|
|
|
332
477
|
const entries: HookActivityEntry[] = [];
|
|
333
478
|
for (const line of content.trim().split("\n")) {
|
|
334
479
|
try {
|
|
335
|
-
entries.push(JSON.parse(line) as
|
|
480
|
+
entries.push(withRenamedCloudFields(JSON.parse(line) as Record<string, unknown>));
|
|
336
481
|
} catch {
|
|
337
482
|
// Skip malformed lines
|
|
338
483
|
}
|
|
@@ -342,7 +487,7 @@ function readJsonlFile(filePath: string): HookActivityEntry[] {
|
|
|
342
487
|
|
|
343
488
|
function getArchiveFiles(): string[] {
|
|
344
489
|
try {
|
|
345
|
-
const files = readdirSync(
|
|
490
|
+
const files = readdirSync(storeDirValue());
|
|
346
491
|
return files
|
|
347
492
|
.filter((f) => f.startsWith("page-") && f.endsWith(".jsonl"))
|
|
348
493
|
.sort((a, b) => {
|
|
@@ -364,5 +509,7 @@ function getArchiveFiles(): string[] {
|
|
|
364
509
|
|
|
365
510
|
export function _resetForTest(testDir?: string): void {
|
|
366
511
|
rotateSeq = 0;
|
|
367
|
-
|
|
512
|
+
// null, not the default path: clearing the override lets the getter re-read
|
|
513
|
+
// FAILPROOFAI_HOME, which a test may have changed since this module loaded.
|
|
514
|
+
storeDirOverride = testDir ?? null;
|
|
368
515
|
}
|
package/src/hooks/hook-logger.ts
CHANGED
|
@@ -21,14 +21,14 @@ import {
|
|
|
21
21
|
statSync,
|
|
22
22
|
} from "node:fs";
|
|
23
23
|
import { join } from "node:path";
|
|
24
|
-
import {
|
|
24
|
+
import { logsDir } from "./fp-home";
|
|
25
25
|
|
|
26
26
|
export type LogLevel = "info" | "warn" | "error";
|
|
27
27
|
|
|
28
28
|
const LEVEL_ORDER: Record<LogLevel, number> = { info: 0, warn: 1, error: 2 };
|
|
29
29
|
const MAX_FILE_SIZE = 512 * 1024; // 500 KB
|
|
30
30
|
const LOG_FILENAME = "hooks.log";
|
|
31
|
-
const DEFAULT_LOG_DIR =
|
|
31
|
+
const DEFAULT_LOG_DIR = logsDir();
|
|
32
32
|
|
|
33
33
|
// ── State (lazy-initialized) ──
|
|
34
34
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { version } from "../../package.json";
|
|
9
9
|
import { POSTHOG_API_KEY, POSTHOG_PRODUCT } from "../posthog-key";
|
|
10
|
+
import { isTelemetryEnabled } from "../../lib/telemetry-enabled";
|
|
10
11
|
|
|
11
12
|
const API_KEY = POSTHOG_API_KEY;
|
|
12
13
|
const CAPTURE_URL = "https://us.i.posthog.com/capture/";
|
|
@@ -57,7 +58,10 @@ export async function trackHookEvent(
|
|
|
57
58
|
event: string,
|
|
58
59
|
properties?: Record<string, unknown>,
|
|
59
60
|
): Promise<void> {
|
|
60
|
-
|
|
61
|
+
// Honours the config file as well as the env var — the env var alone cannot
|
|
62
|
+
// reach a system-scope daemon. Memoised, so this stays off the hook path's
|
|
63
|
+
// latency budget. See lib/telemetry-enabled.ts.
|
|
64
|
+
if (!isTelemetryEnabled()) return;
|
|
61
65
|
|
|
62
66
|
const p = sendEvent(distinctId, event, properties);
|
|
63
67
|
pending.add(p);
|
|
@@ -7,6 +7,7 @@ import { homedir } from "node:os";
|
|
|
7
7
|
import type { HooksConfig, ConventionPolicyRecord } from "./policy-types";
|
|
8
8
|
import type { HookScope } from "./types";
|
|
9
9
|
import { hookLogInfo, hookLogWarn } from "./hook-logger";
|
|
10
|
+
import { globalPolicyConfigFile } from "./fp-home";
|
|
10
11
|
|
|
11
12
|
/** Normalize plural and legacy singular explicit policy paths for consumers. */
|
|
12
13
|
export function configuredCustomPolicyPaths(
|
|
@@ -69,7 +70,7 @@ export function readMergedHooksConfig(cwd?: string): HooksConfig {
|
|
|
69
70
|
const base = findProjectConfigDir(cwd ?? process.cwd());
|
|
70
71
|
const projectPath = resolve(base, ".failproofai", "policies-config.json");
|
|
71
72
|
const localPath = resolve(base, ".failproofai", "policies-config.local.json");
|
|
72
|
-
const globalPath =
|
|
73
|
+
const globalPath = globalPolicyConfigFile();
|
|
73
74
|
|
|
74
75
|
const project = readConfigAt(projectPath);
|
|
75
76
|
const local = readConfigAt(localPath);
|
|
@@ -128,7 +129,7 @@ export function readMergedHooksConfig(cwd?: string): HooksConfig {
|
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
function getConfigPath(): string {
|
|
131
|
-
return
|
|
132
|
+
return globalPolicyConfigFile();
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
export function readHooksConfig(): HooksConfig {
|
|
@@ -160,7 +161,7 @@ export function writeHooksConfig(config: HooksConfig): void {
|
|
|
160
161
|
export function getConfigPathForScope(scope: HookScope, cwd?: string): string {
|
|
161
162
|
switch (scope) {
|
|
162
163
|
case "user":
|
|
163
|
-
return
|
|
164
|
+
return globalPolicyConfigFile();
|
|
164
165
|
case "project":
|
|
165
166
|
return resolve(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies-config.json");
|
|
166
167
|
case "local":
|
|
@@ -14,7 +14,6 @@ import { homedir } from "node:os";
|
|
|
14
14
|
import { parseDocument, type Document } from "yaml";
|
|
15
15
|
import { listHermesProfiles, hermesRoot } from "../../lib/hermes-profiles";
|
|
16
16
|
import {
|
|
17
|
-
HOOK_EVENT_TYPES,
|
|
18
17
|
CLAUDE_INSTALL_EVENT_TYPES,
|
|
19
18
|
HOOK_SCOPES,
|
|
20
19
|
CODEX_HOOK_EVENT_TYPES,
|
|
@@ -11,12 +11,75 @@
|
|
|
11
11
|
*
|
|
12
12
|
* The ESM shim includes hooks API exports.
|
|
13
13
|
*/
|
|
14
|
-
import { readFile, writeFile, unlink, access } from "fs/promises";
|
|
15
|
-
import {
|
|
14
|
+
import { readFile, writeFile, unlink, access, mkdir, lstat } from "fs/promises";
|
|
15
|
+
import { tmpdir } from "os";
|
|
16
|
+
import { resolve, dirname, relative, join } from "path";
|
|
16
17
|
import { pathToFileURL } from "url";
|
|
18
|
+
import { createHash } from "crypto";
|
|
19
|
+
import { shimsDir } from "./fp-home";
|
|
20
|
+
import { hookLogWarn } from "./hook-logger";
|
|
17
21
|
|
|
18
22
|
export const TMP_SUFFIX = ".__failproofai_tmp__.mjs";
|
|
19
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Anything this loader generated, by name.
|
|
26
|
+
*
|
|
27
|
+
* The temporary tree is written BESIDE the user's sources, because that is the
|
|
28
|
+
* only place a rewritten relative import still resolves. That was harmless
|
|
29
|
+
* while the name was fixed — an abnormally terminated hook left at most one
|
|
30
|
+
* stale file per source, overwritten on the next load — but the name now
|
|
31
|
+
* carries a pid and a sequence number, so every kill (a CLI hook timeout, a
|
|
32
|
+
* Ctrl-C) leaves a uniquely-named file behind forever.
|
|
33
|
+
*
|
|
34
|
+
* Two consequences, and the second is the visible one: the directory fills up,
|
|
35
|
+
* and `findSkippedPolicyFiles` reports each leftover back to the user as a
|
|
36
|
+
* policy file that will not load — an accusation about a file failproofai
|
|
37
|
+
* wrote itself.
|
|
38
|
+
*/
|
|
39
|
+
const TMP_ARTIFACT_RE = /\.__failproofai_tmp__\./;
|
|
40
|
+
|
|
41
|
+
/** True for a file this loader generated rather than one the user wrote. */
|
|
42
|
+
export function isTmpArtifact(name: string): boolean {
|
|
43
|
+
return TMP_ARTIFACT_RE.test(name);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* How stale a generated file must be before a sweep will remove it.
|
|
48
|
+
*
|
|
49
|
+
* Another process may be mid-load right now, and its temporary tree is live
|
|
50
|
+
* until its `import()` resolves. Well past the 10s a module load is allowed,
|
|
51
|
+
* so a sweep can never delete a file another loader is still using.
|
|
52
|
+
*/
|
|
53
|
+
const TMP_SWEEP_MIN_AGE_MS = 60_000;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Remove generated files left behind by a load that was killed.
|
|
57
|
+
*
|
|
58
|
+
* Best-effort and never throws: this runs on the hook path, and failing to
|
|
59
|
+
* tidy up is not worth failing an evaluation over.
|
|
60
|
+
*/
|
|
61
|
+
export async function sweepStaleTmpArtifacts(dir: string, now = Date.now()): Promise<number> {
|
|
62
|
+
let removed = 0;
|
|
63
|
+
try {
|
|
64
|
+
const { readdir, stat } = await import("fs/promises");
|
|
65
|
+
for (const name of await readdir(dir)) {
|
|
66
|
+
if (!isTmpArtifact(name)) continue;
|
|
67
|
+
const path = resolve(dir, name);
|
|
68
|
+
try {
|
|
69
|
+
const info = await stat(path);
|
|
70
|
+
if (now - info.mtimeMs < TMP_SWEEP_MIN_AGE_MS) continue;
|
|
71
|
+
await unlink(path);
|
|
72
|
+
removed++;
|
|
73
|
+
} catch {
|
|
74
|
+
/* raced with another sweep, or not ours to remove */
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
/* unreadable directory — nothing to sweep */
|
|
79
|
+
}
|
|
80
|
+
return removed;
|
|
81
|
+
}
|
|
82
|
+
|
|
20
83
|
/** Regex to find local relative import specifiers (ESM). */
|
|
21
84
|
const LOCAL_IMPORT_RE = /(?:import\s+(?:[\s\S]*?\s+from\s+)?|export\s+(?:[\s\S]*?\s+from\s+))(['"])(\.\.?\/[^'"]+)\1/g;
|
|
22
85
|
|
|
@@ -74,11 +137,57 @@ export async function resolveLocalImport(
|
|
|
74
137
|
* Exports the full public API of failproofai: customPolicies, allow, deny, instruct,
|
|
75
138
|
* getCustomHooks, clearCustomHooks.
|
|
76
139
|
*/
|
|
140
|
+
/**
|
|
141
|
+
* Create the ESM shim that lets a policy file's `import ... from 'failproofai'`
|
|
142
|
+
* resolve, and return where it landed.
|
|
143
|
+
*
|
|
144
|
+
* NOT beside `dist/index.js`, which is where this used to go. That directory
|
|
145
|
+
* belongs to whoever installed the package, and on a system-wide install
|
|
146
|
+
* (`sudo npm i -g`, a container image, a shared build host, a CI runner) it is
|
|
147
|
+
* root-owned — so every non-root user running a hook failed with EACCES, the
|
|
148
|
+
* policy never loaded, and the hook exited 0. Builtins kept firing while
|
|
149
|
+
* cloud-managed and custom policies silently stopped, so the machine looked
|
|
150
|
+
* protected. That is the worst available failure for a policy engine.
|
|
151
|
+
*
|
|
152
|
+
* Two locations, and the ORDER OF OPERATIONS matters more than the choice:
|
|
153
|
+
*
|
|
154
|
+
* 1. `<home>/state/shims` — created at 0700 and owned by the user running the
|
|
155
|
+
* hook. Unlike a shared `/tmp`, no other local user can pre-plant a file at
|
|
156
|
+
* a path we are about to `import`.
|
|
157
|
+
* 2. `os.tmpdir()` — only when the first is unusable.
|
|
158
|
+
*
|
|
159
|
+
* **The write lives inside the same `try` as the mkdir on purpose.** `mkdir`
|
|
160
|
+
* with `recursive` RESOLVES on a directory that already exists, whatever its
|
|
161
|
+
* mode — so a `state/shims` left behind unwritable (a container that ran the
|
|
162
|
+
* CLI as root and then dropped to a non-root user; this very function creates
|
|
163
|
+
* that chain) would sail past a mkdir-only guard and throw EACCES on the write,
|
|
164
|
+
* reproducing the exact fail-open this function exists to fix. Any failure of
|
|
165
|
+
* either step falls through to the fallback, and says so — a silent slide into
|
|
166
|
+
* the weaker path is the same class of bug again.
|
|
167
|
+
*/
|
|
168
|
+
async function writeShim(
|
|
169
|
+
dir: string,
|
|
170
|
+
name: string,
|
|
171
|
+
code: string,
|
|
172
|
+
exclusive: boolean,
|
|
173
|
+
): Promise<string> {
|
|
174
|
+
const shimPath = join(dir, name);
|
|
175
|
+
// 0600 explicitly: an unset mode is `0666 & ~umask`, and `umask 000` is
|
|
176
|
+
// routine in containers — which in the fallback means a world-writable file
|
|
177
|
+
// in shared /tmp that this process then imports.
|
|
178
|
+
await writeFile(shimPath, code, {
|
|
179
|
+
encoding: "utf-8",
|
|
180
|
+
mode: 0o600,
|
|
181
|
+
...(exclusive ? { flag: "wx" as const } : {}),
|
|
182
|
+
});
|
|
183
|
+
return shimPath;
|
|
184
|
+
}
|
|
185
|
+
|
|
77
186
|
export async function createEsmShim(
|
|
78
|
-
|
|
187
|
+
_distIndex: string,
|
|
79
188
|
distUrl: string,
|
|
189
|
+
tmpSuffix = TMP_SUFFIX,
|
|
80
190
|
): Promise<{ shimPath: string; shimUrl: string }> {
|
|
81
|
-
const shimPath = distIndex + ".__failproofai_esm_shim__.mjs";
|
|
82
191
|
const shimCode = [
|
|
83
192
|
`import _cjs from '${distUrl}';`,
|
|
84
193
|
`export const customPolicies = _cjs.customPolicies;`,
|
|
@@ -89,7 +198,35 @@ export async function createEsmShim(
|
|
|
89
198
|
`export const instruct = _cjs.instruct;`,
|
|
90
199
|
`export default _cjs;`,
|
|
91
200
|
].join("\n");
|
|
92
|
-
|
|
201
|
+
// The name still carries `tmpSuffix` (pid + load sequence + a random id).
|
|
202
|
+
// `fingerprintTemporaryTree` normalises exactly that substring away before
|
|
203
|
+
// hashing, so the policy module cache still hits; a name it could not
|
|
204
|
+
// normalise would force a cold module load on every single hook call.
|
|
205
|
+
const name = `failproofai-dist${tmpSuffix}.shim.mjs`;
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
const dir = shimsDir();
|
|
209
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
210
|
+
// `mkdir` accepts a pre-existing SYMLINK here and a plain write would
|
|
211
|
+
// follow it out of the home. Confirm we got a real directory we own,
|
|
212
|
+
// unreadable to anyone else, before writing something we are about to
|
|
213
|
+
// execute.
|
|
214
|
+
const st = await lstat(dir);
|
|
215
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
216
|
+
if (!st.isDirectory() || st.isSymbolicLink() || (uid !== null && st.uid !== uid) || st.mode & 0o077) {
|
|
217
|
+
throw new Error(`${dir} is not a private directory owned by this user`);
|
|
218
|
+
}
|
|
219
|
+
const shimPath = await writeShim(dir, name, shimCode, false);
|
|
220
|
+
return { shimPath, shimUrl: pathToFileURL(shimPath).href };
|
|
221
|
+
} catch (err) {
|
|
222
|
+
hookLogWarn(
|
|
223
|
+
`policy shim directory unusable (${err instanceof Error ? err.message : String(err)}); falling back to ${tmpdir()}`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Predictable name in a shared directory, so O_EXCL: refuse rather than write
|
|
228
|
+
// through something another user planted.
|
|
229
|
+
const shimPath = await writeShim(tmpdir(), name, shimCode, true);
|
|
93
230
|
return { shimPath, shimUrl: pathToFileURL(shimPath).href };
|
|
94
231
|
}
|
|
95
232
|
|
|
@@ -101,6 +238,17 @@ export async function rewriteFileTree(
|
|
|
101
238
|
entryPath: string,
|
|
102
239
|
distUrl: string | null,
|
|
103
240
|
distIndex: string | null,
|
|
241
|
+
tmpSuffix = TMP_SUFFIX,
|
|
242
|
+
/**
|
|
243
|
+
* SHA-256 the ENTRY file's raw bytes must match, re-checked here at the moment
|
|
244
|
+
* they are read for rewriting and import. Cloud-managed policies pass their
|
|
245
|
+
* pinned digest so the bytes that get imported are the same bytes that were
|
|
246
|
+
* verified — closing a TOCTOU where an earlier caller hashed the file, then
|
|
247
|
+
* this function independently re-read it (a window in which a same-user
|
|
248
|
+
* attacker could swap the bytes so a verified file imports unverified code).
|
|
249
|
+
* Only the entry is content-addressed; transitive local imports are not.
|
|
250
|
+
*/
|
|
251
|
+
entrySha256?: string | null,
|
|
104
252
|
): Promise<string[]> {
|
|
105
253
|
const queue: string[] = [entryPath];
|
|
106
254
|
const visited = new Set<string>();
|
|
@@ -108,7 +256,7 @@ export async function rewriteFileTree(
|
|
|
108
256
|
|
|
109
257
|
let esmShimUrl: string | null = null;
|
|
110
258
|
if (distIndex && distUrl) {
|
|
111
|
-
const shim = await createEsmShim(distIndex, distUrl);
|
|
259
|
+
const shim = await createEsmShim(distIndex, distUrl, tmpSuffix);
|
|
112
260
|
tmpFiles.push(shim.shimPath);
|
|
113
261
|
esmShimUrl = shim.shimUrl;
|
|
114
262
|
}
|
|
@@ -118,7 +266,22 @@ export async function rewriteFileTree(
|
|
|
118
266
|
if (visited.has(filePath)) continue;
|
|
119
267
|
visited.add(filePath);
|
|
120
268
|
|
|
121
|
-
let code
|
|
269
|
+
let code: string;
|
|
270
|
+
if (entrySha256 && filePath === entryPath) {
|
|
271
|
+
// Read the raw bytes, verify the pin against THEM, then decode and rewrite
|
|
272
|
+
// that same in-memory content — filePath is never read again, so the bytes
|
|
273
|
+
// imported below are exactly the bytes verified here.
|
|
274
|
+
const buf = await readFile(filePath);
|
|
275
|
+
const actual = createHash("sha256").update(buf).digest("hex");
|
|
276
|
+
if (actual !== entrySha256) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`cloud-managed policy failed integrity re-verification at load: expected ${entrySha256}, got ${actual}`,
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
code = buf.toString("utf-8");
|
|
282
|
+
} else {
|
|
283
|
+
code = await readFile(filePath, "utf-8");
|
|
284
|
+
}
|
|
122
285
|
|
|
123
286
|
// Rewrite 'failproofai' or legacy 'claudeye' imports to the ESM shim (or direct CJS for require)
|
|
124
287
|
if (esmShimUrl) {
|
|
@@ -148,7 +311,7 @@ export async function rewriteFileTree(
|
|
|
148
311
|
if (!visited.has(resolved) && !queue.includes(resolved)) {
|
|
149
312
|
queue.push(resolved);
|
|
150
313
|
}
|
|
151
|
-
let relPath = relative(dir, resolved +
|
|
314
|
+
let relPath = relative(dir, resolved + tmpSuffix).split("\\").join("/");
|
|
152
315
|
if (!relPath.startsWith(".")) relPath = "./" + relPath;
|
|
153
316
|
rewrites.set(specifier, relPath);
|
|
154
317
|
}
|
|
@@ -163,7 +326,7 @@ export async function rewriteFileTree(
|
|
|
163
326
|
code = code.replace(new RegExp(`"${escaped}"`, "g"), `"${replacement}"`);
|
|
164
327
|
}
|
|
165
328
|
|
|
166
|
-
const tmpPath = filePath +
|
|
329
|
+
const tmpPath = filePath + tmpSuffix;
|
|
167
330
|
await writeFile(tmpPath, code, "utf-8");
|
|
168
331
|
tmpFiles.push(tmpPath);
|
|
169
332
|
}
|