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
package/dist/worker.mjs
CHANGED
|
@@ -362,6 +362,43 @@ function normalizeCliPayload(cli, parsed) {
|
|
|
362
362
|
}
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
// src/hooks/fp-home.ts
|
|
366
|
+
import { homedir } from "node:os";
|
|
367
|
+
import { resolve } from "node:path";
|
|
368
|
+
function failproofaiHome(home) {
|
|
369
|
+
if (home)
|
|
370
|
+
return resolve(home, ".failproofai");
|
|
371
|
+
return process.env.FAILPROOFAI_HOME || resolve(homedir(), ".failproofai");
|
|
372
|
+
}
|
|
373
|
+
var LAYOUT_VERSION = 3, at = (...parts) => resolve(failproofaiHome(), ...parts), atHome = (home, ...parts) => home ? resolve(failproofaiHome(home), ...parts) : at(...parts), versionFile = (home) => atHome(home, "VERSION"), configFile = (home) => atHome(home, "config.json"), policiesDir = (home) => atHome(home, "policies"), customPoliciesDir, globalPolicyConfigFile = (home) => atHome(home, "policies-config.json"), cloudPoliciesDir = (home) => resolve(policiesDir(home), "cloud-policies"), hookActivityDir = (home) => atHome(home, "hook-activity"), stateDir = (home) => atHome(home, "state"), shimsDir = (home) => resolve(stateDir(home), "shims"), sessionPauseDir = () => resolve(stateDir(), "sessions"), telemetryIdFile = (home) => resolve(stateDir(home), "telemetry-id"), codexSessionPathsFile = (home) => resolve(stateDir(home), "codex-session-paths.json"), logsDir = (home) => atHome(home, "logs"), legacy;
|
|
374
|
+
var init_fp_home = __esm(() => {
|
|
375
|
+
customPoliciesDir = policiesDir;
|
|
376
|
+
legacy = {
|
|
377
|
+
policyConfig: () => at("policies-config.json"),
|
|
378
|
+
configToml: () => at("config.toml"),
|
|
379
|
+
credentialsToml: () => at("credentials.toml"),
|
|
380
|
+
localPoliciesDir: () => at("policies", "local-policies"),
|
|
381
|
+
localPolicyConfig: () => at("policies", "local-policies", "policies-config.json"),
|
|
382
|
+
customPoliciesDir: () => at("policies", "custom-policies"),
|
|
383
|
+
policyConfigLocal: () => at("policies-config.local.json"),
|
|
384
|
+
cloudCredentials: () => at("cloud.json"),
|
|
385
|
+
ingestCredentials: () => at("ingest.json"),
|
|
386
|
+
launcherMarker: () => at(".launcher-configured"),
|
|
387
|
+
lastVersion: () => at("last-version"),
|
|
388
|
+
auditDashboard: () => at("audit-dashboard.json"),
|
|
389
|
+
cacheDir: () => at("cache"),
|
|
390
|
+
hookActivityDir: () => at("cache", "hook-activity"),
|
|
391
|
+
auditCacheDir: () => at("cache", "audit"),
|
|
392
|
+
codexSessionPaths: () => at("cache", "codex-session-paths.json"),
|
|
393
|
+
spoolDir: () => at("spool"),
|
|
394
|
+
failedDir: () => at("failed"),
|
|
395
|
+
collectorHealth: () => at("collector-health.json"),
|
|
396
|
+
sessionPauseDir: () => at("state", "sessions"),
|
|
397
|
+
cloudManagedPolicies: () => at("policies", "cloud-managed"),
|
|
398
|
+
onboardingLock: () => at(".onboarding.lock")
|
|
399
|
+
};
|
|
400
|
+
});
|
|
401
|
+
|
|
365
402
|
// src/hooks/hook-logger.ts
|
|
366
403
|
import {
|
|
367
404
|
appendFileSync,
|
|
@@ -371,7 +408,6 @@ import {
|
|
|
371
408
|
statSync
|
|
372
409
|
} from "node:fs";
|
|
373
410
|
import { join } from "node:path";
|
|
374
|
-
import { homedir } from "node:os";
|
|
375
411
|
function ensureResolved() {
|
|
376
412
|
if (resolved)
|
|
377
413
|
return;
|
|
@@ -443,15 +479,16 @@ function hookLogError(msg) {
|
|
|
443
479
|
}
|
|
444
480
|
var LEVEL_ORDER, MAX_FILE_SIZE, LOG_FILENAME = "hooks.log", DEFAULT_LOG_DIR, resolved = false, currentLevel = "warn", fileLoggingEnabled = false, logDir;
|
|
445
481
|
var init_hook_logger = __esm(() => {
|
|
482
|
+
init_fp_home();
|
|
446
483
|
LEVEL_ORDER = { info: 0, warn: 1, error: 2 };
|
|
447
484
|
MAX_FILE_SIZE = 512 * 1024;
|
|
448
|
-
DEFAULT_LOG_DIR =
|
|
485
|
+
DEFAULT_LOG_DIR = logsDir();
|
|
449
486
|
logDir = DEFAULT_LOG_DIR;
|
|
450
487
|
});
|
|
451
488
|
|
|
452
489
|
// src/hooks/hooks-config.ts
|
|
453
490
|
import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, statSync as statSync2 } from "node:fs";
|
|
454
|
-
import { resolve, dirname } from "node:path";
|
|
491
|
+
import { resolve as resolve2, dirname } from "node:path";
|
|
455
492
|
import { homedir as homedir2 } from "node:os";
|
|
456
493
|
function readConfigAt(path) {
|
|
457
494
|
if (!existsSync2(path))
|
|
@@ -466,9 +503,9 @@ function readConfigAt(path) {
|
|
|
466
503
|
}
|
|
467
504
|
function findProjectConfigDir(start) {
|
|
468
505
|
const home = homedir2();
|
|
469
|
-
let dir =
|
|
506
|
+
let dir = resolve2(start);
|
|
470
507
|
while (dir !== home) {
|
|
471
|
-
const marker =
|
|
508
|
+
const marker = resolve2(dir, ".failproofai");
|
|
472
509
|
try {
|
|
473
510
|
if (statSync2(marker).isDirectory())
|
|
474
511
|
return dir;
|
|
@@ -478,13 +515,13 @@ function findProjectConfigDir(start) {
|
|
|
478
515
|
break;
|
|
479
516
|
dir = parent;
|
|
480
517
|
}
|
|
481
|
-
return
|
|
518
|
+
return resolve2(start);
|
|
482
519
|
}
|
|
483
520
|
function readMergedHooksConfig(cwd) {
|
|
484
521
|
const base = findProjectConfigDir(cwd ?? process.cwd());
|
|
485
|
-
const projectPath =
|
|
486
|
-
const localPath =
|
|
487
|
-
const globalPath =
|
|
522
|
+
const projectPath = resolve2(base, ".failproofai", "policies-config.json");
|
|
523
|
+
const localPath = resolve2(base, ".failproofai", "policies-config.local.json");
|
|
524
|
+
const globalPath = globalPolicyConfigFile();
|
|
488
525
|
const project = readConfigAt(projectPath);
|
|
489
526
|
const local = readConfigAt(localPath);
|
|
490
527
|
const global_ = readConfigAt(globalPath);
|
|
@@ -522,6 +559,7 @@ function readMergedHooksConfig(cwd) {
|
|
|
522
559
|
}
|
|
523
560
|
var init_hooks_config = __esm(() => {
|
|
524
561
|
init_hook_logger();
|
|
562
|
+
init_fp_home();
|
|
525
563
|
});
|
|
526
564
|
|
|
527
565
|
// src/hooks/policy-helpers.ts
|
|
@@ -596,7 +634,7 @@ function clearPolicies() {
|
|
|
596
634
|
var REGISTRY_KEY = "__FAILPROOFAI_POLICY_REGISTRY__", INDEX_CACHE_KEY = "__FAILPROOFAI_POLICY_INDEX_CACHE__", DEFAULT_POLICY_NAMESPACE = "failproofai";
|
|
597
635
|
|
|
598
636
|
// src/hooks/builtin-policies.ts
|
|
599
|
-
import { resolve as
|
|
637
|
+
import { resolve as resolve3, join as join2 } from "node:path";
|
|
600
638
|
import { statSync as statSync3 } from "node:fs";
|
|
601
639
|
import { readFile, writeFile } from "node:fs/promises";
|
|
602
640
|
import { execSync, execFileSync } from "node:child_process";
|
|
@@ -655,6 +693,65 @@ function getFilePath(ctx) {
|
|
|
655
693
|
function parseArgvTokens(cmd) {
|
|
656
694
|
return cmd.trim().split(/\s+/).map((t) => t.replace(/^['"]|['"]$/g, ""));
|
|
657
695
|
}
|
|
696
|
+
function namesSelfPause(command) {
|
|
697
|
+
for (const segment of command.split(SEGMENT_SEPARATORS)) {
|
|
698
|
+
const tokens = segment.split(/\s+/).filter(Boolean);
|
|
699
|
+
let i = 0;
|
|
700
|
+
while (i < tokens.length) {
|
|
701
|
+
const token = tokens[i];
|
|
702
|
+
const isSkippable = ENV_ASSIGNMENT_RE.test(token) || token.startsWith("-") || token.startsWith(">") || token.startsWith("<") || RUNNER_OPERAND_RE.test(token) || COMMAND_PREFIX_TOKENS.has(token.slice(token.lastIndexOf("/") + 1));
|
|
703
|
+
if (!isSkippable)
|
|
704
|
+
break;
|
|
705
|
+
i++;
|
|
706
|
+
}
|
|
707
|
+
if (i >= tokens.length)
|
|
708
|
+
continue;
|
|
709
|
+
if (!SELF_BINARY_TOKEN_RE.test(tokens[i]))
|
|
710
|
+
continue;
|
|
711
|
+
const args = tokens.slice(i + 1);
|
|
712
|
+
const configAt = args.findIndex((a) => CONFIG_SUBCOMMAND_RE.test(a));
|
|
713
|
+
if (configAt === -1)
|
|
714
|
+
continue;
|
|
715
|
+
if (args.slice(configAt + 1).some((a) => PAUSE_FLAG_RE.test(a)))
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
718
|
+
return false;
|
|
719
|
+
}
|
|
720
|
+
function decodeAnsiC(body) {
|
|
721
|
+
return body.replace(/\\(x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{1,4}|U[0-9A-Fa-f]{1,8}|[0-7]{1,3}|.)/g, (_, seq) => {
|
|
722
|
+
try {
|
|
723
|
+
if (seq[0] === "x")
|
|
724
|
+
return String.fromCharCode(parseInt(seq.slice(1), 16));
|
|
725
|
+
if (seq[0] === "u" || seq[0] === "U")
|
|
726
|
+
return String.fromCodePoint(parseInt(seq.slice(1), 16));
|
|
727
|
+
if (/^[0-7]+$/.test(seq))
|
|
728
|
+
return String.fromCharCode(parseInt(seq, 8));
|
|
729
|
+
} catch {
|
|
730
|
+
return seq;
|
|
731
|
+
}
|
|
732
|
+
const named = {
|
|
733
|
+
a: "\x07",
|
|
734
|
+
b: "\b",
|
|
735
|
+
e: "\x1B",
|
|
736
|
+
f: "\f",
|
|
737
|
+
n: `
|
|
738
|
+
`,
|
|
739
|
+
r: "\r",
|
|
740
|
+
t: "\t",
|
|
741
|
+
v: "\v",
|
|
742
|
+
"\\": "\\",
|
|
743
|
+
"'": "'",
|
|
744
|
+
'"': '"',
|
|
745
|
+
"?": "?"
|
|
746
|
+
};
|
|
747
|
+
return named[seq] ?? seq;
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
function stripShellQuoting(command) {
|
|
751
|
+
const joined = command.replace(/\\\r?\n/g, "");
|
|
752
|
+
const ansiDecoded = joined.replace(/\$'((?:[^'\\]|\\.)*)'/g, (_, body) => decodeAnsiC(body));
|
|
753
|
+
return ansiDecoded.replace(/\\(.)/g, "$1").replace(/['"]/g, "");
|
|
754
|
+
}
|
|
658
755
|
function statGitHeadMtimeMs(cwd) {
|
|
659
756
|
try {
|
|
660
757
|
return statSync3(join2(cwd, ".git", "HEAD")).mtimeMs;
|
|
@@ -899,11 +996,82 @@ function blockEnvFiles(ctx) {
|
|
|
899
996
|
}
|
|
900
997
|
return allow();
|
|
901
998
|
}
|
|
999
|
+
function quoteAwareSegments(command) {
|
|
1000
|
+
const segments = [];
|
|
1001
|
+
let tokens = [];
|
|
1002
|
+
let token = "";
|
|
1003
|
+
let quote = null;
|
|
1004
|
+
const endToken = () => {
|
|
1005
|
+
if (token)
|
|
1006
|
+
tokens.push(token);
|
|
1007
|
+
token = "";
|
|
1008
|
+
};
|
|
1009
|
+
const endSegment = () => {
|
|
1010
|
+
endToken();
|
|
1011
|
+
if (tokens.length)
|
|
1012
|
+
segments.push(tokens);
|
|
1013
|
+
tokens = [];
|
|
1014
|
+
};
|
|
1015
|
+
for (let i = 0;i < command.length; i++) {
|
|
1016
|
+
const c = command[i];
|
|
1017
|
+
if (quote) {
|
|
1018
|
+
if (c === "\\" && quote === '"' && i + 1 < command.length) {
|
|
1019
|
+
token += command[++i];
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
if (c === quote) {
|
|
1023
|
+
quote = null;
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
token += c;
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
if (c === "\\" && i + 1 < command.length) {
|
|
1030
|
+
token += command[++i];
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
if (c === '"' || c === "'") {
|
|
1034
|
+
quote = c;
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
if (/\s/.test(c)) {
|
|
1038
|
+
endToken();
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
1041
|
+
if (/[;&|\n\r(){}`]/.test(c)) {
|
|
1042
|
+
endSegment();
|
|
1043
|
+
continue;
|
|
1044
|
+
}
|
|
1045
|
+
token += c;
|
|
1046
|
+
}
|
|
1047
|
+
endSegment();
|
|
1048
|
+
return segments;
|
|
1049
|
+
}
|
|
1050
|
+
function namesElevation(command, depth = 0) {
|
|
1051
|
+
for (const tokens of quoteAwareSegments(command)) {
|
|
1052
|
+
let i = 0;
|
|
1053
|
+
while (i < tokens.length) {
|
|
1054
|
+
const token = tokens[i];
|
|
1055
|
+
const base = token.slice(token.lastIndexOf("/") + 1);
|
|
1056
|
+
if (base === "sudo" || base === "doas")
|
|
1057
|
+
return true;
|
|
1058
|
+
const isSkippable = ENV_ASSIGNMENT_RE.test(token) || token.startsWith("-") || token.startsWith(">") || token.startsWith("<") || RUNNER_OPERAND_RE.test(token) || COMMAND_PREFIX_TOKENS.has(base);
|
|
1059
|
+
if (!isSkippable)
|
|
1060
|
+
break;
|
|
1061
|
+
if (EVAL_FLAG_RE.test(token) && depth < 3 && i + 1 < tokens.length) {
|
|
1062
|
+
if (namesElevation(tokens[i + 1], depth + 1))
|
|
1063
|
+
return true;
|
|
1064
|
+
}
|
|
1065
|
+
i++;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
return false;
|
|
1069
|
+
}
|
|
902
1070
|
function blockSudo(ctx) {
|
|
903
1071
|
if (ctx.toolName !== "Bash")
|
|
904
1072
|
return allow();
|
|
905
1073
|
const cmd = getCommand(ctx).trimStart();
|
|
906
|
-
if (
|
|
1074
|
+
if (namesElevation(cmd)) {
|
|
907
1075
|
const allowPatterns = ctx.params?.allowPatterns ?? [];
|
|
908
1076
|
if (allowPatterns.some((p) => matchesAllowedPattern(cmd, p)))
|
|
909
1077
|
return allow();
|
|
@@ -917,6 +1085,15 @@ function blockSudo(ctx) {
|
|
|
917
1085
|
}
|
|
918
1086
|
return allow();
|
|
919
1087
|
}
|
|
1088
|
+
function blockSelfPause(ctx) {
|
|
1089
|
+
if (ctx.toolName !== "Bash")
|
|
1090
|
+
return allow();
|
|
1091
|
+
const cmd = getCommand(ctx);
|
|
1092
|
+
if (namesSelfPause(cmd) || namesSelfPause(stripShellQuoting(cmd))) {
|
|
1093
|
+
return deny("Pausing failproofai enforcement is a human action, not an agent one. " + "If a policy is blocking legitimate work, say so and let the operator decide.");
|
|
1094
|
+
}
|
|
1095
|
+
return allow();
|
|
1096
|
+
}
|
|
920
1097
|
function blockCurlPipeSh(ctx) {
|
|
921
1098
|
if (ctx.toolName !== "Bash")
|
|
922
1099
|
return allow();
|
|
@@ -1142,7 +1319,7 @@ function blockReadOutsideCwd(ctx) {
|
|
|
1142
1319
|
const paths = extractAbsolutePaths(cmd);
|
|
1143
1320
|
const cwdWithSep2 = cwd.endsWith("/") ? cwd : cwd + "/";
|
|
1144
1321
|
for (const p of paths) {
|
|
1145
|
-
const resolved3 =
|
|
1322
|
+
const resolved3 = resolve3(cwd, p);
|
|
1146
1323
|
if (isClaudeSettingsFile(resolved3)) {
|
|
1147
1324
|
return deny(`Reading agent settings file blocked: ${resolved3}`);
|
|
1148
1325
|
}
|
|
@@ -1163,7 +1340,7 @@ function blockReadOutsideCwd(ctx) {
|
|
|
1163
1340
|
const target = filePath || searchPath;
|
|
1164
1341
|
if (!target)
|
|
1165
1342
|
return allow();
|
|
1166
|
-
const resolved2 =
|
|
1343
|
+
const resolved2 = resolve3(cwd, target);
|
|
1167
1344
|
if (isClaudeSettingsFile(resolved2)) {
|
|
1168
1345
|
return deny(`Reading agent settings file blocked: ${resolved2}`);
|
|
1169
1346
|
}
|
|
@@ -1654,7 +1831,7 @@ function registerBuiltinPolicies(enabledNames) {
|
|
|
1654
1831
|
}
|
|
1655
1832
|
}
|
|
1656
1833
|
}
|
|
1657
|
-
var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE,
|
|
1834
|
+
var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, SEGMENT_SEPARATORS, COMMAND_PREFIX_TOKENS, SELF_BINARY_TOKEN_RE, CONFIG_SUBCOMMAND_RE, PAUSE_FLAG_RE, ENV_ASSIGNMENT_RE, RUNNER_OPERAND_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, EVAL_FLAG_RE, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
|
|
1658
1835
|
var init_builtin_policies = __esm(() => {
|
|
1659
1836
|
init_hook_logger();
|
|
1660
1837
|
isClaudeInternalPath = isAgentInternalPath;
|
|
@@ -1691,10 +1868,48 @@ var init_builtin_policies = __esm(() => {
|
|
|
1691
1868
|
CMD_ECHO_ENV_RE = /echo\s+%[A-Za-z_]/i;
|
|
1692
1869
|
ENV_FILE_PATH_RE = /(?:^|[\\/])\.env(?:\.|$)/;
|
|
1693
1870
|
ENV_CMD_RE = /\.env(?:\b|\s|$|\.)/;
|
|
1694
|
-
SUDO_RE = /(?:^|;|&&|\|\|)\s*sudo\s/;
|
|
1695
1871
|
PS_ELEVATION_RE = /Start-Process\s+.*-Verb\s+RunAs/i;
|
|
1696
1872
|
RUNAS_RE = /(?:^|;|&&|\|\|)\s*runas\s/i;
|
|
1697
1873
|
CURL_PIPE_SH_RE = /(?:curl|wget)\s.*\|\s*(?:sh|bash|zsh|dash|ksh|csh|tcsh|fish|ash)\b/;
|
|
1874
|
+
SEGMENT_SEPARATORS = /[;&|\n\r(){}`]+/;
|
|
1875
|
+
COMMAND_PREFIX_TOKENS = new Set([
|
|
1876
|
+
"npx",
|
|
1877
|
+
"bunx",
|
|
1878
|
+
"pnpx",
|
|
1879
|
+
"npm",
|
|
1880
|
+
"pnpm",
|
|
1881
|
+
"yarn",
|
|
1882
|
+
"dlx",
|
|
1883
|
+
"exec",
|
|
1884
|
+
"run",
|
|
1885
|
+
"node",
|
|
1886
|
+
"bun",
|
|
1887
|
+
"deno",
|
|
1888
|
+
"env",
|
|
1889
|
+
"command",
|
|
1890
|
+
"builtin",
|
|
1891
|
+
"nohup",
|
|
1892
|
+
"setsid",
|
|
1893
|
+
"time",
|
|
1894
|
+
"timeout",
|
|
1895
|
+
"nice",
|
|
1896
|
+
"stdbuf",
|
|
1897
|
+
"xargs",
|
|
1898
|
+
"sudo",
|
|
1899
|
+
"doas",
|
|
1900
|
+
"sh",
|
|
1901
|
+
"bash",
|
|
1902
|
+
"zsh",
|
|
1903
|
+
"dash",
|
|
1904
|
+
"ksh",
|
|
1905
|
+
"fish",
|
|
1906
|
+
"ash"
|
|
1907
|
+
]);
|
|
1908
|
+
SELF_BINARY_TOKEN_RE = /(?:^|\/)failproofai[^/]*$/;
|
|
1909
|
+
CONFIG_SUBCOMMAND_RE = /^(?:config|configure|setup)$/;
|
|
1910
|
+
PAUSE_FLAG_RE = /^--pause(?:=|$)/;
|
|
1911
|
+
ENV_ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
1912
|
+
RUNNER_OPERAND_RE = /^\d+[a-z]*$/i;
|
|
1698
1913
|
PS_WEB_PIPE_RE = /(?:Invoke-WebRequest|iwr|Invoke-RestMethod|irm)\s+.*\|\s*(?:Invoke-Expression|iex)/i;
|
|
1699
1914
|
SHORT_FLAG_BUNDLE_RE = /^-[a-zA-Z]*f[a-zA-Z]*$/;
|
|
1700
1915
|
SAFE_FORCE_PREFIXES = ["--force-with-lease", "--force-if-includes"];
|
|
@@ -1738,6 +1953,7 @@ var init_builtin_policies = __esm(() => {
|
|
|
1738
1953
|
HELM_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*helm(?:\s|$)/;
|
|
1739
1954
|
GH_PIPELINE_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*gh\s+(?:workflow\s+(?:run|enable|disable)|run\s+(?:rerun|cancel)|pr\s+merge|release\s+(?:create|delete)|cache\s+delete|secret\s+(?:set|delete))\b/;
|
|
1740
1955
|
gitBranchCache = new Map;
|
|
1956
|
+
EVAL_FLAG_RE = /^-{1,2}c$/;
|
|
1741
1957
|
HOME_PREFIX_RE = /^(?:~[A-Za-z0-9_.-]*|\$HOME|\$\{HOME\})(?=$|\/)/;
|
|
1742
1958
|
RM_CMD_RE = /^(?:\/\S*\/)?rm$/;
|
|
1743
1959
|
FIND_CMD_RE = /^(?:\/\S*\/)?find$/;
|
|
@@ -1842,6 +2058,16 @@ var init_builtin_policies = __esm(() => {
|
|
|
1842
2058
|
}
|
|
1843
2059
|
}
|
|
1844
2060
|
},
|
|
2061
|
+
{
|
|
2062
|
+
name: "block-self-pause",
|
|
2063
|
+
displayTitle: "Tried to pause failproofai enforcement",
|
|
2064
|
+
impact: "An agent that can pause enforcement can switch off every other policy.",
|
|
2065
|
+
description: "Block agents from pausing failproofai enforcement",
|
|
2066
|
+
fn: blockSelfPause,
|
|
2067
|
+
match: { events: ["PreToolUse", "PermissionRequest"], toolNames: ["Bash"] },
|
|
2068
|
+
defaultEnabled: true,
|
|
2069
|
+
category: "Dangerous Commands"
|
|
2070
|
+
},
|
|
1845
2071
|
{
|
|
1846
2072
|
name: "block-sudo",
|
|
1847
2073
|
displayTitle: "Tried to run a command with sudo",
|
|
@@ -2285,12 +2511,154 @@ var init_builtin_policies = __esm(() => {
|
|
|
2285
2511
|
});
|
|
2286
2512
|
|
|
2287
2513
|
// package.json
|
|
2288
|
-
var version = "1.0.0-beta.
|
|
2514
|
+
var version = "1.0.0-beta.21";
|
|
2289
2515
|
var init_package = () => {};
|
|
2290
2516
|
|
|
2291
2517
|
// src/posthog-key.ts
|
|
2292
2518
|
var POSTHOG_API_KEY = "phc_Ac1Ww1GqKc0z1SyrRWbmatEeQdlOQIsDEEdP8l8JRgX", POSTHOG_PRODUCT = "failproofai-oss";
|
|
2293
2519
|
|
|
2520
|
+
// src/hooks/fp-config.ts
|
|
2521
|
+
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, statSync as statSync4, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2522
|
+
function detectLayout() {
|
|
2523
|
+
const raw = readVersionFile();
|
|
2524
|
+
if (raw) {
|
|
2525
|
+
if (raw.layout === LAYOUT_VERSION)
|
|
2526
|
+
return { kind: "current", version: raw };
|
|
2527
|
+
return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
|
|
2528
|
+
}
|
|
2529
|
+
if (existsSync3(configFile())) {
|
|
2530
|
+
return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
|
|
2531
|
+
}
|
|
2532
|
+
if (existsSync3(legacy.configToml()))
|
|
2533
|
+
return { kind: "stale", found: LAYOUT_VERSION - 1 };
|
|
2534
|
+
const layoutOneMarkers = [
|
|
2535
|
+
legacy.policyConfig(),
|
|
2536
|
+
legacy.cacheDir(),
|
|
2537
|
+
legacy.launcherMarker(),
|
|
2538
|
+
legacy.lastVersion(),
|
|
2539
|
+
legacy.cloudCredentials(),
|
|
2540
|
+
legacy.ingestCredentials(),
|
|
2541
|
+
legacy.auditDashboard()
|
|
2542
|
+
];
|
|
2543
|
+
return layoutOneMarkers.some((p) => existsSync3(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
|
|
2544
|
+
}
|
|
2545
|
+
function readLegacyTomlVersion(text) {
|
|
2546
|
+
const layout = Number(/^\s*layout\s*=\s*(\d+)\s*$/m.exec(text)?.[1]);
|
|
2547
|
+
if (!Number.isFinite(layout))
|
|
2548
|
+
return null;
|
|
2549
|
+
const str = (key) => new RegExp(`^\\s*${key}\\s*=\\s*"([^"]*)"`, "m").exec(text)?.[1];
|
|
2550
|
+
return { layout, cli: str("cli") ?? "", daemon: str("daemon") };
|
|
2551
|
+
}
|
|
2552
|
+
function readVersionFile() {
|
|
2553
|
+
let text;
|
|
2554
|
+
try {
|
|
2555
|
+
text = readFileSync2(versionFile(), "utf8");
|
|
2556
|
+
} catch {
|
|
2557
|
+
return null;
|
|
2558
|
+
}
|
|
2559
|
+
try {
|
|
2560
|
+
const parsed = JSON.parse(text);
|
|
2561
|
+
const layout = Number(parsed.layout);
|
|
2562
|
+
if (!Number.isFinite(layout))
|
|
2563
|
+
return null;
|
|
2564
|
+
return {
|
|
2565
|
+
layout,
|
|
2566
|
+
cli: typeof parsed.cli === "string" ? parsed.cli : "",
|
|
2567
|
+
daemon: typeof parsed.daemon === "string" ? parsed.daemon : undefined
|
|
2568
|
+
};
|
|
2569
|
+
} catch {
|
|
2570
|
+
return readLegacyTomlVersion(text);
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
function readIntervalDays(raw) {
|
|
2574
|
+
if (typeof raw !== "number" || !Number.isFinite(raw))
|
|
2575
|
+
return DEFAULT_AUDIT_INTERVAL_DAYS;
|
|
2576
|
+
const days = Math.floor(raw);
|
|
2577
|
+
if (days < MIN_AUDIT_INTERVAL_DAYS)
|
|
2578
|
+
return DEFAULT_AUDIT_INTERVAL_DAYS;
|
|
2579
|
+
return Math.min(days, MAX_AUDIT_INTERVAL_DAYS);
|
|
2580
|
+
}
|
|
2581
|
+
function readSources(raw) {
|
|
2582
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
2583
|
+
return;
|
|
2584
|
+
const out = {};
|
|
2585
|
+
for (const [name, table] of Object.entries(raw)) {
|
|
2586
|
+
if (!table || typeof table !== "object" || Array.isArray(table))
|
|
2587
|
+
continue;
|
|
2588
|
+
const entries = table.extra_paths;
|
|
2589
|
+
if (!Array.isArray(entries))
|
|
2590
|
+
continue;
|
|
2591
|
+
const paths = entries.filter((e) => typeof e === "string" && e.trim() !== "");
|
|
2592
|
+
if (paths.length > 0)
|
|
2593
|
+
out[name] = { extraPaths: paths };
|
|
2594
|
+
}
|
|
2595
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
2596
|
+
}
|
|
2597
|
+
function readConfig() {
|
|
2598
|
+
try {
|
|
2599
|
+
const parsed = JSON.parse(readFileSync2(configFile(), "utf8"));
|
|
2600
|
+
return projectConfig(parsed);
|
|
2601
|
+
} catch {
|
|
2602
|
+
return structuredClone(DEFAULT_CONFIG);
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
function projectConfig(parsed) {
|
|
2606
|
+
{
|
|
2607
|
+
const mode = parsed.mode?.kind;
|
|
2608
|
+
const daemon = parsed.daemon ?? {};
|
|
2609
|
+
const collector = parsed.collector ?? {};
|
|
2610
|
+
const telemetry = parsed.telemetry ?? {};
|
|
2611
|
+
const audit = parsed.audit ?? {};
|
|
2612
|
+
return {
|
|
2613
|
+
mode: mode === "cloud" ? "cloud" : "oss",
|
|
2614
|
+
daemon: { configured: daemon.configured === true },
|
|
2615
|
+
collector: {
|
|
2616
|
+
sessions: collector.sessions === true,
|
|
2617
|
+
hooks: collector.hooks !== false,
|
|
2618
|
+
hooksVerbosity: collector.hooks_verbosity === "all" || collector.hooks_verbosity === "off" ? collector.hooks_verbosity : "decisions",
|
|
2619
|
+
redact: collector.redact === "off" ? "off" : "minimal",
|
|
2620
|
+
environment: typeof collector.environment === "string" ? collector.environment : "local",
|
|
2621
|
+
machineId: typeof collector.machine_id === "string" ? collector.machine_id : undefined,
|
|
2622
|
+
sources: readSources(collector.sources)
|
|
2623
|
+
},
|
|
2624
|
+
telemetry: { enabled: telemetry.enabled !== false },
|
|
2625
|
+
audit: { auto: audit.auto === true, intervalDays: readIntervalDays(audit.interval_days) }
|
|
2626
|
+
};
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
var DEFAULT_AUDIT_INTERVAL_DAYS = 7, MIN_AUDIT_INTERVAL_DAYS = 1, MAX_AUDIT_INTERVAL_DAYS = 90, DEFAULT_CONFIG;
|
|
2630
|
+
var init_fp_config = __esm(() => {
|
|
2631
|
+
init_package();
|
|
2632
|
+
init_fp_home();
|
|
2633
|
+
DEFAULT_CONFIG = {
|
|
2634
|
+
mode: "oss",
|
|
2635
|
+
daemon: { configured: false },
|
|
2636
|
+
collector: {
|
|
2637
|
+
sessions: false,
|
|
2638
|
+
hooks: true,
|
|
2639
|
+
hooksVerbosity: "decisions",
|
|
2640
|
+
redact: "minimal",
|
|
2641
|
+
environment: "local"
|
|
2642
|
+
},
|
|
2643
|
+
telemetry: { enabled: true },
|
|
2644
|
+
audit: { auto: false, intervalDays: DEFAULT_AUDIT_INTERVAL_DAYS }
|
|
2645
|
+
};
|
|
2646
|
+
});
|
|
2647
|
+
|
|
2648
|
+
// lib/telemetry-enabled.ts
|
|
2649
|
+
function isTelemetryEnabled() {
|
|
2650
|
+
if (process.env.FAILPROOFAI_TELEMETRY_DISABLED === "1")
|
|
2651
|
+
return false;
|
|
2652
|
+
try {
|
|
2653
|
+
return readConfig().telemetry.enabled;
|
|
2654
|
+
} catch {
|
|
2655
|
+
return true;
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
var init_telemetry_enabled = __esm(() => {
|
|
2659
|
+
init_fp_config();
|
|
2660
|
+
});
|
|
2661
|
+
|
|
2294
2662
|
// src/hooks/hook-telemetry.ts
|
|
2295
2663
|
async function sendEvent(distinctId, event, properties) {
|
|
2296
2664
|
const body = JSON.stringify({
|
|
@@ -2309,7 +2677,7 @@ async function sendEvent(distinctId, event, properties) {
|
|
|
2309
2677
|
} catch {}
|
|
2310
2678
|
}
|
|
2311
2679
|
async function trackHookEvent(distinctId, event, properties) {
|
|
2312
|
-
if (
|
|
2680
|
+
if (!isTelemetryEnabled())
|
|
2313
2681
|
return;
|
|
2314
2682
|
const p = sendEvent(distinctId, event, properties);
|
|
2315
2683
|
pending.add(p);
|
|
@@ -2324,6 +2692,7 @@ async function flushHookTelemetry() {
|
|
|
2324
2692
|
var API_KEY, CAPTURE_URL = "https://us.i.posthog.com/capture/", pending;
|
|
2325
2693
|
var init_hook_telemetry = __esm(() => {
|
|
2326
2694
|
init_package();
|
|
2695
|
+
init_telemetry_enabled();
|
|
2327
2696
|
API_KEY = POSTHOG_API_KEY;
|
|
2328
2697
|
pending = new Set;
|
|
2329
2698
|
});
|
|
@@ -2387,24 +2756,48 @@ function getFileBasedId() {
|
|
|
2387
2756
|
} catch {}
|
|
2388
2757
|
return id;
|
|
2389
2758
|
}
|
|
2759
|
+
function publishInstanceId(id) {
|
|
2760
|
+
let target;
|
|
2761
|
+
try {
|
|
2762
|
+
target = telemetryIdFile();
|
|
2763
|
+
if (fs.readFileSync(target, "utf-8").trim() === id)
|
|
2764
|
+
return;
|
|
2765
|
+
} catch {
|
|
2766
|
+
try {
|
|
2767
|
+
target = telemetryIdFile();
|
|
2768
|
+
} catch {
|
|
2769
|
+
return;
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
try {
|
|
2773
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
2774
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
2775
|
+
fs.writeFileSync(tmp, id, { encoding: "utf-8", mode: 384 });
|
|
2776
|
+
fs.renameSync(tmp, target);
|
|
2777
|
+
} catch {}
|
|
2778
|
+
}
|
|
2390
2779
|
function getInstanceId() {
|
|
2391
2780
|
if (cachedId)
|
|
2392
2781
|
return cachedId;
|
|
2393
2782
|
const machineId = getPlatformMachineId();
|
|
2394
2783
|
if (machineId) {
|
|
2395
2784
|
cachedId = hashToId(machineId);
|
|
2785
|
+
publishInstanceId(cachedId);
|
|
2396
2786
|
return cachedId;
|
|
2397
2787
|
}
|
|
2398
2788
|
const sysProps = getSystemPropertiesId();
|
|
2399
2789
|
if (sysProps) {
|
|
2400
2790
|
cachedId = hashToId(sysProps);
|
|
2791
|
+
publishInstanceId(cachedId);
|
|
2401
2792
|
return cachedId;
|
|
2402
2793
|
}
|
|
2403
2794
|
cachedId = getFileBasedId();
|
|
2795
|
+
publishInstanceId(cachedId);
|
|
2404
2796
|
return cachedId;
|
|
2405
2797
|
}
|
|
2406
2798
|
var NAMESPACE = "failproofai-telemetry-v1", ID_DIR, ID_FILE, cachedId;
|
|
2407
2799
|
var init_telemetry_id = __esm(() => {
|
|
2800
|
+
init_fp_home();
|
|
2408
2801
|
ID_DIR = path.join(os.homedir(), ".failproofai");
|
|
2409
2802
|
ID_FILE = path.join(ID_DIR, "instance-id");
|
|
2410
2803
|
});
|
|
@@ -3158,13 +3551,43 @@ function clearCustomHooks() {
|
|
|
3158
3551
|
const g = globalThis;
|
|
3159
3552
|
g[REGISTRY_KEY2] = [];
|
|
3160
3553
|
}
|
|
3161
|
-
var REGISTRY_KEY2 = "__failproofai_custom_hooks__";
|
|
3162
|
-
var init_custom_hooks_registry = () => {
|
|
3554
|
+
var REGISTRY_KEY2 = "__failproofai_custom_hooks__", customPolicies;
|
|
3555
|
+
var init_custom_hooks_registry = __esm(() => {
|
|
3556
|
+
customPolicies = {
|
|
3557
|
+
add(hook) {
|
|
3558
|
+
getRegistry2().push(hook);
|
|
3559
|
+
}
|
|
3560
|
+
};
|
|
3561
|
+
});
|
|
3163
3562
|
|
|
3164
3563
|
// src/hooks/loader-utils.ts
|
|
3165
|
-
import { readFile as readFile2, writeFile as writeFile2, unlink, access } from "fs/promises";
|
|
3166
|
-
import {
|
|
3564
|
+
import { readFile as readFile2, writeFile as writeFile2, unlink, access, mkdir, lstat } from "fs/promises";
|
|
3565
|
+
import { tmpdir } from "os";
|
|
3566
|
+
import { resolve as resolve4, dirname as dirname2, relative, join as join3 } from "path";
|
|
3167
3567
|
import { pathToFileURL } from "url";
|
|
3568
|
+
import { createHash } from "crypto";
|
|
3569
|
+
function isTmpArtifact(name) {
|
|
3570
|
+
return TMP_ARTIFACT_RE.test(name);
|
|
3571
|
+
}
|
|
3572
|
+
async function sweepStaleTmpArtifacts(dir, now = Date.now()) {
|
|
3573
|
+
let removed = 0;
|
|
3574
|
+
try {
|
|
3575
|
+
const { readdir, stat } = await import("fs/promises");
|
|
3576
|
+
for (const name of await readdir(dir)) {
|
|
3577
|
+
if (!isTmpArtifact(name))
|
|
3578
|
+
continue;
|
|
3579
|
+
const path2 = resolve4(dir, name);
|
|
3580
|
+
try {
|
|
3581
|
+
const info = await stat(path2);
|
|
3582
|
+
if (now - info.mtimeMs < TMP_SWEEP_MIN_AGE_MS)
|
|
3583
|
+
continue;
|
|
3584
|
+
await unlink(path2);
|
|
3585
|
+
removed++;
|
|
3586
|
+
} catch {}
|
|
3587
|
+
}
|
|
3588
|
+
} catch {}
|
|
3589
|
+
return removed;
|
|
3590
|
+
}
|
|
3168
3591
|
async function fileExists(path2) {
|
|
3169
3592
|
try {
|
|
3170
3593
|
await access(path2);
|
|
@@ -3176,14 +3599,14 @@ async function fileExists(path2) {
|
|
|
3176
3599
|
async function findDistIndex() {
|
|
3177
3600
|
const distPath = process.env.FAILPROOFAI_DIST_PATH;
|
|
3178
3601
|
if (distPath) {
|
|
3179
|
-
const candidate =
|
|
3602
|
+
const candidate = resolve4(distPath, "index.js");
|
|
3180
3603
|
if (await fileExists(candidate))
|
|
3181
3604
|
return candidate;
|
|
3182
3605
|
}
|
|
3183
3606
|
const candidates = [
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3607
|
+
resolve4(dirname2(process.execPath), "..", "assets", "dist", "index.js"),
|
|
3608
|
+
resolve4(process.cwd(), "dist", "index.js"),
|
|
3609
|
+
resolve4(process.cwd(), "node_modules", "failproofai", "dist", "index.js")
|
|
3187
3610
|
];
|
|
3188
3611
|
for (const c of candidates) {
|
|
3189
3612
|
if (await fileExists(c))
|
|
@@ -3192,16 +3615,24 @@ async function findDistIndex() {
|
|
|
3192
3615
|
return null;
|
|
3193
3616
|
}
|
|
3194
3617
|
async function resolveLocalImport(fromDir, specifier) {
|
|
3195
|
-
const base =
|
|
3196
|
-
const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.ts`,
|
|
3618
|
+
const base = resolve4(fromDir, specifier);
|
|
3619
|
+
const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.ts`, resolve4(base, "index.js")];
|
|
3197
3620
|
for (const c of candidates) {
|
|
3198
3621
|
if (await fileExists(c))
|
|
3199
3622
|
return c;
|
|
3200
3623
|
}
|
|
3201
3624
|
return null;
|
|
3202
3625
|
}
|
|
3203
|
-
async function
|
|
3204
|
-
const shimPath =
|
|
3626
|
+
async function writeShim(dir, name, code, exclusive) {
|
|
3627
|
+
const shimPath = join3(dir, name);
|
|
3628
|
+
await writeFile2(shimPath, code, {
|
|
3629
|
+
encoding: "utf-8",
|
|
3630
|
+
mode: 384,
|
|
3631
|
+
...exclusive ? { flag: "wx" } : {}
|
|
3632
|
+
});
|
|
3633
|
+
return shimPath;
|
|
3634
|
+
}
|
|
3635
|
+
async function createEsmShim(_distIndex, distUrl, tmpSuffix = TMP_SUFFIX) {
|
|
3205
3636
|
const shimCode = [
|
|
3206
3637
|
`import _cjs from '${distUrl}';`,
|
|
3207
3638
|
`export const customPolicies = _cjs.customPolicies;`,
|
|
@@ -3213,16 +3644,30 @@ async function createEsmShim(distIndex, distUrl) {
|
|
|
3213
3644
|
`export default _cjs;`
|
|
3214
3645
|
].join(`
|
|
3215
3646
|
`);
|
|
3216
|
-
|
|
3647
|
+
const name = `failproofai-dist${tmpSuffix}.shim.mjs`;
|
|
3648
|
+
try {
|
|
3649
|
+
const dir = shimsDir();
|
|
3650
|
+
await mkdir(dir, { recursive: true, mode: 448 });
|
|
3651
|
+
const st = await lstat(dir);
|
|
3652
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
3653
|
+
if (!st.isDirectory() || st.isSymbolicLink() || uid !== null && st.uid !== uid || st.mode & 63) {
|
|
3654
|
+
throw new Error(`${dir} is not a private directory owned by this user`);
|
|
3655
|
+
}
|
|
3656
|
+
const shimPath2 = await writeShim(dir, name, shimCode, false);
|
|
3657
|
+
return { shimPath: shimPath2, shimUrl: pathToFileURL(shimPath2).href };
|
|
3658
|
+
} catch (err) {
|
|
3659
|
+
hookLogWarn(`policy shim directory unusable (${err instanceof Error ? err.message : String(err)}); falling back to ${tmpdir()}`);
|
|
3660
|
+
}
|
|
3661
|
+
const shimPath = await writeShim(tmpdir(), name, shimCode, true);
|
|
3217
3662
|
return { shimPath, shimUrl: pathToFileURL(shimPath).href };
|
|
3218
3663
|
}
|
|
3219
|
-
async function rewriteFileTree(entryPath, distUrl, distIndex) {
|
|
3664
|
+
async function rewriteFileTree(entryPath, distUrl, distIndex, tmpSuffix = TMP_SUFFIX, entrySha256) {
|
|
3220
3665
|
const queue = [entryPath];
|
|
3221
3666
|
const visited = new Set;
|
|
3222
3667
|
const tmpFiles = [];
|
|
3223
3668
|
let esmShimUrl = null;
|
|
3224
3669
|
if (distIndex && distUrl) {
|
|
3225
|
-
const shim = await createEsmShim(distIndex, distUrl);
|
|
3670
|
+
const shim = await createEsmShim(distIndex, distUrl, tmpSuffix);
|
|
3226
3671
|
tmpFiles.push(shim.shimPath);
|
|
3227
3672
|
esmShimUrl = shim.shimUrl;
|
|
3228
3673
|
}
|
|
@@ -3231,7 +3676,17 @@ async function rewriteFileTree(entryPath, distUrl, distIndex) {
|
|
|
3231
3676
|
if (visited.has(filePath))
|
|
3232
3677
|
continue;
|
|
3233
3678
|
visited.add(filePath);
|
|
3234
|
-
let code
|
|
3679
|
+
let code;
|
|
3680
|
+
if (entrySha256 && filePath === entryPath) {
|
|
3681
|
+
const buf = await readFile2(filePath);
|
|
3682
|
+
const actual = createHash("sha256").update(buf).digest("hex");
|
|
3683
|
+
if (actual !== entrySha256) {
|
|
3684
|
+
throw new Error(`cloud-managed policy failed integrity re-verification at load: expected ${entrySha256}, got ${actual}`);
|
|
3685
|
+
}
|
|
3686
|
+
code = buf.toString("utf-8");
|
|
3687
|
+
} else {
|
|
3688
|
+
code = await readFile2(filePath, "utf-8");
|
|
3689
|
+
}
|
|
3235
3690
|
if (esmShimUrl) {
|
|
3236
3691
|
code = code.replace(/from\s+(['"])(?:claudeye|failproofai)\1/g, `from '${esmShimUrl}'`);
|
|
3237
3692
|
}
|
|
@@ -3253,7 +3708,7 @@ async function rewriteFileTree(entryPath, distUrl, distIndex) {
|
|
|
3253
3708
|
if (!visited.has(resolved2) && !queue.includes(resolved2)) {
|
|
3254
3709
|
queue.push(resolved2);
|
|
3255
3710
|
}
|
|
3256
|
-
let relPath = relative(dir, resolved2 +
|
|
3711
|
+
let relPath = relative(dir, resolved2 + tmpSuffix).split("\\").join("/");
|
|
3257
3712
|
if (!relPath.startsWith("."))
|
|
3258
3713
|
relPath = "./" + relPath;
|
|
3259
3714
|
rewrites.set(specifier, relPath);
|
|
@@ -3266,7 +3721,7 @@ async function rewriteFileTree(entryPath, distUrl, distIndex) {
|
|
|
3266
3721
|
code = code.replace(new RegExp(`'${escaped}'`, "g"), `'${replacement}'`);
|
|
3267
3722
|
code = code.replace(new RegExp(`"${escaped}"`, "g"), `"${replacement}"`);
|
|
3268
3723
|
}
|
|
3269
|
-
const tmpPath = filePath +
|
|
3724
|
+
const tmpPath = filePath + tmpSuffix;
|
|
3270
3725
|
await writeFile2(tmpPath, code, "utf-8");
|
|
3271
3726
|
tmpFiles.push(tmpPath);
|
|
3272
3727
|
}
|
|
@@ -3279,36 +3734,61 @@ async function cleanupTmpFiles(tmpFiles) {
|
|
|
3279
3734
|
} catch {}
|
|
3280
3735
|
}
|
|
3281
3736
|
}
|
|
3282
|
-
var TMP_SUFFIX = ".__failproofai_tmp__.mjs", LOCAL_IMPORT_RE, LOCAL_REQUIRE_RE;
|
|
3737
|
+
var TMP_SUFFIX = ".__failproofai_tmp__.mjs", TMP_ARTIFACT_RE, TMP_SWEEP_MIN_AGE_MS = 60000, LOCAL_IMPORT_RE, LOCAL_REQUIRE_RE;
|
|
3283
3738
|
var init_loader_utils = __esm(() => {
|
|
3739
|
+
init_fp_home();
|
|
3740
|
+
init_hook_logger();
|
|
3741
|
+
TMP_ARTIFACT_RE = /\.__failproofai_tmp__\./;
|
|
3284
3742
|
LOCAL_IMPORT_RE = /(?:import\s+(?:[\s\S]*?\s+from\s+)?|export\s+(?:[\s\S]*?\s+from\s+))(['"])(\.\.?\/[^'"]+)\1/g;
|
|
3285
3743
|
LOCAL_REQUIRE_RE = /require\s*\(\s*(['"])(\.\.?\/[^'"]+)\1\s*\)/g;
|
|
3286
3744
|
});
|
|
3287
3745
|
|
|
3288
3746
|
// src/hooks/custom-hooks-loader.ts
|
|
3289
|
-
import { resolve as
|
|
3290
|
-
import {
|
|
3747
|
+
import { resolve as resolve5, isAbsolute, basename } from "node:path";
|
|
3748
|
+
import { randomUUID } from "crypto";
|
|
3749
|
+
import { existsSync as existsSync4, readdirSync } from "node:fs";
|
|
3750
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
3291
3751
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
3292
|
-
import {
|
|
3752
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3293
3753
|
function discoverPolicyFiles(dir) {
|
|
3294
|
-
if (!
|
|
3754
|
+
if (!existsSync4(dir))
|
|
3295
3755
|
return [];
|
|
3296
3756
|
try {
|
|
3297
3757
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
3298
|
-
return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) =>
|
|
3758
|
+
return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => resolve5(dir, e.name));
|
|
3299
3759
|
} catch {
|
|
3300
3760
|
return [];
|
|
3301
3761
|
}
|
|
3302
3762
|
}
|
|
3303
3763
|
function findSkippedPolicyFiles(dir) {
|
|
3304
|
-
if (!
|
|
3764
|
+
if (!existsSync4(dir))
|
|
3305
3765
|
return [];
|
|
3306
3766
|
try {
|
|
3307
|
-
return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && LOADABLE_EXT_RE.test(e.name) && !CONVENTION_FILE_RE.test(e.name) && !e.name.endsWith(".d.ts")).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
3767
|
+
return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && LOADABLE_EXT_RE.test(e.name) && !CONVENTION_FILE_RE.test(e.name) && !e.name.endsWith(".d.ts") && !isTmpArtifact(e.name)).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
3308
3768
|
} catch {
|
|
3309
3769
|
return [];
|
|
3310
3770
|
}
|
|
3311
3771
|
}
|
|
3772
|
+
function moduleLoadTimeoutMs() {
|
|
3773
|
+
const override = Number(process.env.FAILPROOFAI_POLICY_LOAD_TIMEOUT_MS);
|
|
3774
|
+
return Number.isFinite(override) && override > 0 ? override : 1e4;
|
|
3775
|
+
}
|
|
3776
|
+
async function importWithDeadline(fileUrl) {
|
|
3777
|
+
const budget = moduleLoadTimeoutMs();
|
|
3778
|
+
let timer;
|
|
3779
|
+
try {
|
|
3780
|
+
await Promise.race([
|
|
3781
|
+
import(fileUrl),
|
|
3782
|
+
new Promise((_, reject) => {
|
|
3783
|
+
timer = setTimeout(() => reject(new Error(`timed out loading module after ${budget}ms`)), budget);
|
|
3784
|
+
timer.unref?.();
|
|
3785
|
+
})
|
|
3786
|
+
]);
|
|
3787
|
+
} finally {
|
|
3788
|
+
if (timer)
|
|
3789
|
+
clearTimeout(timer);
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3312
3792
|
async function loadSingleFile(absPath, opts) {
|
|
3313
3793
|
const g = globalThis;
|
|
3314
3794
|
g[LOADING_KEY] = true;
|
|
@@ -3316,13 +3796,29 @@ async function loadSingleFile(absPath, opts) {
|
|
|
3316
3796
|
try {
|
|
3317
3797
|
const distIndex = await findDistIndex();
|
|
3318
3798
|
const distUrl = distIndex ? pathToFileURL2(distIndex).href : null;
|
|
3319
|
-
|
|
3320
|
-
const
|
|
3321
|
-
|
|
3322
|
-
await
|
|
3799
|
+
const sequence = ++loadSequence;
|
|
3800
|
+
const tmpSuffix = `${TMP_SUFFIX}.${process.pid}.${sequence}.${randomUUID()}.mjs`;
|
|
3801
|
+
tmpFiles = await rewriteFileTree(absPath, distUrl, distIndex, tmpSuffix, opts?.verifyEntrySha);
|
|
3802
|
+
const fingerprint = await fingerprintTemporaryTree(tmpFiles, tmpSuffix);
|
|
3803
|
+
const cached = policyModuleCache.get(absPath);
|
|
3804
|
+
if (cached?.fingerprint === fingerprint) {
|
|
3805
|
+
for (const hook of cached.hooks)
|
|
3806
|
+
customPolicies.add({ ...hook });
|
|
3807
|
+
return;
|
|
3808
|
+
}
|
|
3809
|
+
const entryTmp = absPath + tmpSuffix;
|
|
3810
|
+
const fileUrl = pathToFileURL2(entryTmp).href;
|
|
3811
|
+
const hooksBefore = getCustomHooks().length;
|
|
3812
|
+
await importWithDeadline(fileUrl);
|
|
3813
|
+
if (policyModuleCache.size >= POLICY_MODULE_CACHE_MAX_ENTRIES)
|
|
3814
|
+
policyModuleCache.clear();
|
|
3815
|
+
policyModuleCache.set(absPath, {
|
|
3816
|
+
fingerprint,
|
|
3817
|
+
hooks: getCustomHooks().slice(hooksBefore).map((hook) => ({ ...hook }))
|
|
3818
|
+
});
|
|
3323
3819
|
} catch (err) {
|
|
3324
3820
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3325
|
-
const errorType = /Cannot find module|MODULE_NOT_FOUND|ENOENT/i.test(msg) ? "module_not_found" : /SyntaxError|Unexpected token/i.test(msg) ? "syntax_error" : "runtime_error";
|
|
3821
|
+
const errorType = /Cannot find module|MODULE_NOT_FOUND|ENOENT/i.test(msg) ? "module_not_found" : /SyntaxError|Unexpected token/i.test(msg) ? "syntax_error" : /timed out loading module/i.test(msg) ? "load_timeout" : "runtime_error";
|
|
3326
3822
|
trackHookEvent(getInstanceId(), "custom_hooks_load_error", {
|
|
3327
3823
|
error_type: errorType,
|
|
3328
3824
|
is_convention: !!opts?.conventionScope,
|
|
@@ -3337,6 +3833,15 @@ async function loadSingleFile(absPath, opts) {
|
|
|
3337
3833
|
await cleanupTmpFiles(tmpFiles);
|
|
3338
3834
|
}
|
|
3339
3835
|
}
|
|
3836
|
+
async function fingerprintTemporaryTree(tmpFiles, tmpSuffix) {
|
|
3837
|
+
const hash = createHash2("sha256");
|
|
3838
|
+
for (const tmpFile of [...tmpFiles].sort()) {
|
|
3839
|
+
hash.update(tmpFile.replaceAll(tmpSuffix, TMP_SUFFIX));
|
|
3840
|
+
const contents = await readFile3(tmpFile, "utf8");
|
|
3841
|
+
hash.update(contents.replaceAll(tmpSuffix, TMP_SUFFIX));
|
|
3842
|
+
}
|
|
3843
|
+
return hash.digest("hex");
|
|
3844
|
+
}
|
|
3340
3845
|
function customPolicyId(file, name) {
|
|
3341
3846
|
return `custom:${file}:${name}`;
|
|
3342
3847
|
}
|
|
@@ -3356,15 +3861,37 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
3356
3861
|
const projectRoot = findProjectConfigDir(opts?.sessionCwd ?? process.cwd());
|
|
3357
3862
|
const conventionEnabled = opts?.customPoliciesEnabled !== false;
|
|
3358
3863
|
const loadedPaths = new Set;
|
|
3864
|
+
const cloudManagedByPath = new Map;
|
|
3865
|
+
for (const policy of opts?.cloudManagedPolicies ?? []) {
|
|
3866
|
+
const key = resolve5(policy.path);
|
|
3867
|
+
const existing = cloudManagedByPath.get(key);
|
|
3868
|
+
if (!existing) {
|
|
3869
|
+
cloudManagedByPath.set(key, policy);
|
|
3870
|
+
continue;
|
|
3871
|
+
}
|
|
3872
|
+
hookLogWarn(`cloud-managed policies ${existing.id} and ${policy.id} have identical source, so they share ` + `one artifact and load as one policy; enforcing it if either asks to enforce`);
|
|
3873
|
+
if (existing.effect !== "enforce" && policy.effect === "enforce") {
|
|
3874
|
+
cloudManagedByPath.set(key, policy);
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3359
3877
|
for (const customPoliciesPath of typeof customPoliciesPaths === "string" ? [customPoliciesPaths] : customPoliciesPaths ?? []) {
|
|
3360
|
-
const absPath =
|
|
3361
|
-
if (
|
|
3878
|
+
const absPath = resolve5(projectRoot, customPoliciesPath);
|
|
3879
|
+
if (existsSync4(absPath)) {
|
|
3362
3880
|
if (!loadedPaths.has(absPath)) {
|
|
3363
3881
|
loadedPaths.add(absPath);
|
|
3364
3882
|
const hooksBefore = getCustomHooks().length;
|
|
3365
|
-
await loadSingleFile(absPath
|
|
3883
|
+
await loadSingleFile(absPath, {
|
|
3884
|
+
verifyEntrySha: cloudManagedByPath.get(absPath)?.sha256
|
|
3885
|
+
});
|
|
3366
3886
|
for (const hook of getCustomHooks().slice(hooksBefore)) {
|
|
3367
|
-
|
|
3887
|
+
const cloudManaged = cloudManagedByPath.get(absPath);
|
|
3888
|
+
const tagged = hook;
|
|
3889
|
+
if (cloudManaged) {
|
|
3890
|
+
tagged.__cloudManaged = cloudManaged;
|
|
3891
|
+
tagged.__policyId = `cloud:${cloudManaged.id}@${cloudManaged.version}:${hook.name}`;
|
|
3892
|
+
} else {
|
|
3893
|
+
tagged.__policyId = customPolicyId(absPath, hook.name);
|
|
3894
|
+
}
|
|
3368
3895
|
}
|
|
3369
3896
|
}
|
|
3370
3897
|
} else {
|
|
@@ -3372,29 +3899,35 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
3372
3899
|
}
|
|
3373
3900
|
}
|
|
3374
3901
|
const hooksBeforeConvention = getCustomHooks().length;
|
|
3375
|
-
const projectDir =
|
|
3376
|
-
|
|
3377
|
-
|
|
3902
|
+
const projectDir = resolve5(projectRoot, ".failproofai", "policies");
|
|
3903
|
+
const projectScope = projectDir === customPoliciesDir() ? "user" : "project";
|
|
3904
|
+
if (conventionEnabled) {
|
|
3905
|
+
sweepStaleTmpArtifacts(projectDir);
|
|
3906
|
+
warnSkippedPolicyFiles(projectDir, projectScope);
|
|
3907
|
+
}
|
|
3908
|
+
sweepStaleTmpArtifacts(shimsDir());
|
|
3378
3909
|
const projectFiles = conventionEnabled ? discoverPolicyFiles(projectDir).filter((f) => !loadedPaths.has(f)) : [];
|
|
3379
3910
|
for (const file of projectFiles) {
|
|
3380
3911
|
loadedPaths.add(file);
|
|
3381
3912
|
const hooksBefore = getCustomHooks().length;
|
|
3382
|
-
await loadSingleFile(file, { conventionScope:
|
|
3913
|
+
await loadSingleFile(file, { conventionScope: projectScope });
|
|
3383
3914
|
const newHooks = getCustomHooks().slice(hooksBefore);
|
|
3384
3915
|
for (const hook of newHooks) {
|
|
3385
|
-
hook.__policyId = conventionPolicyId(
|
|
3916
|
+
hook.__policyId = conventionPolicyId(projectScope, basename(file), hook.name);
|
|
3386
3917
|
}
|
|
3387
3918
|
if (newHooks.length > 0) {
|
|
3388
3919
|
conventionSources.push({
|
|
3389
|
-
scope:
|
|
3920
|
+
scope: projectScope,
|
|
3390
3921
|
file: basename(file),
|
|
3391
3922
|
hookNames: newHooks.map((h) => h.name)
|
|
3392
3923
|
});
|
|
3393
3924
|
}
|
|
3394
3925
|
}
|
|
3395
|
-
const userDir =
|
|
3396
|
-
if (conventionEnabled && userDir !== projectDir)
|
|
3926
|
+
const userDir = customPoliciesDir();
|
|
3927
|
+
if (conventionEnabled && userDir !== projectDir) {
|
|
3928
|
+
sweepStaleTmpArtifacts(userDir);
|
|
3397
3929
|
warnSkippedPolicyFiles(userDir, "user");
|
|
3930
|
+
}
|
|
3398
3931
|
const userFiles = conventionEnabled && userDir !== projectDir ? discoverPolicyFiles(userDir).filter((f) => !loadedPaths.has(f)) : [];
|
|
3399
3932
|
for (const file of userFiles) {
|
|
3400
3933
|
loadedPaths.add(file);
|
|
@@ -3436,7 +3969,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
3436
3969
|
}
|
|
3437
3970
|
return { hooks: allHooks, conventionSources };
|
|
3438
3971
|
}
|
|
3439
|
-
var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, loadSequence = 0, LOADABLE_EXT_RE;
|
|
3972
|
+
var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, loadSequence = 0, policyModuleCache, POLICY_MODULE_CACHE_MAX_ENTRIES = 500, LOADABLE_EXT_RE;
|
|
3440
3973
|
var init_custom_hooks_loader = __esm(() => {
|
|
3441
3974
|
init_hook_logger();
|
|
3442
3975
|
init_custom_hooks_registry();
|
|
@@ -3444,44 +3977,45 @@ var init_custom_hooks_loader = __esm(() => {
|
|
|
3444
3977
|
init_hooks_config();
|
|
3445
3978
|
init_hook_telemetry();
|
|
3446
3979
|
init_telemetry_id();
|
|
3980
|
+
init_fp_home();
|
|
3447
3981
|
CONVENTION_FILE_RE = /policies\.(js|mjs|ts)$/;
|
|
3982
|
+
policyModuleCache = new Map;
|
|
3448
3983
|
LOADABLE_EXT_RE = /\.(js|mjs|ts)$/;
|
|
3449
3984
|
});
|
|
3450
3985
|
|
|
3451
3986
|
// src/hooks/hook-activity-store.ts
|
|
3452
3987
|
import {
|
|
3453
|
-
readFileSync as
|
|
3454
|
-
writeFileSync as
|
|
3988
|
+
readFileSync as readFileSync3,
|
|
3989
|
+
writeFileSync as writeFileSync3,
|
|
3455
3990
|
appendFileSync as appendFileSync2,
|
|
3456
3991
|
renameSync as renameSync2,
|
|
3457
3992
|
readdirSync as readdirSync2,
|
|
3458
|
-
mkdirSync as
|
|
3459
|
-
existsSync as
|
|
3460
|
-
statSync as
|
|
3993
|
+
mkdirSync as mkdirSync4,
|
|
3994
|
+
existsSync as existsSync5,
|
|
3995
|
+
statSync as statSync5,
|
|
3461
3996
|
unlinkSync
|
|
3462
3997
|
} from "node:fs";
|
|
3463
|
-
import { join as
|
|
3464
|
-
import { homedir as homedir5 } from "node:os";
|
|
3998
|
+
import { join as join4 } from "node:path";
|
|
3465
3999
|
function ensureDir() {
|
|
3466
|
-
if (!
|
|
3467
|
-
|
|
4000
|
+
if (!existsSync5(storeDirValue())) {
|
|
4001
|
+
mkdirSync4(storeDirValue(), { recursive: true });
|
|
3468
4002
|
}
|
|
3469
4003
|
}
|
|
3470
4004
|
function acquireLock() {
|
|
3471
4005
|
ensureDir();
|
|
3472
|
-
const lockPath =
|
|
4006
|
+
const lockPath = join4(storeDirValue(), LOCK_FILE);
|
|
3473
4007
|
const deadline = Date.now() + LOCK_STALE_MS;
|
|
3474
4008
|
while (Date.now() < deadline) {
|
|
3475
4009
|
try {
|
|
3476
|
-
|
|
4010
|
+
writeFileSync3(lockPath, String(process.pid), { flag: "wx" });
|
|
3477
4011
|
return;
|
|
3478
4012
|
} catch (e) {
|
|
3479
4013
|
if (e.code !== "EEXIST")
|
|
3480
4014
|
return;
|
|
3481
4015
|
try {
|
|
3482
|
-
const s =
|
|
4016
|
+
const s = statSync5(lockPath);
|
|
3483
4017
|
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
3484
|
-
|
|
4018
|
+
writeFileSync3(lockPath, String(process.pid), "utf-8");
|
|
3485
4019
|
return;
|
|
3486
4020
|
}
|
|
3487
4021
|
} catch {}
|
|
@@ -3490,15 +4024,15 @@ function acquireLock() {
|
|
|
3490
4024
|
}
|
|
3491
4025
|
function releaseLock() {
|
|
3492
4026
|
try {
|
|
3493
|
-
unlinkSync(
|
|
4027
|
+
unlinkSync(join4(storeDirValue(), LOCK_FILE));
|
|
3494
4028
|
} catch {}
|
|
3495
4029
|
}
|
|
3496
4030
|
function persistHookActivity(entry) {
|
|
3497
4031
|
ensureDir();
|
|
3498
4032
|
acquireLock();
|
|
3499
4033
|
try {
|
|
3500
|
-
const currentPath =
|
|
3501
|
-
const countPath =
|
|
4034
|
+
const currentPath = join4(storeDirValue(), CURRENT_FILE);
|
|
4035
|
+
const countPath = join4(storeDirValue(), COUNT_FILE);
|
|
3502
4036
|
const lineCount = readCount(countPath);
|
|
3503
4037
|
if (lineCount >= PAGE_SIZE) {
|
|
3504
4038
|
try {
|
|
@@ -3518,13 +4052,13 @@ function persistHookActivity(entry) {
|
|
|
3518
4052
|
}
|
|
3519
4053
|
function rotate(currentPath, countPath) {
|
|
3520
4054
|
const archiveName = `page-${Date.now()}-${rotateSeq++}.jsonl`;
|
|
3521
|
-
const archivePath =
|
|
4055
|
+
const archivePath = join4(storeDirValue(), archiveName);
|
|
3522
4056
|
renameSync2(currentPath, archivePath);
|
|
3523
4057
|
writeCount(countPath, 0);
|
|
3524
4058
|
}
|
|
3525
4059
|
function readCount(countPath) {
|
|
3526
4060
|
try {
|
|
3527
|
-
const n = parseInt(
|
|
4061
|
+
const n = parseInt(readFileSync3(countPath, "utf-8"), 10);
|
|
3528
4062
|
return isNaN(n) ? 0 : n;
|
|
3529
4063
|
} catch {
|
|
3530
4064
|
return 0;
|
|
@@ -3532,18 +4066,17 @@ function readCount(countPath) {
|
|
|
3532
4066
|
}
|
|
3533
4067
|
function writeCount(countPath, n) {
|
|
3534
4068
|
try {
|
|
3535
|
-
|
|
4069
|
+
writeFileSync3(countPath, String(n), "utf-8");
|
|
3536
4070
|
} catch {}
|
|
3537
4071
|
}
|
|
3538
4072
|
function readStoredStats() {
|
|
3539
4073
|
try {
|
|
3540
|
-
return JSON.parse(
|
|
4074
|
+
return JSON.parse(readFileSync3(join4(storeDirValue(), STATS_FILE), "utf-8"));
|
|
3541
4075
|
} catch {
|
|
3542
4076
|
return { totalEvents: 0, denyCount: 0, policyMap: {} };
|
|
3543
4077
|
}
|
|
3544
4078
|
}
|
|
3545
|
-
function
|
|
3546
|
-
const s = readStoredStats();
|
|
4079
|
+
function foldEntry(s, entry) {
|
|
3547
4080
|
s.totalEvents += 1;
|
|
3548
4081
|
if (entry.decision === "deny")
|
|
3549
4082
|
s.denyCount += 1;
|
|
@@ -3554,20 +4087,28 @@ function updateStats(entry) {
|
|
|
3554
4087
|
} else if (entry.policyName) {
|
|
3555
4088
|
s.policyMap[entry.policyName] = (s.policyMap[entry.policyName] ?? 0) + 1;
|
|
3556
4089
|
}
|
|
3557
|
-
|
|
4090
|
+
}
|
|
4091
|
+
function writeStoredStats(s) {
|
|
4092
|
+
const tmpPath = join4(storeDirValue(), `stats.json.${process.pid}.tmp`);
|
|
3558
4093
|
try {
|
|
3559
|
-
|
|
3560
|
-
renameSync2(tmpPath,
|
|
4094
|
+
writeFileSync3(tmpPath, JSON.stringify(s), "utf-8");
|
|
4095
|
+
renameSync2(tmpPath, join4(storeDirValue(), STATS_FILE));
|
|
4096
|
+
return true;
|
|
3561
4097
|
} catch {
|
|
3562
4098
|
try {
|
|
3563
4099
|
unlinkSync(tmpPath);
|
|
3564
4100
|
} catch {}
|
|
4101
|
+
return false;
|
|
3565
4102
|
}
|
|
3566
4103
|
}
|
|
3567
|
-
|
|
4104
|
+
function updateStats(entry) {
|
|
4105
|
+
const s = readStoredStats();
|
|
4106
|
+
foldEntry(s, entry);
|
|
4107
|
+
writeStoredStats(s);
|
|
4108
|
+
}
|
|
4109
|
+
var PAGE_SIZE = 25, defaultStoreDir = () => hookActivityDir(), CURRENT_FILE = "current.jsonl", COUNT_FILE = "current.count", STATS_FILE = "stats.json", LOCK_FILE = "current.lock", LOCK_STALE_MS = 2000, storeDirOverride = null, storeDirValue = () => storeDirOverride ?? defaultStoreDir(), rotateSeq = 0;
|
|
3568
4110
|
var init_hook_activity_store = __esm(() => {
|
|
3569
|
-
|
|
3570
|
-
storeDir = DEFAULT_STORE_DIR;
|
|
4111
|
+
init_fp_home();
|
|
3571
4112
|
});
|
|
3572
4113
|
|
|
3573
4114
|
// src/hooks/resolve-cwd.ts
|
|
@@ -3616,10 +4157,10 @@ function runtimeCache(fn, revalidateSeconds, options) {
|
|
|
3616
4157
|
}
|
|
3617
4158
|
|
|
3618
4159
|
// lib/paths.ts
|
|
3619
|
-
import { homedir as
|
|
3620
|
-
import { join as
|
|
4160
|
+
import { homedir as homedir4 } from "os";
|
|
4161
|
+
import { join as join5 } from "path";
|
|
3621
4162
|
function getDefaultClaudeProjectsPath() {
|
|
3622
|
-
return
|
|
4163
|
+
return join5(homedir4(), ".claude", "projects");
|
|
3623
4164
|
}
|
|
3624
4165
|
function decodeFolderName(name) {
|
|
3625
4166
|
if (/^[A-Za-z]--/.test(name)) {
|
|
@@ -3712,9 +4253,9 @@ __export(exports_codex_projects, {
|
|
|
3712
4253
|
getCachedCodexSessionsByEncodedName: () => getCachedCodexSessionsByEncodedName,
|
|
3713
4254
|
getCachedCodexProjects: () => getCachedCodexProjects
|
|
3714
4255
|
});
|
|
3715
|
-
import { open
|
|
3716
|
-
import { homedir as
|
|
3717
|
-
import { join as
|
|
4256
|
+
import { open, readdir } from "node:fs/promises";
|
|
4257
|
+
import { homedir as homedir5 } from "node:os";
|
|
4258
|
+
import { join as join6 } from "node:path";
|
|
3718
4259
|
async function safeReaddir(dir) {
|
|
3719
4260
|
try {
|
|
3720
4261
|
return await readdir(dir, { withFileTypes: true });
|
|
@@ -3725,7 +4266,7 @@ async function safeReaddir(dir) {
|
|
|
3725
4266
|
async function readFirstLine(filePath) {
|
|
3726
4267
|
let fh = null;
|
|
3727
4268
|
try {
|
|
3728
|
-
fh = await
|
|
4269
|
+
fh = await open(filePath, "r");
|
|
3729
4270
|
const buf = Buffer.alloc(FIRST_LINE_CHUNK_BYTES);
|
|
3730
4271
|
const { bytesRead } = await fh.read(buf, 0, FIRST_LINE_CHUNK_BYTES, 0);
|
|
3731
4272
|
if (bytesRead === 0)
|
|
@@ -3762,7 +4303,7 @@ async function listJsonlFiles(dir) {
|
|
|
3762
4303
|
const entries = await safeReaddir(dir);
|
|
3763
4304
|
if (!entries)
|
|
3764
4305
|
return [];
|
|
3765
|
-
return entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) =>
|
|
4306
|
+
return entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => join6(dir, e.name));
|
|
3766
4307
|
}
|
|
3767
4308
|
async function scanCodexSessions() {
|
|
3768
4309
|
const yearDirs = await safeReaddir(CODEX_SESSIONS_ROOT);
|
|
@@ -3772,19 +4313,19 @@ async function scanCodexSessions() {
|
|
|
3772
4313
|
for (const y of yearDirs) {
|
|
3773
4314
|
if (!y.isDirectory())
|
|
3774
4315
|
continue;
|
|
3775
|
-
const monthDirs = await safeReaddir(
|
|
4316
|
+
const monthDirs = await safeReaddir(join6(CODEX_SESSIONS_ROOT, y.name));
|
|
3776
4317
|
if (!monthDirs)
|
|
3777
4318
|
continue;
|
|
3778
4319
|
for (const m of monthDirs) {
|
|
3779
4320
|
if (!m.isDirectory())
|
|
3780
4321
|
continue;
|
|
3781
|
-
const dayDirs = await safeReaddir(
|
|
4322
|
+
const dayDirs = await safeReaddir(join6(CODEX_SESSIONS_ROOT, y.name, m.name));
|
|
3782
4323
|
if (!dayDirs)
|
|
3783
4324
|
continue;
|
|
3784
4325
|
for (const d of dayDirs) {
|
|
3785
4326
|
if (!d.isDirectory())
|
|
3786
4327
|
continue;
|
|
3787
|
-
const dayPath =
|
|
4328
|
+
const dayPath = join6(CODEX_SESSIONS_ROOT, y.name, m.name, d.name);
|
|
3788
4329
|
filePaths.push(...await listJsonlFiles(dayPath));
|
|
3789
4330
|
}
|
|
3790
4331
|
}
|
|
@@ -3803,10 +4344,10 @@ async function scanCodexSessions() {
|
|
|
3803
4344
|
return null;
|
|
3804
4345
|
let fileMtime;
|
|
3805
4346
|
try {
|
|
3806
|
-
const fh = await
|
|
4347
|
+
const fh = await open(filePath, "r");
|
|
3807
4348
|
try {
|
|
3808
|
-
const
|
|
3809
|
-
fileMtime =
|
|
4349
|
+
const stat = await fh.stat();
|
|
4350
|
+
fileMtime = stat.mtime;
|
|
3810
4351
|
} finally {
|
|
3811
4352
|
await fh.close().catch(() => {});
|
|
3812
4353
|
}
|
|
@@ -3892,7 +4433,7 @@ var CODEX_SESSIONS_ROOT, SESSION_ID_RE, FIRST_LINE_CHUNK_BYTES, cachedScan, getC
|
|
|
3892
4433
|
var init_codex_projects = __esm(() => {
|
|
3893
4434
|
init_paths();
|
|
3894
4435
|
init_logger();
|
|
3895
|
-
CODEX_SESSIONS_ROOT =
|
|
4436
|
+
CODEX_SESSIONS_ROOT = join6(homedir5(), ".codex", "sessions");
|
|
3896
4437
|
SESSION_ID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
|
|
3897
4438
|
FIRST_LINE_CHUNK_BYTES = 256 * 1024;
|
|
3898
4439
|
cachedScan = runtimeCache(scanCodexSessions, 30);
|
|
@@ -3911,11 +4452,11 @@ __export(exports_copilot_projects, {
|
|
|
3911
4452
|
getCachedCopilotSessionsByEncodedName: () => getCachedCopilotSessionsByEncodedName,
|
|
3912
4453
|
getCachedCopilotProjects: () => getCachedCopilotProjects
|
|
3913
4454
|
});
|
|
3914
|
-
import { readdir as readdir2, readFile as
|
|
3915
|
-
import { homedir as
|
|
3916
|
-
import { join as
|
|
4455
|
+
import { readdir as readdir2, readFile as readFile4, stat } from "node:fs/promises";
|
|
4456
|
+
import { homedir as homedir6 } from "node:os";
|
|
4457
|
+
import { join as join7 } from "node:path";
|
|
3917
4458
|
function getCopilotSessionStateRoot() {
|
|
3918
|
-
return
|
|
4459
|
+
return join7(process.env.COPILOT_HOME || join7(homedir6(), ".copilot"), "session-state");
|
|
3919
4460
|
}
|
|
3920
4461
|
async function safeReaddir2(dir) {
|
|
3921
4462
|
try {
|
|
@@ -3932,7 +4473,7 @@ function parseCwdFromWorkspace(text) {
|
|
|
3932
4473
|
}
|
|
3933
4474
|
async function statMtime(path2) {
|
|
3934
4475
|
try {
|
|
3935
|
-
return (await
|
|
4476
|
+
return (await stat(path2)).mtime;
|
|
3936
4477
|
} catch {
|
|
3937
4478
|
return null;
|
|
3938
4479
|
}
|
|
@@ -3944,13 +4485,13 @@ async function scanCopilotSessions() {
|
|
|
3944
4485
|
return [];
|
|
3945
4486
|
const candidates = entries.filter((e) => e.isDirectory()).map((e) => ({
|
|
3946
4487
|
sessionId: e.name,
|
|
3947
|
-
workspacePath:
|
|
3948
|
-
eventsPath:
|
|
4488
|
+
workspacePath: join7(root, e.name, "workspace.yaml"),
|
|
4489
|
+
eventsPath: join7(root, e.name, "events.jsonl")
|
|
3949
4490
|
}));
|
|
3950
4491
|
const settled = await batchAll(candidates.map((c) => async () => {
|
|
3951
4492
|
let workspaceText;
|
|
3952
4493
|
try {
|
|
3953
|
-
workspaceText = await
|
|
4494
|
+
workspaceText = await readFile4(c.workspacePath, "utf-8");
|
|
3954
4495
|
} catch {
|
|
3955
4496
|
return null;
|
|
3956
4497
|
}
|
|
@@ -4059,11 +4600,11 @@ __export(exports_cursor_projects, {
|
|
|
4059
4600
|
getCachedCursorSessionsByEncodedName: () => getCachedCursorSessionsByEncodedName,
|
|
4060
4601
|
getCachedCursorProjects: () => getCachedCursorProjects
|
|
4061
4602
|
});
|
|
4062
|
-
import { readdir as readdir3, readFile as
|
|
4063
|
-
import { homedir as
|
|
4064
|
-
import { join as
|
|
4603
|
+
import { readdir as readdir3, readFile as readFile5, stat as stat2 } from "node:fs/promises";
|
|
4604
|
+
import { homedir as homedir7 } from "node:os";
|
|
4605
|
+
import { join as join8 } from "node:path";
|
|
4065
4606
|
function getCursorHome() {
|
|
4066
|
-
return process.env.CURSOR_HOME ||
|
|
4607
|
+
return process.env.CURSOR_HOME || join8(homedir7(), ".cursor");
|
|
4067
4608
|
}
|
|
4068
4609
|
async function safeReaddir3(dir) {
|
|
4069
4610
|
try {
|
|
@@ -4074,7 +4615,7 @@ async function safeReaddir3(dir) {
|
|
|
4074
4615
|
}
|
|
4075
4616
|
async function statMtime2(path2) {
|
|
4076
4617
|
try {
|
|
4077
|
-
return (await
|
|
4618
|
+
return (await stat2(path2)).mtime;
|
|
4078
4619
|
} catch {
|
|
4079
4620
|
return null;
|
|
4080
4621
|
}
|
|
@@ -4095,12 +4636,12 @@ function parseCwdFromMetaText(text) {
|
|
|
4095
4636
|
}
|
|
4096
4637
|
async function findFirstUsableMeta(dir, candidates) {
|
|
4097
4638
|
for (const name of candidates) {
|
|
4098
|
-
const path2 =
|
|
4639
|
+
const path2 = join8(dir, name);
|
|
4099
4640
|
if (await statMtime2(path2) === null)
|
|
4100
4641
|
continue;
|
|
4101
4642
|
let text;
|
|
4102
4643
|
try {
|
|
4103
|
-
text = await
|
|
4644
|
+
text = await readFile5(path2, "utf-8");
|
|
4104
4645
|
} catch {
|
|
4105
4646
|
continue;
|
|
4106
4647
|
}
|
|
@@ -4112,7 +4653,7 @@ async function findFirstUsableMeta(dir, candidates) {
|
|
|
4112
4653
|
}
|
|
4113
4654
|
async function findFirstExistingPath(dir, candidates) {
|
|
4114
4655
|
for (const name of candidates) {
|
|
4115
|
-
const path2 =
|
|
4656
|
+
const path2 = join8(dir, name);
|
|
4116
4657
|
if (await statMtime2(path2) !== null)
|
|
4117
4658
|
return path2;
|
|
4118
4659
|
}
|
|
@@ -4122,7 +4663,7 @@ async function scanCursorSessions() {
|
|
|
4122
4663
|
const home = getCursorHome();
|
|
4123
4664
|
const newCandidates = [];
|
|
4124
4665
|
const legacyCandidates = [];
|
|
4125
|
-
const projectsRoot =
|
|
4666
|
+
const projectsRoot = join8(home, NEW_PROJECTS_DIR);
|
|
4126
4667
|
const projectEntries = await safeReaddir3(projectsRoot);
|
|
4127
4668
|
if (projectEntries) {
|
|
4128
4669
|
for (const proj of projectEntries) {
|
|
@@ -4130,7 +4671,7 @@ async function scanCursorSessions() {
|
|
|
4130
4671
|
continue;
|
|
4131
4672
|
const decoded = decodeFolderName(proj.name);
|
|
4132
4673
|
const cwd = decoded.startsWith("/") || /^[A-Za-z]:\//.test(decoded) ? decoded : `/${decoded}`;
|
|
4133
|
-
const transcriptsRoot =
|
|
4674
|
+
const transcriptsRoot = join8(projectsRoot, proj.name, NEW_AGENT_TRANSCRIPTS_DIR);
|
|
4134
4675
|
const sessionDirs = await safeReaddir3(transcriptsRoot);
|
|
4135
4676
|
if (!sessionDirs)
|
|
4136
4677
|
continue;
|
|
@@ -4139,28 +4680,28 @@ async function scanCursorSessions() {
|
|
|
4139
4680
|
continue;
|
|
4140
4681
|
newCandidates.push({
|
|
4141
4682
|
sessionId: sd.name,
|
|
4142
|
-
dir:
|
|
4683
|
+
dir: join8(transcriptsRoot, sd.name),
|
|
4143
4684
|
cwd
|
|
4144
4685
|
});
|
|
4145
4686
|
}
|
|
4146
4687
|
}
|
|
4147
4688
|
}
|
|
4148
4689
|
for (const sub of LEGACY_SESSION_ROOT_CANDIDATES) {
|
|
4149
|
-
const root =
|
|
4690
|
+
const root = join8(home, sub);
|
|
4150
4691
|
const entries = await safeReaddir3(root);
|
|
4151
4692
|
if (!entries)
|
|
4152
4693
|
continue;
|
|
4153
4694
|
for (const e of entries) {
|
|
4154
4695
|
if (!e.isDirectory())
|
|
4155
4696
|
continue;
|
|
4156
|
-
legacyCandidates.push({ sessionId: e.name, dir:
|
|
4697
|
+
legacyCandidates.push({ sessionId: e.name, dir: join8(root, e.name) });
|
|
4157
4698
|
}
|
|
4158
4699
|
}
|
|
4159
4700
|
if (newCandidates.length === 0 && legacyCandidates.length === 0)
|
|
4160
4701
|
return [];
|
|
4161
4702
|
const settled = await batchAll([
|
|
4162
4703
|
...newCandidates.map((c) => async () => {
|
|
4163
|
-
const transcriptPath =
|
|
4704
|
+
const transcriptPath = join8(c.dir, `${c.sessionId}.jsonl`);
|
|
4164
4705
|
const transcriptMtime = await statMtime2(transcriptPath);
|
|
4165
4706
|
if (!transcriptMtime)
|
|
4166
4707
|
return null;
|
|
@@ -4432,11 +4973,11 @@ __export(exports_pi_projects, {
|
|
|
4432
4973
|
getCachedPiSessionsByEncodedName: () => getCachedPiSessionsByEncodedName,
|
|
4433
4974
|
getCachedPiProjects: () => getCachedPiProjects
|
|
4434
4975
|
});
|
|
4435
|
-
import { readdir as readdir4, readFile as
|
|
4436
|
-
import { homedir as
|
|
4437
|
-
import { join as
|
|
4976
|
+
import { readdir as readdir4, readFile as readFile6, stat as stat3 } from "node:fs/promises";
|
|
4977
|
+
import { homedir as homedir8 } from "node:os";
|
|
4978
|
+
import { join as join9 } from "node:path";
|
|
4438
4979
|
function getPiSessionsRoot() {
|
|
4439
|
-
return process.env.PI_SESSIONS_DIR ||
|
|
4980
|
+
return process.env.PI_SESSIONS_DIR || join9(homedir8(), ".pi", "agent", "sessions");
|
|
4440
4981
|
}
|
|
4441
4982
|
async function safeReaddir4(dir) {
|
|
4442
4983
|
try {
|
|
@@ -4447,7 +4988,7 @@ async function safeReaddir4(dir) {
|
|
|
4447
4988
|
}
|
|
4448
4989
|
async function statMtime3(path2) {
|
|
4449
4990
|
try {
|
|
4450
|
-
return (await
|
|
4991
|
+
return (await stat3(path2)).mtime;
|
|
4451
4992
|
} catch {
|
|
4452
4993
|
return null;
|
|
4453
4994
|
}
|
|
@@ -4455,7 +4996,7 @@ async function statMtime3(path2) {
|
|
|
4455
4996
|
async function readSessionCwd(filePath) {
|
|
4456
4997
|
let text;
|
|
4457
4998
|
try {
|
|
4458
|
-
text = await
|
|
4999
|
+
text = await readFile6(filePath, "utf-8");
|
|
4459
5000
|
} catch {
|
|
4460
5001
|
return null;
|
|
4461
5002
|
}
|
|
@@ -4484,7 +5025,7 @@ async function scanPiSessions() {
|
|
|
4484
5025
|
for (const cwdDir of cwdDirs) {
|
|
4485
5026
|
if (!cwdDir.isDirectory())
|
|
4486
5027
|
continue;
|
|
4487
|
-
const cwdPath =
|
|
5028
|
+
const cwdPath = join9(root, cwdDir.name);
|
|
4488
5029
|
const sessionFiles = await safeReaddir4(cwdPath);
|
|
4489
5030
|
if (!sessionFiles)
|
|
4490
5031
|
continue;
|
|
@@ -4494,7 +5035,7 @@ async function scanPiSessions() {
|
|
|
4494
5035
|
const m = SESSION_FILE_RE.exec(f.name);
|
|
4495
5036
|
if (!m)
|
|
4496
5037
|
continue;
|
|
4497
|
-
candidates.push({ sessionId: m[1], filePath:
|
|
5038
|
+
candidates.push({ sessionId: m[1], filePath: join9(cwdPath, f.name) });
|
|
4498
5039
|
}
|
|
4499
5040
|
}
|
|
4500
5041
|
if (candidates.length === 0)
|
|
@@ -4596,7 +5137,7 @@ var init_pi_projects = __esm(() => {
|
|
|
4596
5137
|
});
|
|
4597
5138
|
|
|
4598
5139
|
// lib/sqlite-reader.ts
|
|
4599
|
-
import { existsSync as
|
|
5140
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
|
|
4600
5141
|
import initSqlJs from "sql.js/dist/sql-asm.js";
|
|
4601
5142
|
async function tryNodeSqlite(dbPath) {
|
|
4602
5143
|
try {
|
|
@@ -4623,7 +5164,7 @@ function loadSqlJs() {
|
|
|
4623
5164
|
async function trySqlJs(dbPath) {
|
|
4624
5165
|
try {
|
|
4625
5166
|
const SQL = await loadSqlJs();
|
|
4626
|
-
const db = new SQL.Database(
|
|
5167
|
+
const db = new SQL.Database(readFileSync4(dbPath));
|
|
4627
5168
|
return {
|
|
4628
5169
|
query(sql, params) {
|
|
4629
5170
|
const stmt = db.prepare(sql);
|
|
@@ -4647,7 +5188,7 @@ async function trySqlJs(dbPath) {
|
|
|
4647
5188
|
}
|
|
4648
5189
|
}
|
|
4649
5190
|
async function openSqliteReadonly(dbPath) {
|
|
4650
|
-
if (!
|
|
5191
|
+
if (!existsSync6(dbPath))
|
|
4651
5192
|
return null;
|
|
4652
5193
|
return await tryNodeSqlite(dbPath) ?? await trySqlJs(dbPath);
|
|
4653
5194
|
}
|
|
@@ -4656,18 +5197,18 @@ var init_sqlite_reader = () => {};
|
|
|
4656
5197
|
|
|
4657
5198
|
// lib/hermes-profiles.ts
|
|
4658
5199
|
import { readdirSync as readdirSync3 } from "node:fs";
|
|
4659
|
-
import { homedir as
|
|
4660
|
-
import { basename as basename2, dirname as dirname3, join as
|
|
5200
|
+
import { homedir as homedir9 } from "node:os";
|
|
5201
|
+
import { basename as basename2, dirname as dirname3, join as join10, resolve as resolve6 } from "node:path";
|
|
4661
5202
|
function hermesRoot() {
|
|
4662
5203
|
const env = (process.env.HERMES_HOME || "").trim();
|
|
4663
5204
|
if (env) {
|
|
4664
|
-
const home =
|
|
5205
|
+
const home = resolve6(env);
|
|
4665
5206
|
const parent = dirname3(home);
|
|
4666
5207
|
if (basename2(parent) === PROFILES_DIR)
|
|
4667
5208
|
return dirname3(parent);
|
|
4668
5209
|
return home;
|
|
4669
5210
|
}
|
|
4670
|
-
return
|
|
5211
|
+
return join10(homedir9(), ".hermes");
|
|
4671
5212
|
}
|
|
4672
5213
|
function listHermesProfiles() {
|
|
4673
5214
|
const root = hermesRoot();
|
|
@@ -4675,7 +5216,7 @@ function listHermesProfiles() {
|
|
|
4675
5216
|
const seen = new Set([HERMES_DEFAULT_PROFILE]);
|
|
4676
5217
|
let names;
|
|
4677
5218
|
try {
|
|
4678
|
-
names = readdirSync3(
|
|
5219
|
+
names = readdirSync3(join10(root, PROFILES_DIR), { withFileTypes: true }).filter((e) => (e.isDirectory() || e.isSymbolicLink()) && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
4679
5220
|
} catch {
|
|
4680
5221
|
return out;
|
|
4681
5222
|
}
|
|
@@ -4683,7 +5224,7 @@ function listHermesProfiles() {
|
|
|
4683
5224
|
if (seen.has(name))
|
|
4684
5225
|
continue;
|
|
4685
5226
|
seen.add(name);
|
|
4686
|
-
out.push({ name, home:
|
|
5227
|
+
out.push({ name, home: join10(root, PROFILES_DIR, name) });
|
|
4687
5228
|
}
|
|
4688
5229
|
return out;
|
|
4689
5230
|
}
|
|
@@ -4734,14 +5275,14 @@ function formatDuration(ms) {
|
|
|
4734
5275
|
}
|
|
4735
5276
|
|
|
4736
5277
|
// lib/hermes-sessions.ts
|
|
4737
|
-
import { join as
|
|
5278
|
+
import { join as join11 } from "node:path";
|
|
4738
5279
|
function hermesDbPaths() {
|
|
4739
5280
|
const override = process.env.HERMES_DB_PATH;
|
|
4740
5281
|
if (override)
|
|
4741
5282
|
return [{ profile: HERMES_DEFAULT_PROFILE, dbPath: override }];
|
|
4742
5283
|
return listHermesProfiles().map((p) => ({
|
|
4743
5284
|
profile: p.name,
|
|
4744
|
-
dbPath:
|
|
5285
|
+
dbPath: join11(p.home, "state.db")
|
|
4745
5286
|
}));
|
|
4746
5287
|
}
|
|
4747
5288
|
function epochToMs(value) {
|
|
@@ -5037,12 +5578,12 @@ var init_hermes_projects = __esm(() => {
|
|
|
5037
5578
|
});
|
|
5038
5579
|
|
|
5039
5580
|
// lib/openclaw-sessions.ts
|
|
5040
|
-
import { readFile as
|
|
5041
|
-
import { readdirSync as readdirSync4, statSync as
|
|
5042
|
-
import { homedir as
|
|
5043
|
-
import { join as
|
|
5581
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
5582
|
+
import { readdirSync as readdirSync4, statSync as statSync6 } from "node:fs";
|
|
5583
|
+
import { homedir as homedir10 } from "node:os";
|
|
5584
|
+
import { join as join12 } from "node:path";
|
|
5044
5585
|
function openclawHome() {
|
|
5045
|
-
return process.env.OPENCLAW_HOME ||
|
|
5586
|
+
return process.env.OPENCLAW_HOME || join12(homedir10(), ".openclaw");
|
|
5046
5587
|
}
|
|
5047
5588
|
function isPlainObject2(v) {
|
|
5048
5589
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
@@ -5157,13 +5698,13 @@ function openclawLinesToLogEntries(lines, source = "session") {
|
|
|
5157
5698
|
}
|
|
5158
5699
|
function listOpenClawAgents() {
|
|
5159
5700
|
try {
|
|
5160
|
-
return readdirSync4(
|
|
5701
|
+
return readdirSync4(join12(openclawHome(), "agents"), { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name).sort();
|
|
5161
5702
|
} catch {
|
|
5162
5703
|
return [];
|
|
5163
5704
|
}
|
|
5164
5705
|
}
|
|
5165
5706
|
function listOpenClawTranscripts() {
|
|
5166
|
-
const agentsDir =
|
|
5707
|
+
const agentsDir = join12(openclawHome(), "agents");
|
|
5167
5708
|
const out = [];
|
|
5168
5709
|
let agentIds;
|
|
5169
5710
|
try {
|
|
@@ -5172,7 +5713,7 @@ function listOpenClawTranscripts() {
|
|
|
5172
5713
|
return out;
|
|
5173
5714
|
}
|
|
5174
5715
|
for (const agentId of agentIds) {
|
|
5175
|
-
const sessionsDir =
|
|
5716
|
+
const sessionsDir = join12(agentsDir, agentId, "sessions");
|
|
5176
5717
|
let files;
|
|
5177
5718
|
try {
|
|
5178
5719
|
files = readdirSync4(sessionsDir);
|
|
@@ -5185,9 +5726,9 @@ function listOpenClawTranscripts() {
|
|
|
5185
5726
|
const sessionId = file.slice(0, -".jsonl".length);
|
|
5186
5727
|
if (!OPENCLAW_SESSION_ID_RE.test(sessionId))
|
|
5187
5728
|
continue;
|
|
5188
|
-
const transcriptPath =
|
|
5729
|
+
const transcriptPath = join12(sessionsDir, file);
|
|
5189
5730
|
try {
|
|
5190
|
-
const st =
|
|
5731
|
+
const st = statSync6(transcriptPath);
|
|
5191
5732
|
out.push({ agentId, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
|
|
5192
5733
|
} catch {}
|
|
5193
5734
|
}
|
|
@@ -5209,7 +5750,7 @@ async function getOpenClawSessionLog(sessionId) {
|
|
|
5209
5750
|
return null;
|
|
5210
5751
|
let content;
|
|
5211
5752
|
try {
|
|
5212
|
-
content = await
|
|
5753
|
+
content = await readFile7(filePath, "utf-8");
|
|
5213
5754
|
} catch {
|
|
5214
5755
|
return null;
|
|
5215
5756
|
}
|
|
@@ -5245,17 +5786,17 @@ __export(exports_openclaw_projects, {
|
|
|
5245
5786
|
getCachedOpenClawSessions: () => getCachedOpenClawSessions,
|
|
5246
5787
|
getCachedOpenClawProjects: () => getCachedOpenClawProjects
|
|
5247
5788
|
});
|
|
5248
|
-
import { readFileSync as
|
|
5249
|
-
import { join as
|
|
5789
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
5790
|
+
import { join as join13 } from "node:path";
|
|
5250
5791
|
function str(v) {
|
|
5251
5792
|
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
5252
5793
|
}
|
|
5253
5794
|
function readSessionsIndex(agentId) {
|
|
5254
5795
|
const out = new Map;
|
|
5255
|
-
const indexPath =
|
|
5796
|
+
const indexPath = join13(openclawHome(), "agents", agentId, "sessions", "sessions.json");
|
|
5256
5797
|
let raw;
|
|
5257
5798
|
try {
|
|
5258
|
-
raw = JSON.parse(
|
|
5799
|
+
raw = JSON.parse(readFileSync5(indexPath, "utf-8"));
|
|
5259
5800
|
} catch {
|
|
5260
5801
|
return out;
|
|
5261
5802
|
}
|
|
@@ -5406,15 +5947,15 @@ var init_openclaw_projects = __esm(() => {
|
|
|
5406
5947
|
});
|
|
5407
5948
|
|
|
5408
5949
|
// lib/factory-sessions.ts
|
|
5409
|
-
import { readFile as
|
|
5410
|
-
import { readdirSync as readdirSync5, statSync as
|
|
5411
|
-
import { homedir as
|
|
5412
|
-
import { join as
|
|
5950
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
5951
|
+
import { readdirSync as readdirSync5, statSync as statSync7 } from "node:fs";
|
|
5952
|
+
import { homedir as homedir11 } from "node:os";
|
|
5953
|
+
import { join as join14 } from "node:path";
|
|
5413
5954
|
function factoryHome() {
|
|
5414
|
-
return process.env.FACTORY_HOME ||
|
|
5955
|
+
return process.env.FACTORY_HOME || join14(homedir11(), ".factory");
|
|
5415
5956
|
}
|
|
5416
5957
|
function factorySessionsRoot() {
|
|
5417
|
-
return
|
|
5958
|
+
return join14(factoryHome(), "sessions");
|
|
5418
5959
|
}
|
|
5419
5960
|
function isPlainObject3(v) {
|
|
5420
5961
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
@@ -5560,7 +6101,7 @@ function listFactoryTranscripts() {
|
|
|
5560
6101
|
for (const dir of projectDirs) {
|
|
5561
6102
|
const projectName = dir.name;
|
|
5562
6103
|
const cwd = decodeFolderName(projectName);
|
|
5563
|
-
const projectPath =
|
|
6104
|
+
const projectPath = join14(root, projectName);
|
|
5564
6105
|
let files;
|
|
5565
6106
|
try {
|
|
5566
6107
|
files = readdirSync5(projectPath);
|
|
@@ -5573,9 +6114,9 @@ function listFactoryTranscripts() {
|
|
|
5573
6114
|
const sessionId = file.slice(0, -".jsonl".length);
|
|
5574
6115
|
if (!FACTORY_SESSION_ID_RE.test(sessionId))
|
|
5575
6116
|
continue;
|
|
5576
|
-
const transcriptPath =
|
|
6117
|
+
const transcriptPath = join14(projectPath, file);
|
|
5577
6118
|
try {
|
|
5578
|
-
const st =
|
|
6119
|
+
const st = statSync7(transcriptPath);
|
|
5579
6120
|
out.push({ projectName, cwd, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
|
|
5580
6121
|
} catch {}
|
|
5581
6122
|
}
|
|
@@ -5597,7 +6138,7 @@ async function getFactorySessionLog(sessionId) {
|
|
|
5597
6138
|
return null;
|
|
5598
6139
|
let content;
|
|
5599
6140
|
try {
|
|
5600
|
-
content = await
|
|
6141
|
+
content = await readFile8(filePath, "utf-8");
|
|
5601
6142
|
} catch {
|
|
5602
6143
|
return null;
|
|
5603
6144
|
}
|
|
@@ -5699,13 +6240,13 @@ var init_factory_projects = __esm(() => {
|
|
|
5699
6240
|
});
|
|
5700
6241
|
|
|
5701
6242
|
// lib/devin-sessions.ts
|
|
5702
|
-
import { homedir as
|
|
5703
|
-
import { join as
|
|
6243
|
+
import { homedir as homedir12 } from "node:os";
|
|
6244
|
+
import { join as join15 } from "node:path";
|
|
5704
6245
|
function devinHome() {
|
|
5705
|
-
return process.env.DEVIN_HOME ||
|
|
6246
|
+
return process.env.DEVIN_HOME || join15(homedir12(), ".local", "share", "devin");
|
|
5706
6247
|
}
|
|
5707
6248
|
function devinDbPath() {
|
|
5708
|
-
return process.env.DEVIN_DB_PATH ||
|
|
6249
|
+
return process.env.DEVIN_DB_PATH || join15(devinHome(), "cli", "sessions.db");
|
|
5709
6250
|
}
|
|
5710
6251
|
function devinEpochToMs(value) {
|
|
5711
6252
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -6001,18 +6542,18 @@ var init_devin_projects = __esm(() => {
|
|
|
6001
6542
|
});
|
|
6002
6543
|
|
|
6003
6544
|
// lib/antigravity-sessions.ts
|
|
6004
|
-
import { readFile as
|
|
6005
|
-
import { readdirSync as readdirSync6, statSync as
|
|
6006
|
-
import { homedir as
|
|
6007
|
-
import { join as
|
|
6545
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
6546
|
+
import { readdirSync as readdirSync6, statSync as statSync8 } from "node:fs";
|
|
6547
|
+
import { homedir as homedir13 } from "node:os";
|
|
6548
|
+
import { join as join16 } from "node:path";
|
|
6008
6549
|
function antigravityHome() {
|
|
6009
|
-
return process.env.ANTIGRAVITY_HOME ||
|
|
6550
|
+
return process.env.ANTIGRAVITY_HOME || join16(homedir13(), ".gemini", "antigravity-cli");
|
|
6010
6551
|
}
|
|
6011
6552
|
function antigravityBrainRoot() {
|
|
6012
|
-
return
|
|
6553
|
+
return join16(antigravityHome(), "brain");
|
|
6013
6554
|
}
|
|
6014
6555
|
function antigravityTranscriptRelPath() {
|
|
6015
|
-
return
|
|
6556
|
+
return join16(".system_generated", "logs", "transcript_full.jsonl");
|
|
6016
6557
|
}
|
|
6017
6558
|
function isPlainObject5(v) {
|
|
6018
6559
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
@@ -6135,9 +6676,9 @@ function listAntigravityTranscripts() {
|
|
|
6135
6676
|
const conversationId = dir.name;
|
|
6136
6677
|
if (!ANTIGRAVITY_CONVERSATION_ID_RE.test(conversationId))
|
|
6137
6678
|
continue;
|
|
6138
|
-
const transcriptPath =
|
|
6679
|
+
const transcriptPath = join16(root, conversationId, rel);
|
|
6139
6680
|
try {
|
|
6140
|
-
const st =
|
|
6681
|
+
const st = statSync8(transcriptPath);
|
|
6141
6682
|
if (!st.isFile())
|
|
6142
6683
|
continue;
|
|
6143
6684
|
out.push({ conversationId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
|
|
@@ -6148,9 +6689,9 @@ function listAntigravityTranscripts() {
|
|
|
6148
6689
|
function findAntigravityTranscript(conversationId) {
|
|
6149
6690
|
if (!ANTIGRAVITY_CONVERSATION_ID_RE.test(conversationId))
|
|
6150
6691
|
return null;
|
|
6151
|
-
const transcriptPath =
|
|
6692
|
+
const transcriptPath = join16(antigravityBrainRoot(), conversationId, antigravityTranscriptRelPath());
|
|
6152
6693
|
try {
|
|
6153
|
-
if (
|
|
6694
|
+
if (statSync8(transcriptPath).isFile())
|
|
6154
6695
|
return transcriptPath;
|
|
6155
6696
|
} catch {}
|
|
6156
6697
|
return null;
|
|
@@ -6161,7 +6702,7 @@ async function getAntigravitySessionLog(conversationId) {
|
|
|
6161
6702
|
return null;
|
|
6162
6703
|
let content;
|
|
6163
6704
|
try {
|
|
6164
|
-
content = await
|
|
6705
|
+
content = await readFile9(filePath, "utf-8");
|
|
6165
6706
|
} catch {
|
|
6166
6707
|
return null;
|
|
6167
6708
|
}
|
|
@@ -6188,9 +6729,9 @@ __export(exports_antigravity_projects, {
|
|
|
6188
6729
|
getAntigravityIndex: () => getAntigravityIndex,
|
|
6189
6730
|
antigravitySummariesDbPath: () => antigravitySummariesDbPath
|
|
6190
6731
|
});
|
|
6191
|
-
import { join as
|
|
6732
|
+
import { join as join17 } from "node:path";
|
|
6192
6733
|
function antigravitySummariesDbPath() {
|
|
6193
|
-
return
|
|
6734
|
+
return join17(antigravityHome(), "conversation_summaries.db");
|
|
6194
6735
|
}
|
|
6195
6736
|
function parseWorkspaceCwd(raw) {
|
|
6196
6737
|
if (!raw)
|
|
@@ -6333,16 +6874,16 @@ var init_antigravity_projects = __esm(() => {
|
|
|
6333
6874
|
});
|
|
6334
6875
|
|
|
6335
6876
|
// lib/goose-sessions.ts
|
|
6336
|
-
import { homedir as
|
|
6337
|
-
import { join as
|
|
6877
|
+
import { homedir as homedir14 } from "node:os";
|
|
6878
|
+
import { join as join18 } from "node:path";
|
|
6338
6879
|
function gooseHome() {
|
|
6339
6880
|
if (process.env.GOOSE_HOME)
|
|
6340
6881
|
return process.env.GOOSE_HOME;
|
|
6341
6882
|
const xdg = process.env.XDG_DATA_HOME;
|
|
6342
|
-
return
|
|
6883
|
+
return join18(xdg && xdg.length > 0 ? xdg : join18(homedir14(), ".local", "share"), "goose");
|
|
6343
6884
|
}
|
|
6344
6885
|
function gooseDbPath() {
|
|
6345
|
-
return process.env.GOOSE_DB_PATH ||
|
|
6886
|
+
return process.env.GOOSE_DB_PATH || join18(gooseHome(), "sessions", "sessions.db");
|
|
6346
6887
|
}
|
|
6347
6888
|
function gooseTimestampToMs(value) {
|
|
6348
6889
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -6609,11 +7150,11 @@ var init_goose_projects = __esm(() => {
|
|
|
6609
7150
|
});
|
|
6610
7151
|
|
|
6611
7152
|
// lib/projects.ts
|
|
6612
|
-
import { readdir as readdir5, stat as
|
|
6613
|
-
import { join as
|
|
7153
|
+
import { readdir as readdir5, stat as stat4 } from "node:fs/promises";
|
|
7154
|
+
import { join as join19, resolve as resolve7, sep } from "node:path";
|
|
6614
7155
|
async function getMtime(path2, label) {
|
|
6615
7156
|
try {
|
|
6616
|
-
return (await
|
|
7157
|
+
return (await stat4(path2)).mtime;
|
|
6617
7158
|
} catch (error) {
|
|
6618
7159
|
logWarn(`Failed to stat ${label}:`, error);
|
|
6619
7160
|
return new Date(0);
|
|
@@ -6621,7 +7162,7 @@ async function getMtime(path2, label) {
|
|
|
6621
7162
|
}
|
|
6622
7163
|
async function safeReaddir5(dirPath) {
|
|
6623
7164
|
try {
|
|
6624
|
-
const s = await
|
|
7165
|
+
const s = await stat4(dirPath);
|
|
6625
7166
|
if (!s.isDirectory())
|
|
6626
7167
|
return null;
|
|
6627
7168
|
return await readdir5(dirPath, { withFileTypes: true });
|
|
@@ -6636,7 +7177,7 @@ async function getClaudeProjectFolders() {
|
|
|
6636
7177
|
if (!entries)
|
|
6637
7178
|
return [];
|
|
6638
7179
|
const settled = await batchAll(entries.filter((entry) => entry.isDirectory()).map((entry) => async () => {
|
|
6639
|
-
const folderPath =
|
|
7180
|
+
const folderPath = join19(projectsPath, entry.name);
|
|
6640
7181
|
const mtime = await getMtime(folderPath, entry.name);
|
|
6641
7182
|
return {
|
|
6642
7183
|
name: entry.name,
|
|
@@ -6762,8 +7303,8 @@ function resolveProjectPath(projectName) {
|
|
|
6762
7303
|
throw new RangeError("Empty project name");
|
|
6763
7304
|
if (/^[/\\]/.test(projectName))
|
|
6764
7305
|
throw new RangeError("Absolute project name");
|
|
6765
|
-
const projectsPath =
|
|
6766
|
-
const candidate =
|
|
7306
|
+
const projectsPath = resolve7(getClaudeProjectsPath());
|
|
7307
|
+
const candidate = resolve7(join19(projectsPath, projectName));
|
|
6767
7308
|
if (!candidate.startsWith(projectsPath + sep)) {
|
|
6768
7309
|
throw new RangeError("Project path escapes root");
|
|
6769
7310
|
}
|
|
@@ -6781,7 +7322,7 @@ async function getSessionFiles(projectPath) {
|
|
|
6781
7322
|
return [];
|
|
6782
7323
|
const jsonlEntries = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl") && extractSessionId2(entry.name));
|
|
6783
7324
|
const settled = await batchAll(jsonlEntries.map((entry) => async () => {
|
|
6784
|
-
const filePath =
|
|
7325
|
+
const filePath = join19(projectPath, entry.name);
|
|
6785
7326
|
const mtime = await getMtime(filePath, entry.name);
|
|
6786
7327
|
return {
|
|
6787
7328
|
name: entry.name,
|
|
@@ -6810,13 +7351,13 @@ var init_projects = __esm(() => {
|
|
|
6810
7351
|
|
|
6811
7352
|
// lib/resolve-subagent-path.ts
|
|
6812
7353
|
import { access as access2 } from "fs/promises";
|
|
6813
|
-
import { join as
|
|
7354
|
+
import { join as join20, relative as relative2 } from "path";
|
|
6814
7355
|
async function resolveSubagentPath(projectsPath, projectName, sessionId, agentId) {
|
|
6815
7356
|
const fileName = `agent-${agentId}.jsonl`;
|
|
6816
7357
|
const candidatePaths = [
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
7358
|
+
join20(projectsPath, projectName, fileName),
|
|
7359
|
+
join20(projectsPath, projectName, sessionId, fileName),
|
|
7360
|
+
join20(projectsPath, projectName, sessionId, "subagents", fileName)
|
|
6820
7361
|
];
|
|
6821
7362
|
for (const candidatePath of candidatePaths) {
|
|
6822
7363
|
if (relative2(projectsPath, candidatePath).startsWith("..")) {
|
|
@@ -6837,8 +7378,8 @@ async function resolveSubagentPath(projectsPath, projectName, sessionId, agentId
|
|
|
6837
7378
|
var init_resolve_subagent_path = () => {};
|
|
6838
7379
|
|
|
6839
7380
|
// lib/log-entries.ts
|
|
6840
|
-
import { readFile as
|
|
6841
|
-
import { join as
|
|
7381
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
7382
|
+
import { join as join21 } from "path";
|
|
6842
7383
|
function formatTimestamp(date) {
|
|
6843
7384
|
const base = formatDate(date);
|
|
6844
7385
|
const ms = date.getMilliseconds().toString().padStart(3, "0");
|
|
@@ -7032,8 +7573,8 @@ async function parseFileContent(fileContent, source) {
|
|
|
7032
7573
|
async function parseSessionLog(projectName, sessionId) {
|
|
7033
7574
|
const projectDir = resolveProjectPath(projectName);
|
|
7034
7575
|
const projectsPath = getClaudeProjectsPath();
|
|
7035
|
-
const filePath =
|
|
7036
|
-
const fileContent = await
|
|
7576
|
+
const filePath = join21(projectDir, `${sessionId}.jsonl`);
|
|
7577
|
+
const fileContent = await readFile10(filePath, "utf-8");
|
|
7037
7578
|
const { entries: sessionEntries, rawLines: sessionRawLines, subagentIds } = await parseFileContent(fileContent, "session");
|
|
7038
7579
|
if (subagentIds.length === 0) {
|
|
7039
7580
|
return { entries: sessionEntries, rawLines: sessionRawLines, subagentIds: [] };
|
|
@@ -7043,7 +7584,7 @@ async function parseSessionLog(projectName, sessionId) {
|
|
|
7043
7584
|
const agentPath = await resolveSubagentPath(projectsPath, projectName, sessionId, agentId);
|
|
7044
7585
|
if (!agentPath)
|
|
7045
7586
|
return null;
|
|
7046
|
-
const agentContent = await
|
|
7587
|
+
const agentContent = await readFile10(agentPath, "utf-8");
|
|
7047
7588
|
const { entries, rawLines } = await parseFileContent(agentContent, agentSource);
|
|
7048
7589
|
return { entries, rawLines };
|
|
7049
7590
|
}), SUBAGENT_CONCURRENCY);
|
|
@@ -7069,32 +7610,32 @@ var init_log_entries = __esm(() => {
|
|
|
7069
7610
|
});
|
|
7070
7611
|
|
|
7071
7612
|
// lib/codex-sessions.ts
|
|
7072
|
-
import { readFileSync as
|
|
7073
|
-
import { readFile as
|
|
7074
|
-
import { dirname as dirname4, join as
|
|
7075
|
-
import { homedir as
|
|
7613
|
+
import { readFileSync as readFileSync6, readdirSync as readdirSync7, existsSync as existsSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, statSync as statSync9 } from "node:fs";
|
|
7614
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
7615
|
+
import { dirname as dirname4, join as join22 } from "node:path";
|
|
7616
|
+
import { homedir as homedir15 } from "node:os";
|
|
7076
7617
|
function readCache() {
|
|
7077
7618
|
try {
|
|
7078
|
-
if (!
|
|
7619
|
+
if (!existsSync7(CACHE_PATH))
|
|
7079
7620
|
return {};
|
|
7080
|
-
return JSON.parse(
|
|
7621
|
+
return JSON.parse(readFileSync6(CACHE_PATH, "utf-8"));
|
|
7081
7622
|
} catch {
|
|
7082
7623
|
return {};
|
|
7083
7624
|
}
|
|
7084
7625
|
}
|
|
7085
7626
|
function writeCacheEntry(sessionId, path2) {
|
|
7086
7627
|
try {
|
|
7087
|
-
|
|
7628
|
+
mkdirSync5(dirname4(CACHE_PATH), { recursive: true });
|
|
7088
7629
|
const cache = readCache();
|
|
7089
7630
|
cache[sessionId] = path2;
|
|
7090
|
-
|
|
7631
|
+
writeFileSync4(CACHE_PATH, JSON.stringify(cache), "utf-8");
|
|
7091
7632
|
} catch {}
|
|
7092
7633
|
}
|
|
7093
7634
|
function dirSearch(dir, sessionId) {
|
|
7094
7635
|
try {
|
|
7095
7636
|
for (const f of readdirSync7(dir, { withFileTypes: true })) {
|
|
7096
7637
|
if (f.isFile() && f.name.includes(sessionId) && f.name.endsWith(".jsonl")) {
|
|
7097
|
-
return
|
|
7638
|
+
return join22(dir, f.name);
|
|
7098
7639
|
}
|
|
7099
7640
|
}
|
|
7100
7641
|
} catch {}
|
|
@@ -7103,16 +7644,16 @@ function dirSearch(dir, sessionId) {
|
|
|
7103
7644
|
function findCodexTranscript(sessionId) {
|
|
7104
7645
|
const cache = readCache();
|
|
7105
7646
|
const cached = cache[sessionId];
|
|
7106
|
-
if (cached &&
|
|
7647
|
+
if (cached && existsSync7(cached))
|
|
7107
7648
|
return cached;
|
|
7108
|
-
const root =
|
|
7649
|
+
const root = join22(homedir15(), ".codex", "sessions");
|
|
7109
7650
|
const today = new Date;
|
|
7110
7651
|
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
|
|
7111
7652
|
const datedDirs = [today, yesterday].map((d) => {
|
|
7112
7653
|
const y = String(d.getUTCFullYear());
|
|
7113
7654
|
const m = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
7114
7655
|
const day = String(d.getUTCDate()).padStart(2, "0");
|
|
7115
|
-
return
|
|
7656
|
+
return join22(root, y, m, day);
|
|
7116
7657
|
});
|
|
7117
7658
|
for (const dir of datedDirs) {
|
|
7118
7659
|
const hit = dirSearch(dir, sessionId);
|
|
@@ -7125,13 +7666,13 @@ function findCodexTranscript(sessionId) {
|
|
|
7125
7666
|
for (const y of readdirSync7(root, { withFileTypes: true })) {
|
|
7126
7667
|
if (!y.isDirectory())
|
|
7127
7668
|
continue;
|
|
7128
|
-
for (const m of readdirSync7(
|
|
7669
|
+
for (const m of readdirSync7(join22(root, y.name), { withFileTypes: true })) {
|
|
7129
7670
|
if (!m.isDirectory())
|
|
7130
7671
|
continue;
|
|
7131
|
-
for (const d of readdirSync7(
|
|
7672
|
+
for (const d of readdirSync7(join22(root, y.name, m.name), { withFileTypes: true })) {
|
|
7132
7673
|
if (!d.isDirectory())
|
|
7133
7674
|
continue;
|
|
7134
|
-
const hit = dirSearch(
|
|
7675
|
+
const hit = dirSearch(join22(root, y.name, m.name, d.name), sessionId);
|
|
7135
7676
|
if (hit) {
|
|
7136
7677
|
writeCacheEntry(sessionId, hit);
|
|
7137
7678
|
return hit;
|
|
@@ -7344,19 +7885,20 @@ async function getCodexSessionLog(sessionId) {
|
|
|
7344
7885
|
const filePath = findCodexTranscript(sessionId);
|
|
7345
7886
|
if (!filePath)
|
|
7346
7887
|
return null;
|
|
7347
|
-
const fileContent = await
|
|
7888
|
+
const fileContent = await readFile11(filePath, "utf-8");
|
|
7348
7889
|
const { entries, rawLines, cwd } = await parseCodexLog(fileContent, "session");
|
|
7349
7890
|
return { entries, rawLines, cwd, filePath };
|
|
7350
7891
|
}
|
|
7351
7892
|
var CACHE_PATH, getCachedCodexSessionLog;
|
|
7352
7893
|
var init_codex_sessions = __esm(() => {
|
|
7353
7894
|
init_log_entries();
|
|
7354
|
-
|
|
7895
|
+
init_fp_home();
|
|
7896
|
+
CACHE_PATH = codexSessionPathsFile();
|
|
7355
7897
|
getCachedCodexSessionLog = runtimeCache((sessionId) => getCodexSessionLog(sessionId), 60, { maxSize: 50 });
|
|
7356
7898
|
});
|
|
7357
7899
|
|
|
7358
7900
|
// src/hooks/resolve-permission-mode.ts
|
|
7359
|
-
import { readFileSync as
|
|
7901
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
7360
7902
|
function resolvePermissionMode(integration, parsed, sessionId) {
|
|
7361
7903
|
if (integration === "claude") {
|
|
7362
7904
|
return parsed.permission_mode ?? "default";
|
|
@@ -7371,7 +7913,7 @@ function resolveCodexMode(sessionId) {
|
|
|
7371
7913
|
const path2 = findCodexTranscript(sessionId);
|
|
7372
7914
|
if (!path2)
|
|
7373
7915
|
return;
|
|
7374
|
-
for (const line of
|
|
7916
|
+
for (const line of readFileSync7(path2, "utf-8").split(`
|
|
7375
7917
|
`)) {
|
|
7376
7918
|
if (!line.includes("turn_context"))
|
|
7377
7919
|
continue;
|
|
@@ -7396,21 +7938,21 @@ var init_resolve_permission_mode = __esm(() => {
|
|
|
7396
7938
|
});
|
|
7397
7939
|
|
|
7398
7940
|
// lib/copilot-sessions.ts
|
|
7399
|
-
import { readFileSync as
|
|
7400
|
-
import { readFile as
|
|
7401
|
-
import { join as
|
|
7402
|
-
import { homedir as
|
|
7941
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync8, existsSync as existsSync8, statSync as statSync10 } from "node:fs";
|
|
7942
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
7943
|
+
import { join as join23, resolve as resolve8, sep as sep2 } from "node:path";
|
|
7944
|
+
import { homedir as homedir16 } from "node:os";
|
|
7403
7945
|
function getCopilotHome() {
|
|
7404
|
-
return process.env.COPILOT_HOME ||
|
|
7946
|
+
return process.env.COPILOT_HOME || join23(homedir16(), ".copilot");
|
|
7405
7947
|
}
|
|
7406
7948
|
function getCopilotSessionStateRoot2() {
|
|
7407
|
-
return
|
|
7949
|
+
return join23(getCopilotHome(), "session-state");
|
|
7408
7950
|
}
|
|
7409
7951
|
function getCopilotSessionDir(sessionId) {
|
|
7410
7952
|
if (!sessionId)
|
|
7411
7953
|
return null;
|
|
7412
|
-
const root =
|
|
7413
|
-
const candidate =
|
|
7954
|
+
const root = resolve8(getCopilotSessionStateRoot2());
|
|
7955
|
+
const candidate = resolve8(root, sessionId);
|
|
7414
7956
|
if (candidate === root || !candidate.startsWith(`${root}${sep2}`))
|
|
7415
7957
|
return null;
|
|
7416
7958
|
return candidate;
|
|
@@ -7419,23 +7961,23 @@ function resolveSessionFile(sessionId, filename) {
|
|
|
7419
7961
|
const dir = getCopilotSessionDir(sessionId);
|
|
7420
7962
|
if (!dir)
|
|
7421
7963
|
return null;
|
|
7422
|
-
return
|
|
7964
|
+
return join23(dir, filename);
|
|
7423
7965
|
}
|
|
7424
7966
|
function findCopilotTranscript(sessionId) {
|
|
7425
7967
|
const candidate = resolveSessionFile(sessionId, "events.jsonl");
|
|
7426
7968
|
if (!candidate)
|
|
7427
7969
|
return null;
|
|
7428
|
-
return
|
|
7970
|
+
return existsSync8(candidate) ? candidate : null;
|
|
7429
7971
|
}
|
|
7430
7972
|
function findCopilotWorkspace(sessionId) {
|
|
7431
7973
|
const candidate = resolveSessionFile(sessionId, "workspace.yaml");
|
|
7432
7974
|
if (!candidate)
|
|
7433
7975
|
return null;
|
|
7434
|
-
return
|
|
7976
|
+
return existsSync8(candidate) ? candidate : null;
|
|
7435
7977
|
}
|
|
7436
7978
|
function readYamlScalar(path2, key) {
|
|
7437
7979
|
try {
|
|
7438
|
-
const text =
|
|
7980
|
+
const text = readFileSync8(path2, "utf-8");
|
|
7439
7981
|
const re = new RegExp(`^${key}\\s*:\\s*(.+?)\\s*$`, "m");
|
|
7440
7982
|
const m = text.match(re);
|
|
7441
7983
|
if (!m)
|
|
@@ -7598,7 +8140,7 @@ async function getCopilotSessionLog(sessionId) {
|
|
|
7598
8140
|
return null;
|
|
7599
8141
|
let fileContent;
|
|
7600
8142
|
try {
|
|
7601
|
-
fileContent = await
|
|
8143
|
+
fileContent = await readFile12(filePath, "utf-8");
|
|
7602
8144
|
} catch {
|
|
7603
8145
|
return null;
|
|
7604
8146
|
}
|
|
@@ -7613,18 +8155,18 @@ var init_copilot_sessions = __esm(() => {
|
|
|
7613
8155
|
});
|
|
7614
8156
|
|
|
7615
8157
|
// lib/cursor-sessions.ts
|
|
7616
|
-
import { readFileSync as
|
|
7617
|
-
import { readFile as
|
|
7618
|
-
import { basename as basename3, join as
|
|
7619
|
-
import { homedir as
|
|
8158
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync9, existsSync as existsSync9, statSync as statSync11 } from "node:fs";
|
|
8159
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
8160
|
+
import { basename as basename3, join as join24, resolve as resolve9, sep as sep3 } from "node:path";
|
|
8161
|
+
import { homedir as homedir17 } from "node:os";
|
|
7620
8162
|
function getCursorHome2() {
|
|
7621
|
-
return process.env.CURSOR_HOME ||
|
|
8163
|
+
return process.env.CURSOR_HOME || join24(homedir17(), ".cursor");
|
|
7622
8164
|
}
|
|
7623
8165
|
function getCursorSessionDir(sessionId) {
|
|
7624
8166
|
if (!sessionId)
|
|
7625
8167
|
return null;
|
|
7626
|
-
const home =
|
|
7627
|
-
const projectsRoot =
|
|
8168
|
+
const home = resolve9(getCursorHome2());
|
|
8169
|
+
const projectsRoot = resolve9(home, NEW_PROJECTS_DIR2);
|
|
7628
8170
|
let projectEntries = [];
|
|
7629
8171
|
try {
|
|
7630
8172
|
projectEntries = readdirSync9(projectsRoot, { withFileTypes: true });
|
|
@@ -7632,19 +8174,19 @@ function getCursorSessionDir(sessionId) {
|
|
|
7632
8174
|
for (const entry of projectEntries) {
|
|
7633
8175
|
if (!entry.isDirectory())
|
|
7634
8176
|
continue;
|
|
7635
|
-
const candidate =
|
|
7636
|
-
const transcriptRoot =
|
|
8177
|
+
const candidate = resolve9(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2, sessionId);
|
|
8178
|
+
const transcriptRoot = resolve9(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2);
|
|
7637
8179
|
if (candidate === transcriptRoot || !candidate.startsWith(`${transcriptRoot}${sep3}`))
|
|
7638
8180
|
continue;
|
|
7639
|
-
if (
|
|
8181
|
+
if (existsSync9(candidate))
|
|
7640
8182
|
return candidate;
|
|
7641
8183
|
}
|
|
7642
8184
|
for (const sub of LEGACY_SESSION_ROOT_CANDIDATES2) {
|
|
7643
|
-
const root =
|
|
7644
|
-
const candidate =
|
|
8185
|
+
const root = resolve9(home, sub);
|
|
8186
|
+
const candidate = resolve9(root, sessionId);
|
|
7645
8187
|
if (candidate === root || !candidate.startsWith(`${root}${sep3}`))
|
|
7646
8188
|
continue;
|
|
7647
|
-
if (
|
|
8189
|
+
if (existsSync9(candidate))
|
|
7648
8190
|
return candidate;
|
|
7649
8191
|
}
|
|
7650
8192
|
return null;
|
|
@@ -7653,12 +8195,12 @@ function findCursorTranscript(sessionId) {
|
|
|
7653
8195
|
const dir = getCursorSessionDir(sessionId);
|
|
7654
8196
|
if (!dir)
|
|
7655
8197
|
return null;
|
|
7656
|
-
const newCandidate =
|
|
7657
|
-
if (
|
|
8198
|
+
const newCandidate = join24(dir, `${basename3(dir)}.jsonl`);
|
|
8199
|
+
if (existsSync9(newCandidate))
|
|
7658
8200
|
return newCandidate;
|
|
7659
8201
|
for (const name of LEGACY_TRANSCRIPT_FILE_CANDIDATES2) {
|
|
7660
|
-
const candidate =
|
|
7661
|
-
if (
|
|
8202
|
+
const candidate = join24(dir, name);
|
|
8203
|
+
if (existsSync9(candidate))
|
|
7662
8204
|
return candidate;
|
|
7663
8205
|
}
|
|
7664
8206
|
return null;
|
|
@@ -7853,7 +8395,7 @@ async function getCursorSessionLog(sessionId) {
|
|
|
7853
8395
|
return null;
|
|
7854
8396
|
let fileContent;
|
|
7855
8397
|
try {
|
|
7856
|
-
fileContent = await
|
|
8398
|
+
fileContent = await readFile13(filePath, "utf-8");
|
|
7857
8399
|
} catch {
|
|
7858
8400
|
return null;
|
|
7859
8401
|
}
|
|
@@ -7869,12 +8411,12 @@ var init_cursor_sessions = __esm(() => {
|
|
|
7869
8411
|
});
|
|
7870
8412
|
|
|
7871
8413
|
// lib/pi-sessions.ts
|
|
7872
|
-
import { readFileSync as
|
|
7873
|
-
import { readFile as
|
|
7874
|
-
import { join as
|
|
7875
|
-
import { homedir as
|
|
8414
|
+
import { readFileSync as readFileSync10, readdirSync as readdirSync10, existsSync as existsSync10, statSync as statSync12 } from "node:fs";
|
|
8415
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
8416
|
+
import { join as join25, resolve as resolve10, sep as sep4 } from "node:path";
|
|
8417
|
+
import { homedir as homedir18 } from "node:os";
|
|
7876
8418
|
function getPiSessionStateRoot() {
|
|
7877
|
-
return process.env.PI_SESSIONS_DIR ||
|
|
8419
|
+
return process.env.PI_SESSIONS_DIR || join25(homedir18(), ".pi", "agent", "sessions");
|
|
7878
8420
|
}
|
|
7879
8421
|
function isSafeSessionId(sessionId) {
|
|
7880
8422
|
return UUID_RE.test(sessionId);
|
|
@@ -7882,7 +8424,7 @@ function isSafeSessionId(sessionId) {
|
|
|
7882
8424
|
function findPiTranscript(sessionId) {
|
|
7883
8425
|
if (!isSafeSessionId(sessionId))
|
|
7884
8426
|
return null;
|
|
7885
|
-
const root =
|
|
8427
|
+
const root = resolve10(getPiSessionStateRoot());
|
|
7886
8428
|
let cwdDirs;
|
|
7887
8429
|
try {
|
|
7888
8430
|
cwdDirs = readdirSync10(root);
|
|
@@ -7890,7 +8432,7 @@ function findPiTranscript(sessionId) {
|
|
|
7890
8432
|
return null;
|
|
7891
8433
|
}
|
|
7892
8434
|
for (const cwdDir of cwdDirs) {
|
|
7893
|
-
const cwdPath =
|
|
8435
|
+
const cwdPath = resolve10(root, cwdDir);
|
|
7894
8436
|
if (!cwdPath.startsWith(`${root}${sep4}`))
|
|
7895
8437
|
continue;
|
|
7896
8438
|
let files;
|
|
@@ -7903,10 +8445,10 @@ function findPiTranscript(sessionId) {
|
|
|
7903
8445
|
const m = SESSION_FILE_RE2.exec(f);
|
|
7904
8446
|
if (!m || m[1].toLowerCase() !== sessionId.toLowerCase())
|
|
7905
8447
|
continue;
|
|
7906
|
-
const candidate =
|
|
8448
|
+
const candidate = resolve10(cwdPath, f);
|
|
7907
8449
|
if (!candidate.startsWith(`${cwdPath}${sep4}`))
|
|
7908
8450
|
continue;
|
|
7909
|
-
if (
|
|
8451
|
+
if (existsSync10(candidate))
|
|
7910
8452
|
return candidate;
|
|
7911
8453
|
}
|
|
7912
8454
|
}
|
|
@@ -7924,7 +8466,7 @@ function extractMessageText(content) {
|
|
|
7924
8466
|
|
|
7925
8467
|
`);
|
|
7926
8468
|
}
|
|
7927
|
-
function buildAssistantContent(content) {
|
|
8469
|
+
function buildAssistantContent(content, onToolUse) {
|
|
7928
8470
|
if (!Array.isArray(content))
|
|
7929
8471
|
return [];
|
|
7930
8472
|
const blocks = [];
|
|
@@ -7935,6 +8477,14 @@ function buildAssistantContent(content) {
|
|
|
7935
8477
|
if (block?.type === "thinking" && typeof block.thinking === "string" && block.thinking.length > 0) {
|
|
7936
8478
|
blocks.push({ type: "text", text: `[thinking] ${block.thinking}` });
|
|
7937
8479
|
}
|
|
8480
|
+
if (block?.type === "toolCall") {
|
|
8481
|
+
const id = typeof block.id === "string" && block.id.length > 0 ? block.id : `${typeof block.name === "string" ? block.name : "tool"}-${blocks.length}`;
|
|
8482
|
+
const name = typeof block.name === "string" ? block.name : "tool";
|
|
8483
|
+
const input = block.arguments && typeof block.arguments === "object" && !Array.isArray(block.arguments) ? block.arguments : {};
|
|
8484
|
+
const toolUse = { type: "tool_use", id, name, input };
|
|
8485
|
+
blocks.push(toolUse);
|
|
8486
|
+
onToolUse?.(toolUse);
|
|
8487
|
+
}
|
|
7938
8488
|
}
|
|
7939
8489
|
return blocks;
|
|
7940
8490
|
}
|
|
@@ -7945,6 +8495,8 @@ async function parsePiLog(fileContent, source = "session") {
|
|
|
7945
8495
|
const rawLines = [];
|
|
7946
8496
|
let cwd;
|
|
7947
8497
|
let seenSessionStart = false;
|
|
8498
|
+
const toolUseById = new Map;
|
|
8499
|
+
const toolUseStartMs = new Map;
|
|
7948
8500
|
for (let i = 0;i < lines.length; i++) {
|
|
7949
8501
|
if (i > 0 && i % 200 === 0)
|
|
7950
8502
|
await new Promise((r) => setImmediate(r));
|
|
@@ -7992,7 +8544,10 @@ async function parsePiLog(fileContent, source = "session") {
|
|
|
7992
8544
|
continue;
|
|
7993
8545
|
}
|
|
7994
8546
|
if (role === "assistant") {
|
|
7995
|
-
const blocks = buildAssistantContent(content)
|
|
8547
|
+
const blocks = buildAssistantContent(content, (block) => {
|
|
8548
|
+
toolUseById.set(block.id, block);
|
|
8549
|
+
toolUseStartMs.set(block.id, date.getTime());
|
|
8550
|
+
});
|
|
7996
8551
|
if (blocks.length === 0) {
|
|
7997
8552
|
entries.push({
|
|
7998
8553
|
type: "system",
|
|
@@ -8008,6 +8563,22 @@ async function parsePiLog(fileContent, source = "session") {
|
|
|
8008
8563
|
});
|
|
8009
8564
|
continue;
|
|
8010
8565
|
}
|
|
8566
|
+
if (role === "toolResult") {
|
|
8567
|
+
const callId = raw.message.toolCallId;
|
|
8568
|
+
const block = typeof callId === "string" ? toolUseById.get(callId) : undefined;
|
|
8569
|
+
if (block) {
|
|
8570
|
+
const startMs = typeof callId === "string" && toolUseStartMs.get(callId) || date.getTime();
|
|
8571
|
+
const durationMs = Math.max(0, date.getTime() - startMs);
|
|
8572
|
+
block.result = {
|
|
8573
|
+
timestamp,
|
|
8574
|
+
timestampFormatted: formatTimestamp(date),
|
|
8575
|
+
content: extractMessageText(content),
|
|
8576
|
+
durationMs,
|
|
8577
|
+
durationFormatted: formatDuration(durationMs)
|
|
8578
|
+
};
|
|
8579
|
+
continue;
|
|
8580
|
+
}
|
|
8581
|
+
}
|
|
8011
8582
|
entries.push({
|
|
8012
8583
|
type: "system",
|
|
8013
8584
|
...baseEntry(rawCopy, timestamp, date, source),
|
|
@@ -8032,7 +8603,7 @@ async function getPiSessionLog(sessionId) {
|
|
|
8032
8603
|
return null;
|
|
8033
8604
|
let fileContent;
|
|
8034
8605
|
try {
|
|
8035
|
-
fileContent = await
|
|
8606
|
+
fileContent = await readFile14(filePath, "utf-8");
|
|
8036
8607
|
} catch {
|
|
8037
8608
|
return null;
|
|
8038
8609
|
}
|
|
@@ -8101,6 +8672,214 @@ var init_resolve_transcript_path = __esm(() => {
|
|
|
8101
8672
|
init_factory_sessions();
|
|
8102
8673
|
init_antigravity_sessions();
|
|
8103
8674
|
});
|
|
8675
|
+
// src/hooks/cloud-managed-policies.ts
|
|
8676
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
8677
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11, realpathSync, rmSync } from "node:fs";
|
|
8678
|
+
import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve11 } from "node:path";
|
|
8679
|
+
function cloudManagedPolicyRoot() {
|
|
8680
|
+
return process.env.FAILPROOFAI_CLOUD_POLICY_DIR ?? cloudPoliciesDir();
|
|
8681
|
+
}
|
|
8682
|
+
function parseManifest(value) {
|
|
8683
|
+
if (!value || typeof value !== "object")
|
|
8684
|
+
throw new Error("active manifest is not an object");
|
|
8685
|
+
const raw = value;
|
|
8686
|
+
if (!ACCEPTED_ACTIVE_SCHEMA_VERSIONS.includes(raw.schemaVersion)) {
|
|
8687
|
+
throw new Error(`unsupported active manifest schema ${String(raw.schemaVersion)} ` + `(supported: ${ACCEPTED_ACTIVE_SCHEMA_VERSIONS.join(", ")})`);
|
|
8688
|
+
}
|
|
8689
|
+
const deployment = raw.deployment ?? raw.generation;
|
|
8690
|
+
if (!Number.isSafeInteger(deployment) || (deployment ?? -1) < 0) {
|
|
8691
|
+
throw new Error("active manifest deployment is invalid");
|
|
8692
|
+
}
|
|
8693
|
+
if (!Array.isArray(raw.policies))
|
|
8694
|
+
throw new Error("active manifest policies is not an array");
|
|
8695
|
+
const policies = raw.policies.map((p) => {
|
|
8696
|
+
const entry = p;
|
|
8697
|
+
return { ...entry, version: entry.version ?? entry.revision };
|
|
8698
|
+
});
|
|
8699
|
+
return { schemaVersion: raw.schemaVersion, deployment, policies };
|
|
8700
|
+
}
|
|
8701
|
+
function resolveManagedPath(root, candidate) {
|
|
8702
|
+
if (!candidate || isAbsolute2(candidate))
|
|
8703
|
+
throw new Error(`unsafe managed policy path ${JSON.stringify(candidate)}`);
|
|
8704
|
+
const absolute = resolve11(root, candidate);
|
|
8705
|
+
const lexicalRelative = relative3(root, absolute);
|
|
8706
|
+
if (lexicalRelative.startsWith("..") || isAbsolute2(lexicalRelative)) {
|
|
8707
|
+
throw new Error(`managed policy path escapes its root: ${JSON.stringify(candidate)}`);
|
|
8708
|
+
}
|
|
8709
|
+
const realRoot = realpathSync(root);
|
|
8710
|
+
const realFile = realpathSync(absolute);
|
|
8711
|
+
const physicalRelative = relative3(realRoot, realFile);
|
|
8712
|
+
if (physicalRelative.startsWith("..") || isAbsolute2(physicalRelative)) {
|
|
8713
|
+
throw new Error(`managed policy symlink escapes its root: ${JSON.stringify(candidate)}`);
|
|
8714
|
+
}
|
|
8715
|
+
return realFile;
|
|
8716
|
+
}
|
|
8717
|
+
function readActiveCloudManagedPolicies() {
|
|
8718
|
+
const root = cloudManagedPolicyRoot();
|
|
8719
|
+
const activePath = resolve11(root, "active.json");
|
|
8720
|
+
if (!existsSync11(activePath))
|
|
8721
|
+
return [];
|
|
8722
|
+
let raw;
|
|
8723
|
+
try {
|
|
8724
|
+
raw = JSON.parse(readFileSync11(activePath, "utf8"));
|
|
8725
|
+
} catch (err) {
|
|
8726
|
+
throw new Error(`failed to read cloud-managed active manifest: ${err instanceof Error ? err.message : String(err)}`);
|
|
8727
|
+
}
|
|
8728
|
+
const manifest = parseManifest(raw);
|
|
8729
|
+
const seen = new Set;
|
|
8730
|
+
return manifest.policies.map((policy) => {
|
|
8731
|
+
if (!POLICY_ID_RE.test(policy.id) || policy.id === "." || policy.id === "..") {
|
|
8732
|
+
throw new Error(`unsafe cloud-managed policy id ${JSON.stringify(policy.id)}`);
|
|
8733
|
+
}
|
|
8734
|
+
if (!Number.isSafeInteger(policy.version) || policy.version < 0) {
|
|
8735
|
+
throw new Error(`invalid version for cloud-managed policy ${policy.id}`);
|
|
8736
|
+
}
|
|
8737
|
+
if (!SHA256_RE.test(policy.sha256)) {
|
|
8738
|
+
throw new Error(`invalid SHA-256 for cloud-managed policy ${policy.id}`);
|
|
8739
|
+
}
|
|
8740
|
+
if (seen.has(policy.id))
|
|
8741
|
+
throw new Error(`duplicate cloud-managed policy id ${policy.id}`);
|
|
8742
|
+
seen.add(policy.id);
|
|
8743
|
+
const path2 = resolveManagedPath(root, policy.path);
|
|
8744
|
+
const bytes = readFileSync11(path2);
|
|
8745
|
+
const actual = createHash3("sha256").update(bytes).digest("hex");
|
|
8746
|
+
if (actual !== policy.sha256) {
|
|
8747
|
+
throw new Error(`cloud-managed policy ${policy.id} failed integrity verification: expected ${policy.sha256}, got ${actual}`);
|
|
8748
|
+
}
|
|
8749
|
+
if (policy.effect !== undefined && policy.effect !== "enforce" && policy.effect !== "observe") {
|
|
8750
|
+
throw new Error(`unknown effect ${JSON.stringify(policy.effect)} for cloud-managed policy ${policy.id}`);
|
|
8751
|
+
}
|
|
8752
|
+
return {
|
|
8753
|
+
id: policy.id,
|
|
8754
|
+
version: policy.version,
|
|
8755
|
+
effect: policy.effect ?? "enforce",
|
|
8756
|
+
sha256: policy.sha256,
|
|
8757
|
+
path: path2,
|
|
8758
|
+
deployment: manifest.deployment
|
|
8759
|
+
};
|
|
8760
|
+
});
|
|
8761
|
+
}
|
|
8762
|
+
var ACCEPTED_ACTIVE_SCHEMA_VERSIONS, SHA256_RE, POLICY_ID_RE;
|
|
8763
|
+
var init_cloud_managed_policies = __esm(() => {
|
|
8764
|
+
init_fp_home();
|
|
8765
|
+
ACCEPTED_ACTIVE_SCHEMA_VERSIONS = [1, 2];
|
|
8766
|
+
SHA256_RE = /^[a-f0-9]{64}$/;
|
|
8767
|
+
POLICY_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
|
|
8768
|
+
});
|
|
8769
|
+
|
|
8770
|
+
// lib/atomic-write.ts
|
|
8771
|
+
var init_atomic_write = () => {};
|
|
8772
|
+
|
|
8773
|
+
// src/hooks/session-pause.ts
|
|
8774
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync11, readFileSync as readFileSync12, rmSync as rmSync2 } from "node:fs";
|
|
8775
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
8776
|
+
import { join as join26, resolve as resolve12 } from "node:path";
|
|
8777
|
+
function pauseStateDir() {
|
|
8778
|
+
const override = process.env.FAILPROOFAI_STATE_DIR;
|
|
8779
|
+
if (override)
|
|
8780
|
+
return resolve12(override, "sessions");
|
|
8781
|
+
return sessionPauseDir();
|
|
8782
|
+
}
|
|
8783
|
+
function pauseFilePath(sessionId) {
|
|
8784
|
+
const digest = createHash4("sha256").update(sessionId).digest("hex").slice(0, 32);
|
|
8785
|
+
return join26(pauseStateDir(), `${digest}.json`);
|
|
8786
|
+
}
|
|
8787
|
+
function parseStored(raw) {
|
|
8788
|
+
if (!raw || typeof raw !== "object")
|
|
8789
|
+
return null;
|
|
8790
|
+
const p = raw;
|
|
8791
|
+
if (p.schemaVersion !== SCHEMA_VERSION)
|
|
8792
|
+
return null;
|
|
8793
|
+
if (typeof p.sessionId !== "string" || p.sessionId.length === 0)
|
|
8794
|
+
return null;
|
|
8795
|
+
if (!Number.isFinite(p.pausedAt) || !Number.isFinite(p.expiresAt))
|
|
8796
|
+
return null;
|
|
8797
|
+
const pausedAt = p.pausedAt;
|
|
8798
|
+
const firstPausedAt = Number.isFinite(p.firstPausedAt) ? p.firstPausedAt : pausedAt;
|
|
8799
|
+
return {
|
|
8800
|
+
schemaVersion: SCHEMA_VERSION,
|
|
8801
|
+
sessionId: p.sessionId,
|
|
8802
|
+
pausedAt,
|
|
8803
|
+
firstPausedAt,
|
|
8804
|
+
expiresAt: Math.min(p.expiresAt, firstPausedAt + PAUSE_CEILING_MS),
|
|
8805
|
+
setBy: typeof p.setBy === "string" ? p.setBy : "unknown",
|
|
8806
|
+
...typeof p.cwd === "string" ? { cwd: p.cwd } : {}
|
|
8807
|
+
};
|
|
8808
|
+
}
|
|
8809
|
+
function readActivePause(sessionId, now = Date.now()) {
|
|
8810
|
+
if (!sessionId)
|
|
8811
|
+
return null;
|
|
8812
|
+
try {
|
|
8813
|
+
const path2 = pauseFilePath(sessionId);
|
|
8814
|
+
if (!existsSync12(path2))
|
|
8815
|
+
return null;
|
|
8816
|
+
const stored = parseStored(JSON.parse(readFileSync12(path2, "utf8")));
|
|
8817
|
+
if (!stored)
|
|
8818
|
+
return null;
|
|
8819
|
+
if (stored.expiresAt <= now)
|
|
8820
|
+
return null;
|
|
8821
|
+
const { schemaVersion: _ignored, ...pause } = stored;
|
|
8822
|
+
return pause;
|
|
8823
|
+
} catch {
|
|
8824
|
+
return null;
|
|
8825
|
+
}
|
|
8826
|
+
}
|
|
8827
|
+
var SCHEMA_VERSION = 1, PAUSE_DEFAULT_MS, PAUSE_CEILING_MS;
|
|
8828
|
+
var init_session_pause = __esm(() => {
|
|
8829
|
+
init_atomic_write();
|
|
8830
|
+
init_hook_activity_store();
|
|
8831
|
+
init_fp_home();
|
|
8832
|
+
PAUSE_DEFAULT_MS = 30 * 60000;
|
|
8833
|
+
PAUSE_CEILING_MS = 8 * 60 * 60000;
|
|
8834
|
+
});
|
|
8835
|
+
|
|
8836
|
+
// src/hooks/daemon-download.ts
|
|
8837
|
+
var init_daemon_download = __esm(() => {
|
|
8838
|
+
init_package();
|
|
8839
|
+
init_package();
|
|
8840
|
+
init_fp_home();
|
|
8841
|
+
});
|
|
8842
|
+
|
|
8843
|
+
// src/hooks/daemon-service.ts
|
|
8844
|
+
var init_daemon_service = __esm(() => {
|
|
8845
|
+
init_hook_logger();
|
|
8846
|
+
init_daemon_download();
|
|
8847
|
+
init_fp_home();
|
|
8848
|
+
init_package();
|
|
8849
|
+
init_fp_config();
|
|
8850
|
+
});
|
|
8851
|
+
|
|
8852
|
+
// src/hooks/fp-reset.ts
|
|
8853
|
+
import {
|
|
8854
|
+
copyFileSync,
|
|
8855
|
+
existsSync as existsSync13,
|
|
8856
|
+
mkdirSync as mkdirSync7,
|
|
8857
|
+
readFileSync as readFileSync13,
|
|
8858
|
+
readdirSync as readdirSync12,
|
|
8859
|
+
renameSync as renameSync3,
|
|
8860
|
+
rmSync as rmSync3,
|
|
8861
|
+
rmdirSync,
|
|
8862
|
+
statSync as statSync13,
|
|
8863
|
+
writeFileSync as writeFileSync5
|
|
8864
|
+
} from "node:fs";
|
|
8865
|
+
function layoutWarningForHook() {
|
|
8866
|
+
const state = detectLayout();
|
|
8867
|
+
if (state.kind === "current" || state.kind === "absent")
|
|
8868
|
+
return null;
|
|
8869
|
+
if (state.kind === "future") {
|
|
8870
|
+
return `[failproofai] this directory was written by a newer version (layout ${state.found} vs ${LAYOUT_VERSION}) — policies are NOT being enforced. ` + `Upgrade failproofai.`;
|
|
8871
|
+
}
|
|
8872
|
+
if (existsSync13(globalPolicyConfigFile()))
|
|
8873
|
+
return null;
|
|
8874
|
+
return `[failproofai] setup predates this version — global policies are NOT being enforced. ` + `Run \`failproofai config\` to re-create it.`;
|
|
8875
|
+
}
|
|
8876
|
+
var init_fp_reset = __esm(() => {
|
|
8877
|
+
init_hook_activity_store();
|
|
8878
|
+
init_fp_home();
|
|
8879
|
+
init_fp_config();
|
|
8880
|
+
init_daemon_service();
|
|
8881
|
+
});
|
|
8882
|
+
|
|
8104
8883
|
// src/hooks/handler.ts
|
|
8105
8884
|
function canonicalizeEventType(raw, cli) {
|
|
8106
8885
|
if (cli === "codex") {
|
|
@@ -8135,8 +8914,29 @@ function canonicalizeEventType(raw, cli) {
|
|
|
8135
8914
|
}
|
|
8136
8915
|
return raw;
|
|
8137
8916
|
}
|
|
8917
|
+
async function runObserved(hook, ctx, hookName, eventType, cli) {
|
|
8918
|
+
try {
|
|
8919
|
+
return await Promise.race([
|
|
8920
|
+
hook.fn(ctx),
|
|
8921
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 1e4))
|
|
8922
|
+
]);
|
|
8923
|
+
} catch (err) {
|
|
8924
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8925
|
+
trackHookEvent(getInstanceId(), "custom_hook_error", {
|
|
8926
|
+
hook_name: hookName,
|
|
8927
|
+
error_type: msg === "timeout" ? "timeout" : "exception",
|
|
8928
|
+
event_type: eventType,
|
|
8929
|
+
cli,
|
|
8930
|
+
is_observe_mode: true
|
|
8931
|
+
});
|
|
8932
|
+
return { decision: "allow" };
|
|
8933
|
+
}
|
|
8934
|
+
}
|
|
8138
8935
|
async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts) {
|
|
8139
8936
|
const startTime = performance.now();
|
|
8937
|
+
const layoutWarning = layoutWarningForHook();
|
|
8938
|
+
if (layoutWarning)
|
|
8939
|
+
hookLogWarn(layoutWarning);
|
|
8140
8940
|
try {
|
|
8141
8941
|
let parsed = {};
|
|
8142
8942
|
if (stdinPayload) {
|
|
@@ -8176,6 +8976,10 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
8176
8976
|
let config;
|
|
8177
8977
|
let customHooksList = [];
|
|
8178
8978
|
let conventionHookNames = new Set;
|
|
8979
|
+
let activePause = null;
|
|
8980
|
+
const policyAttribution = new Map;
|
|
8981
|
+
let cloudDeployment;
|
|
8982
|
+
const observedResults = [];
|
|
8179
8983
|
if (opts?.forceDecision) {
|
|
8180
8984
|
config = { enabledPolicies: [] };
|
|
8181
8985
|
clearPolicies();
|
|
@@ -8184,23 +8988,59 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
8184
8988
|
} else {
|
|
8185
8989
|
config = readMergedHooksConfig(session.cwd);
|
|
8186
8990
|
clearPolicies();
|
|
8187
|
-
|
|
8188
|
-
|
|
8991
|
+
activePause = readActivePause(session.sessionId);
|
|
8992
|
+
registerBuiltinPolicies(activePause ? [] : config.enabledPolicies);
|
|
8993
|
+
let cloudManagedPolicies = [];
|
|
8994
|
+
try {
|
|
8995
|
+
cloudManagedPolicies = readActiveCloudManagedPolicies();
|
|
8996
|
+
} catch (err) {
|
|
8997
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8998
|
+
hookLogWarn(`[failproofai] cloud-managed policies could NOT be loaded and are not being enforced: ${msg}. ` + `Local policies are unaffected. Run \`failproofai config --status\` to check this machine's enrolment.`);
|
|
8999
|
+
trackHookEvent(getInstanceId(), "cloud_managed_load_error", {
|
|
9000
|
+
event_type: eventType,
|
|
9001
|
+
cli
|
|
9002
|
+
});
|
|
9003
|
+
}
|
|
9004
|
+
cloudDeployment = cloudManagedPolicies[0]?.deployment;
|
|
9005
|
+
const configuredCustomPaths = config.customPoliciesPaths ?? config.customPoliciesPath;
|
|
9006
|
+
const allExplicitPaths = cloudManagedPolicies.length === 0 ? configuredCustomPaths : [
|
|
9007
|
+
...typeof configuredCustomPaths === "string" ? [configuredCustomPaths] : configuredCustomPaths ?? [],
|
|
9008
|
+
...cloudManagedPolicies.map((policy) => policy.path)
|
|
9009
|
+
];
|
|
9010
|
+
const loadResult = await loadAllCustomHooks(allExplicitPaths, {
|
|
8189
9011
|
sessionCwd: session.cwd,
|
|
8190
|
-
customPoliciesEnabled: config.customPoliciesEnabled
|
|
9012
|
+
customPoliciesEnabled: config.customPoliciesEnabled,
|
|
9013
|
+
...cloudManagedPolicies.length > 0 ? { cloudManagedPolicies } : {}
|
|
8191
9014
|
});
|
|
8192
9015
|
customHooksList = loadResult.hooks;
|
|
8193
9016
|
const disabledCustomPolicies = new Set(config.disabledCustomPolicies ?? []);
|
|
8194
9017
|
conventionHookNames = new Set(loadResult.conventionSources.flatMap((s) => s.hookNames));
|
|
8195
9018
|
for (const hook of customHooksList) {
|
|
8196
|
-
const
|
|
8197
|
-
|
|
9019
|
+
const taggedHook = hook;
|
|
9020
|
+
const policyId = taggedHook.__policyId;
|
|
9021
|
+
const cloudManaged = taggedHook.__cloudManaged;
|
|
9022
|
+
if (!cloudManaged && policyId && disabledCustomPolicies.has(policyId))
|
|
9023
|
+
continue;
|
|
9024
|
+
if (!cloudManaged && activePause)
|
|
8198
9025
|
continue;
|
|
8199
9026
|
const hookName = hook.name;
|
|
8200
9027
|
const conventionScope = hook.__conventionScope;
|
|
8201
9028
|
const isConvention = !!conventionScope;
|
|
8202
|
-
const prefix = isConvention ? `.failproofai-${conventionScope}` : "custom";
|
|
9029
|
+
const prefix = cloudManaged ? `cloud/${cloudManaged.id}@${cloudManaged.version}` : isConvention ? `.failproofai-${conventionScope}` : "custom";
|
|
9030
|
+
const observeOnly = cloudManaged?.effect === "observe";
|
|
8203
9031
|
const fn = async (ctx) => {
|
|
9032
|
+
if (observeOnly) {
|
|
9033
|
+
const shadow = await runObserved(hook, ctx, hookName, eventType, cli);
|
|
9034
|
+
if (shadow.decision !== "allow") {
|
|
9035
|
+
observedResults.push({
|
|
9036
|
+
policyId: cloudManaged.id,
|
|
9037
|
+
version: cloudManaged.version,
|
|
9038
|
+
decision: shadow.decision,
|
|
9039
|
+
reason: shadow.reason ?? null
|
|
9040
|
+
});
|
|
9041
|
+
}
|
|
9042
|
+
return { decision: "allow" };
|
|
9043
|
+
}
|
|
8204
9044
|
try {
|
|
8205
9045
|
const result2 = await Promise.race([
|
|
8206
9046
|
hook.fn(ctx),
|
|
@@ -8222,7 +9062,12 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
8222
9062
|
return { decision: "allow" };
|
|
8223
9063
|
}
|
|
8224
9064
|
};
|
|
8225
|
-
|
|
9065
|
+
const registeredName = `${prefix}/${hookName}`;
|
|
9066
|
+
policyAttribution.set(registeredName, {
|
|
9067
|
+
source: cloudManaged ? "cloud" : isConvention ? "convention" : "custom",
|
|
9068
|
+
...cloudManaged ? { cloudPolicyId: cloudManaged.id, cloudVersion: cloudManaged.version } : {}
|
|
9069
|
+
});
|
|
9070
|
+
registerPolicy(registeredName, hook.description ?? "", fn, hook.match ?? {}, -1);
|
|
8226
9071
|
}
|
|
8227
9072
|
if (customHooksList.length > 0) {
|
|
8228
9073
|
trackHookEvent(getInstanceId(), "custom_hooks_loaded", {
|
|
@@ -8263,7 +9108,18 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
8263
9108
|
transcriptPath: session.transcriptPath,
|
|
8264
9109
|
cwd: session.cwd,
|
|
8265
9110
|
permissionMode: session.permissionMode,
|
|
8266
|
-
hookEventName: session.hookEventName
|
|
9111
|
+
hookEventName: session.hookEventName,
|
|
9112
|
+
...result.policyName ? (() => {
|
|
9113
|
+
const attribution = policyAttribution.get(result.policyName);
|
|
9114
|
+
return {
|
|
9115
|
+
policySource: attribution?.source ?? "builtin",
|
|
9116
|
+
...attribution?.cloudPolicyId ? { cloudPolicyId: attribution.cloudPolicyId } : {},
|
|
9117
|
+
...attribution?.cloudVersion !== undefined ? { cloudVersion: attribution.cloudVersion } : {}
|
|
9118
|
+
};
|
|
9119
|
+
})() : {},
|
|
9120
|
+
...cloudDeployment !== undefined ? { cloudDeployment } : {},
|
|
9121
|
+
...observedResults.length > 0 ? { observed: observedResults } : {},
|
|
9122
|
+
...activePause ? { pausedBy: activePause.setBy, pauseExpiresAt: activePause.expiresAt } : {}
|
|
8267
9123
|
};
|
|
8268
9124
|
try {
|
|
8269
9125
|
persistHookActivity(activityEntry);
|
|
@@ -8315,6 +9171,9 @@ var init_handler = __esm(() => {
|
|
|
8315
9171
|
init_resolve_transcript_path();
|
|
8316
9172
|
init_telemetry_id();
|
|
8317
9173
|
init_hook_logger();
|
|
9174
|
+
init_cloud_managed_policies();
|
|
9175
|
+
init_session_pause();
|
|
9176
|
+
init_fp_reset();
|
|
8318
9177
|
});
|
|
8319
9178
|
|
|
8320
9179
|
// src/hooks/worker-server.ts
|
|
@@ -8322,13 +9181,13 @@ var exports_worker_server = {};
|
|
|
8322
9181
|
__export(exports_worker_server, {
|
|
8323
9182
|
startWorkerServer: () => startWorkerServer
|
|
8324
9183
|
});
|
|
8325
|
-
import { createServer } from "node:net";
|
|
8326
|
-
import { existsSync as
|
|
9184
|
+
import { createConnection, createServer } from "node:net";
|
|
9185
|
+
import { existsSync as existsSync14, unlinkSync as unlinkSync2 } from "node:fs";
|
|
8327
9186
|
function isWorkerHookRequest(msg) {
|
|
8328
9187
|
if (!msg || typeof msg !== "object")
|
|
8329
9188
|
return false;
|
|
8330
9189
|
const m = msg;
|
|
8331
|
-
return m.type === "hook" && typeof m.hookEvent === "string" && typeof m.cli === "string" && typeof m.stdin === "string" && (m.cwd === undefined || typeof m.cwd === "string");
|
|
9190
|
+
return m.type === "hook" && typeof m.hookEvent === "string" && typeof m.cli === "string" && typeof m.stdin === "string" && (m.cwd === undefined || m.cwd === null || typeof m.cwd === "string");
|
|
8332
9191
|
}
|
|
8333
9192
|
function encodeFrame(value) {
|
|
8334
9193
|
const body = Buffer.from(JSON.stringify(value), "utf8");
|
|
@@ -8337,9 +9196,20 @@ function encodeFrame(value) {
|
|
|
8337
9196
|
return Buffer.concat([header, body]);
|
|
8338
9197
|
}
|
|
8339
9198
|
function enqueue(task) {
|
|
8340
|
-
processingChain = processingChain.then(
|
|
9199
|
+
processingChain = processingChain.then(() => new Promise((settle) => {
|
|
9200
|
+
const timer = setTimeout(() => {
|
|
9201
|
+
hookLogWarn(`worker: a request did not settle within ${TASK_DEADLINE_MS}ms; ` + `exiting so the supervisor can respawn a clean worker`);
|
|
9202
|
+
process.exit(WEDGED_EXIT_CODE);
|
|
9203
|
+
}, TASK_DEADLINE_MS);
|
|
9204
|
+
timer.unref?.();
|
|
9205
|
+
const done = () => {
|
|
9206
|
+
clearTimeout(timer);
|
|
9207
|
+
settle();
|
|
9208
|
+
};
|
|
9209
|
+
task().then(done, done);
|
|
9210
|
+
})).catch(() => {});
|
|
8341
9211
|
}
|
|
8342
|
-
function handleConnection(socket) {
|
|
9212
|
+
function handleConnection(socket, shutdown) {
|
|
8343
9213
|
let recvBuf = Buffer.alloc(0);
|
|
8344
9214
|
let declaredLen = null;
|
|
8345
9215
|
socket.on("data", (chunk) => {
|
|
@@ -8367,6 +9237,10 @@ function handleConnection(socket) {
|
|
|
8367
9237
|
socket.write(encodeFrame({ type: "error", message: "malformed request frame" }));
|
|
8368
9238
|
continue;
|
|
8369
9239
|
}
|
|
9240
|
+
if (message && typeof message === "object" && message.type === "shutdown") {
|
|
9241
|
+
socket.end(encodeFrame({ type: "shutdownAccepted" }), shutdown);
|
|
9242
|
+
return;
|
|
9243
|
+
}
|
|
8370
9244
|
if (!isWorkerHookRequest(message)) {
|
|
8371
9245
|
socket.write(encodeFrame({ type: "error", message: "unrecognized request shape" }));
|
|
8372
9246
|
continue;
|
|
@@ -8376,7 +9250,7 @@ function handleConnection(socket) {
|
|
|
8376
9250
|
try {
|
|
8377
9251
|
const result = await evaluateHookEvent(request.hookEvent, request.cli, request.stdin, {
|
|
8378
9252
|
awaitTelemetryFlush: false,
|
|
8379
|
-
fallbackCwd: request.cwd
|
|
9253
|
+
fallbackCwd: request.cwd ?? undefined
|
|
8380
9254
|
});
|
|
8381
9255
|
socket.write(encodeFrame({
|
|
8382
9256
|
type: "hookResult",
|
|
@@ -8394,17 +9268,67 @@ function handleConnection(socket) {
|
|
|
8394
9268
|
});
|
|
8395
9269
|
socket.on("error", () => {});
|
|
8396
9270
|
}
|
|
8397
|
-
function
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
9271
|
+
function requestExistingWorkerShutdown(socketPath) {
|
|
9272
|
+
return new Promise((resolvePromise, reject) => {
|
|
9273
|
+
const socket = createConnection({ path: socketPath });
|
|
9274
|
+
let recvBuf = Buffer.alloc(0);
|
|
9275
|
+
let settled = false;
|
|
9276
|
+
const timer = setTimeout(() => finish(new Error("existing worker did not acknowledge shutdown")), STALE_WORKER_SHUTDOWN_TIMEOUT_MS);
|
|
9277
|
+
timer.unref?.();
|
|
9278
|
+
const finish = (result) => {
|
|
9279
|
+
if (settled)
|
|
9280
|
+
return;
|
|
9281
|
+
settled = true;
|
|
9282
|
+
clearTimeout(timer);
|
|
9283
|
+
socket.destroy();
|
|
9284
|
+
if (result instanceof Error)
|
|
9285
|
+
reject(result);
|
|
9286
|
+
else
|
|
9287
|
+
resolvePromise(result);
|
|
9288
|
+
};
|
|
9289
|
+
socket.on("connect", () => socket.write(encodeFrame({ type: "shutdown" })));
|
|
9290
|
+
socket.on("data", (chunk) => {
|
|
9291
|
+
recvBuf = Buffer.concat([recvBuf, chunk]);
|
|
9292
|
+
if (recvBuf.length < 4)
|
|
9293
|
+
return;
|
|
9294
|
+
const len = recvBuf.readUInt32BE(0);
|
|
9295
|
+
if (len > MAX_FRAME_LEN || recvBuf.length < 4 + len)
|
|
9296
|
+
return;
|
|
9297
|
+
try {
|
|
9298
|
+
const response = JSON.parse(recvBuf.subarray(4, 4 + len).toString("utf8"));
|
|
9299
|
+
if (response.type === "shutdownAccepted")
|
|
9300
|
+
finish("stopped");
|
|
9301
|
+
else
|
|
9302
|
+
finish(new Error("existing socket belongs to a process that does not support shutdown"));
|
|
9303
|
+
} catch {
|
|
9304
|
+
finish(new Error("existing worker returned a malformed shutdown response"));
|
|
9305
|
+
}
|
|
9306
|
+
});
|
|
9307
|
+
socket.on("error", (err) => {
|
|
9308
|
+
if (err.code === "ECONNREFUSED" || err.code === "ENOENT")
|
|
9309
|
+
finish("stale");
|
|
9310
|
+
else
|
|
9311
|
+
finish(err);
|
|
9312
|
+
});
|
|
9313
|
+
});
|
|
9314
|
+
}
|
|
9315
|
+
function startWorkerServer(socketPath, shutdown) {
|
|
9316
|
+
const server = createServer((socket) => handleConnection(socket, shutdown ?? (() => server.close())));
|
|
9317
|
+
const listen = () => {
|
|
9318
|
+
if (existsSync14(socketPath)) {
|
|
9319
|
+
try {
|
|
9320
|
+
unlinkSync2(socketPath);
|
|
9321
|
+
} catch {}
|
|
9322
|
+
}
|
|
9323
|
+
server.listen(socketPath);
|
|
9324
|
+
};
|
|
9325
|
+
if (!existsSync14(socketPath))
|
|
9326
|
+
listen();
|
|
9327
|
+
else
|
|
9328
|
+
requestExistingWorkerShutdown(socketPath).then(listen, (err) => server.emit("error", err));
|
|
8405
9329
|
return server;
|
|
8406
9330
|
}
|
|
8407
|
-
var MAX_FRAME_LEN, processingChain;
|
|
9331
|
+
var MAX_FRAME_LEN, TASK_DEADLINE_MS = 60000, WEDGED_EXIT_CODE = 75, processingChain, STALE_WORKER_SHUTDOWN_TIMEOUT_MS = 2000;
|
|
8408
9332
|
var init_worker_server = __esm(() => {
|
|
8409
9333
|
init_handler();
|
|
8410
9334
|
init_hook_logger();
|
|
@@ -8413,14 +9337,14 @@ var init_worker_server = __esm(() => {
|
|
|
8413
9337
|
});
|
|
8414
9338
|
|
|
8415
9339
|
// bin/failproofai-worker.mjs
|
|
8416
|
-
import { realpathSync } from "node:fs";
|
|
8417
|
-
import { dirname as dirname5, resolve as
|
|
9340
|
+
import { realpathSync as realpathSync2 } from "node:fs";
|
|
9341
|
+
import { dirname as dirname5, resolve as resolve13 } from "node:path";
|
|
8418
9342
|
import { fileURLToPath } from "node:url";
|
|
8419
9343
|
if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
|
8420
|
-
process.env.FAILPROOFAI_PACKAGE_ROOT =
|
|
9344
|
+
process.env.FAILPROOFAI_PACKAGE_ROOT = resolve13(dirname5(realpathSync2(fileURLToPath(import.meta.url))), "..");
|
|
8421
9345
|
}
|
|
8422
9346
|
if (!process.env.FAILPROOFAI_DIST_PATH) {
|
|
8423
|
-
process.env.FAILPROOFAI_DIST_PATH =
|
|
9347
|
+
process.env.FAILPROOFAI_DIST_PATH = resolve13(dirname5(realpathSync2(fileURLToPath(import.meta.url))), "..", "dist");
|
|
8424
9348
|
}
|
|
8425
9349
|
var socketPath = process.env.FAILPROOFAI_WORKER_SOCKET;
|
|
8426
9350
|
if (!socketPath) {
|
|
@@ -8428,7 +9352,7 @@ if (!socketPath) {
|
|
|
8428
9352
|
process.exit(1);
|
|
8429
9353
|
}
|
|
8430
9354
|
var { startWorkerServer: startWorkerServer2 } = await Promise.resolve().then(() => (init_worker_server(), exports_worker_server));
|
|
8431
|
-
var server = startWorkerServer2(socketPath);
|
|
9355
|
+
var server = startWorkerServer2(socketPath, shutdown);
|
|
8432
9356
|
server.on("error", (err) => {
|
|
8433
9357
|
console.error(`[failproofai-worker] server error: ${err.message}`);
|
|
8434
9358
|
process.exit(1);
|