failproofai 1.0.0 → 1.0.1-beta.1
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 +0 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +5 -5
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/routes-manifest.json +0 -6
- 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 +4 -3
- 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 +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +4 -3
- 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/_not-found/__PAGE__.segment.rsc +14 -14
- 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 +2 -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 +3 -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 +2 -2
- 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 +4 -3
- 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 +2 -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/status/route.js +4 -3
- 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.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +5 -3
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +4 -3
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +10 -10
- package/.next/standalone/.next/server/app/policies/page.js +6 -5
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +5 -4
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +5 -4
- 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/server-reference-manifest.json +4 -4
- package/.next/standalone/.next/server/app/settings/page.js +6 -4
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +0 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__05ja_05._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0agdw2a._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d1u_na._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0nne229._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ny9q5d._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1hstmft._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p8poxd._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +1 -1
- package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_0jx6cp6._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0f-x2b0._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_1f290jp._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0hwvl7e._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_1g2ji1t._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_1ctjdzl._.js} +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__03eykvd._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__07175vb._.js → [root-of-the-server]__07oh8b5._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cxe_2_._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kgy9da._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0m98kzy._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0sjzdjg._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0v5_91z._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-a3135._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1hvwiw8._.js → [root-of-the-server]__11z_299._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1apc90t._.js → [root-of-the-server]__15c_z97._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1wmno1q._.js → [root-of-the-server]__17d_ffl._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_dfocn._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1e5g_ur._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1hmqefd._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1jiwfsj._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kzapg5._.js +6 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zvtylo._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_04j57fb._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0hom1s1._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0qk91ba._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1q7j05x._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1wboum1._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +3 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- 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 +15 -15
- package/.next/standalone/.next/static/chunks/{2676v-lyzgo91.js → 01b0vu6t3wtfh.js} +1 -1
- package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +1 -0
- package/.next/standalone/.next/static/chunks/0w-2enf8raksk.js +1 -0
- package/.next/standalone/.next/static/chunks/{2kopjq_or743g.js → 13pe0rgzztjxa.js} +2 -2
- package/.next/standalone/.next/static/chunks/{3xvtgz6arztp_.js → 1cn6xl9lfp3ei.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0zi8zjav5a4rq.js → 1e_2h_-zvxnke.js} +1 -1
- package/.next/standalone/.next/static/chunks/1eyl7fzkmnkf_.js +1 -0
- package/.next/standalone/.next/static/chunks/{0zm3_8jpqitxy.js → 1x9238y5kw9vi.js} +22 -22
- package/.next/standalone/.next/static/chunks/1xajksrefvav8.js +1 -0
- package/.next/standalone/.next/static/chunks/{1hfocb0sdl85c.js → 25c48grb44a-n.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2ouq44jv_0x04.js → 28bdm54bngvc7.js} +1 -1
- package/.next/standalone/.next/static/chunks/29-iqnp9v_axt.css +1 -0
- package/.next/standalone/.next/static/chunks/{31k3yxpy57_fy.js → 2hhselnqeo4ll.js} +1 -1
- package/.next/standalone/.next/static/chunks/2k9f4tyv04809.css +1 -0
- package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +1 -0
- package/.next/standalone/.next/static/chunks/{0km-jn9o5xyqs.css → 3wq9x0c-7ld5d.css} +1 -1
- package/.next/standalone/app/actions/get-scheduled-audit.ts +65 -6
- package/.next/standalone/app/actions/update-scheduled-audit.ts +90 -17
- package/.next/standalone/app/api/auth/status/route.ts +7 -17
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +2 -6
- package/.next/standalone/app/audit/_components/come-back-better-section.tsx +101 -281
- package/.next/standalone/app/audit/_components/empty-state.tsx +2 -2
- package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
- package/.next/standalone/app/audit/audit-styles.css +150 -26
- package/.next/standalone/app/globals.css +39 -1
- package/.next/standalone/app/settings/page.tsx +38 -21
- package/.next/standalone/app/settings/settings-client.tsx +583 -392
- package/.next/standalone/app/settings/settings.css +354 -0
- package/.next/standalone/components/navbar.tsx +33 -4
- package/.next/standalone/lib/auth/api-server-client.ts +51 -29
- package/.next/standalone/lib/auth/auth-store.ts +44 -59
- package/.next/standalone/lib/telemetry.ts +45 -40
- package/.next/standalone/package.json +12 -10
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +6 -0
- package/bin/failproofaid-shim.mjs +0 -0
- package/dist/cli.mjs +1915 -472
- package/dist/worker.mjs +27 -5
- package/lib/auth/api-server-client.ts +51 -29
- package/lib/auth/auth-store.ts +44 -59
- package/lib/telemetry.ts +45 -40
- package/package.json +12 -10
- package/scripts/docs-audit.ts +493 -0
- package/scripts/translate-docs/cli.ts +16 -2
- package/scripts/translate-docs/mdx-translator.ts +6 -1
- package/scripts/translate-docs/readme-translator.ts +4 -2
- package/src/audit/cli-login.ts +330 -0
- package/src/audit/cli.ts +164 -31
- package/src/audit/dashboard-cache.ts +44 -8
- package/src/audit/harm-report.ts +260 -0
- package/src/audit/machine-store.ts +120 -0
- package/src/audit/redact-example.ts +312 -0
- package/src/audit/report-harm.ts +167 -0
- package/src/audit/schedule-cli.ts +352 -0
- package/src/hooks/builtin-policies.ts +26 -0
- package/src/hooks/daemon-service.ts +76 -0
- package/src/hooks/fp-config.ts +96 -5
- package/src/hooks/fp-home.ts +118 -8
- package/src/hooks/fp-reset.ts +79 -9
- package/src/hooks/hook-telemetry.ts +3 -2
- package/src/hooks/migrations.ts +241 -4
- package/src/hooks/tui.ts +61 -3
- package/.next/standalone/.next/server/app/api/auth/reminder/route/app-paths-manifest.json +0 -3
- package/.next/standalone/.next/server/app/api/auth/reminder/route/build-manifest.json +0 -12
- package/.next/standalone/.next/server/app/api/auth/reminder/route/server-reference-manifest.json +0 -4
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js +0 -9
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js.map +0 -5
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +0 -1
- package/.next/standalone/.next/server/app/api/auth/reminder/route_client-reference-manifest.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-xggru._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__00ds05_._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1dxagv_._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fgqj1_._.js +0 -3
- package/.next/standalone/.next/server/chunks/_084toou._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0g4t8c9._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0km521a._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0t8kefc._.js +0 -3
- package/.next/standalone/.next/server/chunks/_16dwkfn._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1phm9ui._.js +0 -3
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_auth_reminder_route_actions_1kjgxf8.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0dk8r_l._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kviq2m._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ogyhu0._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-rfu32._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mmefsr._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1t62w37._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_032wo7w._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_08840em._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0gl2ujp._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0j0ggkc._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0kbv5-q._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0r1jgvp._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0t5xq1b._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0w_be0g._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_15gba46._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/_1qyhb9r._.js +0 -23
- package/.next/standalone/.next/server/chunks/ssr/_1vel2ey._.js +0 -3
- package/.next/standalone/.next/static/chunks/0wd503tv801gy.js +0 -1
- package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +0 -1
- package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +0 -2
- package/.next/standalone/.next/static/chunks/2ynl0-6aqng_b.js +0 -1
- package/.next/standalone/app/api/auth/reminder/route.ts +0 -213
- /package/.next/standalone/.next/static/{Aulh3DRWwRfwh2POCvZR_ → zMC0Yqypg3GF82-3KN6ul}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{Aulh3DRWwRfwh2POCvZR_ → zMC0Yqypg3GF82-3KN6ul}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{Aulh3DRWwRfwh2POCvZR_ → zMC0Yqypg3GF82-3KN6ul}/_ssgManifest.js +0 -0
package/dist/cli.mjs
CHANGED
|
@@ -19,7 +19,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
19
19
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
20
20
|
|
|
21
21
|
// package.json
|
|
22
|
-
var version2 = "1.0.
|
|
22
|
+
var version2 = "1.0.1-beta.1", package_default;
|
|
23
23
|
var init_package = __esm(() => {
|
|
24
24
|
package_default = {
|
|
25
25
|
name: "failproofai",
|
|
@@ -63,7 +63,8 @@ var init_package = __esm(() => {
|
|
|
63
63
|
"translate:docs": "bun scripts/translate-docs/cli.ts --docs-only",
|
|
64
64
|
"translate:dry-run": "bun scripts/translate-docs/cli.ts --dry-run",
|
|
65
65
|
"translate:validate": "bun scripts/translate-docs/cli.ts --validate",
|
|
66
|
-
"validate:mdx": "bun scripts/validate-mdx.ts"
|
|
66
|
+
"validate:mdx": "bun scripts/validate-mdx.ts",
|
|
67
|
+
"docs:audit": "bun scripts/docs-audit.ts"
|
|
67
68
|
},
|
|
68
69
|
keywords: [
|
|
69
70
|
"claude",
|
|
@@ -127,6 +128,7 @@ var init_package = __esm(() => {
|
|
|
127
128
|
yaml: "^2.9.0"
|
|
128
129
|
},
|
|
129
130
|
overrides: {
|
|
131
|
+
nanoid: "3.3.18",
|
|
130
132
|
postcss: "8.5.26",
|
|
131
133
|
"eslint-plugin-react-hooks": "7.0.1",
|
|
132
134
|
vite: "8.0.16",
|
|
@@ -135,16 +137,16 @@ var init_package = __esm(() => {
|
|
|
135
137
|
sharp: "0.35.0"
|
|
136
138
|
},
|
|
137
139
|
optionalDependencies: {
|
|
138
|
-
"@failproofai/failproofaid-linux-x64": "1.0.
|
|
139
|
-
"@failproofai/failproofaid-linux-arm64": "1.0.
|
|
140
|
-
"@failproofai/failproofaid-darwin-x64": "1.0.
|
|
141
|
-
"@failproofai/failproofaid-darwin-arm64": "1.0.
|
|
140
|
+
"@failproofai/failproofaid-linux-x64": "1.0.1-beta.1",
|
|
141
|
+
"@failproofai/failproofaid-linux-arm64": "1.0.1-beta.1",
|
|
142
|
+
"@failproofai/failproofaid-darwin-x64": "1.0.1-beta.1",
|
|
143
|
+
"@failproofai/failproofaid-darwin-arm64": "1.0.1-beta.1"
|
|
142
144
|
},
|
|
143
145
|
failproofaidBinaries: {
|
|
144
|
-
"linux-x64": "
|
|
145
|
-
"linux-arm64": "
|
|
146
|
-
"darwin-x64": "
|
|
147
|
-
"darwin-arm64": "
|
|
146
|
+
"linux-x64": "cf623441aa67363e423a1dba7db734d3e9243f0abff5e63a9ed7ddc096213e51",
|
|
147
|
+
"linux-arm64": "339281280ae8e60d03415da88a372ff4d3fb356c375f0e1f9eea17063233d52b",
|
|
148
|
+
"darwin-x64": "b972c37a9ca83b085d11d1312b3fa8487dead09dbc81e1e115000d2614051248",
|
|
149
|
+
"darwin-arm64": "597cbdae5f564fd0c7c917b2b0b94a91ba8465e568487da0d1cac0508b94cee5"
|
|
148
150
|
}
|
|
149
151
|
};
|
|
150
152
|
});
|
|
@@ -175,6 +177,7 @@ function retiredLayoutPaths() {
|
|
|
175
177
|
legacy.lastVersion(),
|
|
176
178
|
legacy.auditDashboard(),
|
|
177
179
|
legacy.auditCacheDir(),
|
|
180
|
+
legacy.auditReminder(),
|
|
178
181
|
legacy.codexSessionPaths(),
|
|
179
182
|
legacy.spoolDir(),
|
|
180
183
|
legacy.failedDir(),
|
|
@@ -186,7 +189,7 @@ function retiredLayoutPaths() {
|
|
|
186
189
|
legacy.localPoliciesDir()
|
|
187
190
|
];
|
|
188
191
|
}
|
|
189
|
-
var LAYOUT_VERSION =
|
|
192
|
+
var LAYOUT_VERSION = 4, 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"), credentialsFile = (home) => atHome(home, "credentials.json"), binDir = (home) => atHome(home, "bin"), policiesDir = (home) => atHome(home, "policies"), customPoliciesDir, globalPolicyConfigFile = (home) => atHome(home, "policies-config.json"), cloudPoliciesDir = (home) => resolve(policiesDir(home), "cloud-policies"), cursorsDir = (source, home) => source ? resolve(atHome(home, "cursors"), source) : atHome(home, "cursors"), customAgentsDir = (home) => atHome(home, "custom-agents"), auditDir = (home) => atHome(home, "audit"), auditDashboardFile = (home) => resolve(auditDir(home), "dashboard.json"), auditCacheDir = (home) => resolve(auditDir(home), "cache"), auditSessionFile = (home) => resolve(auditDir(home), "session.json"), auditMachineFile = (home) => resolve(auditDir(home), "machine.json"), hookActivityDir = (home) => atHome(home, "hook-activity"), runDir = (home) => atHome(home, "run"), daemonSocket = () => process.env.FAILPROOFAI_DAEMON_SOCKET || resolve(runDir(), "failproofaid.sock"), auditLockFile = (home) => resolve(runDir(home), "audit.lock"), stateDir = (home) => atHome(home, "state"), shimsDir = (home) => resolve(stateDir(home), "shims"), spoolDir = (home) => resolve(stateDir(home), "spool"), failedDir = (home) => resolve(stateDir(home), "failed"), collectorHealthFile = (home) => resolve(stateDir(home), "collector-health.json"), sessionPauseDir = () => resolve(stateDir(), "sessions"), auditScheduleFile = (home) => resolve(auditDir(home), "schedule.json"), telemetryIdFile = (home) => resolve(stateDir(home), "telemetry-id"), launcherMarker = (home) => resolve(stateDir(home), "launcher-configured"), onboardingAttemptFile = (home) => resolve(stateDir(home), "onboarding-attempt.json"), lastVersionFile = (home) => resolve(stateDir(home), "last-version"), onboardingLockFile = (home) => resolve(stateDir(home), "onboarding.lock"), codexSessionPathsFile = (home) => resolve(stateDir(home), "codex-session-paths.json"), logsDir = (home) => atHome(home, "logs"), migrationsDir = (home) => atHome(home, "migrations"), migrationLedgerFile = (home) => resolve(migrationsDir(home), "applied.json"), migrationBackupDir = (layout, home) => resolve(migrationsDir(home), `backup-layout${layout}`), HOME_CLASSES, legacy;
|
|
190
193
|
var init_fp_home = __esm(() => {
|
|
191
194
|
customPoliciesDir = policiesDir;
|
|
192
195
|
HOME_CLASSES = [
|
|
@@ -194,6 +197,7 @@ var init_fp_home = __esm(() => {
|
|
|
194
197
|
{ path: configFile, class: "user-typed" },
|
|
195
198
|
{ path: globalPolicyConfigFile, class: "user-typed" },
|
|
196
199
|
{ path: policiesDir, class: "user-typed" },
|
|
200
|
+
{ path: auditSessionFile, class: "user-typed" },
|
|
197
201
|
{ path: spoolDir, class: "undelivered" },
|
|
198
202
|
{ path: failedDir, class: "undelivered" },
|
|
199
203
|
{ path: customAgentsDir, class: "undelivered" },
|
|
@@ -205,9 +209,11 @@ var init_fp_home = __esm(() => {
|
|
|
205
209
|
{ path: (home) => resolve(stateDir(home), "daemon-telemetry-id"), class: "identity" },
|
|
206
210
|
{ path: versionFile, class: "identity" },
|
|
207
211
|
{ path: migrationsDir, class: "identity" },
|
|
208
|
-
{ path:
|
|
209
|
-
{ path:
|
|
212
|
+
{ path: auditMachineFile, class: "identity" },
|
|
213
|
+
{ path: auditDashboardFile, class: "derived" },
|
|
214
|
+
{ path: auditCacheDir, class: "derived" },
|
|
210
215
|
{ path: auditScheduleFile, class: "derived" },
|
|
216
|
+
{ path: collectorHealthFile, class: "derived" },
|
|
211
217
|
{ path: codexSessionPathsFile, class: "derived" },
|
|
212
218
|
{ path: shimsDir, class: "derived" },
|
|
213
219
|
{ path: sessionPauseDir, class: "derived" },
|
|
@@ -233,6 +239,10 @@ var init_fp_home = __esm(() => {
|
|
|
233
239
|
launcherMarker: () => at(".launcher-configured"),
|
|
234
240
|
lastVersion: () => at("last-version"),
|
|
235
241
|
auditDashboard: () => at("audit-dashboard.json"),
|
|
242
|
+
authJson: () => at("auth.json"),
|
|
243
|
+
nextAudit: () => at("next-audit.json"),
|
|
244
|
+
auditReminder: () => at("audit", "reminder.json"),
|
|
245
|
+
auditSchedule: () => at("state", "audit-schedule.json"),
|
|
236
246
|
cacheDir: () => at("cache"),
|
|
237
247
|
hookActivityDir: () => at("cache", "hook-activity"),
|
|
238
248
|
auditCacheDir: () => at("cache", "audit"),
|
|
@@ -257,10 +267,17 @@ function detectLayout() {
|
|
|
257
267
|
return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
|
|
258
268
|
}
|
|
259
269
|
if (existsSync(configFile())) {
|
|
270
|
+
const layoutThreePositions = [
|
|
271
|
+
legacy.authJson(),
|
|
272
|
+
legacy.nextAudit(),
|
|
273
|
+
legacy.auditSchedule()
|
|
274
|
+
];
|
|
275
|
+
if (layoutThreePositions.some((p) => existsSync(p)))
|
|
276
|
+
return { kind: "stale", found: 3 };
|
|
260
277
|
return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
|
|
261
278
|
}
|
|
262
279
|
if (existsSync(legacy.configToml()))
|
|
263
|
-
return { kind: "stale", found:
|
|
280
|
+
return { kind: "stale", found: 2 };
|
|
264
281
|
const layoutOneMarkers = [
|
|
265
282
|
legacy.policyConfig(),
|
|
266
283
|
legacy.cacheDir(),
|
|
@@ -303,7 +320,7 @@ function readVersionFile() {
|
|
|
303
320
|
function writeVersionFile(v = {}) {
|
|
304
321
|
const existing = readVersionFile();
|
|
305
322
|
const next = {
|
|
306
|
-
layout: LAYOUT_VERSION,
|
|
323
|
+
layout: v.layout ?? LAYOUT_VERSION,
|
|
307
324
|
cli: v.cli ?? version2,
|
|
308
325
|
daemon: v.clearDaemon ? undefined : v.daemon ?? existing?.daemon
|
|
309
326
|
};
|
|
@@ -372,7 +389,11 @@ function projectConfig(parsed) {
|
|
|
372
389
|
sources: readSources(collector.sources)
|
|
373
390
|
},
|
|
374
391
|
telemetry: { enabled: telemetry.enabled !== false },
|
|
375
|
-
audit: {
|
|
392
|
+
audit: {
|
|
393
|
+
auto: audit.auto === true,
|
|
394
|
+
intervalDays: readIntervalDays(audit.interval_days),
|
|
395
|
+
reportsConsentedAt: typeof audit.reports_consented_at === "number" && Number.isFinite(audit.reports_consented_at) ? audit.reports_consented_at : undefined
|
|
396
|
+
}
|
|
376
397
|
};
|
|
377
398
|
}
|
|
378
399
|
}
|
|
@@ -421,7 +442,11 @@ function writeConfig(config, raw) {
|
|
|
421
442
|
} : {}
|
|
422
443
|
},
|
|
423
444
|
...config.telemetry.enabled ? {} : { telemetry: { enabled: false } },
|
|
424
|
-
audit: {
|
|
445
|
+
audit: {
|
|
446
|
+
auto: config.audit.auto,
|
|
447
|
+
interval_days: config.audit.intervalDays,
|
|
448
|
+
...config.audit.reportsConsentedAt === undefined ? {} : { reports_consented_at: config.audit.reportsConsentedAt }
|
|
449
|
+
}
|
|
425
450
|
};
|
|
426
451
|
const merged = structuredClone(raw ?? readConfigRaw().raw);
|
|
427
452
|
for (const path of OWNED_CONFIG_KEYS)
|
|
@@ -567,7 +592,8 @@ var init_fp_config = __esm(() => {
|
|
|
567
592
|
["collector", "sources", "*", "extra_paths"],
|
|
568
593
|
["telemetry", "enabled"],
|
|
569
594
|
["audit", "auto"],
|
|
570
|
-
["audit", "interval_days"]
|
|
595
|
+
["audit", "interval_days"],
|
|
596
|
+
["audit", "reports_consented_at"]
|
|
571
597
|
];
|
|
572
598
|
OWNED_CREDENTIAL_KEYS = [
|
|
573
599
|
["cloud", "url"],
|
|
@@ -2910,7 +2936,7 @@ function registerBuiltinPolicies(enabledNames) {
|
|
|
2910
2936
|
}
|
|
2911
2937
|
}
|
|
2912
2938
|
}
|
|
2913
|
-
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;
|
|
2939
|
+
var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SECRET_PATTERNS, 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;
|
|
2914
2940
|
var init_builtin_policies = __esm(() => {
|
|
2915
2941
|
init_hook_logger();
|
|
2916
2942
|
isClaudeInternalPath = isAgentInternalPath;
|
|
@@ -2932,6 +2958,13 @@ var init_builtin_policies = __esm(() => {
|
|
|
2932
2958
|
CONNECTION_STRING_RE = /(?:postgresql|postgres|mysql|mongodb(?:\+srv)?|redis|amqps?|smtps?):\/\/[^@\s]+@/;
|
|
2933
2959
|
PRIVATE_KEY_RE = /-----BEGIN (?:[A-Z]+ )?PRIVATE KEY-----/;
|
|
2934
2960
|
BEARER_TOKEN_RE = /Authorization:\s*Bearer\s+[A-Za-z0-9\-._~+/]{20,}/i;
|
|
2961
|
+
SECRET_PATTERNS = [
|
|
2962
|
+
[PRIVATE_KEY_RE, "private key"],
|
|
2963
|
+
[JWT_RE, "JWT"],
|
|
2964
|
+
[BEARER_TOKEN_RE, "bearer token"],
|
|
2965
|
+
[CONNECTION_STRING_RE, "database credentials"],
|
|
2966
|
+
...API_KEY_PATTERNS
|
|
2967
|
+
];
|
|
2935
2968
|
SQL_TOOL_RE = /\b(?:psql|mysql|sqlite3|pgcli|clickhouse-client)\b/;
|
|
2936
2969
|
DESTRUCTIVE_SQL_RE = /\b(?:DROP\s+(?:TABLE|DATABASE|SCHEMA)|TRUNCATE\b)/i;
|
|
2937
2970
|
DELETE_NO_WHERE_RE = /\bDELETE\s+FROM\b/i;
|
|
@@ -11432,7 +11465,7 @@ function readCarriedLegacyCredentials() {
|
|
|
11432
11465
|
const creds = projectCredentials(raw);
|
|
11433
11466
|
return Object.keys(creds).length > 0 ? creds : null;
|
|
11434
11467
|
}
|
|
11435
|
-
function resetHome(from) {
|
|
11468
|
+
function resetHome(from, to = LAYOUT_VERSION) {
|
|
11436
11469
|
const migrated = migrateConventionPolicies();
|
|
11437
11470
|
const activity = migrateHookActivity();
|
|
11438
11471
|
const pendingPolicyConfig = readCarriedPolicyConfig();
|
|
@@ -11457,7 +11490,7 @@ function resetHome(from) {
|
|
|
11457
11490
|
writeConfig(pendingConfig);
|
|
11458
11491
|
if (telemetryOptOut)
|
|
11459
11492
|
updateConfig({ telemetry: { enabled: false } });
|
|
11460
|
-
writeVersionFile();
|
|
11493
|
+
writeVersionFile({ layout: to });
|
|
11461
11494
|
return { removed, migrated, activity, policyConfig, spooled, from };
|
|
11462
11495
|
}
|
|
11463
11496
|
function layoutWarningForHook() {
|
|
@@ -12033,14 +12066,71 @@ __export(exports_migrations, {
|
|
|
12033
12066
|
runMigrations: () => runMigrations,
|
|
12034
12067
|
restoreBackup: () => restoreBackup,
|
|
12035
12068
|
readLedger: () => readLedger,
|
|
12069
|
+
pruneMigratedCredentials: () => pruneMigratedCredentials,
|
|
12036
12070
|
planMigration: () => planMigration,
|
|
12037
12071
|
migrationCoverageGap: () => migrationCoverageGap,
|
|
12038
12072
|
describePlan: () => describePlan,
|
|
12039
12073
|
backupBeforeMigrating: () => backupBeforeMigrating,
|
|
12040
12074
|
MIGRATIONS: () => MIGRATIONS
|
|
12041
12075
|
});
|
|
12042
|
-
import {
|
|
12043
|
-
|
|
12076
|
+
import {
|
|
12077
|
+
chmodSync as chmodSync4,
|
|
12078
|
+
copyFileSync as copyFileSync2,
|
|
12079
|
+
existsSync as existsSync21,
|
|
12080
|
+
mkdirSync as mkdirSync12,
|
|
12081
|
+
readFileSync as readFileSync16,
|
|
12082
|
+
renameSync as renameSync6,
|
|
12083
|
+
rmSync as rmSync7,
|
|
12084
|
+
writeFileSync as writeFileSync10
|
|
12085
|
+
} from "node:fs";
|
|
12086
|
+
import { basename as basename4, dirname as dirname10, join as join29, resolve as resolve16 } from "node:path";
|
|
12087
|
+
function migrateToLayout4() {
|
|
12088
|
+
const moves = [
|
|
12089
|
+
{ from: legacy.authJson(), to: auditSessionFile() },
|
|
12090
|
+
{ from: legacy.nextAudit(), to: legacy.auditReminder() },
|
|
12091
|
+
{ from: legacy.auditSchedule(), to: auditScheduleFile() }
|
|
12092
|
+
];
|
|
12093
|
+
const authDirOverride = process.env.FAILPROOFAI_AUTH_DIR;
|
|
12094
|
+
if (authDirOverride) {
|
|
12095
|
+
moves.push({ from: join29(authDirOverride, "auth.json"), to: join29(authDirOverride, "session.json") }, {
|
|
12096
|
+
from: join29(authDirOverride, "next-audit.json"),
|
|
12097
|
+
to: join29(authDirOverride, "reminder.json")
|
|
12098
|
+
});
|
|
12099
|
+
}
|
|
12100
|
+
const migrated = [];
|
|
12101
|
+
for (const { from, to } of moves) {
|
|
12102
|
+
if (!existsSync21(from))
|
|
12103
|
+
continue;
|
|
12104
|
+
if (existsSync21(to)) {
|
|
12105
|
+
rmSync7(from, { force: true });
|
|
12106
|
+
continue;
|
|
12107
|
+
}
|
|
12108
|
+
mkdirSync12(dirname10(to), { recursive: true });
|
|
12109
|
+
try {
|
|
12110
|
+
renameSync6(from, to);
|
|
12111
|
+
} catch {
|
|
12112
|
+
try {
|
|
12113
|
+
copyFileSync2(from, to);
|
|
12114
|
+
} catch (err) {
|
|
12115
|
+
try {
|
|
12116
|
+
rmSync7(to, { force: true });
|
|
12117
|
+
} catch {}
|
|
12118
|
+
throw err;
|
|
12119
|
+
}
|
|
12120
|
+
rmSync7(from, { force: true });
|
|
12121
|
+
}
|
|
12122
|
+
migrated.push(`${basename4(from)} → audit/${basename4(to)}`);
|
|
12123
|
+
}
|
|
12124
|
+
for (const secret of [auditSessionFile()]) {
|
|
12125
|
+
if (!existsSync21(secret))
|
|
12126
|
+
continue;
|
|
12127
|
+
try {
|
|
12128
|
+
chmodSync4(secret, 384);
|
|
12129
|
+
} catch {}
|
|
12130
|
+
}
|
|
12131
|
+
writeVersionFile({ layout: 4 });
|
|
12132
|
+
return { removed: [], migrated, activity: [], policyConfig: [], spooled: [], from: 3 };
|
|
12133
|
+
}
|
|
12044
12134
|
function planMigration(from, registry = MIGRATIONS) {
|
|
12045
12135
|
if (from >= LAYOUT_VERSION)
|
|
12046
12136
|
return [];
|
|
@@ -12095,6 +12185,23 @@ function appendLedger(entry) {
|
|
|
12095
12185
|
function backupNameOf(f) {
|
|
12096
12186
|
return f.as ?? basename4(f.at());
|
|
12097
12187
|
}
|
|
12188
|
+
function pruneMigratedCredentials(from) {
|
|
12189
|
+
const pruned = [];
|
|
12190
|
+
for (let layout = from;layout < LAYOUT_VERSION; layout++) {
|
|
12191
|
+
for (const { as, landsAt } of PRUNED_AFTER_SUCCESS) {
|
|
12192
|
+
try {
|
|
12193
|
+
if (!existsSync21(landsAt()))
|
|
12194
|
+
continue;
|
|
12195
|
+
const copy = resolve16(migrationBackupDir(layout), as);
|
|
12196
|
+
if (!existsSync21(copy))
|
|
12197
|
+
continue;
|
|
12198
|
+
rmSync7(copy, { force: true });
|
|
12199
|
+
pruned.push(as);
|
|
12200
|
+
} catch {}
|
|
12201
|
+
}
|
|
12202
|
+
}
|
|
12203
|
+
return pruned;
|
|
12204
|
+
}
|
|
12098
12205
|
function filesToBackUp() {
|
|
12099
12206
|
const seen = new Map;
|
|
12100
12207
|
for (const f of [...BACKED_UP, ...BACKED_UP_LEGACY]) {
|
|
@@ -12156,6 +12263,11 @@ function runMigrations(from, chain = planMigration(from)) {
|
|
|
12156
12263
|
});
|
|
12157
12264
|
} catch (err) {
|
|
12158
12265
|
steps.push({ from: step.from, to: step.to, ok: false });
|
|
12266
|
+
try {
|
|
12267
|
+
const marker = readVersionFile();
|
|
12268
|
+
if (marker && marker.layout >= LAYOUT_VERSION)
|
|
12269
|
+
writeVersionFile({ layout: step.from });
|
|
12270
|
+
} catch {}
|
|
12159
12271
|
failed = {
|
|
12160
12272
|
from: step.from,
|
|
12161
12273
|
to: step.to,
|
|
@@ -12172,6 +12284,8 @@ function runMigrations(from, chain = planMigration(from)) {
|
|
|
12172
12284
|
break;
|
|
12173
12285
|
}
|
|
12174
12286
|
}
|
|
12287
|
+
if (!failed)
|
|
12288
|
+
pruneMigratedCredentials(from);
|
|
12175
12289
|
return { from, steps, backedUp, outcome, failed };
|
|
12176
12290
|
}
|
|
12177
12291
|
function describePlan(from) {
|
|
@@ -12214,23 +12328,30 @@ function restoreBackup(layout) {
|
|
|
12214
12328
|
}
|
|
12215
12329
|
return restored.sort((a, b) => a.localeCompare(b));
|
|
12216
12330
|
}
|
|
12217
|
-
var MIGRATIONS, BACKED_UP, BACKED_UP_LEGACY;
|
|
12331
|
+
var MIGRATIONS, BACKED_UP, BACKED_UP_LEGACY, PRUNED_AFTER_SUCCESS;
|
|
12218
12332
|
var init_migrations = __esm(() => {
|
|
12219
12333
|
init_package();
|
|
12220
12334
|
init_fp_home();
|
|
12335
|
+
init_fp_config();
|
|
12221
12336
|
init_fp_reset();
|
|
12222
12337
|
MIGRATIONS = [
|
|
12223
12338
|
{
|
|
12224
12339
|
from: 1,
|
|
12225
12340
|
to: 3,
|
|
12226
12341
|
describe: "layout 1 → 3: carry the decision log out of cache/, keep the policy config in place, drop the layout-1 credential files",
|
|
12227
|
-
run: () => resetHome(1)
|
|
12342
|
+
run: () => resetHome(1, 3)
|
|
12228
12343
|
},
|
|
12229
12344
|
{
|
|
12230
12345
|
from: 2,
|
|
12231
12346
|
to: 3,
|
|
12232
12347
|
describe: "layout 2 → 3: carry config.toml and credentials.toml into JSON, move custom-policies/ back up into policies/, nest the policy config at the root",
|
|
12233
|
-
run: () => resetHome(2)
|
|
12348
|
+
run: () => resetHome(2, 3)
|
|
12349
|
+
},
|
|
12350
|
+
{
|
|
12351
|
+
from: 3,
|
|
12352
|
+
to: 4,
|
|
12353
|
+
describe: "layout 3 → 4: gather the audit's files under audit/ — auth.json becomes audit/session.json, next-audit.json becomes audit/reminder.json, state/audit-schedule.json becomes audit/schedule.json",
|
|
12354
|
+
run: migrateToLayout4
|
|
12234
12355
|
}
|
|
12235
12356
|
];
|
|
12236
12357
|
BACKED_UP = [
|
|
@@ -12245,7 +12366,13 @@ var init_migrations = __esm(() => {
|
|
|
12245
12366
|
{ at: legacy.policyConfig },
|
|
12246
12367
|
{ at: legacy.localPolicyConfig, as: "local-policies-policies-config.json" },
|
|
12247
12368
|
{ at: legacy.cloudCredentials },
|
|
12248
|
-
{ at: legacy.ingestCredentials }
|
|
12369
|
+
{ at: legacy.ingestCredentials },
|
|
12370
|
+
{ at: legacy.authJson },
|
|
12371
|
+
{ at: legacy.nextAudit },
|
|
12372
|
+
{ at: legacy.auditSchedule }
|
|
12373
|
+
];
|
|
12374
|
+
PRUNED_AFTER_SUCCESS = [
|
|
12375
|
+
{ as: basename4(legacy.authJson()), landsAt: auditSessionFile }
|
|
12249
12376
|
];
|
|
12250
12377
|
});
|
|
12251
12378
|
|
|
@@ -12272,7 +12399,7 @@ import {
|
|
|
12272
12399
|
mkdirSync as mkdirSync13,
|
|
12273
12400
|
readFileSync as readFileSync17,
|
|
12274
12401
|
readdirSync as readdirSync16,
|
|
12275
|
-
renameSync as
|
|
12402
|
+
renameSync as renameSync7,
|
|
12276
12403
|
rmSync as rmSync8,
|
|
12277
12404
|
rmdirSync as rmdirSync2,
|
|
12278
12405
|
statSync as statSync17,
|
|
@@ -12304,7 +12431,7 @@ function migrateConventionPolicies2() {
|
|
|
12304
12431
|
const name = `${prefix}${entry.name}`;
|
|
12305
12432
|
if (!existsSync22(target)) {
|
|
12306
12433
|
try {
|
|
12307
|
-
|
|
12434
|
+
renameSync7(source, target);
|
|
12308
12435
|
moved.push(name);
|
|
12309
12436
|
} catch {}
|
|
12310
12437
|
continue;
|
|
@@ -12341,7 +12468,7 @@ function migrateLegacySpool2() {
|
|
|
12341
12468
|
while (existsSync22(resolve17(to, name)))
|
|
12342
12469
|
name = `legacy-${n++}-${entry.name}`;
|
|
12343
12470
|
try {
|
|
12344
|
-
|
|
12471
|
+
renameSync7(resolve17(from, entry.name), resolve17(to, name));
|
|
12345
12472
|
moved.push(name);
|
|
12346
12473
|
} catch {
|
|
12347
12474
|
try {
|
|
@@ -12376,7 +12503,7 @@ function migrateHookActivity2() {
|
|
|
12376
12503
|
target = resolve17(to, name);
|
|
12377
12504
|
}
|
|
12378
12505
|
try {
|
|
12379
|
-
|
|
12506
|
+
renameSync7(source, target);
|
|
12380
12507
|
moved.push(name);
|
|
12381
12508
|
} catch {
|
|
12382
12509
|
try {
|
|
@@ -12584,7 +12711,7 @@ function readCarriedLegacyCredentials2() {
|
|
|
12584
12711
|
const creds = projectCredentials(raw);
|
|
12585
12712
|
return Object.keys(creds).length > 0 ? creds : null;
|
|
12586
12713
|
}
|
|
12587
|
-
function resetHome2(from) {
|
|
12714
|
+
function resetHome2(from, to = LAYOUT_VERSION) {
|
|
12588
12715
|
const migrated = migrateConventionPolicies2();
|
|
12589
12716
|
const activity = migrateHookActivity2();
|
|
12590
12717
|
const pendingPolicyConfig = readCarriedPolicyConfig2();
|
|
@@ -12609,7 +12736,7 @@ function resetHome2(from) {
|
|
|
12609
12736
|
writeConfig(pendingConfig);
|
|
12610
12737
|
if (telemetryOptOut)
|
|
12611
12738
|
updateConfig({ telemetry: { enabled: false } });
|
|
12612
|
-
writeVersionFile();
|
|
12739
|
+
writeVersionFile({ layout: to });
|
|
12613
12740
|
return { removed, migrated, activity, policyConfig, spooled, from };
|
|
12614
12741
|
}
|
|
12615
12742
|
async function drainSpoolAfterMigrating() {
|
|
@@ -12647,6 +12774,7 @@ async function checkLayoutForCli() {
|
|
|
12647
12774
|
const run = runMigrations2(state.found);
|
|
12648
12775
|
const { removed, migrated, activity } = run.outcome;
|
|
12649
12776
|
const pending3 = await drainSpoolAfterMigrating();
|
|
12777
|
+
const daemonHint = staleDaemonHint();
|
|
12650
12778
|
return {
|
|
12651
12779
|
state,
|
|
12652
12780
|
fatal: false,
|
|
@@ -12678,7 +12806,8 @@ async function checkLayoutForCli() {
|
|
|
12678
12806
|
`Step ${run.failed.from} → ${run.failed.to} did not finish: ${run.failed.error}`,
|
|
12679
12807
|
`The home is still marked layout ${state.found} and will be retried. Copies of`,
|
|
12680
12808
|
`your settings and enrolment were saved first, in ${migrationBackupDir(state.found)}.`
|
|
12681
|
-
] : []
|
|
12809
|
+
] : [],
|
|
12810
|
+
...daemonHint.length > 0 ? ["", ...daemonHint] : []
|
|
12682
12811
|
]
|
|
12683
12812
|
};
|
|
12684
12813
|
}
|
|
@@ -12743,8 +12872,23 @@ function staleDaemonHint() {
|
|
|
12743
12872
|
const skew = daemonVersionSkew();
|
|
12744
12873
|
if (!skew)
|
|
12745
12874
|
return [];
|
|
12875
|
+
let requiresDaemon = false;
|
|
12876
|
+
try {
|
|
12877
|
+
requiresDaemon = readConfig().daemon.configured;
|
|
12878
|
+
} catch {}
|
|
12879
|
+
if (requiresDaemon) {
|
|
12880
|
+
return [
|
|
12881
|
+
`[failproofai] daemon is ${skew.installed}, CLI is ${skew.expected}.`,
|
|
12882
|
+
`This machine is configured to REQUIRE the daemon. A daemon built against a`,
|
|
12883
|
+
`different on-disk layout refuses to start, and this version moved it — so the`,
|
|
12884
|
+
`next reboot or restart can leave the service down, which denies every tool`,
|
|
12885
|
+
`call until it is fixed.`,
|
|
12886
|
+
`Run \`failproofai update\` now to bring the daemon in line.`,
|
|
12887
|
+
``
|
|
12888
|
+
];
|
|
12889
|
+
}
|
|
12746
12890
|
return [
|
|
12747
|
-
`[failproofai] daemon is ${skew.installed}, CLI is ${skew.expected} — ` + `run \`failproofai
|
|
12891
|
+
`[failproofai] daemon is ${skew.installed}, CLI is ${skew.expected} — ` + `run \`failproofai update\` to update it.`,
|
|
12748
12892
|
``
|
|
12749
12893
|
];
|
|
12750
12894
|
} catch {
|
|
@@ -12785,13 +12929,13 @@ import { createHmac, randomUUID as randomUUID2 } from "node:crypto";
|
|
|
12785
12929
|
import { execSync as execSync4 } from "node:child_process";
|
|
12786
12930
|
import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, mkdirSync as mkdirSync14 } from "node:fs";
|
|
12787
12931
|
import { homedir as homedir20, platform, arch, hostname, cpus } from "node:os";
|
|
12788
|
-
import { join as
|
|
12932
|
+
import { join as join30 } from "node:path";
|
|
12789
12933
|
function hashToId3(raw) {
|
|
12790
12934
|
return createHmac("sha256", NAMESPACE3).update(raw).digest("hex");
|
|
12791
12935
|
}
|
|
12792
12936
|
function getInstanceId3() {
|
|
12793
|
-
const idDir =
|
|
12794
|
-
const idFile =
|
|
12937
|
+
const idDir = join30(homedir20(), ".failproofai");
|
|
12938
|
+
const idFile = join30(idDir, "instance-id");
|
|
12795
12939
|
try {
|
|
12796
12940
|
const existing = readFileSync18(idFile, "utf-8").trim();
|
|
12797
12941
|
if (existing)
|
|
@@ -13187,6 +13331,27 @@ function renderLaunchBanner(version3, stdout = process.stdout) {
|
|
|
13187
13331
|
""
|
|
13188
13332
|
];
|
|
13189
13333
|
}
|
|
13334
|
+
function step(message, summary, stdout = process.stdout) {
|
|
13335
|
+
const c = paint(colorsEnabled(stdout));
|
|
13336
|
+
const rows = summary === undefined ? [] : Array.isArray(summary) ? summary : [summary];
|
|
13337
|
+
if (!stdout.isTTY) {
|
|
13338
|
+
if (rows.length)
|
|
13339
|
+
stdout.write(`${message}: ${rows.join(" ")}
|
|
13340
|
+
`);
|
|
13341
|
+
return;
|
|
13342
|
+
}
|
|
13343
|
+
const cols = stdout.columns || 80;
|
|
13344
|
+
const lines = [c.dim(BAR), truncate(`${c.dim(STEP_DONE)} ${message}`, cols - 1)];
|
|
13345
|
+
for (const r of rows)
|
|
13346
|
+
lines.push(truncate(`${c.dim(BAR)} ${c.dim(r)}`, cols - 1));
|
|
13347
|
+
writeLines(stdout, lines);
|
|
13348
|
+
}
|
|
13349
|
+
function stepOpen(message, stdout = process.stdout) {
|
|
13350
|
+
const c = paint(colorsEnabled(stdout));
|
|
13351
|
+
if (!stdout.isTTY)
|
|
13352
|
+
return;
|
|
13353
|
+
writeLines(stdout, [c.dim(BAR), `${c.guide(STEP_ACTIVE)} ${c.bold(message)}`]);
|
|
13354
|
+
}
|
|
13190
13355
|
function outro(message, opts = {}, stdout = process.stdout) {
|
|
13191
13356
|
const c = paint(colorsEnabled(stdout));
|
|
13192
13357
|
const ok = opts.ok !== false;
|
|
@@ -13449,10 +13614,10 @@ function promptText(opts) {
|
|
|
13449
13614
|
const draw = (error) => {
|
|
13450
13615
|
const cols = stdout.columns || 80;
|
|
13451
13616
|
const shown = opts.mask ? "•".repeat(value.length) : value;
|
|
13452
|
-
const hint = opts.hint ? ` ${c.dim(opts.hint)}` : "";
|
|
13453
|
-
const line = truncate(`${c.bold(opts.message)} ${shown}${hint}`, cols - 1);
|
|
13617
|
+
const hint = opts.hint && value.length === 0 ? ` ${c.dim(opts.hint)}` : "";
|
|
13618
|
+
const line = truncate(`${opts.prefix ?? ""}${c.bold(opts.message)} ${shown}${hint}`, cols - 1);
|
|
13454
13619
|
const err = error ? `
|
|
13455
|
-
${truncate(c.warn(error), cols - 3)}` : "";
|
|
13620
|
+
${opts.prefix ?? " "}${truncate(c.warn(error), cols - 3)}` : "";
|
|
13456
13621
|
stdout.write(`\r\x1B[2K${line}${err}`);
|
|
13457
13622
|
if (err)
|
|
13458
13623
|
stdout.write("\x1B[1A");
|
|
@@ -23469,7 +23634,7 @@ var init_onboarding_lock = __esm(() => {
|
|
|
23469
23634
|
|
|
23470
23635
|
// lib/claude-sessions.ts
|
|
23471
23636
|
import { readdirSync as readdirSync17, statSync as statSync19, existsSync as existsSync29 } from "node:fs";
|
|
23472
|
-
import { join as
|
|
23637
|
+
import { join as join31, basename as basename6 } from "node:path";
|
|
23473
23638
|
function getClaudeProjectsRoot() {
|
|
23474
23639
|
return getClaudeProjectsPath();
|
|
23475
23640
|
}
|
|
@@ -23484,7 +23649,7 @@ function listClaudeProjects() {
|
|
|
23484
23649
|
return entries.filter((e) => e.isDirectory()).map((e) => ({
|
|
23485
23650
|
name: e.name,
|
|
23486
23651
|
cwd: decodeFolderName(e.name),
|
|
23487
|
-
path:
|
|
23652
|
+
path: join31(root, e.name)
|
|
23488
23653
|
}));
|
|
23489
23654
|
}
|
|
23490
23655
|
function listClaudeTranscripts(project) {
|
|
@@ -23500,7 +23665,7 @@ function listClaudeTranscripts(project) {
|
|
|
23500
23665
|
const sessionId = entry.name.slice(0, -".jsonl".length);
|
|
23501
23666
|
if (!UUID_RE2.test(sessionId))
|
|
23502
23667
|
continue;
|
|
23503
|
-
const transcriptPath =
|
|
23668
|
+
const transcriptPath = join31(project.path, entry.name);
|
|
23504
23669
|
try {
|
|
23505
23670
|
const s = statSync19(transcriptPath);
|
|
23506
23671
|
out.push({
|
|
@@ -23514,7 +23679,7 @@ function listClaudeTranscripts(project) {
|
|
|
23514
23679
|
});
|
|
23515
23680
|
} catch {}
|
|
23516
23681
|
} else if (entry.isDirectory() && UUID_RE2.test(entry.name)) {
|
|
23517
|
-
const subDir =
|
|
23682
|
+
const subDir = join31(project.path, entry.name, "subagents");
|
|
23518
23683
|
if (!existsSync29(subDir))
|
|
23519
23684
|
continue;
|
|
23520
23685
|
let subEntries;
|
|
@@ -23527,7 +23692,7 @@ function listClaudeTranscripts(project) {
|
|
|
23527
23692
|
if (!sub.isFile() || !sub.name.endsWith(".jsonl"))
|
|
23528
23693
|
continue;
|
|
23529
23694
|
const agentId = sub.name.slice(0, -".jsonl".length);
|
|
23530
|
-
const transcriptPath =
|
|
23695
|
+
const transcriptPath = join31(subDir, sub.name);
|
|
23531
23696
|
try {
|
|
23532
23697
|
const s = statSync19(transcriptPath);
|
|
23533
23698
|
out.push({
|
|
@@ -24790,8 +24955,8 @@ var init_features = __esm(() => {
|
|
|
24790
24955
|
|
|
24791
24956
|
// src/audit/cache.ts
|
|
24792
24957
|
import { createHash as createHash6 } from "node:crypto";
|
|
24793
|
-
import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync24, writeFileSync as writeFileSync17, chmodSync as
|
|
24794
|
-
import { join as
|
|
24958
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync24, writeFileSync as writeFileSync17, chmodSync as chmodSync5 } from "node:fs";
|
|
24959
|
+
import { join as join32 } from "node:path";
|
|
24795
24960
|
function getEngineVersion() {
|
|
24796
24961
|
if (cachedEngineVersion)
|
|
24797
24962
|
return cachedEngineVersion;
|
|
@@ -24811,7 +24976,7 @@ function getDetectorVersion() {
|
|
|
24811
24976
|
function getCachePathFor(transcriptPath) {
|
|
24812
24977
|
const root = auditCacheDir();
|
|
24813
24978
|
const key = createHash6("sha1").update(transcriptPath).digest("hex");
|
|
24814
|
-
return
|
|
24979
|
+
return join32(root, `${key}.json`);
|
|
24815
24980
|
}
|
|
24816
24981
|
function readCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes) {
|
|
24817
24982
|
if (sizeBytes === 0)
|
|
@@ -24856,7 +25021,7 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result)
|
|
|
24856
25021
|
};
|
|
24857
25022
|
writeFileSync17(cachePath, JSON.stringify(entry), { encoding: "utf-8", mode: 384 });
|
|
24858
25023
|
try {
|
|
24859
|
-
|
|
25024
|
+
chmodSync5(cachePath, 384);
|
|
24860
25025
|
} catch {}
|
|
24861
25026
|
} catch {}
|
|
24862
25027
|
}
|
|
@@ -25343,6 +25508,7 @@ var init_audit_lock = __esm(() => {
|
|
|
25343
25508
|
});
|
|
25344
25509
|
|
|
25345
25510
|
// src/audit/dashboard-cache.ts
|
|
25511
|
+
import { existsSync as existsSync31, readFileSync as readFileSync26 } from "node:fs";
|
|
25346
25512
|
function getCachePath() {
|
|
25347
25513
|
return auditDashboardFile();
|
|
25348
25514
|
}
|
|
@@ -25360,6 +25526,27 @@ function writeDashboardCache(params, result) {
|
|
|
25360
25526
|
return false;
|
|
25361
25527
|
}
|
|
25362
25528
|
}
|
|
25529
|
+
function readDashboardCacheMeta() {
|
|
25530
|
+
const cachePath = getCachePath();
|
|
25531
|
+
if (!existsSync31(cachePath))
|
|
25532
|
+
return null;
|
|
25533
|
+
try {
|
|
25534
|
+
const raw = readFileSync26(cachePath, "utf-8");
|
|
25535
|
+
const entry = JSON.parse(raw);
|
|
25536
|
+
if (!entry || entry.schemaVersion !== DASHBOARD_CACHE_SCHEMA_VERSION || typeof entry.cachedAt !== "string" || Number.isNaN(new Date(entry.cachedAt).getTime()))
|
|
25537
|
+
return null;
|
|
25538
|
+
const result = entry.result;
|
|
25539
|
+
const count = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
25540
|
+
return {
|
|
25541
|
+
cachedAt: entry.cachedAt,
|
|
25542
|
+
findings: count(result?.totals?.hits),
|
|
25543
|
+
sessionsScanned: count(result?.transcripts?.scanned),
|
|
25544
|
+
eventsScanned: count(result?.eventsScanned)
|
|
25545
|
+
};
|
|
25546
|
+
} catch {
|
|
25547
|
+
return null;
|
|
25548
|
+
}
|
|
25549
|
+
}
|
|
25363
25550
|
var DASHBOARD_CACHE_TTL_MINUTES, DASHBOARD_CACHE_SCHEMA_VERSION = 2;
|
|
25364
25551
|
var init_dashboard_cache = __esm(() => {
|
|
25365
25552
|
init_atomic_write();
|
|
@@ -25434,160 +25621,1095 @@ function openWhenReady(port, path3) {
|
|
|
25434
25621
|
var POLL_INTERVAL_MS = 150, POLL_TIMEOUT_MS = 30000;
|
|
25435
25622
|
var init_open_browser = () => {};
|
|
25436
25623
|
|
|
25437
|
-
//
|
|
25438
|
-
|
|
25439
|
-
|
|
25440
|
-
|
|
25441
|
-
|
|
25442
|
-
|
|
25443
|
-
|
|
25444
|
-
|
|
25445
|
-
|
|
25446
|
-
|
|
25624
|
+
// lib/telemetry.ts
|
|
25625
|
+
function trackEvent(name, properties) {
|
|
25626
|
+
if (!isTelemetryEnabled())
|
|
25627
|
+
return;
|
|
25628
|
+
const client = globalThis.__FAILPROOFAI_POSTHOG__;
|
|
25629
|
+
if (!client)
|
|
25630
|
+
return;
|
|
25631
|
+
client.capture({
|
|
25632
|
+
distinctId: getInstanceId2(),
|
|
25633
|
+
event: name,
|
|
25634
|
+
properties: { ...properties, $lib: "failproofai", failproofai_version: version2, product: POSTHOG_PRODUCT }
|
|
25635
|
+
});
|
|
25447
25636
|
}
|
|
25448
|
-
|
|
25449
|
-
|
|
25450
|
-
|
|
25451
|
-
|
|
25452
|
-
|
|
25453
|
-
|
|
25454
|
-
|
|
25455
|
-
|
|
25456
|
-
|
|
25457
|
-
|
|
25458
|
-
|
|
25459
|
-
|
|
25460
|
-
|
|
25461
|
-
|
|
25462
|
-
|
|
25463
|
-
|
|
25464
|
-
|
|
25465
|
-
|
|
25466
|
-
|
|
25467
|
-
|
|
25468
|
-
|
|
25469
|
-
|
|
25470
|
-
|
|
25471
|
-
|
|
25472
|
-
|
|
25473
|
-
continue;
|
|
25474
|
-
}
|
|
25475
|
-
if (flag === "--disable-telemetry") {
|
|
25476
|
-
disableTelemetry = true;
|
|
25477
|
-
args.splice(i, 1);
|
|
25478
|
-
i--;
|
|
25479
|
-
continue;
|
|
25480
|
-
}
|
|
25481
|
-
if (flag === "--allowed-origins") {
|
|
25482
|
-
const { value, spliceCount } = parseStringFlag(flag, "a comma-separated list of origins", inlineValue, args, i);
|
|
25483
|
-
allowedDevOrigins = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
25484
|
-
args.splice(i, spliceCount);
|
|
25485
|
-
i--;
|
|
25486
|
-
continue;
|
|
25637
|
+
var init_telemetry = __esm(() => {
|
|
25638
|
+
init_telemetry_id2();
|
|
25639
|
+
init_telemetry_enabled();
|
|
25640
|
+
init_package();
|
|
25641
|
+
});
|
|
25642
|
+
|
|
25643
|
+
// lib/auth/api-server-client.ts
|
|
25644
|
+
function getApiBase() {
|
|
25645
|
+
const raw = process.env.FAILPROOF_API_URL ?? process.env.FAILPROOFAI_API_URL ?? DEFAULT_API_BASE;
|
|
25646
|
+
return raw.replace(/\/+$/, "");
|
|
25647
|
+
}
|
|
25648
|
+
async function parseError(res) {
|
|
25649
|
+
let body = {};
|
|
25650
|
+
try {
|
|
25651
|
+
body = await res.json();
|
|
25652
|
+
} catch {}
|
|
25653
|
+
const code = body.code ?? `http_${res.status}`;
|
|
25654
|
+
const message = body.message ?? body.detail ?? res.statusText ?? "request failed";
|
|
25655
|
+
let retryAfterSecs = body.retry_after_secs;
|
|
25656
|
+
if (retryAfterSecs === undefined) {
|
|
25657
|
+
const h = res.headers.get("retry-after");
|
|
25658
|
+
if (h) {
|
|
25659
|
+
const n = Number(h);
|
|
25660
|
+
if (Number.isFinite(n))
|
|
25661
|
+
retryAfterSecs = n;
|
|
25487
25662
|
}
|
|
25488
|
-
|
|
25489
|
-
|
|
25490
|
-
|
|
25491
|
-
|
|
25492
|
-
|
|
25493
|
-
|
|
25663
|
+
}
|
|
25664
|
+
if (retryAfterSecs !== undefined) {
|
|
25665
|
+
retryAfterSecs = Math.max(0, Math.min(86400, retryAfterSecs));
|
|
25666
|
+
}
|
|
25667
|
+
return new AuthApiError(res.status, code, message, retryAfterSecs);
|
|
25668
|
+
}
|
|
25669
|
+
function timeoutSignal(extra) {
|
|
25670
|
+
const t = AbortSignal.timeout(REQUEST_TIMEOUT_MS);
|
|
25671
|
+
if (!extra)
|
|
25672
|
+
return t;
|
|
25673
|
+
const anyFn = AbortSignal.any;
|
|
25674
|
+
if (anyFn)
|
|
25675
|
+
return anyFn([t, extra]);
|
|
25676
|
+
const composed = new AbortController;
|
|
25677
|
+
const onAbort = (s) => composed.abort(s.reason);
|
|
25678
|
+
if (t.aborted)
|
|
25679
|
+
onAbort(t);
|
|
25680
|
+
else
|
|
25681
|
+
t.addEventListener("abort", () => onAbort(t), { once: true });
|
|
25682
|
+
if (extra.aborted)
|
|
25683
|
+
onAbort(extra);
|
|
25684
|
+
else
|
|
25685
|
+
extra.addEventListener("abort", () => onAbort(extra), { once: true });
|
|
25686
|
+
return composed.signal;
|
|
25687
|
+
}
|
|
25688
|
+
function pathFromUrl(url) {
|
|
25689
|
+
try {
|
|
25690
|
+
return new URL(url).pathname;
|
|
25691
|
+
} catch {
|
|
25692
|
+
return url;
|
|
25693
|
+
}
|
|
25694
|
+
}
|
|
25695
|
+
async function fetchWithTimeout2(url, init) {
|
|
25696
|
+
try {
|
|
25697
|
+
return await fetch(url, { ...init, signal: timeoutSignal(init.signal ?? undefined) });
|
|
25698
|
+
} catch (err) {
|
|
25699
|
+
const isTimeout = isAbortError(err);
|
|
25700
|
+
trackEvent("api_server_unreachable", {
|
|
25701
|
+
kind: isTimeout ? "timeout" : "network",
|
|
25702
|
+
path: pathFromUrl(url),
|
|
25703
|
+
method: typeof init.method === "string" ? init.method : "GET"
|
|
25704
|
+
});
|
|
25705
|
+
if (isTimeout) {
|
|
25706
|
+
throw new AuthApiError(0, "timeout", `request to ${url} timed out after ${REQUEST_TIMEOUT_MS}ms`);
|
|
25494
25707
|
}
|
|
25708
|
+
throw err;
|
|
25495
25709
|
}
|
|
25496
|
-
return { loggingLevel, disableTelemetry, allowedDevOrigins, host, remainingArgs: args };
|
|
25497
25710
|
}
|
|
25498
|
-
|
|
25711
|
+
async function postJson(path3, body, init) {
|
|
25712
|
+
const headers = { "content-type": "application/json" };
|
|
25713
|
+
if (init?.accessToken)
|
|
25714
|
+
headers["authorization"] = `Bearer ${init.accessToken}`;
|
|
25715
|
+
const res = await fetchWithTimeout2(`${getApiBase()}${path3}`, {
|
|
25716
|
+
method: "POST",
|
|
25717
|
+
headers,
|
|
25718
|
+
body: JSON.stringify(body)
|
|
25719
|
+
});
|
|
25720
|
+
if (res.status === 204)
|
|
25721
|
+
return;
|
|
25722
|
+
if (!res.ok)
|
|
25723
|
+
throw await parseError(res);
|
|
25724
|
+
return await res.json();
|
|
25725
|
+
}
|
|
25726
|
+
async function requestLoginCode(email) {
|
|
25727
|
+
return postJson("/v0/auth/login/request", { email });
|
|
25728
|
+
}
|
|
25729
|
+
async function verifyLoginCode(email, code) {
|
|
25730
|
+
return postJson("/v0/auth/login/verify", { email, code });
|
|
25731
|
+
}
|
|
25732
|
+
async function refreshAccessToken(refreshToken) {
|
|
25733
|
+
return postJson("/v0/auth/token/refresh", {
|
|
25734
|
+
refresh_token: refreshToken
|
|
25735
|
+
});
|
|
25736
|
+
}
|
|
25737
|
+
async function submitAuditReport(accessToken, body) {
|
|
25738
|
+
return postJson("/v0/audit-reports", body, { accessToken });
|
|
25739
|
+
}
|
|
25740
|
+
var DEFAULT_API_BASE = "https://api.befailproof.ai", AuthApiError, REQUEST_TIMEOUT_MS = 1e4;
|
|
25741
|
+
var init_api_server_client = __esm(() => {
|
|
25742
|
+
init_telemetry();
|
|
25743
|
+
AuthApiError = class AuthApiError extends Error {
|
|
25744
|
+
status;
|
|
25745
|
+
code;
|
|
25746
|
+
retryAfterSecs;
|
|
25747
|
+
constructor(status, code, message, retryAfterSecs) {
|
|
25748
|
+
super(message);
|
|
25749
|
+
this.status = status;
|
|
25750
|
+
this.code = code;
|
|
25751
|
+
this.retryAfterSecs = retryAfterSecs;
|
|
25752
|
+
this.name = "AuthApiError";
|
|
25753
|
+
}
|
|
25754
|
+
};
|
|
25755
|
+
});
|
|
25499
25756
|
|
|
25500
|
-
//
|
|
25501
|
-
import { existsSync as
|
|
25502
|
-
import {
|
|
25503
|
-
|
|
25504
|
-
|
|
25505
|
-
|
|
25757
|
+
// lib/auth/auth-store.ts
|
|
25758
|
+
import { existsSync as existsSync32, readFileSync as readFileSync27, readdirSync as readdirSync18, rmSync as rmSync12 } from "node:fs";
|
|
25759
|
+
import { join as join33, resolve as resolve22 } from "node:path";
|
|
25760
|
+
function getAuthFilePath() {
|
|
25761
|
+
const override = process.env.FAILPROOFAI_AUTH_DIR;
|
|
25762
|
+
return override ? join33(override, "session.json") : auditSessionFile();
|
|
25763
|
+
}
|
|
25764
|
+
function readAuth() {
|
|
25765
|
+
const p = getAuthFilePath();
|
|
25766
|
+
if (!existsSync32(p))
|
|
25767
|
+
return null;
|
|
25506
25768
|
try {
|
|
25507
|
-
|
|
25508
|
-
|
|
25509
|
-
|
|
25769
|
+
const raw = readFileSync27(p, "utf-8");
|
|
25770
|
+
const parsed = JSON.parse(raw);
|
|
25771
|
+
if (typeof parsed.access_token !== "string" || typeof parsed.refresh_token !== "string" || typeof parsed.access_expires_at !== "number" || typeof parsed.user !== "object" || !parsed.user || typeof parsed.user.id !== "string" || typeof parsed.user.email !== "string") {
|
|
25772
|
+
return null;
|
|
25510
25773
|
}
|
|
25511
|
-
|
|
25512
|
-
|
|
25513
|
-
|
|
25514
|
-
|
|
25515
|
-
|
|
25516
|
-
|
|
25517
|
-
|
|
25518
|
-
|
|
25774
|
+
return {
|
|
25775
|
+
access_token: parsed.access_token,
|
|
25776
|
+
refresh_token: parsed.refresh_token,
|
|
25777
|
+
access_expires_at: parsed.access_expires_at,
|
|
25778
|
+
refresh_expires_at: typeof parsed.refresh_expires_at === "number" ? parsed.refresh_expires_at : parsed.access_expires_at,
|
|
25779
|
+
user: {
|
|
25780
|
+
id: parsed.user.id,
|
|
25781
|
+
email: parsed.user.email
|
|
25519
25782
|
}
|
|
25520
|
-
|
|
25521
|
-
if (parent === dir)
|
|
25522
|
-
return null;
|
|
25523
|
-
dir = parent;
|
|
25524
|
-
}
|
|
25783
|
+
};
|
|
25525
25784
|
} catch {
|
|
25526
25785
|
return null;
|
|
25527
25786
|
}
|
|
25528
25787
|
}
|
|
25529
|
-
function
|
|
25530
|
-
|
|
25531
|
-
|
|
25788
|
+
function writeAuth(auth) {
|
|
25789
|
+
writeJsonAtomically(getAuthFilePath(), auth);
|
|
25790
|
+
}
|
|
25791
|
+
function deleteAuth() {
|
|
25792
|
+
const p = getAuthFilePath();
|
|
25793
|
+
if (existsSync32(p))
|
|
25794
|
+
rmSync12(p, { force: true });
|
|
25532
25795
|
try {
|
|
25533
|
-
const
|
|
25534
|
-
|
|
25535
|
-
|
|
25536
|
-
|
|
25796
|
+
const root = migrationsDir();
|
|
25797
|
+
if (!existsSync32(root))
|
|
25798
|
+
return;
|
|
25799
|
+
for (const entry of readdirSync18(root)) {
|
|
25800
|
+
if (!entry.startsWith("backup-layout"))
|
|
25801
|
+
continue;
|
|
25802
|
+
const copy = resolve22(root, entry, "auth.json");
|
|
25803
|
+
if (existsSync32(copy))
|
|
25804
|
+
rmSync12(copy, { force: true });
|
|
25805
|
+
}
|
|
25806
|
+
} catch {}
|
|
25807
|
+
}
|
|
25808
|
+
function authFromTokenResponse(token, existingUser) {
|
|
25809
|
+
const now = Math.floor(Date.now() / 1000);
|
|
25810
|
+
const user = token.user ?? existingUser;
|
|
25811
|
+
if (!user) {
|
|
25812
|
+
throw new Error("authFromTokenResponse: missing user identity");
|
|
25537
25813
|
}
|
|
25814
|
+
return {
|
|
25815
|
+
access_token: token.access_token,
|
|
25816
|
+
refresh_token: token.refresh_token,
|
|
25817
|
+
access_expires_at: now + token.access_expires_in,
|
|
25818
|
+
refresh_expires_at: now + token.refresh_expires_in,
|
|
25819
|
+
user
|
|
25820
|
+
};
|
|
25538
25821
|
}
|
|
25539
|
-
function
|
|
25822
|
+
async function dedupedRefresh(auth) {
|
|
25823
|
+
const existing = inFlightRefreshes.get(auth.refresh_token);
|
|
25824
|
+
if (existing)
|
|
25825
|
+
return existing;
|
|
25826
|
+
const p = (async () => {
|
|
25827
|
+
const refreshed = await refreshAccessToken(auth.refresh_token);
|
|
25828
|
+
const next = authFromTokenResponse(refreshed, auth.user);
|
|
25829
|
+
writeAuth(next);
|
|
25830
|
+
return next;
|
|
25831
|
+
})();
|
|
25832
|
+
inFlightRefreshes.set(auth.refresh_token, p);
|
|
25540
25833
|
try {
|
|
25541
|
-
|
|
25542
|
-
|
|
25543
|
-
|
|
25544
|
-
return null;
|
|
25545
|
-
const first = (res.stdout || "").split(/\r?\n/).find((l) => l.trim().length > 0);
|
|
25546
|
-
return first ? first.trim() : null;
|
|
25547
|
-
} catch {
|
|
25548
|
-
return null;
|
|
25834
|
+
return await p;
|
|
25835
|
+
} finally {
|
|
25836
|
+
inFlightRefreshes.delete(auth.refresh_token);
|
|
25549
25837
|
}
|
|
25550
25838
|
}
|
|
25551
|
-
function
|
|
25839
|
+
async function getValidAccessToken() {
|
|
25840
|
+
const auth = readAuth();
|
|
25841
|
+
if (!auth)
|
|
25842
|
+
return null;
|
|
25843
|
+
const now = Math.floor(Date.now() / 1000);
|
|
25844
|
+
if (auth.access_expires_at - now > REFRESH_LEEWAY_SECS)
|
|
25845
|
+
return auth;
|
|
25552
25846
|
try {
|
|
25553
|
-
|
|
25554
|
-
|
|
25555
|
-
|
|
25556
|
-
|
|
25557
|
-
if (!root)
|
|
25847
|
+
return await dedupedRefresh(auth);
|
|
25848
|
+
} catch (err) {
|
|
25849
|
+
if (err instanceof AuthApiError && err.status === 401) {
|
|
25850
|
+
deleteAuth();
|
|
25558
25851
|
return null;
|
|
25559
|
-
|
|
25560
|
-
return existsSync31(resolve23(candidate, "package.json")) ? candidate : null;
|
|
25561
|
-
} catch {
|
|
25852
|
+
}
|
|
25562
25853
|
return null;
|
|
25563
25854
|
}
|
|
25564
25855
|
}
|
|
25565
|
-
|
|
25566
|
-
|
|
25567
|
-
|
|
25568
|
-
|
|
25569
|
-
|
|
25570
|
-
|
|
25856
|
+
var REFRESH_LEEWAY_SECS = 60, inFlightRefreshes;
|
|
25857
|
+
var init_auth_store = __esm(() => {
|
|
25858
|
+
init_atomic_write();
|
|
25859
|
+
init_fp_home();
|
|
25860
|
+
init_api_server_client();
|
|
25861
|
+
inFlightRefreshes = new Map;
|
|
25862
|
+
});
|
|
25863
|
+
|
|
25864
|
+
// src/audit/redact-example.ts
|
|
25865
|
+
import { homedir as homedir25 } from "node:os";
|
|
25866
|
+
function maskTruncatedSecret(input) {
|
|
25867
|
+
for (const [pattern, label] of SECRET_PREFIXES) {
|
|
25868
|
+
if (pattern.test(input)) {
|
|
25869
|
+
return input.replace(pattern, `[REDACTED: ${label}]`);
|
|
25870
|
+
}
|
|
25571
25871
|
}
|
|
25872
|
+
return input;
|
|
25572
25873
|
}
|
|
25573
|
-
function
|
|
25574
|
-
|
|
25575
|
-
|
|
25576
|
-
|
|
25577
|
-
|
|
25578
|
-
|
|
25579
|
-
|
|
25580
|
-
|
|
25874
|
+
function isSecretName(name) {
|
|
25875
|
+
const upper = name.toUpperCase();
|
|
25876
|
+
if (SECRET_NAME_SUBSTRINGS.some((word) => upper.includes(word)))
|
|
25877
|
+
return true;
|
|
25878
|
+
return upper.split("_").some((part) => SECRET_NAME_COMPONENTS.includes(part));
|
|
25879
|
+
}
|
|
25880
|
+
function maskAssignedSecrets(input) {
|
|
25881
|
+
let out = input.replace(ASSIGNMENT_RE, (match, name, value) => {
|
|
25882
|
+
if (!isSecretName(name))
|
|
25883
|
+
return match;
|
|
25884
|
+
if (value.startsWith("[REDACTED"))
|
|
25885
|
+
return match;
|
|
25886
|
+
return `${name}=[REDACTED: assigned secret]`;
|
|
25887
|
+
});
|
|
25888
|
+
out = out.replace(URL_CREDENTIALS_RE, "$1[REDACTED: URL credentials]@");
|
|
25889
|
+
out = out.replace(BASIC_AUTH_FLAG_RE, "$1[REDACTED: basic auth]");
|
|
25890
|
+
return out;
|
|
25891
|
+
}
|
|
25892
|
+
function maskSecrets(input) {
|
|
25893
|
+
let out = input;
|
|
25894
|
+
for (const [pattern, label] of SECRET_PATTERNS) {
|
|
25895
|
+
const global = new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
|
|
25896
|
+
out = out.replace(global, `[REDACTED: ${label}]`);
|
|
25581
25897
|
}
|
|
25582
|
-
return
|
|
25898
|
+
return out;
|
|
25583
25899
|
}
|
|
25584
|
-
function
|
|
25585
|
-
const
|
|
25586
|
-
|
|
25587
|
-
|
|
25588
|
-
|
|
25589
|
-
|
|
25590
|
-
|
|
25900
|
+
function shortenPaths(input, home = homedir25()) {
|
|
25901
|
+
const homeRoot = home.replace(/\/+$/, "");
|
|
25902
|
+
return input.replace(ABSOLUTE_PATH_RE, (match, offset, whole) => {
|
|
25903
|
+
if (PUBLIC_PATH_ROOTS.some((root2) => match.startsWith(root2)))
|
|
25904
|
+
return match;
|
|
25905
|
+
if (offset > 0 && whole[offset - 1] === "/" && /[a-z][a-z0-9+.\-]*:$/i.test(whole.slice(0, offset - 1))) {
|
|
25906
|
+
const urlSegments = match.split("/").filter(Boolean);
|
|
25907
|
+
if (urlSegments.length <= 1)
|
|
25908
|
+
return match;
|
|
25909
|
+
const host = urlSegments[0];
|
|
25910
|
+
const leaf = urlSegments[urlSegments.length - 1];
|
|
25911
|
+
const elided = urlSegments.length > 2 ? "/…" : "";
|
|
25912
|
+
return `/${host}${elided}/${leaf}${match.endsWith("/") ? "/" : ""}`;
|
|
25913
|
+
}
|
|
25914
|
+
const trailingSlash = match.endsWith("/");
|
|
25915
|
+
const segments = match.split("/").filter(Boolean);
|
|
25916
|
+
if (segments.length === 0)
|
|
25917
|
+
return match;
|
|
25918
|
+
const basename7 = segments[segments.length - 1];
|
|
25919
|
+
const kept = segments.slice(Math.max(0, segments.length - 1 - KEPT_PARENT_SEGMENTS), segments.length - 1);
|
|
25920
|
+
const matchRoot = match.replace(/\/+$/, "");
|
|
25921
|
+
const underHome = homeRoot.length > 0 && (matchRoot === homeRoot || matchRoot.startsWith(`${homeRoot}/`));
|
|
25922
|
+
if (matchRoot === homeRoot && homeRoot.length > 0) {
|
|
25923
|
+
return trailingSlash ? "~/" : "~";
|
|
25924
|
+
}
|
|
25925
|
+
const root = underHome ? "~" : "";
|
|
25926
|
+
const middle = segments.length - kept.length - 1 > 0 ? "/…" : "";
|
|
25927
|
+
const tail = [...kept, basename7].join("/");
|
|
25928
|
+
return `${root}${middle}/${tail}${trailingSlash ? "/" : ""}`;
|
|
25929
|
+
});
|
|
25930
|
+
}
|
|
25931
|
+
function redactExample(input, home = homedir25()) {
|
|
25932
|
+
const masked = maskAssignedSecrets(maskTruncatedSecret(maskSecrets(input)));
|
|
25933
|
+
const shortened = shortenPaths(masked, home);
|
|
25934
|
+
const collapsed = shortened.replace(/\s+/g, " ").trim();
|
|
25935
|
+
return collapsed.length > REDACTED_EXAMPLE_MAX_CHARS ? `${collapsed.slice(0, REDACTED_EXAMPLE_MAX_CHARS - 1)}…` : collapsed;
|
|
25936
|
+
}
|
|
25937
|
+
var REDACTED_EXAMPLE_MAX_CHARS = 160, KEPT_PARENT_SEGMENTS = 0, ABSOLUTE_PATH_RE, PUBLIC_PATH_ROOTS, SECRET_PREFIXES, SECRET_NAME_SUBSTRINGS, SECRET_NAME_COMPONENTS, ASSIGNMENT_RE, URL_CREDENTIALS_RE, BASIC_AUTH_FLAG_RE;
|
|
25938
|
+
var init_redact_example = __esm(() => {
|
|
25939
|
+
init_builtin_policies();
|
|
25940
|
+
ABSOLUTE_PATH_RE = /(?:\/[\w.\-@+]+){2,}\/?/g;
|
|
25941
|
+
PUBLIC_PATH_ROOTS = ["/dev/", "/proc/", "/sys/"];
|
|
25942
|
+
SECRET_PREFIXES = [
|
|
25943
|
+
[/(?<!\w)(?:Authorization:\s*)?Bearer\s+\S*$/i, "bearer token"],
|
|
25944
|
+
[/(?<!\w)sk-ant-\S*$/, "Anthropic API key"],
|
|
25945
|
+
[/(?<!\w)sk-proj-\S*$/, "OpenAI project API key"],
|
|
25946
|
+
[/(?<!\w)sk-\S*$/, "OpenAI API key"],
|
|
25947
|
+
[/(?<!\w)ghp_\S*$/, "GitHub personal access token"],
|
|
25948
|
+
[/(?<!\w)github_pat_\S*$/, "GitHub fine-grained token"],
|
|
25949
|
+
[/(?<!\w)AKIA\S*$/, "AWS access key ID"],
|
|
25950
|
+
[/(?<!\w)sk_live_\S*$/, "Stripe live secret key"],
|
|
25951
|
+
[/(?<!\w)sk_test_\S*$/, "Stripe test secret key"],
|
|
25952
|
+
[/(?<!\w)AIza\S*$/, "Google API key"],
|
|
25953
|
+
[/-----BEGIN\s[A-Z ]*$/, "private key"]
|
|
25954
|
+
];
|
|
25955
|
+
SECRET_NAME_SUBSTRINGS = [
|
|
25956
|
+
"TOKEN",
|
|
25957
|
+
"SECRET",
|
|
25958
|
+
"PASSWORD",
|
|
25959
|
+
"PASSWD",
|
|
25960
|
+
"CREDENTIAL",
|
|
25961
|
+
"APIKEY",
|
|
25962
|
+
"PRIVATEKEY"
|
|
25963
|
+
];
|
|
25964
|
+
SECRET_NAME_COMPONENTS = ["KEY", "PASS", "AUTH", "PAT", "SIG", "SIGNATURE", "SESSION", "COOKIE"];
|
|
25965
|
+
ASSIGNMENT_RE = /\b([A-Za-z_][A-Za-z0-9_]*)=("[^"]*"|'[^']*'|[^\s;|&"']+)/g;
|
|
25966
|
+
URL_CREDENTIALS_RE = /([a-z][a-z0-9+.\-]*:\/\/)[^\s/:@]+:[^\s/@]+@/gi;
|
|
25967
|
+
BASIC_AUTH_FLAG_RE = /((?:^|\s)(?:-u|--user)[\s=])\S+:\S+/g;
|
|
25968
|
+
});
|
|
25969
|
+
|
|
25970
|
+
// src/audit/harm-report.ts
|
|
25971
|
+
function shortName2(name) {
|
|
25972
|
+
const slash = name.indexOf("/");
|
|
25973
|
+
return slash === -1 ? name : name.slice(slash + 1);
|
|
25974
|
+
}
|
|
25975
|
+
function isHarmful(count) {
|
|
25976
|
+
if (count.source !== "builtin")
|
|
25977
|
+
return false;
|
|
25978
|
+
const short = shortName2(count.name);
|
|
25979
|
+
return HARMFUL_SEVERITIES.has(count.severity) || ALSO_HARMFUL.has(short);
|
|
25980
|
+
}
|
|
25981
|
+
function ts(value) {
|
|
25982
|
+
if (!value)
|
|
25983
|
+
return null;
|
|
25984
|
+
const n = Date.parse(value);
|
|
25985
|
+
return Number.isFinite(n) ? n : null;
|
|
25986
|
+
}
|
|
25987
|
+
function selectHarmful(result, from, to, opts = {}) {
|
|
25988
|
+
const includeUnplaceable = opts.includeUnplaceable ?? from === undefined;
|
|
25989
|
+
const fromMs = from ? from.getTime() : null;
|
|
25990
|
+
const toMs2 = to.getTime();
|
|
25991
|
+
const out = [];
|
|
25992
|
+
for (const count of result.results) {
|
|
25993
|
+
if (!isHarmful(count))
|
|
25994
|
+
continue;
|
|
25995
|
+
const last = ts(count.lastSeen);
|
|
25996
|
+
const first = ts(count.firstSeen);
|
|
25997
|
+
if (fromMs !== null && last !== null && last <= fromMs)
|
|
25998
|
+
continue;
|
|
25999
|
+
if (first !== null && first > toMs2)
|
|
26000
|
+
continue;
|
|
26001
|
+
const inWindow = count.examples.filter((e) => {
|
|
26002
|
+
const at2 = ts(e.timestamp);
|
|
26003
|
+
if (at2 === null)
|
|
26004
|
+
return includeUnplaceable;
|
|
26005
|
+
if (fromMs !== null && at2 <= fromMs)
|
|
26006
|
+
return false;
|
|
26007
|
+
return at2 <= toMs2;
|
|
26008
|
+
});
|
|
26009
|
+
const unplaceable = last === null && first === null;
|
|
26010
|
+
if (unplaceable && !includeUnplaceable)
|
|
26011
|
+
continue;
|
|
26012
|
+
const afterLowerEdge = fromMs === null || first !== null && first > fromMs;
|
|
26013
|
+
const beforeUpperEdge = last !== null && last <= toMs2;
|
|
26014
|
+
const wholly = unplaceable || afterLowerEdge && beforeUpperEdge;
|
|
26015
|
+
const floor = beforeUpperEdge ? 1 : 0;
|
|
26016
|
+
const hits = wholly ? count.hits : Math.max(inWindow.length, floor);
|
|
26017
|
+
if (hits <= 0)
|
|
26018
|
+
continue;
|
|
26019
|
+
out.push({
|
|
26020
|
+
policy: shortName2(count.name),
|
|
26021
|
+
category: count.category,
|
|
26022
|
+
title: count.displayTitle ?? "",
|
|
26023
|
+
hits,
|
|
26024
|
+
first_seen: count.firstSeen,
|
|
26025
|
+
last_seen: count.lastSeen,
|
|
26026
|
+
examples: inWindow.map((e) => redactExample(e.example)).filter((e) => e.length > 0)
|
|
26027
|
+
});
|
|
26028
|
+
}
|
|
26029
|
+
out.sort((a, b) => b.hits - a.hits);
|
|
26030
|
+
return out;
|
|
26031
|
+
}
|
|
26032
|
+
function buildHarmReport(result, lastReportedAt, intervalDays) {
|
|
26033
|
+
const to = new Date(Date.parse(result.scannedAt));
|
|
26034
|
+
const windowTo = Number.isFinite(to.getTime()) ? to : new Date;
|
|
26035
|
+
const watermark = ts(lastReportedAt);
|
|
26036
|
+
const isFirstReport = watermark === null;
|
|
26037
|
+
const oneInterval = Math.max(1, intervalDays) * 86400000;
|
|
26038
|
+
const fallbackFrom = windowTo.getTime() - oneInterval;
|
|
26039
|
+
const from = new Date(isFirstReport || watermark >= windowTo.getTime() ? fallbackFrom : watermark);
|
|
26040
|
+
return {
|
|
26041
|
+
window_from: from.toISOString(),
|
|
26042
|
+
window_to: windowTo.toISOString(),
|
|
26043
|
+
harmful: selectHarmful(result, from, windowTo, { includeUnplaceable: isFirstReport })
|
|
26044
|
+
};
|
|
26045
|
+
}
|
|
26046
|
+
var HARMFUL_SEVERITIES, ALSO_HARMFUL;
|
|
26047
|
+
var init_harm_report = __esm(() => {
|
|
26048
|
+
init_redact_example();
|
|
26049
|
+
HARMFUL_SEVERITIES = new Set(["deny", "sanitize"]);
|
|
26050
|
+
ALSO_HARMFUL = new Set(["protect-env-vars"]);
|
|
26051
|
+
});
|
|
26052
|
+
|
|
26053
|
+
// src/audit/machine-store.ts
|
|
26054
|
+
import { existsSync as existsSync33, readFileSync as readFileSync28, rmSync as rmSync13 } from "node:fs";
|
|
26055
|
+
import { hostname as hostname5 } from "node:os";
|
|
26056
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
26057
|
+
function readMachineIdentity(home) {
|
|
26058
|
+
const path3 = auditMachineFile(home);
|
|
26059
|
+
if (!existsSync33(path3))
|
|
26060
|
+
return null;
|
|
26061
|
+
try {
|
|
26062
|
+
const parsed = JSON.parse(readFileSync28(path3, "utf8"));
|
|
26063
|
+
if (typeof parsed.machine_id !== "string" || !parsed.machine_id)
|
|
26064
|
+
return null;
|
|
26065
|
+
return {
|
|
26066
|
+
machine_id: parsed.machine_id,
|
|
26067
|
+
last_reported_at: typeof parsed.last_reported_at === "string" ? parsed.last_reported_at : undefined,
|
|
26068
|
+
created_at: typeof parsed.created_at === "string" ? parsed.created_at : new Date(0).toISOString()
|
|
26069
|
+
};
|
|
26070
|
+
} catch {
|
|
26071
|
+
return null;
|
|
26072
|
+
}
|
|
26073
|
+
}
|
|
26074
|
+
function ensureMachineIdentity(home) {
|
|
26075
|
+
const existing = readMachineIdentity(home);
|
|
26076
|
+
if (existing)
|
|
26077
|
+
return existing;
|
|
26078
|
+
const fresh = {
|
|
26079
|
+
machine_id: randomUUID4(),
|
|
26080
|
+
created_at: new Date().toISOString()
|
|
26081
|
+
};
|
|
26082
|
+
writeJsonAtomically(auditMachineFile(home), fresh);
|
|
26083
|
+
return fresh;
|
|
26084
|
+
}
|
|
26085
|
+
function recordReportWatermark(nextWindowFrom, home) {
|
|
26086
|
+
const current = ensureMachineIdentity(home);
|
|
26087
|
+
writeJsonAtomically(auditMachineFile(home), {
|
|
26088
|
+
...current,
|
|
26089
|
+
last_reported_at: nextWindowFrom
|
|
26090
|
+
});
|
|
26091
|
+
}
|
|
26092
|
+
function machineLabel() {
|
|
26093
|
+
try {
|
|
26094
|
+
const h = hostname5().trim();
|
|
26095
|
+
return h.length > 0 ? h : undefined;
|
|
26096
|
+
} catch {
|
|
26097
|
+
return;
|
|
26098
|
+
}
|
|
26099
|
+
}
|
|
26100
|
+
var init_machine_store = __esm(() => {
|
|
26101
|
+
init_atomic_write();
|
|
26102
|
+
init_fp_home();
|
|
26103
|
+
});
|
|
26104
|
+
|
|
26105
|
+
// src/audit/report-harm.ts
|
|
26106
|
+
async function reportHarm(result) {
|
|
26107
|
+
let auto = false;
|
|
26108
|
+
let consentedAt;
|
|
26109
|
+
let intervalDays = 7;
|
|
26110
|
+
try {
|
|
26111
|
+
const config = readConfig();
|
|
26112
|
+
auto = config.audit.auto;
|
|
26113
|
+
consentedAt = config.audit.reportsConsentedAt;
|
|
26114
|
+
intervalDays = config.audit.intervalDays;
|
|
26115
|
+
} catch {
|
|
26116
|
+
return { kind: "disabled" };
|
|
26117
|
+
}
|
|
26118
|
+
if (!auto)
|
|
26119
|
+
return { kind: "disabled" };
|
|
26120
|
+
if (consentedAt === undefined)
|
|
26121
|
+
return { kind: "consent-required" };
|
|
26122
|
+
const auth = await getValidAccessToken();
|
|
26123
|
+
if (!auth) {
|
|
26124
|
+
return { kind: "signed-out" };
|
|
26125
|
+
}
|
|
26126
|
+
let identity2;
|
|
26127
|
+
try {
|
|
26128
|
+
identity2 = ensureMachineIdentity();
|
|
26129
|
+
} catch (err) {
|
|
26130
|
+
return { kind: "failed", error: err instanceof Error ? err.message : String(err) };
|
|
26131
|
+
}
|
|
26132
|
+
const report = buildHarmReport(result, identity2.last_reported_at, intervalDays);
|
|
26133
|
+
const hits = report.harmful.reduce((n, p) => n + p.hits, 0);
|
|
26134
|
+
try {
|
|
26135
|
+
const res = await submitAuditReport(auth.access_token, {
|
|
26136
|
+
machine_id: identity2.machine_id,
|
|
26137
|
+
label: machineLabel(),
|
|
26138
|
+
platform: process.platform,
|
|
26139
|
+
window_from: report.window_from,
|
|
26140
|
+
window_to: report.window_to,
|
|
26141
|
+
harmful: report.harmful
|
|
26142
|
+
});
|
|
26143
|
+
try {
|
|
26144
|
+
recordReportWatermark(res.next_window_from);
|
|
26145
|
+
} catch {}
|
|
26146
|
+
return res.emailed ? { kind: "sent", hits } : { kind: "held", hits, reason: res.reason ?? "not_sent" };
|
|
26147
|
+
} catch (err) {
|
|
26148
|
+
const error = err instanceof AuthApiError ? `${err.code}: ${err.message}` : err instanceof Error ? err.message : String(err);
|
|
26149
|
+
return { kind: "failed", error };
|
|
26150
|
+
}
|
|
26151
|
+
}
|
|
26152
|
+
function describeOutcome2(outcome) {
|
|
26153
|
+
switch (outcome.kind) {
|
|
26154
|
+
case "disabled":
|
|
26155
|
+
return null;
|
|
26156
|
+
case "consent-required":
|
|
26157
|
+
return "failproofai: scheduled scans are on, but emailing what they find needs a fresh opt-in — run `failproofai audit --schedule` (or visit /settings) to turn digests on";
|
|
26158
|
+
case "signed-out":
|
|
26159
|
+
return "failproofai: emailed reports are on but this machine is signed out — run `failproofai audit --schedule` or visit /settings to resume them";
|
|
26160
|
+
case "sent":
|
|
26161
|
+
return `failproofai: emailed a harm digest (${outcome.hits} finding${outcome.hits === 1 ? "" : "s"})`;
|
|
26162
|
+
case "held":
|
|
26163
|
+
return `failproofai: ${outcome.hits} finding${outcome.hits === 1 ? "" : "s"} reported, no email (${outcome.reason})`;
|
|
26164
|
+
case "failed":
|
|
26165
|
+
return `failproofai: could not send the harm report: ${outcome.error}`;
|
|
26166
|
+
}
|
|
26167
|
+
}
|
|
26168
|
+
var init_report_harm = __esm(() => {
|
|
26169
|
+
init_auth_store();
|
|
26170
|
+
init_api_server_client();
|
|
26171
|
+
init_fp_config();
|
|
26172
|
+
init_harm_report();
|
|
26173
|
+
init_machine_store();
|
|
26174
|
+
});
|
|
26175
|
+
|
|
26176
|
+
// src/audit/audit-schedule.ts
|
|
26177
|
+
import { readFileSync as readFileSync29 } from "node:fs";
|
|
26178
|
+
function readAuditSchedule(home) {
|
|
26179
|
+
let raw;
|
|
26180
|
+
try {
|
|
26181
|
+
raw = readFileSync29(auditScheduleFile(home), "utf8");
|
|
26182
|
+
} catch {
|
|
26183
|
+
return null;
|
|
26184
|
+
}
|
|
26185
|
+
let parsed;
|
|
26186
|
+
try {
|
|
26187
|
+
parsed = JSON.parse(raw);
|
|
26188
|
+
} catch {
|
|
26189
|
+
return null;
|
|
26190
|
+
}
|
|
26191
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
26192
|
+
return null;
|
|
26193
|
+
const obj = parsed;
|
|
26194
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
26195
|
+
const schema = num(obj.schema);
|
|
26196
|
+
return {
|
|
26197
|
+
schema,
|
|
26198
|
+
nextDueAtMs: num(obj.next_due_at_ms),
|
|
26199
|
+
lastAttemptAtMs: num(obj.last_attempt_at_ms),
|
|
26200
|
+
lastRunAtMs: num(obj.last_run_at_ms),
|
|
26201
|
+
lastExitCode: num(obj.last_exit_code),
|
|
26202
|
+
schemaAhead: schema !== null && schema > AUDIT_SCHEDULE_SCHEMA
|
|
26203
|
+
};
|
|
26204
|
+
}
|
|
26205
|
+
var AUDIT_SCHEDULE_SCHEMA = 1;
|
|
26206
|
+
var init_audit_schedule = __esm(() => {
|
|
26207
|
+
init_fp_home();
|
|
26208
|
+
});
|
|
26209
|
+
|
|
26210
|
+
// src/audit/cli-login.ts
|
|
26211
|
+
var exports_cli_login = {};
|
|
26212
|
+
__export(exports_cli_login, {
|
|
26213
|
+
runLogin: () => runLogin,
|
|
26214
|
+
invalidEmail: () => invalidEmail,
|
|
26215
|
+
extractCode: () => extractCode,
|
|
26216
|
+
ensureSignedIn: () => ensureSignedIn,
|
|
26217
|
+
canPrompt: () => canPrompt,
|
|
26218
|
+
LoginError: () => LoginError
|
|
26219
|
+
});
|
|
26220
|
+
function spine() {
|
|
26221
|
+
return colorsEnabled(process.stdout) ? `${ANSI_DIM_BAR}${BAR}${ANSI_RESET} ` : "";
|
|
26222
|
+
}
|
|
26223
|
+
function extractCode(raw) {
|
|
26224
|
+
const trimmed = raw.trim();
|
|
26225
|
+
if (/^\d+$/.test(trimmed))
|
|
26226
|
+
return trimmed;
|
|
26227
|
+
const runs = trimmed.match(/\d+/g) ?? [];
|
|
26228
|
+
if (runs.length === 0)
|
|
26229
|
+
return trimmed;
|
|
26230
|
+
const whole = runs.find((run) => run.length >= CODE_MIN);
|
|
26231
|
+
if (whole)
|
|
26232
|
+
return whole;
|
|
26233
|
+
return runs.join("");
|
|
26234
|
+
}
|
|
26235
|
+
function canPrompt() {
|
|
26236
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
26237
|
+
}
|
|
26238
|
+
function sessionStillValid(auth) {
|
|
26239
|
+
if (!auth)
|
|
26240
|
+
return null;
|
|
26241
|
+
return auth.refresh_expires_at * 1000 > Date.now() ? auth : null;
|
|
26242
|
+
}
|
|
26243
|
+
async function ensureSignedIn(preset) {
|
|
26244
|
+
const existing = sessionStillValid(readAuth());
|
|
26245
|
+
if (existing) {
|
|
26246
|
+
if (preset && !sameAddress(preset, existing.user.email)) {
|
|
26247
|
+
throw new LoginError(`This machine is already signed in as ${existing.user.email}.
|
|
26248
|
+
` + `To report as ${preset.trim().toLowerCase()} instead, sign out first — ` + `from the dashboard, or by removing ~/.failproofai/audit/session.json.`);
|
|
26249
|
+
}
|
|
26250
|
+
return { user: existing.user, prompted: false };
|
|
26251
|
+
}
|
|
26252
|
+
if (!canPrompt()) {
|
|
26253
|
+
throw new LoginError(`Signing in needs an interactive terminal, and this one is not.
|
|
26254
|
+
` + "Run `failproofai audit --schedule` from a shell you are sitting at,\n" + "or sign in from the dashboard — both write the same session file.");
|
|
26255
|
+
}
|
|
26256
|
+
return { user: await runLogin(preset), prompted: true };
|
|
26257
|
+
}
|
|
26258
|
+
function sameAddress(a, b) {
|
|
26259
|
+
return a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
26260
|
+
}
|
|
26261
|
+
function invalidEmail(address) {
|
|
26262
|
+
return EMAIL_RE.test(address.trim()) ? null : `\`--email\` needs an email address (got: ${address}).`;
|
|
26263
|
+
}
|
|
26264
|
+
async function runLogin(preset) {
|
|
26265
|
+
intro("scheduled audits need somewhere to send the report");
|
|
26266
|
+
let address;
|
|
26267
|
+
if (preset) {
|
|
26268
|
+
address = preset.trim().toLowerCase();
|
|
26269
|
+
step("your email", address);
|
|
26270
|
+
} else {
|
|
26271
|
+
const email = await promptText({
|
|
26272
|
+
prefix: spine(),
|
|
26273
|
+
message: "your email",
|
|
26274
|
+
hint: "you@yourdomain.com",
|
|
26275
|
+
validate: (v) => EMAIL_RE.test(v.trim()) ? null : "that doesn't look like an email"
|
|
26276
|
+
});
|
|
26277
|
+
if (email === null) {
|
|
26278
|
+
outro("Cancelled — nothing was changed.", { ok: false });
|
|
26279
|
+
throw new LoginError("Cancelled.");
|
|
26280
|
+
}
|
|
26281
|
+
address = email.trim().toLowerCase();
|
|
26282
|
+
}
|
|
26283
|
+
let expiresInMin = 10;
|
|
26284
|
+
try {
|
|
26285
|
+
const sent = await requestLoginCode(address);
|
|
26286
|
+
expiresInMin = Math.max(1, Math.ceil(sent.expires_in / 60));
|
|
26287
|
+
} catch (err) {
|
|
26288
|
+
outro("Could not send a login code.", { ok: false });
|
|
26289
|
+
throw new LoginError(describeAuthError(err, "Could not send a login code"));
|
|
26290
|
+
}
|
|
26291
|
+
step("code sent", `to ${address} · expires in ${expiresInMin} min`);
|
|
26292
|
+
const ATTEMPTS = 3;
|
|
26293
|
+
for (let attempt = 1;attempt <= ATTEMPTS; attempt += 1) {
|
|
26294
|
+
stepOpen(attempt === 1 ? "the code from that email" : "try that code again");
|
|
26295
|
+
const typed = await promptText({
|
|
26296
|
+
prefix: spine(),
|
|
26297
|
+
message: "code",
|
|
26298
|
+
hint: attempt === 1 ? "123456 · paste the whole line if you like" : `attempt ${attempt} of ${ATTEMPTS}`,
|
|
26299
|
+
validate: (v) => {
|
|
26300
|
+
if (!/\d/.test(v))
|
|
26301
|
+
return "a code is digits — paste the line from the email";
|
|
26302
|
+
const code = extractCode(v);
|
|
26303
|
+
if (code.length < CODE_MIN)
|
|
26304
|
+
return "that looks too short to be the code";
|
|
26305
|
+
if (code.length > CODE_MAX)
|
|
26306
|
+
return "that looks too long — paste just the code";
|
|
26307
|
+
return null;
|
|
26308
|
+
}
|
|
26309
|
+
});
|
|
26310
|
+
if (typed === null) {
|
|
26311
|
+
outro("Cancelled — nothing was changed.", { ok: false });
|
|
26312
|
+
throw new LoginError("Cancelled.");
|
|
26313
|
+
}
|
|
26314
|
+
try {
|
|
26315
|
+
const tokens = await verifyLoginCode(address, extractCode(typed));
|
|
26316
|
+
writeAuth(authFromTokenResponse(tokens));
|
|
26317
|
+
step("signed in", tokens.user.email);
|
|
26318
|
+
return { id: tokens.user.id, email: tokens.user.email };
|
|
26319
|
+
} catch (err) {
|
|
26320
|
+
const wrongCode = err instanceof AuthApiError && err.code === "invalid_code";
|
|
26321
|
+
if (wrongCode && attempt < ATTEMPTS) {
|
|
26322
|
+
step("that code was wrong or expired", `${ATTEMPTS - attempt} more ${ATTEMPTS - attempt === 1 ? "try" : "tries"} before it asks for a new one`);
|
|
26323
|
+
continue;
|
|
26324
|
+
}
|
|
26325
|
+
outro("Could not verify that code.", { ok: false });
|
|
26326
|
+
throw new LoginError(describeAuthError(err, "Could not verify that code"));
|
|
26327
|
+
}
|
|
26328
|
+
}
|
|
26329
|
+
outro("Too many wrong codes.", { ok: false });
|
|
26330
|
+
throw new LoginError("Too many wrong codes. Run the command again for a fresh one.");
|
|
26331
|
+
}
|
|
26332
|
+
function describeAuthError(err, prefix) {
|
|
26333
|
+
if (err instanceof AuthApiError) {
|
|
26334
|
+
if (err.code === "rate_limited") {
|
|
26335
|
+
const wait = err.retryAfterSecs;
|
|
26336
|
+
return `${prefix}: too many attempts.${wait ? ` Try again in ${wait}s.` : ""}`;
|
|
26337
|
+
}
|
|
26338
|
+
if (err.status === 0 || err.code === "timeout") {
|
|
26339
|
+
return `${prefix}: the api-server did not respond.
|
|
26340
|
+
` + `It is at ${apiBaseForMessage()} — check that it is running,
|
|
26341
|
+
` + `or set FAILPROOF_API_URL to point somewhere else.`;
|
|
26342
|
+
}
|
|
26343
|
+
return `${prefix}: ${err.message}`;
|
|
26344
|
+
}
|
|
26345
|
+
return `${prefix}: ${err instanceof Error ? err.message : String(err)}`;
|
|
26346
|
+
}
|
|
26347
|
+
function apiBaseForMessage() {
|
|
26348
|
+
return process.env.FAILPROOF_API_URL ?? process.env.FAILPROOFAI_API_URL ?? "https://api.befailproof.ai";
|
|
26349
|
+
}
|
|
26350
|
+
var EMAIL_RE, CODE_MIN = 4, CODE_MAX = 12, ANSI_DIM_BAR = "\x1B[2m", LoginError;
|
|
26351
|
+
var init_cli_login = __esm(() => {
|
|
26352
|
+
init_api_server_client();
|
|
26353
|
+
init_auth_store();
|
|
26354
|
+
init_tui();
|
|
26355
|
+
EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
26356
|
+
LoginError = class LoginError extends Error {
|
|
26357
|
+
};
|
|
26358
|
+
});
|
|
26359
|
+
|
|
26360
|
+
// src/audit/schedule-cli.ts
|
|
26361
|
+
var exports_schedule_cli = {};
|
|
26362
|
+
__export(exports_schedule_cli, {
|
|
26363
|
+
runScheduleStatus: () => runScheduleStatus,
|
|
26364
|
+
runScheduleOn: () => runScheduleOn,
|
|
26365
|
+
runScheduleOff: () => runScheduleOff,
|
|
26366
|
+
asScheduleError: () => asScheduleError,
|
|
26367
|
+
ScheduleCliError: () => ScheduleCliError
|
|
26368
|
+
});
|
|
26369
|
+
function row(label, value, note) {
|
|
26370
|
+
const gap = " ".repeat(Math.max(1, LABEL_WIDTH - label.length));
|
|
26371
|
+
return `${rail()} ${dim(label)}${gap}${value}${note ? ` ${dim(note)}` : ""}`;
|
|
26372
|
+
}
|
|
26373
|
+
async function runScheduleOn(daysArg, emailArg) {
|
|
26374
|
+
let days;
|
|
26375
|
+
if (daysArg !== undefined) {
|
|
26376
|
+
const parsed = Number(daysArg);
|
|
26377
|
+
if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) {
|
|
26378
|
+
throw new ScheduleCliError(`\`--schedule\` takes a whole number of days (got: ${daysArg}).`);
|
|
26379
|
+
}
|
|
26380
|
+
if (parsed < MIN_DAYS || parsed > MAX_DAYS) {
|
|
26381
|
+
throw new ScheduleCliError(`\`--schedule\` must be between ${MIN_DAYS} and ${MAX_DAYS} days (got: ${parsed}).`);
|
|
26382
|
+
}
|
|
26383
|
+
days = parsed;
|
|
26384
|
+
}
|
|
26385
|
+
if (emailArg !== undefined) {
|
|
26386
|
+
const bad = invalidEmail(emailArg);
|
|
26387
|
+
if (bad)
|
|
26388
|
+
throw new ScheduleCliError(bad);
|
|
26389
|
+
}
|
|
26390
|
+
const { user, prompted } = await ensureSignedIn(emailArg);
|
|
26391
|
+
const next = updateConfig({
|
|
26392
|
+
audit: {
|
|
26393
|
+
auto: true,
|
|
26394
|
+
reportsConsentedAt: Date.now(),
|
|
26395
|
+
...days !== undefined ? { intervalDays: days } : {}
|
|
26396
|
+
}
|
|
26397
|
+
});
|
|
26398
|
+
const interval = next.audit.intervalDays;
|
|
26399
|
+
const summary = [
|
|
26400
|
+
`every ${interval} day${interval === 1 ? "" : "s"} · reports to ${user.email}`,
|
|
26401
|
+
"you only hear from it when a scan finds something harmful",
|
|
26402
|
+
"each report sends: finding counts, redacted example commands, this machine's name"
|
|
26403
|
+
];
|
|
26404
|
+
if (prompted) {
|
|
26405
|
+
step("scheduled audits are on", summary);
|
|
26406
|
+
} else {
|
|
26407
|
+
process.stdout.write(`
|
|
26408
|
+
${green("✓")} ${bold("scheduled audits are on")}
|
|
26409
|
+
` + summary.map((r) => ` ${dim(r)}
|
|
26410
|
+
`).join(""));
|
|
26411
|
+
}
|
|
26412
|
+
warnIfDaemonWontRun();
|
|
26413
|
+
if (prompted) {
|
|
26414
|
+
outro("failproofai audit --status · when the next scan is due");
|
|
26415
|
+
} else {
|
|
26416
|
+
process.stdout.write(dim(`
|
|
26417
|
+
failproofai audit --status when the next scan is due
|
|
26418
|
+
|
|
26419
|
+
`));
|
|
26420
|
+
}
|
|
26421
|
+
}
|
|
26422
|
+
function runScheduleOff() {
|
|
26423
|
+
const before = readConfig().audit.auto;
|
|
26424
|
+
const next = updateConfig({ audit: { auto: false } });
|
|
26425
|
+
if (next.audit.auto) {
|
|
26426
|
+
process.stdout.write(`Could not turn scheduled audits off.
|
|
26427
|
+
`);
|
|
26428
|
+
return;
|
|
26429
|
+
}
|
|
26430
|
+
if (!before) {
|
|
26431
|
+
process.stdout.write(dim(`
|
|
26432
|
+
Scheduled audits were already off.
|
|
26433
|
+
|
|
26434
|
+
`));
|
|
26435
|
+
return;
|
|
26436
|
+
}
|
|
26437
|
+
process.stdout.write(`
|
|
26438
|
+
${green("✓")} ${bold("scheduled audits are off")}
|
|
26439
|
+
` + ` ${dim("nothing runs on a timer and nothing is sent")}
|
|
26440
|
+
` + ` ${dim("your session is untouched — sign out from the dashboard for that")}
|
|
26441
|
+
|
|
26442
|
+
`);
|
|
26443
|
+
}
|
|
26444
|
+
function runScheduleStatus() {
|
|
26445
|
+
const config = readConfig();
|
|
26446
|
+
const auth = readAuth();
|
|
26447
|
+
const sched = readAuditSchedule();
|
|
26448
|
+
const meta = readDashboardCacheMeta();
|
|
26449
|
+
const machine = readMachineIdentity();
|
|
26450
|
+
const daemon = daemonServiceStatus();
|
|
26451
|
+
const on = config.audit.auto;
|
|
26452
|
+
const out = [""];
|
|
26453
|
+
out.push(`${rail()} ${bold("scheduled audit")} ${on ? green("on") : dim("off")}` + (on ? dim(` every ${config.audit.intervalDays} days`) : ""));
|
|
26454
|
+
out.push(rail());
|
|
26455
|
+
const live = auth && auth.refresh_expires_at * 1000 > Date.now() ? auth : null;
|
|
26456
|
+
out.push(row("reports to", live ? live.user.email : dim("— signed out")));
|
|
26457
|
+
if (on && !live) {
|
|
26458
|
+
out.push(row("", pink("scans continue; digests are paused until you sign in")));
|
|
26459
|
+
} else if (on && config.audit.reportsConsentedAt === undefined) {
|
|
26460
|
+
out.push(row("", pink("scans continue; digests need a fresh opt-in — run `--schedule` to turn them on")));
|
|
26461
|
+
}
|
|
26462
|
+
out.push(row("daemon", describeDaemon(daemon)));
|
|
26463
|
+
if (sched?.nextDueAtMs != null && on) {
|
|
26464
|
+
out.push(row("next scan", untilPhrase(sched.nextDueAtMs)));
|
|
26465
|
+
}
|
|
26466
|
+
if (sched?.lastRunAtMs != null) {
|
|
26467
|
+
const exit = sched.lastExitCode;
|
|
26468
|
+
out.push(row("last scheduled", agoPhrase(sched.lastRunAtMs), exit != null && exit !== 0 && exit !== 75 ? pink(`exit ${exit}`) : undefined));
|
|
26469
|
+
}
|
|
26470
|
+
out.push(row("last result", meta?.cachedAt ? agoPhrase(Date.parse(meta.cachedAt)) : dim("none yet")));
|
|
26471
|
+
if (machine?.last_reported_at) {
|
|
26472
|
+
out.push(row("last reported", agoPhrase(Date.parse(machine.last_reported_at))));
|
|
26473
|
+
}
|
|
26474
|
+
out.push("");
|
|
26475
|
+
process.stdout.write(out.join(`
|
|
26476
|
+
`));
|
|
26477
|
+
}
|
|
26478
|
+
function describeDaemon(status) {
|
|
26479
|
+
switch (status) {
|
|
26480
|
+
case "running":
|
|
26481
|
+
return green("running");
|
|
26482
|
+
case "stopped":
|
|
26483
|
+
return pink("stopped — run `failproofai config` to repair it");
|
|
26484
|
+
case "not-installed":
|
|
26485
|
+
return pink("not installed — run `failproofai config`");
|
|
26486
|
+
case "condition-failed":
|
|
26487
|
+
return pink("installed but its binary is missing — run `failproofai config`");
|
|
26488
|
+
default:
|
|
26489
|
+
return isDaemonSupportedPlatform() ? dim(String(status)) : dim("unavailable on this platform");
|
|
26490
|
+
}
|
|
26491
|
+
}
|
|
26492
|
+
function warnIfDaemonWontRun() {
|
|
26493
|
+
const status = daemonServiceStatus();
|
|
26494
|
+
if (status === "running")
|
|
26495
|
+
return;
|
|
26496
|
+
if (status === "unknown")
|
|
26497
|
+
return;
|
|
26498
|
+
if (!isDaemonSupportedPlatform()) {
|
|
26499
|
+
process.stderr.write(`
|
|
26500
|
+
${pink("!")} The background service is not available on this platform,
|
|
26501
|
+
` + ` so nothing will run on the timer here. \`failproofai audit\` still works.
|
|
26502
|
+
`);
|
|
26503
|
+
return;
|
|
26504
|
+
}
|
|
26505
|
+
process.stderr.write(`
|
|
26506
|
+
${pink("!")} The background service is ${status.replace("-", " ")}, ` + `so nothing will run on the timer yet.
|
|
26507
|
+
` + ` Run \`failproofai config\` to install or repair it.
|
|
26508
|
+
`);
|
|
26509
|
+
}
|
|
26510
|
+
function untilPhrase(ms) {
|
|
26511
|
+
const diff = ms - Date.now();
|
|
26512
|
+
if (diff <= 0)
|
|
26513
|
+
return "due now";
|
|
26514
|
+
const d = Math.floor(diff / 86400000);
|
|
26515
|
+
const h = Math.floor(diff % 86400000 / 3600000);
|
|
26516
|
+
if (d > 0)
|
|
26517
|
+
return `in ${d}d${h > 0 ? ` ${h}h` : ""}`;
|
|
26518
|
+
if (h > 0)
|
|
26519
|
+
return `in ${h}h`;
|
|
26520
|
+
return `in ${Math.max(1, Math.floor(diff / 60000))}m`;
|
|
26521
|
+
}
|
|
26522
|
+
function agoPhrase(ms) {
|
|
26523
|
+
if (!Number.isFinite(ms))
|
|
26524
|
+
return "unknown";
|
|
26525
|
+
const diff = Date.now() - ms;
|
|
26526
|
+
if (diff < 0)
|
|
26527
|
+
return "just now";
|
|
26528
|
+
const d = Math.floor(diff / 86400000);
|
|
26529
|
+
const h = Math.floor(diff / 3600000);
|
|
26530
|
+
const m = Math.floor(diff / 60000);
|
|
26531
|
+
if (d > 0)
|
|
26532
|
+
return `${d}d ago`;
|
|
26533
|
+
if (h > 0)
|
|
26534
|
+
return `${h}h ago`;
|
|
26535
|
+
if (m > 0)
|
|
26536
|
+
return `${m}m ago`;
|
|
26537
|
+
return "just now";
|
|
26538
|
+
}
|
|
26539
|
+
function asScheduleError(err) {
|
|
26540
|
+
if (err instanceof LoginError || err instanceof ScheduleCliError) {
|
|
26541
|
+
throw new ScheduleCliError(err.message);
|
|
26542
|
+
}
|
|
26543
|
+
throw err;
|
|
26544
|
+
}
|
|
26545
|
+
var MIN_DAYS = 1, MAX_DAYS = 90, ScheduleCliError, styled = () => colorsEnabled(process.stdout), wrap = (open2, s) => styled() ? `${open2}${s}${ANSI_RESET}` : s, pink = (s) => wrap(brandAnsi("pink"), s), green = (s) => wrap(brandAnsi("guide"), s), dim = (s) => wrap(ANSI_DIM, s), bold = (s) => wrap(ANSI_BOLD, s), rail = () => " ", LABEL_WIDTH = 15;
|
|
26546
|
+
var init_schedule_cli = __esm(() => {
|
|
26547
|
+
init_fp_config();
|
|
26548
|
+
init_daemon_service();
|
|
26549
|
+
init_auth_store();
|
|
26550
|
+
init_audit_schedule();
|
|
26551
|
+
init_dashboard_cache();
|
|
26552
|
+
init_machine_store();
|
|
26553
|
+
init_cli_login();
|
|
26554
|
+
init_tui();
|
|
26555
|
+
ScheduleCliError = class ScheduleCliError extends Error {
|
|
26556
|
+
};
|
|
26557
|
+
});
|
|
26558
|
+
|
|
26559
|
+
// scripts/parse-script-args.ts
|
|
26560
|
+
import { resolve as resolve23 } from "path";
|
|
26561
|
+
function parseStringFlag(flagName, errorLabel, inlineValue, args, index, options) {
|
|
26562
|
+
const raw = inlineValue ?? args[index + 1];
|
|
26563
|
+
if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
|
|
26564
|
+
console.error(`Error: ${flagName} requires ${errorLabel}`);
|
|
26565
|
+
process.exit(1);
|
|
26566
|
+
}
|
|
26567
|
+
const value = options?.resolve ? resolve23(raw) : raw;
|
|
26568
|
+
return { value, spliceCount: inlineValue !== null ? 1 : 2 };
|
|
26569
|
+
}
|
|
26570
|
+
function parseScriptArgs(argv) {
|
|
26571
|
+
const args = [...argv];
|
|
26572
|
+
let loggingLevel;
|
|
26573
|
+
let disableTelemetry = false;
|
|
26574
|
+
let allowedDevOrigins;
|
|
26575
|
+
let host;
|
|
26576
|
+
for (let i = 0;i < args.length; i++) {
|
|
26577
|
+
const arg = args[i];
|
|
26578
|
+
const eqIdx = arg.indexOf("=");
|
|
26579
|
+
const flag = eqIdx >= 0 ? arg.slice(0, eqIdx) : arg;
|
|
26580
|
+
const inlineValue = eqIdx >= 0 ? arg.slice(eqIdx + 1) : null;
|
|
26581
|
+
if (flag === "--logging") {
|
|
26582
|
+
const raw = inlineValue ?? args[i + 1];
|
|
26583
|
+
if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
|
|
26584
|
+
console.error("Error: --logging requires a level (info, warn, error)");
|
|
26585
|
+
process.exit(1);
|
|
26586
|
+
}
|
|
26587
|
+
const val = raw.toLowerCase();
|
|
26588
|
+
if (val !== "info" && val !== "warn" && val !== "error") {
|
|
26589
|
+
console.error("Error: --logging must be one of: info, warn, error");
|
|
26590
|
+
process.exit(1);
|
|
26591
|
+
}
|
|
26592
|
+
loggingLevel = val;
|
|
26593
|
+
args.splice(i, inlineValue !== null ? 1 : 2);
|
|
26594
|
+
i--;
|
|
26595
|
+
continue;
|
|
26596
|
+
}
|
|
26597
|
+
if (flag === "--disable-telemetry") {
|
|
26598
|
+
disableTelemetry = true;
|
|
26599
|
+
args.splice(i, 1);
|
|
26600
|
+
i--;
|
|
26601
|
+
continue;
|
|
26602
|
+
}
|
|
26603
|
+
if (flag === "--allowed-origins") {
|
|
26604
|
+
const { value, spliceCount } = parseStringFlag(flag, "a comma-separated list of origins", inlineValue, args, i);
|
|
26605
|
+
allowedDevOrigins = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
26606
|
+
args.splice(i, spliceCount);
|
|
26607
|
+
i--;
|
|
26608
|
+
continue;
|
|
26609
|
+
}
|
|
26610
|
+
if (flag === "--host" || flag === "-H" || flag === "--hostname") {
|
|
26611
|
+
const { value, spliceCount } = parseStringFlag(flag, "an address to bind (e.g. 127.0.0.1)", inlineValue, args, i);
|
|
26612
|
+
host = value;
|
|
26613
|
+
args.splice(i, spliceCount);
|
|
26614
|
+
i--;
|
|
26615
|
+
continue;
|
|
26616
|
+
}
|
|
26617
|
+
}
|
|
26618
|
+
return { loggingLevel, disableTelemetry, allowedDevOrigins, host, remainingArgs: args };
|
|
26619
|
+
}
|
|
26620
|
+
var init_parse_script_args = () => {};
|
|
26621
|
+
|
|
26622
|
+
// scripts/install-diagnosis.mjs
|
|
26623
|
+
import { existsSync as existsSync34, readFileSync as readFileSync30, realpathSync as realpathSync3 } from "node:fs";
|
|
26624
|
+
import { dirname as dirname16, resolve as resolve24 } from "node:path";
|
|
26625
|
+
import { homedir as homedir26, platform as platform4 } from "node:os";
|
|
26626
|
+
import { spawnSync } from "node:child_process";
|
|
26627
|
+
function findPackageRoot(start) {
|
|
26628
|
+
try {
|
|
26629
|
+
let dir = realpathSync3(start);
|
|
26630
|
+
if (existsSync34(dir) && !existsSync34(resolve24(dir, "package.json"))) {
|
|
26631
|
+
dir = dirname16(dir);
|
|
26632
|
+
}
|
|
26633
|
+
while (true) {
|
|
26634
|
+
const pkgPath = resolve24(dir, "package.json");
|
|
26635
|
+
if (existsSync34(pkgPath)) {
|
|
26636
|
+
try {
|
|
26637
|
+
const pkg = JSON.parse(readFileSync30(pkgPath, "utf8"));
|
|
26638
|
+
if (pkg.name === PKG_NAME)
|
|
26639
|
+
return dir;
|
|
26640
|
+
} catch {}
|
|
26641
|
+
}
|
|
26642
|
+
const parent = dirname16(dir);
|
|
26643
|
+
if (parent === dir)
|
|
26644
|
+
return null;
|
|
26645
|
+
dir = parent;
|
|
26646
|
+
}
|
|
26647
|
+
} catch {
|
|
26648
|
+
return null;
|
|
26649
|
+
}
|
|
26650
|
+
}
|
|
26651
|
+
function readPackageVersion(packageRoot) {
|
|
26652
|
+
if (!packageRoot)
|
|
26653
|
+
return null;
|
|
26654
|
+
try {
|
|
26655
|
+
const pkg = JSON.parse(readFileSync30(resolve24(packageRoot, "package.json"), "utf8"));
|
|
26656
|
+
return typeof pkg.version === "string" ? pkg.version : null;
|
|
26657
|
+
} catch {
|
|
26658
|
+
return null;
|
|
26659
|
+
}
|
|
26660
|
+
}
|
|
26661
|
+
function resolvePathFirstBinary() {
|
|
26662
|
+
try {
|
|
26663
|
+
const isWin = platform4() === "win32";
|
|
26664
|
+
const res = isWin ? spawnSync("where", [PKG_NAME], { encoding: "utf8" }) : spawnSync("sh", ["-c", `command -v ${PKG_NAME}`], { encoding: "utf8" });
|
|
26665
|
+
if (res.status !== 0)
|
|
26666
|
+
return null;
|
|
26667
|
+
const first = (res.stdout || "").split(/\r?\n/).find((l) => l.trim().length > 0);
|
|
26668
|
+
return first ? first.trim() : null;
|
|
26669
|
+
} catch {
|
|
26670
|
+
return null;
|
|
26671
|
+
}
|
|
26672
|
+
}
|
|
26673
|
+
function locateNpmGlobal() {
|
|
26674
|
+
try {
|
|
26675
|
+
const res = spawnSync("npm", ["root", "-g"], { encoding: "utf8" });
|
|
26676
|
+
if (res.status !== 0)
|
|
26677
|
+
return null;
|
|
26678
|
+
const root = (res.stdout || "").trim();
|
|
26679
|
+
if (!root)
|
|
26680
|
+
return null;
|
|
26681
|
+
const candidate = resolve24(root, PKG_NAME);
|
|
26682
|
+
return existsSync34(resolve24(candidate, "package.json")) ? candidate : null;
|
|
26683
|
+
} catch {
|
|
26684
|
+
return null;
|
|
26685
|
+
}
|
|
26686
|
+
}
|
|
26687
|
+
function locateBunGlobal() {
|
|
26688
|
+
try {
|
|
26689
|
+
const candidate = resolve24(homedir26(), ".bun", "install", "global", "node_modules", PKG_NAME);
|
|
26690
|
+
return existsSync34(resolve24(candidate, "package.json")) ? candidate : null;
|
|
26691
|
+
} catch {
|
|
26692
|
+
return null;
|
|
26693
|
+
}
|
|
26694
|
+
}
|
|
26695
|
+
function buildRecommendation(pathFirstBin) {
|
|
26696
|
+
if (!pathFirstBin)
|
|
26697
|
+
return null;
|
|
26698
|
+
const bunBinPrefix = resolve24(homedir26(), ".bun", "bin") + "/";
|
|
26699
|
+
const bunGlobalPrefix = resolve24(homedir26(), ".bun", "install", "global") + "/";
|
|
26700
|
+
const isBun = pathFirstBin.startsWith(bunBinPrefix) || pathFirstBin.startsWith(bunGlobalPrefix);
|
|
26701
|
+
if (isBun) {
|
|
26702
|
+
return `rm -f ~/.bun/bin/${PKG_NAME} && rm -rf ~/.bun/install/global/node_modules/${PKG_NAME}`;
|
|
26703
|
+
}
|
|
26704
|
+
return `npm rm -g ${PKG_NAME}`;
|
|
26705
|
+
}
|
|
26706
|
+
function diagnoseShadow(self) {
|
|
26707
|
+
const selfPackageRoot = (() => {
|
|
26708
|
+
try {
|
|
26709
|
+
return self?.selfPackageRoot ? realpathSync3(self.selfPackageRoot) : null;
|
|
26710
|
+
} catch {
|
|
26711
|
+
return self?.selfPackageRoot ?? null;
|
|
26712
|
+
}
|
|
25591
26713
|
})();
|
|
25592
26714
|
const selfVersion = self?.selfVersion ?? null;
|
|
25593
26715
|
const pathFirstBin = resolvePathFirstBinary();
|
|
@@ -25649,8 +26771,8 @@ function makeSkewLogFilter() {
|
|
|
25649
26771
|
}
|
|
25650
26772
|
|
|
25651
26773
|
// lib/dashboard-host.ts
|
|
25652
|
-
function isLoopbackHostname(
|
|
25653
|
-
const h =
|
|
26774
|
+
function isLoopbackHostname(hostname6) {
|
|
26775
|
+
const h = hostname6.trim().toLowerCase();
|
|
25654
26776
|
if (!h)
|
|
25655
26777
|
return false;
|
|
25656
26778
|
if (LOOPBACK_HOSTNAMES.has(h))
|
|
@@ -25672,8 +26794,8 @@ __export(exports_launch, {
|
|
|
25672
26794
|
launch: () => launch
|
|
25673
26795
|
});
|
|
25674
26796
|
import { spawn as spawn2 } from "child_process";
|
|
25675
|
-
import { realpathSync as realpathSync4, existsSync as
|
|
25676
|
-
import { resolve as
|
|
26797
|
+
import { realpathSync as realpathSync4, existsSync as existsSync35 } from "node:fs";
|
|
26798
|
+
import { resolve as resolve25, dirname as dirname17 } from "node:path";
|
|
25677
26799
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
25678
26800
|
import { createInterface } from "node:readline";
|
|
25679
26801
|
function launch(mode) {
|
|
@@ -25697,9 +26819,9 @@ function launch(mode) {
|
|
|
25697
26819
|
process.env.PORT = port;
|
|
25698
26820
|
process.env.HOSTNAME = bindHost;
|
|
25699
26821
|
cmd = "node";
|
|
25700
|
-
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ??
|
|
25701
|
-
const serverJsPath =
|
|
25702
|
-
if (!
|
|
26822
|
+
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve25(dirname17(realpathSync4(fileURLToPath2(import.meta.url))), "..");
|
|
26823
|
+
const serverJsPath = resolve25(packageRoot, ".next/standalone/server.js");
|
|
26824
|
+
if (!existsSync35(serverJsPath)) {
|
|
25703
26825
|
let shadowMessage = null;
|
|
25704
26826
|
try {
|
|
25705
26827
|
const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
|
|
@@ -25783,10 +26905,43 @@ __export(exports_cli, {
|
|
|
25783
26905
|
runScheduledAudit: () => runScheduledAudit,
|
|
25784
26906
|
runPostSetupAudit: () => runPostSetupAudit,
|
|
25785
26907
|
runAuditCli: () => runAuditCli,
|
|
26908
|
+
helpText: () => helpText,
|
|
25786
26909
|
buildSummary: () => buildSummary,
|
|
25787
26910
|
EXIT_AUDIT_ALREADY_RUNNING: () => EXIT_AUDIT_ALREADY_RUNNING,
|
|
25788
26911
|
AUDIT_STAGES: () => AUDIT_STAGES
|
|
25789
26912
|
});
|
|
26913
|
+
function helpText() {
|
|
26914
|
+
const row2 = (command, lines) => {
|
|
26915
|
+
const pad = " ".repeat(Math.max(1, HELP_DESC_COL - 2 - command.length));
|
|
26916
|
+
return lines.map((line, i) => i === 0 ? ` ${c(CYAN, command)}${pad}${line}` : `${" ".repeat(HELP_DESC_COL)}${line}`).join(`
|
|
26917
|
+
`);
|
|
26918
|
+
};
|
|
26919
|
+
return [
|
|
26920
|
+
`${c(BOLD, "failproofai audit")} — review your agent CLIs for risky and wasteful patterns.`,
|
|
26921
|
+
c(DIM, "Everything runs on this machine; only a scheduled digest ever leaves it."),
|
|
26922
|
+
"",
|
|
26923
|
+
c(BOLD, "USAGE"),
|
|
26924
|
+
row2("failproofai audit", [
|
|
26925
|
+
"Scan your session history, then open",
|
|
26926
|
+
`http://localhost:${DASHBOARD_PORT}/audit`
|
|
26927
|
+
]),
|
|
26928
|
+
row2("failproofai audit --schedule [days]", [
|
|
26929
|
+
"Scan on a timer and email the findings.",
|
|
26930
|
+
"Default 7 days, range 1-90.",
|
|
26931
|
+
"Signs you in the first time; add",
|
|
26932
|
+
"--email <address> to skip a prompt."
|
|
26933
|
+
]),
|
|
26934
|
+
row2("failproofai audit --no-schedule", ["Stop the timer. Leaves you signed in."]),
|
|
26935
|
+
row2("failproofai audit --status", [
|
|
26936
|
+
"Whether scheduling is on, where reports",
|
|
26937
|
+
"go, the daemon's state, and when the",
|
|
26938
|
+
"next scan is due."
|
|
26939
|
+
]),
|
|
26940
|
+
row2("failproofai audit -h, --help", ["Show this help."]),
|
|
26941
|
+
""
|
|
26942
|
+
].join(`
|
|
26943
|
+
`);
|
|
26944
|
+
}
|
|
25790
26945
|
function colorOn() {
|
|
25791
26946
|
if (process.env.NO_COLOR && process.env.NO_COLOR !== "")
|
|
25792
26947
|
return false;
|
|
@@ -25961,6 +27116,13 @@ async function runScheduledAudit() {
|
|
|
25961
27116
|
}
|
|
25962
27117
|
process.stdout.write(`failproofai: audit complete — ${num(result.eventsScanned)} tool calls across ` + `${num(result.transcripts.scanned)} sessions, ${num(result.totals.hits)} hits
|
|
25963
27118
|
`);
|
|
27119
|
+
const outcome = await reportHarm(result);
|
|
27120
|
+
const line = describeOutcome2(outcome);
|
|
27121
|
+
if (line) {
|
|
27122
|
+
const stream = outcome.kind === "sent" ? process.stdout : process.stderr;
|
|
27123
|
+
stream.write(`${line}
|
|
27124
|
+
`);
|
|
27125
|
+
}
|
|
25964
27126
|
return 0;
|
|
25965
27127
|
} finally {
|
|
25966
27128
|
attempt.lock.release();
|
|
@@ -26016,7 +27178,7 @@ async function runPostSetupAudit() {
|
|
|
26016
27178
|
}
|
|
26017
27179
|
async function runAuditCli(args) {
|
|
26018
27180
|
if (args.includes("--help") || args.includes("-h")) {
|
|
26019
|
-
process.stdout.write(
|
|
27181
|
+
process.stdout.write(helpText());
|
|
26020
27182
|
process.exit(0);
|
|
26021
27183
|
}
|
|
26022
27184
|
if (args.includes("--scheduled")) {
|
|
@@ -26025,15 +27187,71 @@ async function runAuditCli(args) {
|
|
|
26025
27187
|
die(`\`audit --scheduled\` takes no other arguments (got: ${extra}).`);
|
|
26026
27188
|
process.exit(await runScheduledAudit());
|
|
26027
27189
|
}
|
|
27190
|
+
if (args.includes("--status")) {
|
|
27191
|
+
const extra = args.find((a) => a !== "--status");
|
|
27192
|
+
if (extra)
|
|
27193
|
+
die(`\`audit --status\` takes no other arguments (got: ${extra}).`);
|
|
27194
|
+
const { runScheduleStatus: runScheduleStatus2 } = await Promise.resolve().then(() => (init_schedule_cli(), exports_schedule_cli));
|
|
27195
|
+
runScheduleStatus2();
|
|
27196
|
+
process.exit(0);
|
|
27197
|
+
}
|
|
27198
|
+
if (args.includes("--no-schedule")) {
|
|
27199
|
+
const extra = args.find((a) => a !== "--no-schedule");
|
|
27200
|
+
if (extra)
|
|
27201
|
+
die(`\`audit --no-schedule\` takes no other arguments (got: ${extra}).`);
|
|
27202
|
+
const { runScheduleOff: runScheduleOff2 } = await Promise.resolve().then(() => (init_schedule_cli(), exports_schedule_cli));
|
|
27203
|
+
runScheduleOff2();
|
|
27204
|
+
process.exit(0);
|
|
27205
|
+
}
|
|
27206
|
+
const scheduleAt = args.indexOf("--schedule");
|
|
27207
|
+
if (scheduleAt !== -1) {
|
|
27208
|
+
let days;
|
|
27209
|
+
let email;
|
|
27210
|
+
for (let i = 0;i < args.length; i += 1) {
|
|
27211
|
+
const a = args[i];
|
|
27212
|
+
if (a === "--schedule") {
|
|
27213
|
+
const next = args[i + 1];
|
|
27214
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
27215
|
+
days = next;
|
|
27216
|
+
i += 1;
|
|
27217
|
+
}
|
|
27218
|
+
continue;
|
|
27219
|
+
}
|
|
27220
|
+
if (a === "--email" || a.startsWith("--email=")) {
|
|
27221
|
+
if (a.startsWith("--email=")) {
|
|
27222
|
+
email = a.slice("--email=".length);
|
|
27223
|
+
} else {
|
|
27224
|
+
email = args[i + 1];
|
|
27225
|
+
i += 1;
|
|
27226
|
+
}
|
|
27227
|
+
if (email === undefined || email.length === 0 || email.startsWith("-")) {
|
|
27228
|
+
die("`--email` needs an address, e.g. `--email you@yourdomain.com`.");
|
|
27229
|
+
}
|
|
27230
|
+
continue;
|
|
27231
|
+
}
|
|
27232
|
+
die(`\`audit --schedule\` does not take ${a}.`);
|
|
27233
|
+
}
|
|
27234
|
+
const { runScheduleOn: runScheduleOn2, ScheduleCliError: ScheduleCliError2 } = await Promise.resolve().then(() => (init_schedule_cli(), exports_schedule_cli));
|
|
27235
|
+
const { LoginError: LoginError2 } = await Promise.resolve().then(() => (init_cli_login(), exports_cli_login));
|
|
27236
|
+
try {
|
|
27237
|
+
await runScheduleOn2(days, email);
|
|
27238
|
+
} catch (err) {
|
|
27239
|
+
if (err instanceof ScheduleCliError2 || err instanceof LoginError2)
|
|
27240
|
+
die(err.message);
|
|
27241
|
+
throw err;
|
|
27242
|
+
}
|
|
27243
|
+
process.exit(0);
|
|
27244
|
+
}
|
|
26028
27245
|
const stray = args.find((a) => a !== "--help" && a !== "-h");
|
|
26029
27246
|
if (stray) {
|
|
26030
|
-
die(`\`audit\`
|
|
26031
|
-
|
|
27247
|
+
die(`\`audit\` does not take ${stray}.
|
|
27248
|
+
Run \`failproofai audit\` to scan your history and open the dashboard,
|
|
27249
|
+
or \`failproofai audit --help\` for the scheduling commands.`);
|
|
26032
27250
|
}
|
|
26033
27251
|
const attempt = acquireAuditLock("cli");
|
|
26034
27252
|
if (!attempt.ok) {
|
|
26035
27253
|
process.stderr.write(`Error: ${heldByLine(attempt.heldBy)}.
|
|
26036
|
-
|
|
27254
|
+
Two audits write the same cache files, so this one won't start. Try again shortly.
|
|
26037
27255
|
`);
|
|
26038
27256
|
process.exit(EXIT_AUDIT_ALREADY_RUNNING);
|
|
26039
27257
|
}
|
|
@@ -26057,7 +27275,7 @@ async function runAuditCli(args) {
|
|
|
26057
27275
|
if (result.eventsScanned === 0) {
|
|
26058
27276
|
process.stdout.write(`
|
|
26059
27277
|
${c(DIM, "no agent sessions found yet.")}
|
|
26060
|
-
|
|
27278
|
+
install hooks with ${c(CYAN, "failproofai policies --install")} ${c(DIM, "and come back after using your agent.")}
|
|
26061
27279
|
|
|
26062
27280
|
`);
|
|
26063
27281
|
process.exit(0);
|
|
@@ -26072,15 +27290,15 @@ async function runAuditCli(args) {
|
|
|
26072
27290
|
const url = `http://localhost:${DASHBOARD_PORT}/audit`;
|
|
26073
27291
|
process.stdout.write(`
|
|
26074
27292
|
${c(DIM, "starting the dashboard…")}
|
|
26075
|
-
|
|
26076
|
-
|
|
27293
|
+
${c(PINK, "✦")} ${c(BOLD, "here's your audit")} ${c(DIM, "→")} ${c(CYAN, url)}
|
|
27294
|
+
${c(DIM, "(opening in your browser — press Ctrl+C to stop the server)")}
|
|
26077
27295
|
|
|
26078
27296
|
`);
|
|
26079
27297
|
openWhenReady(DASHBOARD_PORT, "/audit");
|
|
26080
27298
|
const { launch: launch2 } = await Promise.resolve().then(() => (init_launch(), exports_launch));
|
|
26081
27299
|
launch2("start");
|
|
26082
27300
|
}
|
|
26083
|
-
var DASHBOARD_PORT = 8020, EXIT_AUDIT_ALREADY_RUNNING = 75, AUDIT_STAGES,
|
|
27301
|
+
var DASHBOARD_PORT = 8020, EXIT_AUDIT_ALREADY_RUNNING = 75, AUDIT_STAGES, HELP_DESC_COL = 40, RESET, DIM, BOLD, PINK, GREEN, CYAN, SPINNER;
|
|
26084
27302
|
var init_cli = __esm(() => {
|
|
26085
27303
|
init_audit();
|
|
26086
27304
|
init_audit_lock();
|
|
@@ -26089,6 +27307,7 @@ var init_cli = __esm(() => {
|
|
|
26089
27307
|
init_telemetry_id2();
|
|
26090
27308
|
init_telemetry_sanitize();
|
|
26091
27309
|
init_open_browser();
|
|
27310
|
+
init_report_harm();
|
|
26092
27311
|
init_tui();
|
|
26093
27312
|
AUDIT_STAGES = [
|
|
26094
27313
|
{ label: "discovering transcripts", detail: "walking ~/.claude, ~/.codex, ~/.cursor, …" },
|
|
@@ -26096,29 +27315,6 @@ var init_cli = __esm(() => {
|
|
|
26096
27315
|
{ label: "running policy checks", detail: "replaying through 30 builtin policies" },
|
|
26097
27316
|
{ label: "aggregating results", detail: "counting hits, ranking by frequency" }
|
|
26098
27317
|
];
|
|
26099
|
-
HELP = `
|
|
26100
|
-
failproofai audit — audit your AI agent's behavior, then open the dashboard
|
|
26101
|
-
|
|
26102
|
-
USAGE
|
|
26103
|
-
failproofai audit Scan your agent-CLI session history for risky and
|
|
26104
|
-
wasteful patterns, then open the audit dashboard.
|
|
26105
|
-
failproofai audit --help Show this help.
|
|
26106
|
-
|
|
26107
|
-
failproofai audit --scheduled
|
|
26108
|
-
Headless run: scan, refresh the dashboard's cached
|
|
26109
|
-
result, print one line, exit. No browser, no
|
|
26110
|
-
server. This is what a scheduled audit runs; exit
|
|
26111
|
-
75 means another audit already had the lock.
|
|
26112
|
-
|
|
26113
|
-
WHAT IT DOES
|
|
26114
|
-
1. Scans past sessions from every installed agent CLI (Claude, Codex, Cursor,
|
|
26115
|
-
Copilot, OpenCode, Pi) — entirely on your machine.
|
|
26116
|
-
2. Starts the local dashboard and opens
|
|
26117
|
-
http://localhost:${DASHBOARD_PORT}/audit with your results.
|
|
26118
|
-
|
|
26119
|
-
Runs fully offline — no account or network required. Press Ctrl+C to stop the
|
|
26120
|
-
dashboard server when you're done.
|
|
26121
|
-
`.trimStart();
|
|
26122
27318
|
RESET = ANSI_RESET;
|
|
26123
27319
|
DIM = ANSI_DIM;
|
|
26124
27320
|
BOLD = ANSI_BOLD;
|
|
@@ -26147,16 +27343,16 @@ __export(exports_configure_wizard, {
|
|
|
26147
27343
|
buildCompletionSummary: () => buildCompletionSummary,
|
|
26148
27344
|
buildAgentChoices: () => buildAgentChoices
|
|
26149
27345
|
});
|
|
26150
|
-
import { existsSync as
|
|
26151
|
-
import { homedir as
|
|
26152
|
-
import { dirname as dirname18, resolve as
|
|
27346
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync21, writeFileSync as writeFileSync19, readFileSync as readFileSync31 } from "node:fs";
|
|
27347
|
+
import { homedir as homedir27 } from "node:os";
|
|
27348
|
+
import { dirname as dirname18, resolve as resolve26, sep as sep5 } from "node:path";
|
|
26153
27349
|
async function emit(event, props) {
|
|
26154
27350
|
try {
|
|
26155
27351
|
await trackHookEvent2(getInstanceId2(), event, props);
|
|
26156
27352
|
} catch {}
|
|
26157
27353
|
}
|
|
26158
27354
|
function homeify2(p) {
|
|
26159
|
-
const home =
|
|
27355
|
+
const home = homedir27();
|
|
26160
27356
|
if (p === home)
|
|
26161
27357
|
return "~";
|
|
26162
27358
|
if (p.startsWith(home + sep5))
|
|
@@ -26280,8 +27476,8 @@ function setCustomPoliciesEnabled(scope, cwd, enabled) {
|
|
|
26280
27476
|
const path3 = getConfigPathForScope(scope, cwd);
|
|
26281
27477
|
let config = {};
|
|
26282
27478
|
try {
|
|
26283
|
-
if (
|
|
26284
|
-
config = JSON.parse(
|
|
27479
|
+
if (existsSync36(path3))
|
|
27480
|
+
config = JSON.parse(readFileSync31(path3, "utf8"));
|
|
26285
27481
|
} catch {
|
|
26286
27482
|
return;
|
|
26287
27483
|
}
|
|
@@ -26321,7 +27517,7 @@ function describeCustomPolicies(cwd) {
|
|
|
26321
27517
|
const scopes = [];
|
|
26322
27518
|
let fileCount = 0;
|
|
26323
27519
|
const dirs = [
|
|
26324
|
-
{ dir:
|
|
27520
|
+
{ dir: resolve26(cwd, ".failproofai", "policies"), label: "project" },
|
|
26325
27521
|
{ dir: customPoliciesDir(), label: "global" }
|
|
26326
27522
|
];
|
|
26327
27523
|
for (const { dir, label } of dirs) {
|
|
@@ -26332,7 +27528,7 @@ function describeCustomPolicies(cwd) {
|
|
|
26332
27528
|
fileCount += found.length;
|
|
26333
27529
|
}
|
|
26334
27530
|
for (const name of findSkippedPolicyFiles(dir)) {
|
|
26335
|
-
warnings.push(`! ${homeify2(
|
|
27531
|
+
warnings.push(`! ${homeify2(resolve26(dir, name))} is NOT loaded — rename to ` + `${name.replace(/\.(js|mjs|ts)$/, "-policies.$1")}`);
|
|
26336
27532
|
}
|
|
26337
27533
|
}
|
|
26338
27534
|
return { active, warnings, fileCount, scopes };
|
|
@@ -26438,7 +27634,7 @@ function firstRunMarkerPath() {
|
|
|
26438
27634
|
return launcherMarker();
|
|
26439
27635
|
}
|
|
26440
27636
|
function hasSeenLauncher() {
|
|
26441
|
-
return
|
|
27637
|
+
return existsSync36(firstRunMarkerPath());
|
|
26442
27638
|
}
|
|
26443
27639
|
function markLauncherSeen() {
|
|
26444
27640
|
try {
|
|
@@ -26740,7 +27936,7 @@ refreshed. Re-run \`failproofai config\` once you can use sudo.
|
|
|
26740
27936
|
let url = null;
|
|
26741
27937
|
let token = null;
|
|
26742
27938
|
let machineId = resolveMachineId();
|
|
26743
|
-
let
|
|
27939
|
+
let machineLabel2 = existing?.machineLabel ?? resolveMachineLabel();
|
|
26744
27940
|
if (existing) {
|
|
26745
27941
|
const reuse = await selectOne({
|
|
26746
27942
|
message: `Use this machine's existing connection to ${existing.url}?`,
|
|
@@ -26761,7 +27957,7 @@ refreshed. Re-run \`failproofai config\` once you can use sudo.
|
|
|
26761
27957
|
url = existing.url;
|
|
26762
27958
|
token = existing.token;
|
|
26763
27959
|
machineId = existing.machineId;
|
|
26764
|
-
|
|
27960
|
+
machineLabel2 = existing.machineLabel ?? machineLabel2;
|
|
26765
27961
|
}
|
|
26766
27962
|
}
|
|
26767
27963
|
if (url === null) {
|
|
@@ -26815,13 +28011,13 @@ That did not work: ${probe.reason}
|
|
|
26815
28011
|
if (retry === "skip") {
|
|
26816
28012
|
stdout.write("Staying local. Connect later with `failproofai config --connect`.\n\n");
|
|
26817
28013
|
} else {
|
|
26818
|
-
connect = { url, token, machineId, machineLabel };
|
|
28014
|
+
connect = { url, token, machineId, machineLabel: machineLabel2 };
|
|
26819
28015
|
}
|
|
26820
28016
|
} else {
|
|
26821
28017
|
stdout.write(`looks good.
|
|
26822
28018
|
|
|
26823
28019
|
`);
|
|
26824
|
-
connect = { url, token, machineId, machineLabel };
|
|
28020
|
+
connect = { url, token, machineId, machineLabel: machineLabel2 };
|
|
26825
28021
|
}
|
|
26826
28022
|
}
|
|
26827
28023
|
}
|
|
@@ -27024,17 +28220,17 @@ __export(exports_flush_cli2, {
|
|
|
27024
28220
|
flushRequestPath: () => flushRequestPath2,
|
|
27025
28221
|
DEFAULT_FLUSH_TIMEOUT_SECS: () => DEFAULT_FLUSH_TIMEOUT_SECS2
|
|
27026
28222
|
});
|
|
27027
|
-
import { existsSync as
|
|
27028
|
-
import { dirname as dirname19, join as
|
|
28223
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync22, writeFileSync as writeFileSync20, readdirSync as readdirSync19 } from "node:fs";
|
|
28224
|
+
import { dirname as dirname19, join as join34 } from "node:path";
|
|
27029
28225
|
function flushRequestPath2(home) {
|
|
27030
|
-
return
|
|
28226
|
+
return join34(failproofaiHome(home), "state", "flush-request.json");
|
|
27031
28227
|
}
|
|
27032
28228
|
function spoolDirs2(home) {
|
|
27033
28229
|
const root = spoolDir(home);
|
|
27034
|
-
if (!
|
|
28230
|
+
if (!existsSync37(root))
|
|
27035
28231
|
return [];
|
|
27036
28232
|
try {
|
|
27037
|
-
return
|
|
28233
|
+
return readdirSync19(root, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name !== "failed").map((e) => join34(root, e.name));
|
|
27038
28234
|
} catch {
|
|
27039
28235
|
return [];
|
|
27040
28236
|
}
|
|
@@ -27043,7 +28239,7 @@ function pendingBatches2(home) {
|
|
|
27043
28239
|
let n = 0;
|
|
27044
28240
|
for (const dir of spoolDirs2(home)) {
|
|
27045
28241
|
try {
|
|
27046
|
-
n +=
|
|
28242
|
+
n += readdirSync19(dir).filter((f) => f.endsWith(".jsonl")).length;
|
|
27047
28243
|
} catch {}
|
|
27048
28244
|
}
|
|
27049
28245
|
return n;
|
|
@@ -27138,16 +28334,16 @@ __export(exports_backfill_cli, {
|
|
|
27138
28334
|
backfillRequestPath: () => backfillRequestPath,
|
|
27139
28335
|
DEFAULT_BACKFILL_DAYS: () => DEFAULT_BACKFILL_DAYS
|
|
27140
28336
|
});
|
|
27141
|
-
import { existsSync as
|
|
27142
|
-
import { dirname as dirname20, join as
|
|
28337
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync23, writeFileSync as writeFileSync21, readdirSync as readdirSync20, statSync as statSync24 } from "node:fs";
|
|
28338
|
+
import { dirname as dirname20, join as join35 } from "node:path";
|
|
27143
28339
|
function backfillRequestPath(home) {
|
|
27144
|
-
return
|
|
28340
|
+
return join35(failproofaiHome(home), "state", "backfill-request.json");
|
|
27145
28341
|
}
|
|
27146
28342
|
function surveySessions(sinceMs, homeDir) {
|
|
27147
28343
|
const out = [];
|
|
27148
28344
|
for (const { cli, dir } of SESSION_DIRS) {
|
|
27149
|
-
const root =
|
|
27150
|
-
if (!
|
|
28345
|
+
const root = join35(homeDir, dir);
|
|
28346
|
+
if (!existsSync38(root))
|
|
27151
28347
|
continue;
|
|
27152
28348
|
let files = 0;
|
|
27153
28349
|
let newest = 0;
|
|
@@ -27156,12 +28352,12 @@ function surveySessions(sinceMs, homeDir) {
|
|
|
27156
28352
|
return;
|
|
27157
28353
|
let entries;
|
|
27158
28354
|
try {
|
|
27159
|
-
entries =
|
|
28355
|
+
entries = readdirSync20(d, { withFileTypes: true });
|
|
27160
28356
|
} catch {
|
|
27161
28357
|
return;
|
|
27162
28358
|
}
|
|
27163
28359
|
for (const e of entries) {
|
|
27164
|
-
const p =
|
|
28360
|
+
const p = join35(d, e.name);
|
|
27165
28361
|
if (e.isDirectory()) {
|
|
27166
28362
|
walk(p, depth + 1);
|
|
27167
28363
|
continue;
|
|
@@ -27185,7 +28381,7 @@ function runBackfillCommand(opts = {}) {
|
|
|
27185
28381
|
const now = opts.now ?? Date.now();
|
|
27186
28382
|
const sinceMs = opts.sinceMs ?? now - DEFAULT_BACKFILL_DAYS * 24 * 60 * 60 * 1000;
|
|
27187
28383
|
const home = failproofaiHome();
|
|
27188
|
-
if (!
|
|
28384
|
+
if (!existsSync38(home)) {
|
|
27189
28385
|
return {
|
|
27190
28386
|
exitCode: 1,
|
|
27191
28387
|
lines: [`No failproofai home at ${home}. Run \`failproofai config\` first.`]
|
|
@@ -27291,8 +28487,8 @@ __export(exports_harness_cli, {
|
|
|
27291
28487
|
addPath: () => addPath,
|
|
27292
28488
|
HARNESS_KEYS: () => HARNESS_KEYS
|
|
27293
28489
|
});
|
|
27294
|
-
import { homedir as
|
|
27295
|
-
import { resolve as
|
|
28490
|
+
import { homedir as homedir28 } from "node:os";
|
|
28491
|
+
import { resolve as resolve27 } from "node:path";
|
|
27296
28492
|
function unknownHarness(name) {
|
|
27297
28493
|
return fail([
|
|
27298
28494
|
`Unknown harness: ${name}`,
|
|
@@ -27335,8 +28531,8 @@ function comparableLabel(raw) {
|
|
|
27335
28531
|
return raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
27336
28532
|
}
|
|
27337
28533
|
function comparablePath(raw) {
|
|
27338
|
-
const home =
|
|
27339
|
-
const expanded = raw === "~" ? home : raw.startsWith("~/") ?
|
|
28534
|
+
const home = homedir28();
|
|
28535
|
+
const expanded = raw === "~" ? home : raw.startsWith("~/") ? resolve27(home, raw.slice(2)) : raw;
|
|
27340
28536
|
return expanded.replace(/\/+$/, "");
|
|
27341
28537
|
}
|
|
27342
28538
|
function effectiveLabel(entry) {
|
|
@@ -27523,7 +28719,7 @@ __export(exports_fp_config, {
|
|
|
27523
28719
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG2,
|
|
27524
28720
|
DEFAULT_AUDIT_INTERVAL_DAYS: () => DEFAULT_AUDIT_INTERVAL_DAYS2
|
|
27525
28721
|
});
|
|
27526
|
-
import { chmodSync as
|
|
28722
|
+
import { chmodSync as chmodSync6, existsSync as existsSync39, mkdirSync as mkdirSync24, readFileSync as readFileSync32, statSync as statSync25, writeFileSync as writeFileSync22 } from "node:fs";
|
|
27527
28723
|
import { dirname as dirname21 } from "node:path";
|
|
27528
28724
|
function detectLayout2() {
|
|
27529
28725
|
const raw = readVersionFile2();
|
|
@@ -27532,11 +28728,18 @@ function detectLayout2() {
|
|
|
27532
28728
|
return { kind: "current", version: raw };
|
|
27533
28729
|
return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
|
|
27534
28730
|
}
|
|
27535
|
-
if (
|
|
28731
|
+
if (existsSync39(configFile())) {
|
|
28732
|
+
const layoutThreePositions = [
|
|
28733
|
+
legacy.authJson(),
|
|
28734
|
+
legacy.nextAudit(),
|
|
28735
|
+
legacy.auditSchedule()
|
|
28736
|
+
];
|
|
28737
|
+
if (layoutThreePositions.some((p) => existsSync39(p)))
|
|
28738
|
+
return { kind: "stale", found: 3 };
|
|
27536
28739
|
return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
|
|
27537
28740
|
}
|
|
27538
|
-
if (
|
|
27539
|
-
return { kind: "stale", found:
|
|
28741
|
+
if (existsSync39(legacy.configToml()))
|
|
28742
|
+
return { kind: "stale", found: 2 };
|
|
27540
28743
|
const layoutOneMarkers = [
|
|
27541
28744
|
legacy.policyConfig(),
|
|
27542
28745
|
legacy.cacheDir(),
|
|
@@ -27546,7 +28749,7 @@ function detectLayout2() {
|
|
|
27546
28749
|
legacy.ingestCredentials(),
|
|
27547
28750
|
legacy.auditDashboard()
|
|
27548
28751
|
];
|
|
27549
|
-
return layoutOneMarkers.some((p) =>
|
|
28752
|
+
return layoutOneMarkers.some((p) => existsSync39(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
|
|
27550
28753
|
}
|
|
27551
28754
|
function readLegacyTomlVersion2(text) {
|
|
27552
28755
|
const layout = Number(/^\s*layout\s*=\s*(\d+)\s*$/m.exec(text)?.[1]);
|
|
@@ -27558,7 +28761,7 @@ function readLegacyTomlVersion2(text) {
|
|
|
27558
28761
|
function readVersionFile2() {
|
|
27559
28762
|
let text;
|
|
27560
28763
|
try {
|
|
27561
|
-
text =
|
|
28764
|
+
text = readFileSync32(versionFile(), "utf8");
|
|
27562
28765
|
} catch {
|
|
27563
28766
|
return null;
|
|
27564
28767
|
}
|
|
@@ -27579,7 +28782,7 @@ function readVersionFile2() {
|
|
|
27579
28782
|
function writeVersionFile2(v = {}) {
|
|
27580
28783
|
const existing = readVersionFile2();
|
|
27581
28784
|
const next = {
|
|
27582
|
-
layout: LAYOUT_VERSION,
|
|
28785
|
+
layout: v.layout ?? LAYOUT_VERSION,
|
|
27583
28786
|
cli: v.cli ?? version2,
|
|
27584
28787
|
daemon: v.clearDaemon ? undefined : v.daemon ?? existing?.daemon
|
|
27585
28788
|
};
|
|
@@ -27613,7 +28816,7 @@ function readSources2(raw) {
|
|
|
27613
28816
|
function readConfigRaw2() {
|
|
27614
28817
|
let raw = {};
|
|
27615
28818
|
try {
|
|
27616
|
-
const parsed = JSON.parse(
|
|
28819
|
+
const parsed = JSON.parse(readFileSync32(configFile(), "utf8"));
|
|
27617
28820
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
27618
28821
|
raw = parsed;
|
|
27619
28822
|
}
|
|
@@ -27622,7 +28825,7 @@ function readConfigRaw2() {
|
|
|
27622
28825
|
}
|
|
27623
28826
|
function readConfig2() {
|
|
27624
28827
|
try {
|
|
27625
|
-
const parsed = JSON.parse(
|
|
28828
|
+
const parsed = JSON.parse(readFileSync32(configFile(), "utf8"));
|
|
27626
28829
|
return projectConfig2(parsed);
|
|
27627
28830
|
} catch {
|
|
27628
28831
|
return structuredClone(DEFAULT_CONFIG2);
|
|
@@ -27648,7 +28851,11 @@ function projectConfig2(parsed) {
|
|
|
27648
28851
|
sources: readSources2(collector.sources)
|
|
27649
28852
|
},
|
|
27650
28853
|
telemetry: { enabled: telemetry.enabled !== false },
|
|
27651
|
-
audit: {
|
|
28854
|
+
audit: {
|
|
28855
|
+
auto: audit.auto === true,
|
|
28856
|
+
intervalDays: readIntervalDays2(audit.interval_days),
|
|
28857
|
+
reportsConsentedAt: typeof audit.reports_consented_at === "number" && Number.isFinite(audit.reports_consented_at) ? audit.reports_consented_at : undefined
|
|
28858
|
+
}
|
|
27652
28859
|
};
|
|
27653
28860
|
}
|
|
27654
28861
|
}
|
|
@@ -27697,7 +28904,11 @@ function writeConfig2(config, raw) {
|
|
|
27697
28904
|
} : {}
|
|
27698
28905
|
},
|
|
27699
28906
|
...config.telemetry.enabled ? {} : { telemetry: { enabled: false } },
|
|
27700
|
-
audit: {
|
|
28907
|
+
audit: {
|
|
28908
|
+
auto: config.audit.auto,
|
|
28909
|
+
interval_days: config.audit.intervalDays,
|
|
28910
|
+
...config.audit.reportsConsentedAt === undefined ? {} : { reports_consented_at: config.audit.reportsConsentedAt }
|
|
28911
|
+
}
|
|
27701
28912
|
};
|
|
27702
28913
|
const merged = structuredClone(raw ?? readConfigRaw2().raw);
|
|
27703
28914
|
for (const path3 of OWNED_CONFIG_KEYS2)
|
|
@@ -27721,7 +28932,7 @@ function updateConfig2(patch) {
|
|
|
27721
28932
|
function readCredentialsRaw2() {
|
|
27722
28933
|
let raw = {};
|
|
27723
28934
|
try {
|
|
27724
|
-
const parsed = JSON.parse(
|
|
28935
|
+
const parsed = JSON.parse(readFileSync32(credentialsFile(), "utf8"));
|
|
27725
28936
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
27726
28937
|
raw = parsed;
|
|
27727
28938
|
}
|
|
@@ -27730,7 +28941,7 @@ function readCredentialsRaw2() {
|
|
|
27730
28941
|
}
|
|
27731
28942
|
function readCredentials2() {
|
|
27732
28943
|
try {
|
|
27733
|
-
return projectCredentials2(JSON.parse(
|
|
28944
|
+
return projectCredentials2(JSON.parse(readFileSync32(credentialsFile(), "utf8")));
|
|
27734
28945
|
} catch {
|
|
27735
28946
|
return {};
|
|
27736
28947
|
}
|
|
@@ -27742,8 +28953,8 @@ function projectCredentials2(parsed) {
|
|
|
27742
28953
|
const auth = parsed.auth;
|
|
27743
28954
|
const out = {};
|
|
27744
28955
|
if (cloud && typeof cloud.url === "string" && typeof cloud.machine_id === "string" && typeof cloud.token === "string" && cloud.url && cloud.token) {
|
|
27745
|
-
const
|
|
27746
|
-
out.cloud = { url: cloud.url, machineId: cloud.machine_id, token: cloud.token, machineLabel };
|
|
28956
|
+
const machineLabel2 = typeof cloud.machine_label === "string" && cloud.machine_label ? cloud.machine_label : undefined;
|
|
28957
|
+
out.cloud = { url: cloud.url, machineId: cloud.machine_id, token: cloud.token, machineLabel: machineLabel2 };
|
|
27747
28958
|
}
|
|
27748
28959
|
const org = parsed.org;
|
|
27749
28960
|
if (org) {
|
|
@@ -27802,12 +29013,12 @@ function writeCredentials2(creds, raw) {
|
|
|
27802
29013
|
mkdirSync24(home, { recursive: true });
|
|
27803
29014
|
try {
|
|
27804
29015
|
if (statSync25(home).mode & 63)
|
|
27805
|
-
|
|
29016
|
+
chmodSync6(home, 448);
|
|
27806
29017
|
} catch {}
|
|
27807
29018
|
writeFileSync22(credentialsFile(), `${JSON.stringify(merged, null, 2)}
|
|
27808
29019
|
`, { mode: 384 });
|
|
27809
29020
|
try {
|
|
27810
|
-
|
|
29021
|
+
chmodSync6(credentialsFile(), 384);
|
|
27811
29022
|
} catch {}
|
|
27812
29023
|
}
|
|
27813
29024
|
function hasCloudCredentials() {
|
|
@@ -27847,7 +29058,8 @@ var init_fp_config2 = __esm(() => {
|
|
|
27847
29058
|
["collector", "sources", "*", "extra_paths"],
|
|
27848
29059
|
["telemetry", "enabled"],
|
|
27849
29060
|
["audit", "auto"],
|
|
27850
|
-
["audit", "interval_days"]
|
|
29061
|
+
["audit", "interval_days"],
|
|
29062
|
+
["audit", "reports_consented_at"]
|
|
27851
29063
|
];
|
|
27852
29064
|
OWNED_CREDENTIAL_KEYS2 = [
|
|
27853
29065
|
["cloud", "url"],
|
|
@@ -27906,7 +29118,9 @@ __export(exports_fp_home, {
|
|
|
27906
29118
|
codexSessionPathsFile: () => codexSessionPathsFile2,
|
|
27907
29119
|
cloudPoliciesDir: () => cloudPoliciesDir2,
|
|
27908
29120
|
binDir: () => binDir2,
|
|
29121
|
+
auditSessionFile: () => auditSessionFile2,
|
|
27909
29122
|
auditScheduleFile: () => auditScheduleFile2,
|
|
29123
|
+
auditMachineFile: () => auditMachineFile2,
|
|
27910
29124
|
auditLockFile: () => auditLockFile2,
|
|
27911
29125
|
auditDir: () => auditDir2,
|
|
27912
29126
|
auditDashboardFile: () => auditDashboardFile2,
|
|
@@ -27914,12 +29128,12 @@ __export(exports_fp_home, {
|
|
|
27914
29128
|
LAYOUT_VERSION: () => LAYOUT_VERSION2,
|
|
27915
29129
|
HOME_CLASSES: () => HOME_CLASSES2
|
|
27916
29130
|
});
|
|
27917
|
-
import { homedir as
|
|
27918
|
-
import { resolve as
|
|
29131
|
+
import { homedir as homedir29 } from "node:os";
|
|
29132
|
+
import { resolve as resolve28 } from "node:path";
|
|
27919
29133
|
function failproofaiHome2(home) {
|
|
27920
29134
|
if (home)
|
|
27921
|
-
return
|
|
27922
|
-
return process.env.FAILPROOFAI_HOME ||
|
|
29135
|
+
return resolve28(home, ".failproofai");
|
|
29136
|
+
return process.env.FAILPROOFAI_HOME || resolve28(homedir29(), ".failproofai");
|
|
27923
29137
|
}
|
|
27924
29138
|
function resettablePaths2() {
|
|
27925
29139
|
return [
|
|
@@ -27936,6 +29150,7 @@ function retiredLayoutPaths2() {
|
|
|
27936
29150
|
legacy2.lastVersion(),
|
|
27937
29151
|
legacy2.auditDashboard(),
|
|
27938
29152
|
legacy2.auditCacheDir(),
|
|
29153
|
+
legacy2.auditReminder(),
|
|
27939
29154
|
legacy2.codexSessionPaths(),
|
|
27940
29155
|
legacy2.spoolDir(),
|
|
27941
29156
|
legacy2.failedDir(),
|
|
@@ -27947,7 +29162,7 @@ function retiredLayoutPaths2() {
|
|
|
27947
29162
|
legacy2.localPoliciesDir()
|
|
27948
29163
|
];
|
|
27949
29164
|
}
|
|
27950
|
-
var LAYOUT_VERSION2 =
|
|
29165
|
+
var LAYOUT_VERSION2 = 4, at2 = (...parts) => resolve28(failproofaiHome2(), ...parts), atHome2 = (home, ...parts) => home ? resolve28(failproofaiHome2(home), ...parts) : at2(...parts), versionFile2 = (home) => atHome2(home, "VERSION"), configFile2 = (home) => atHome2(home, "config.json"), credentialsFile2 = (home) => atHome2(home, "credentials.json"), binDir2 = (home) => atHome2(home, "bin"), daemonBinary = (version3, home) => resolve28(binDir2(home), `failproofaid-${version3}`), policiesDir2 = (home) => atHome2(home, "policies"), customPoliciesDir2, globalPolicyConfigFile2 = (home) => atHome2(home, "policies-config.json"), cloudPoliciesDir2 = (home) => resolve28(policiesDir2(home), "cloud-policies"), cursorsDir2 = (source, home) => source ? resolve28(atHome2(home, "cursors"), source) : atHome2(home, "cursors"), customAgentsDir2 = (home) => atHome2(home, "custom-agents"), customAgentsEventsDir = (home) => resolve28(customAgentsDir2(home), "events"), customAgentsFailedDir = (home) => resolve28(customAgentsDir2(home), "failed"), auditDir2 = (home) => atHome2(home, "audit"), auditDashboardFile2 = (home) => resolve28(auditDir2(home), "dashboard.json"), auditCacheDir2 = (home) => resolve28(auditDir2(home), "cache"), auditSessionFile2 = (home) => resolve28(auditDir2(home), "session.json"), auditMachineFile2 = (home) => resolve28(auditDir2(home), "machine.json"), hookActivityDir2 = (home) => atHome2(home, "hook-activity"), runDir2 = (home) => atHome2(home, "run"), daemonSocket2 = () => process.env.FAILPROOFAI_DAEMON_SOCKET || resolve28(runDir2(), "failproofaid.sock"), workerSocket = (home) => resolve28(runDir2(home), "worker.sock"), daemonLock = (home) => resolve28(runDir2(home), "failproofaid.lock"), auditLockFile2 = (home) => resolve28(runDir2(home), "audit.lock"), stateDir2 = (home) => atHome2(home, "state"), shimsDir2 = (home) => resolve28(stateDir2(home), "shims"), spoolDir2 = (home) => resolve28(stateDir2(home), "spool"), failedDir2 = (home) => resolve28(stateDir2(home), "failed"), collectorHealthFile2 = (home) => resolve28(stateDir2(home), "collector-health.json"), sessionPauseDir2 = () => resolve28(stateDir2(), "sessions"), auditScheduleFile2 = (home) => resolve28(auditDir2(home), "schedule.json"), telemetryIdFile2 = (home) => resolve28(stateDir2(home), "telemetry-id"), launcherMarker2 = (home) => resolve28(stateDir2(home), "launcher-configured"), onboardingAttemptFile2 = (home) => resolve28(stateDir2(home), "onboarding-attempt.json"), lastVersionFile2 = (home) => resolve28(stateDir2(home), "last-version"), onboardingLockFile2 = (home) => resolve28(stateDir2(home), "onboarding.lock"), codexSessionPathsFile2 = (home) => resolve28(stateDir2(home), "codex-session-paths.json"), logsDir2 = (home) => atHome2(home, "logs"), migrationsDir2 = (home) => atHome2(home, "migrations"), migrationLedgerFile2 = (home) => resolve28(migrationsDir2(home), "applied.json"), migrationBackupDir2 = (layout, home) => resolve28(migrationsDir2(home), `backup-layout${layout}`), HOME_CLASSES2, legacy2;
|
|
27951
29166
|
var init_fp_home2 = __esm(() => {
|
|
27952
29167
|
customPoliciesDir2 = policiesDir2;
|
|
27953
29168
|
HOME_CLASSES2 = [
|
|
@@ -27955,20 +29170,23 @@ var init_fp_home2 = __esm(() => {
|
|
|
27955
29170
|
{ path: configFile2, class: "user-typed" },
|
|
27956
29171
|
{ path: globalPolicyConfigFile2, class: "user-typed" },
|
|
27957
29172
|
{ path: policiesDir2, class: "user-typed" },
|
|
29173
|
+
{ path: auditSessionFile2, class: "user-typed" },
|
|
27958
29174
|
{ path: spoolDir2, class: "undelivered" },
|
|
27959
29175
|
{ path: failedDir2, class: "undelivered" },
|
|
27960
29176
|
{ path: customAgentsDir2, class: "undelivered" },
|
|
27961
29177
|
{ path: hookActivityDir2, class: "undelivered" },
|
|
27962
|
-
{ path: (home) =>
|
|
27963
|
-
{ path: (home) =>
|
|
29178
|
+
{ path: (home) => resolve28(stateDir2(home), "flush-request.json"), class: "undelivered" },
|
|
29179
|
+
{ path: (home) => resolve28(stateDir2(home), "backfill-request.json"), class: "undelivered" },
|
|
27964
29180
|
{ path: cursorsDir2, class: "identity" },
|
|
27965
29181
|
{ path: telemetryIdFile2, class: "identity" },
|
|
27966
|
-
{ path: (home) =>
|
|
29182
|
+
{ path: (home) => resolve28(stateDir2(home), "daemon-telemetry-id"), class: "identity" },
|
|
27967
29183
|
{ path: versionFile2, class: "identity" },
|
|
27968
29184
|
{ path: migrationsDir2, class: "identity" },
|
|
27969
|
-
{ path:
|
|
27970
|
-
{ path:
|
|
29185
|
+
{ path: auditMachineFile2, class: "identity" },
|
|
29186
|
+
{ path: auditDashboardFile2, class: "derived" },
|
|
29187
|
+
{ path: auditCacheDir2, class: "derived" },
|
|
27971
29188
|
{ path: auditScheduleFile2, class: "derived" },
|
|
29189
|
+
{ path: collectorHealthFile2, class: "derived" },
|
|
27972
29190
|
{ path: codexSessionPathsFile2, class: "derived" },
|
|
27973
29191
|
{ path: shimsDir2, class: "derived" },
|
|
27974
29192
|
{ path: sessionPauseDir2, class: "derived" },
|
|
@@ -27994,6 +29212,10 @@ var init_fp_home2 = __esm(() => {
|
|
|
27994
29212
|
launcherMarker: () => at2(".launcher-configured"),
|
|
27995
29213
|
lastVersion: () => at2("last-version"),
|
|
27996
29214
|
auditDashboard: () => at2("audit-dashboard.json"),
|
|
29215
|
+
authJson: () => at2("auth.json"),
|
|
29216
|
+
nextAudit: () => at2("next-audit.json"),
|
|
29217
|
+
auditReminder: () => at2("audit", "reminder.json"),
|
|
29218
|
+
auditSchedule: () => at2("state", "audit-schedule.json"),
|
|
27997
29219
|
cacheDir: () => at2("cache"),
|
|
27998
29220
|
hookActivityDir: () => at2("cache", "hook-activity"),
|
|
27999
29221
|
auditCacheDir: () => at2("cache", "audit"),
|
|
@@ -28013,26 +29235,83 @@ __export(exports_migrations2, {
|
|
|
28013
29235
|
runMigrations: () => runMigrations2,
|
|
28014
29236
|
restoreBackup: () => restoreBackup2,
|
|
28015
29237
|
readLedger: () => readLedger2,
|
|
29238
|
+
pruneMigratedCredentials: () => pruneMigratedCredentials2,
|
|
28016
29239
|
planMigration: () => planMigration2,
|
|
28017
29240
|
migrationCoverageGap: () => migrationCoverageGap2,
|
|
28018
29241
|
describePlan: () => describePlan2,
|
|
28019
29242
|
backupBeforeMigrating: () => backupBeforeMigrating2,
|
|
28020
29243
|
MIGRATIONS: () => MIGRATIONS2
|
|
28021
29244
|
});
|
|
28022
|
-
import {
|
|
28023
|
-
|
|
29245
|
+
import {
|
|
29246
|
+
chmodSync as chmodSync7,
|
|
29247
|
+
copyFileSync as copyFileSync4,
|
|
29248
|
+
existsSync as existsSync40,
|
|
29249
|
+
mkdirSync as mkdirSync25,
|
|
29250
|
+
readFileSync as readFileSync33,
|
|
29251
|
+
renameSync as renameSync8,
|
|
29252
|
+
rmSync as rmSync14,
|
|
29253
|
+
writeFileSync as writeFileSync23
|
|
29254
|
+
} from "node:fs";
|
|
29255
|
+
import { basename as basename7, dirname as dirname22, join as join36, resolve as resolve29 } from "node:path";
|
|
29256
|
+
function migrateToLayout42() {
|
|
29257
|
+
const moves = [
|
|
29258
|
+
{ from: legacy.authJson(), to: auditSessionFile() },
|
|
29259
|
+
{ from: legacy.nextAudit(), to: legacy.auditReminder() },
|
|
29260
|
+
{ from: legacy.auditSchedule(), to: auditScheduleFile() }
|
|
29261
|
+
];
|
|
29262
|
+
const authDirOverride = process.env.FAILPROOFAI_AUTH_DIR;
|
|
29263
|
+
if (authDirOverride) {
|
|
29264
|
+
moves.push({ from: join36(authDirOverride, "auth.json"), to: join36(authDirOverride, "session.json") }, {
|
|
29265
|
+
from: join36(authDirOverride, "next-audit.json"),
|
|
29266
|
+
to: join36(authDirOverride, "reminder.json")
|
|
29267
|
+
});
|
|
29268
|
+
}
|
|
29269
|
+
const migrated = [];
|
|
29270
|
+
for (const { from, to } of moves) {
|
|
29271
|
+
if (!existsSync40(from))
|
|
29272
|
+
continue;
|
|
29273
|
+
if (existsSync40(to)) {
|
|
29274
|
+
rmSync14(from, { force: true });
|
|
29275
|
+
continue;
|
|
29276
|
+
}
|
|
29277
|
+
mkdirSync25(dirname22(to), { recursive: true });
|
|
29278
|
+
try {
|
|
29279
|
+
renameSync8(from, to);
|
|
29280
|
+
} catch {
|
|
29281
|
+
try {
|
|
29282
|
+
copyFileSync4(from, to);
|
|
29283
|
+
} catch (err) {
|
|
29284
|
+
try {
|
|
29285
|
+
rmSync14(to, { force: true });
|
|
29286
|
+
} catch {}
|
|
29287
|
+
throw err;
|
|
29288
|
+
}
|
|
29289
|
+
rmSync14(from, { force: true });
|
|
29290
|
+
}
|
|
29291
|
+
migrated.push(`${basename7(from)} → audit/${basename7(to)}`);
|
|
29292
|
+
}
|
|
29293
|
+
for (const secret of [auditSessionFile()]) {
|
|
29294
|
+
if (!existsSync40(secret))
|
|
29295
|
+
continue;
|
|
29296
|
+
try {
|
|
29297
|
+
chmodSync7(secret, 384);
|
|
29298
|
+
} catch {}
|
|
29299
|
+
}
|
|
29300
|
+
writeVersionFile({ layout: 4 });
|
|
29301
|
+
return { removed: [], migrated, activity: [], policyConfig: [], spooled: [], from: 3 };
|
|
29302
|
+
}
|
|
28024
29303
|
function planMigration2(from, registry = MIGRATIONS2) {
|
|
28025
29304
|
if (from >= LAYOUT_VERSION)
|
|
28026
29305
|
return [];
|
|
28027
29306
|
const chain = [];
|
|
28028
29307
|
let at3 = from;
|
|
28029
29308
|
while (at3 < LAYOUT_VERSION) {
|
|
28030
|
-
const
|
|
28031
|
-
if (!
|
|
29309
|
+
const step2 = registry.find((m) => m.from === at3);
|
|
29310
|
+
if (!step2) {
|
|
28032
29311
|
throw new Error(`no migration from layout ${at3} to ${LAYOUT_VERSION} — this build cannot upgrade that home`);
|
|
28033
29312
|
}
|
|
28034
|
-
chain.push(
|
|
28035
|
-
at3 =
|
|
29313
|
+
chain.push(step2);
|
|
29314
|
+
at3 = step2.to;
|
|
28036
29315
|
}
|
|
28037
29316
|
return chain;
|
|
28038
29317
|
}
|
|
@@ -28058,7 +29337,7 @@ function migrationCoverageGap2(registry = MIGRATIONS2) {
|
|
|
28058
29337
|
}
|
|
28059
29338
|
function readLedger2() {
|
|
28060
29339
|
try {
|
|
28061
|
-
const parsed = JSON.parse(
|
|
29340
|
+
const parsed = JSON.parse(readFileSync33(migrationLedgerFile(), "utf8"));
|
|
28062
29341
|
return Array.isArray(parsed) ? parsed : [];
|
|
28063
29342
|
} catch {
|
|
28064
29343
|
return [];
|
|
@@ -28075,11 +29354,28 @@ function appendLedger2(entry) {
|
|
|
28075
29354
|
function backupNameOf2(f) {
|
|
28076
29355
|
return f.as ?? basename7(f.at());
|
|
28077
29356
|
}
|
|
29357
|
+
function pruneMigratedCredentials2(from) {
|
|
29358
|
+
const pruned = [];
|
|
29359
|
+
for (let layout = from;layout < LAYOUT_VERSION; layout++) {
|
|
29360
|
+
for (const { as, landsAt } of PRUNED_AFTER_SUCCESS2) {
|
|
29361
|
+
try {
|
|
29362
|
+
if (!existsSync40(landsAt()))
|
|
29363
|
+
continue;
|
|
29364
|
+
const copy = resolve29(migrationBackupDir(layout), as);
|
|
29365
|
+
if (!existsSync40(copy))
|
|
29366
|
+
continue;
|
|
29367
|
+
rmSync14(copy, { force: true });
|
|
29368
|
+
pruned.push(as);
|
|
29369
|
+
} catch {}
|
|
29370
|
+
}
|
|
29371
|
+
}
|
|
29372
|
+
return pruned;
|
|
29373
|
+
}
|
|
28078
29374
|
function filesToBackUp2() {
|
|
28079
29375
|
const seen = new Map;
|
|
28080
29376
|
for (const f of [...BACKED_UP2, ...BACKED_UP_LEGACY2]) {
|
|
28081
29377
|
const path3 = f.at();
|
|
28082
|
-
if (
|
|
29378
|
+
if (existsSync40(path3))
|
|
28083
29379
|
seen.set(path3, backupNameOf2(f));
|
|
28084
29380
|
}
|
|
28085
29381
|
return [...seen].map(([path3, as]) => ({ path: path3, as }));
|
|
@@ -28088,14 +29384,14 @@ function backupBeforeMigrating2(from) {
|
|
|
28088
29384
|
const dir = migrationBackupDir(from);
|
|
28089
29385
|
const saved = [];
|
|
28090
29386
|
try {
|
|
28091
|
-
|
|
29387
|
+
rmSync14(dir, { recursive: true, force: true });
|
|
28092
29388
|
mkdirSync25(dir, { recursive: true });
|
|
28093
29389
|
} catch {
|
|
28094
29390
|
return [];
|
|
28095
29391
|
}
|
|
28096
29392
|
for (const { path: source, as } of filesToBackUp2()) {
|
|
28097
29393
|
try {
|
|
28098
|
-
copyFileSync4(source,
|
|
29394
|
+
copyFileSync4(source, resolve29(dir, as));
|
|
28099
29395
|
saved.push(as);
|
|
28100
29396
|
} catch {}
|
|
28101
29397
|
}
|
|
@@ -28113,19 +29409,19 @@ function runMigrations2(from, chain = planMigration2(from)) {
|
|
|
28113
29409
|
from
|
|
28114
29410
|
};
|
|
28115
29411
|
let failed;
|
|
28116
|
-
for (const
|
|
29412
|
+
for (const step2 of chain) {
|
|
28117
29413
|
const startedAt = Date.now();
|
|
28118
29414
|
try {
|
|
28119
|
-
const result =
|
|
29415
|
+
const result = step2.run();
|
|
28120
29416
|
outcome.removed.push(...result.removed);
|
|
28121
29417
|
outcome.migrated.push(...result.migrated);
|
|
28122
29418
|
outcome.activity.push(...result.activity);
|
|
28123
29419
|
outcome.policyConfig.push(...result.policyConfig);
|
|
28124
29420
|
outcome.spooled.push(...result.spooled);
|
|
28125
|
-
steps.push({ from:
|
|
29421
|
+
steps.push({ from: step2.from, to: step2.to, ok: true });
|
|
28126
29422
|
appendLedger2({
|
|
28127
|
-
from:
|
|
28128
|
-
to:
|
|
29423
|
+
from: step2.from,
|
|
29424
|
+
to: step2.to,
|
|
28129
29425
|
cli: version2,
|
|
28130
29426
|
at: new Date(startedAt).toISOString(),
|
|
28131
29427
|
durationMs: Date.now() - startedAt,
|
|
@@ -28135,15 +29431,20 @@ function runMigrations2(from, chain = planMigration2(from)) {
|
|
|
28135
29431
|
activity: result.activity.length
|
|
28136
29432
|
});
|
|
28137
29433
|
} catch (err) {
|
|
28138
|
-
steps.push({ from:
|
|
29434
|
+
steps.push({ from: step2.from, to: step2.to, ok: false });
|
|
29435
|
+
try {
|
|
29436
|
+
const marker = readVersionFile();
|
|
29437
|
+
if (marker && marker.layout >= LAYOUT_VERSION)
|
|
29438
|
+
writeVersionFile({ layout: step2.from });
|
|
29439
|
+
} catch {}
|
|
28139
29440
|
failed = {
|
|
28140
|
-
from:
|
|
28141
|
-
to:
|
|
29441
|
+
from: step2.from,
|
|
29442
|
+
to: step2.to,
|
|
28142
29443
|
error: err instanceof Error ? err.message : String(err)
|
|
28143
29444
|
};
|
|
28144
29445
|
appendLedger2({
|
|
28145
|
-
from:
|
|
28146
|
-
to:
|
|
29446
|
+
from: step2.from,
|
|
29447
|
+
to: step2.to,
|
|
28147
29448
|
cli: version2,
|
|
28148
29449
|
at: new Date(startedAt).toISOString(),
|
|
28149
29450
|
durationMs: Date.now() - startedAt,
|
|
@@ -28152,6 +29453,8 @@ function runMigrations2(from, chain = planMigration2(from)) {
|
|
|
28152
29453
|
break;
|
|
28153
29454
|
}
|
|
28154
29455
|
}
|
|
29456
|
+
if (!failed)
|
|
29457
|
+
pruneMigratedCredentials2(from);
|
|
28155
29458
|
return { from, steps, backedUp, outcome, failed };
|
|
28156
29459
|
}
|
|
28157
29460
|
function describePlan2(from) {
|
|
@@ -28178,13 +29481,13 @@ function describePlan2(from) {
|
|
|
28178
29481
|
}
|
|
28179
29482
|
function restoreBackup2(layout) {
|
|
28180
29483
|
const dir = migrationBackupDir(layout);
|
|
28181
|
-
if (!
|
|
29484
|
+
if (!existsSync40(dir))
|
|
28182
29485
|
return [];
|
|
28183
29486
|
const restored = [];
|
|
28184
29487
|
for (const f of [...BACKED_UP2, ...BACKED_UP_LEGACY2]) {
|
|
28185
29488
|
const target = f.at();
|
|
28186
|
-
const source =
|
|
28187
|
-
if (!
|
|
29489
|
+
const source = resolve29(dir, backupNameOf2(f));
|
|
29490
|
+
if (!existsSync40(source))
|
|
28188
29491
|
continue;
|
|
28189
29492
|
try {
|
|
28190
29493
|
mkdirSync25(dirname22(target), { recursive: true });
|
|
@@ -28194,23 +29497,30 @@ function restoreBackup2(layout) {
|
|
|
28194
29497
|
}
|
|
28195
29498
|
return restored.sort((a, b) => a.localeCompare(b));
|
|
28196
29499
|
}
|
|
28197
|
-
var MIGRATIONS2, BACKED_UP2, BACKED_UP_LEGACY2;
|
|
29500
|
+
var MIGRATIONS2, BACKED_UP2, BACKED_UP_LEGACY2, PRUNED_AFTER_SUCCESS2;
|
|
28198
29501
|
var init_migrations2 = __esm(() => {
|
|
28199
29502
|
init_package();
|
|
28200
29503
|
init_fp_home();
|
|
29504
|
+
init_fp_config();
|
|
28201
29505
|
init_fp_reset();
|
|
28202
29506
|
MIGRATIONS2 = [
|
|
28203
29507
|
{
|
|
28204
29508
|
from: 1,
|
|
28205
29509
|
to: 3,
|
|
28206
29510
|
describe: "layout 1 → 3: carry the decision log out of cache/, keep the policy config in place, drop the layout-1 credential files",
|
|
28207
|
-
run: () => resetHome(1)
|
|
29511
|
+
run: () => resetHome(1, 3)
|
|
28208
29512
|
},
|
|
28209
29513
|
{
|
|
28210
29514
|
from: 2,
|
|
28211
29515
|
to: 3,
|
|
28212
29516
|
describe: "layout 2 → 3: carry config.toml and credentials.toml into JSON, move custom-policies/ back up into policies/, nest the policy config at the root",
|
|
28213
|
-
run: () => resetHome(2)
|
|
29517
|
+
run: () => resetHome(2, 3)
|
|
29518
|
+
},
|
|
29519
|
+
{
|
|
29520
|
+
from: 3,
|
|
29521
|
+
to: 4,
|
|
29522
|
+
describe: "layout 3 → 4: gather the audit's files under audit/ — auth.json becomes audit/session.json, next-audit.json becomes audit/reminder.json, state/audit-schedule.json becomes audit/schedule.json",
|
|
29523
|
+
run: migrateToLayout42
|
|
28214
29524
|
}
|
|
28215
29525
|
];
|
|
28216
29526
|
BACKED_UP2 = [
|
|
@@ -28225,7 +29535,13 @@ var init_migrations2 = __esm(() => {
|
|
|
28225
29535
|
{ at: legacy.policyConfig },
|
|
28226
29536
|
{ at: legacy.localPolicyConfig, as: "local-policies-policies-config.json" },
|
|
28227
29537
|
{ at: legacy.cloudCredentials },
|
|
28228
|
-
{ at: legacy.ingestCredentials }
|
|
29538
|
+
{ at: legacy.ingestCredentials },
|
|
29539
|
+
{ at: legacy.authJson },
|
|
29540
|
+
{ at: legacy.nextAudit },
|
|
29541
|
+
{ at: legacy.auditSchedule }
|
|
29542
|
+
];
|
|
29543
|
+
PRUNED_AFTER_SUCCESS2 = [
|
|
29544
|
+
{ as: basename7(legacy.authJson()), landsAt: auditSessionFile }
|
|
28229
29545
|
];
|
|
28230
29546
|
});
|
|
28231
29547
|
|
|
@@ -28237,6 +29553,7 @@ __export(exports_daemon_service, {
|
|
|
28237
29553
|
upgradedServiceDefinition: () => upgradedServiceDefinition2,
|
|
28238
29554
|
uninstallDaemonService: () => uninstallDaemonService2,
|
|
28239
29555
|
systemdUnitContents: () => systemdUnitContents2,
|
|
29556
|
+
startedAtFromMonotonic: () => startedAtFromMonotonic,
|
|
28240
29557
|
setDaemonConfigured: () => setDaemonConfigured2,
|
|
28241
29558
|
resolveWorkerCommand: () => resolveWorkerCommand2,
|
|
28242
29559
|
resolveFailproofaidBinaryPath: () => resolveFailproofaidBinaryPath2,
|
|
@@ -28253,6 +29570,7 @@ __export(exports_daemon_service, {
|
|
|
28253
29570
|
ensureDaemonServiceCurrent: () => ensureDaemonServiceCurrent2,
|
|
28254
29571
|
daemonVersionSkew: () => daemonVersionSkew2,
|
|
28255
29572
|
daemonStatusCommand: () => daemonStatusCommand2,
|
|
29573
|
+
daemonStartedAtMs: () => daemonStartedAtMs,
|
|
28256
29574
|
daemonServiceStatus: () => daemonServiceStatus2,
|
|
28257
29575
|
daemonServiceNeedsUpgrade: () => daemonServiceNeedsUpgrade2,
|
|
28258
29576
|
daemonServiceFilePath: () => daemonServiceFilePath2,
|
|
@@ -28260,16 +29578,16 @@ __export(exports_daemon_service, {
|
|
|
28260
29578
|
canElevate: () => canElevate2
|
|
28261
29579
|
});
|
|
28262
29580
|
import {
|
|
28263
|
-
existsSync as
|
|
29581
|
+
existsSync as existsSync41,
|
|
28264
29582
|
mkdirSync as mkdirSync26,
|
|
28265
29583
|
mkdtempSync as mkdtempSync2,
|
|
28266
|
-
readFileSync as
|
|
29584
|
+
readFileSync as readFileSync34,
|
|
28267
29585
|
writeFileSync as writeFileSync24,
|
|
28268
29586
|
unlinkSync as unlinkSync5,
|
|
28269
|
-
rmSync as
|
|
29587
|
+
rmSync as rmSync15
|
|
28270
29588
|
} from "node:fs";
|
|
28271
|
-
import { homedir as
|
|
28272
|
-
import { resolve as
|
|
29589
|
+
import { homedir as homedir30, tmpdir as tmpdir3, userInfo as userInfo2 } from "node:os";
|
|
29590
|
+
import { resolve as resolve30 } from "node:path";
|
|
28273
29591
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
28274
29592
|
function setDaemonConfigured2(value, installedVersion) {
|
|
28275
29593
|
try {
|
|
@@ -28291,13 +29609,13 @@ function resolveFailproofaidBinaryPath2() {
|
|
|
28291
29609
|
if (process.env.FAILPROOFAI_DAEMON_BINARY)
|
|
28292
29610
|
return process.env.FAILPROOFAI_DAEMON_BINARY;
|
|
28293
29611
|
const downloaded = installedBinaryPath();
|
|
28294
|
-
if (
|
|
29612
|
+
if (existsSync41(downloaded))
|
|
28295
29613
|
return downloaded;
|
|
28296
29614
|
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
28297
29615
|
if (packageRoot) {
|
|
28298
29616
|
for (const profile of ["release", "debug"]) {
|
|
28299
|
-
const candidate =
|
|
28300
|
-
if (
|
|
29617
|
+
const candidate = resolve30(packageRoot, "target", profile, "failproofaid");
|
|
29618
|
+
if (existsSync41(candidate))
|
|
28301
29619
|
return candidate;
|
|
28302
29620
|
}
|
|
28303
29621
|
}
|
|
@@ -28327,8 +29645,8 @@ function resolveWorkerCommand2() {
|
|
|
28327
29645
|
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
28328
29646
|
if (!packageRoot)
|
|
28329
29647
|
return null;
|
|
28330
|
-
const workerScript =
|
|
28331
|
-
if (!
|
|
29648
|
+
const workerScript = resolve30(packageRoot, "dist", "worker.mjs");
|
|
29649
|
+
if (!existsSync41(workerScript))
|
|
28332
29650
|
return null;
|
|
28333
29651
|
return `${shellQuote2(process.execPath)} ${shellQuote2(workerScript)}`;
|
|
28334
29652
|
}
|
|
@@ -28338,8 +29656,8 @@ function resolveCliCommand2() {
|
|
|
28338
29656
|
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
28339
29657
|
if (!packageRoot)
|
|
28340
29658
|
return null;
|
|
28341
|
-
const cliScript =
|
|
28342
|
-
if (!
|
|
29659
|
+
const cliScript = resolve30(packageRoot, "dist", "cli.mjs");
|
|
29660
|
+
if (!existsSync41(cliScript))
|
|
28343
29661
|
return null;
|
|
28344
29662
|
return `${shellQuote2(process.execPath)} ${shellQuote2(cliScript)}`;
|
|
28345
29663
|
}
|
|
@@ -28352,8 +29670,8 @@ function workerScriptPath2() {
|
|
|
28352
29670
|
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
28353
29671
|
if (!packageRoot)
|
|
28354
29672
|
return null;
|
|
28355
|
-
const workerScript =
|
|
28356
|
-
return
|
|
29673
|
+
const workerScript = resolve30(packageRoot, "dist", "worker.mjs");
|
|
29674
|
+
return existsSync41(workerScript) ? workerScript : null;
|
|
28357
29675
|
}
|
|
28358
29676
|
function assertUnitSafe2(value, field) {
|
|
28359
29677
|
if (UNIT_UNSAFE_RE2.test(value)) {
|
|
@@ -28379,10 +29697,10 @@ function systemdUnitDir2() {
|
|
|
28379
29697
|
return process.env.FAILPROOFAI_SYSTEMD_DIR || "/etc/systemd/system";
|
|
28380
29698
|
}
|
|
28381
29699
|
function systemdUnitPath2(user = serviceUser2()) {
|
|
28382
|
-
return
|
|
29700
|
+
return resolve30(systemdUnitDir2(), systemdUnitName2(user));
|
|
28383
29701
|
}
|
|
28384
29702
|
function legacySystemdUserUnitPath2() {
|
|
28385
|
-
return
|
|
29703
|
+
return resolve30(homedir30(), ".config", "systemd", "user", "failproofaid.service");
|
|
28386
29704
|
}
|
|
28387
29705
|
function launchdLabel2(user = serviceUser2()) {
|
|
28388
29706
|
return `ai.failproof.failproofaid.${user}`;
|
|
@@ -28391,7 +29709,7 @@ function launchdPlistPath2(user = serviceUser2()) {
|
|
|
28391
29709
|
return `/Library/LaunchDaemons/${launchdLabel2(user)}.plist`;
|
|
28392
29710
|
}
|
|
28393
29711
|
function legacyLaunchAgentPlistPath2() {
|
|
28394
|
-
return
|
|
29712
|
+
return resolve30(homedir30(), "Library", "LaunchAgents", "ai.failproof.failproofaid.plist");
|
|
28395
29713
|
}
|
|
28396
29714
|
function legacySharedLaunchDaemonPath2() {
|
|
28397
29715
|
return "/Library/LaunchDaemons/ai.failproof.failproofaid.plist";
|
|
@@ -28439,20 +29757,20 @@ function runPrivileged2(command, args) {
|
|
|
28439
29757
|
execFileSync5(cmd, argv, { stdio: "ignore", timeout: SERVICE_CMD_TIMEOUT_MS2 });
|
|
28440
29758
|
}
|
|
28441
29759
|
function writePrivilegedFile2(destination, contents, mode = "0644") {
|
|
28442
|
-
const stagingDir = mkdtempSync2(
|
|
28443
|
-
const staging =
|
|
29760
|
+
const stagingDir = mkdtempSync2(resolve30(tmpdir3(), "failproofaid-stage-"));
|
|
29761
|
+
const staging = resolve30(stagingDir, "service-definition");
|
|
28444
29762
|
try {
|
|
28445
29763
|
writeFileSync24(staging, contents, "utf8");
|
|
28446
29764
|
runPrivileged2("install", ["-m", mode, staging, destination]);
|
|
28447
29765
|
} finally {
|
|
28448
|
-
|
|
29766
|
+
rmSync15(stagingDir, { recursive: true, force: true });
|
|
28449
29767
|
}
|
|
28450
29768
|
}
|
|
28451
29769
|
function removeLegacyUserService2() {
|
|
28452
29770
|
try {
|
|
28453
29771
|
if (process.platform === "linux") {
|
|
28454
29772
|
const legacy3 = legacySystemdUserUnitPath2();
|
|
28455
|
-
if (!
|
|
29773
|
+
if (!existsSync41(legacy3))
|
|
28456
29774
|
return;
|
|
28457
29775
|
try {
|
|
28458
29776
|
execFileSync5("systemctl", ["--user", "disable", "--now", "failproofaid.service"], {
|
|
@@ -28469,7 +29787,7 @@ function removeLegacyUserService2() {
|
|
|
28469
29787
|
} catch {}
|
|
28470
29788
|
} else {
|
|
28471
29789
|
const legacyAgent = legacyLaunchAgentPlistPath2();
|
|
28472
|
-
if (
|
|
29790
|
+
if (existsSync41(legacyAgent)) {
|
|
28473
29791
|
try {
|
|
28474
29792
|
execFileSync5("launchctl", ["unload", "-w", legacyAgent], {
|
|
28475
29793
|
stdio: "ignore",
|
|
@@ -28479,7 +29797,7 @@ function removeLegacyUserService2() {
|
|
|
28479
29797
|
unlinkSync5(legacyAgent);
|
|
28480
29798
|
}
|
|
28481
29799
|
const legacyDaemon = legacySharedLaunchDaemonPath2();
|
|
28482
|
-
if (
|
|
29800
|
+
if (existsSync41(legacyDaemon)) {
|
|
28483
29801
|
try {
|
|
28484
29802
|
runPrivileged2("launchctl", ["unload", "-w", legacyDaemon]);
|
|
28485
29803
|
} catch {}
|
|
@@ -28495,8 +29813,8 @@ function systemdUnitContents2(binaryPath, workerCmd, cliCmd = null) {
|
|
|
28495
29813
|
`).join("");
|
|
28496
29814
|
const user = assertUnitSafe2(serviceUser2(), "User");
|
|
28497
29815
|
assertUnitSafe2(binaryPath, "ExecStart");
|
|
28498
|
-
assertUnitSafe2(
|
|
28499
|
-
const conditionPaths = [binaryPath, workerScriptPath2()].filter((p) => typeof p === "string" &&
|
|
29816
|
+
assertUnitSafe2(homedir30(), "HOME");
|
|
29817
|
+
const conditionPaths = [binaryPath, workerScriptPath2()].filter((p) => typeof p === "string" && existsSync41(p));
|
|
28500
29818
|
const conditionLines = conditionPaths.map((p) => `ConditionPathExists=${assertUnitSafe2(p, "ConditionPathExists")}
|
|
28501
29819
|
`).join("");
|
|
28502
29820
|
return `[Unit]
|
|
@@ -28509,7 +29827,7 @@ User=${user}
|
|
|
28509
29827
|
# Set explicitly rather than relying on systemd deriving it from User=:
|
|
28510
29828
|
# failproofaid stores per-user state and refuses to start without HOME, so this
|
|
28511
29829
|
# required variable is not left to a version-dependent default.
|
|
28512
|
-
Environment="HOME=${
|
|
29830
|
+
Environment="HOME=${homedir30()}"
|
|
28513
29831
|
${envLines}ExecStart=${binaryPath}
|
|
28514
29832
|
Restart=on-failure
|
|
28515
29833
|
RestartSec=2
|
|
@@ -28557,9 +29875,9 @@ ${env.map(([key, value]) => ` <key>${key}</key>
|
|
|
28557
29875
|
<key>UserName</key>
|
|
28558
29876
|
<string>${escapeXml2(serviceUser2())}</string>
|
|
28559
29877
|
${envBlock} <key>StandardOutPath</key>
|
|
28560
|
-
<string>${escapeXml2(
|
|
29878
|
+
<string>${escapeXml2(resolve30(logDir2, "failproofaid.log"))}</string>
|
|
28561
29879
|
<key>StandardErrorPath</key>
|
|
28562
|
-
<string>${escapeXml2(
|
|
29880
|
+
<string>${escapeXml2(resolve30(logDir2, "failproofaid.err.log"))}</string>
|
|
28563
29881
|
</dict>
|
|
28564
29882
|
</plist>
|
|
28565
29883
|
`;
|
|
@@ -28691,10 +30009,10 @@ function readInstalledServiceDefinition2() {
|
|
|
28691
30009
|
if (!isDaemonSupportedPlatform2())
|
|
28692
30010
|
return null;
|
|
28693
30011
|
const path3 = daemonServiceFilePath2();
|
|
28694
|
-
if (!path3 || !
|
|
30012
|
+
if (!path3 || !existsSync41(path3))
|
|
28695
30013
|
return null;
|
|
28696
30014
|
try {
|
|
28697
|
-
return
|
|
30015
|
+
return readFileSync34(path3, "utf8");
|
|
28698
30016
|
} catch {
|
|
28699
30017
|
return null;
|
|
28700
30018
|
}
|
|
@@ -28855,7 +30173,7 @@ async function uninstallDaemonService2() {
|
|
|
28855
30173
|
try {
|
|
28856
30174
|
if (process.platform === "linux") {
|
|
28857
30175
|
const unitPath = systemdUnitPath2();
|
|
28858
|
-
if (!
|
|
30176
|
+
if (!existsSync41(unitPath))
|
|
28859
30177
|
return;
|
|
28860
30178
|
try {
|
|
28861
30179
|
runPrivileged2("systemctl", ["disable", "--now", systemdUnitName2()]);
|
|
@@ -28866,7 +30184,7 @@ async function uninstallDaemonService2() {
|
|
|
28866
30184
|
} catch {}
|
|
28867
30185
|
} else {
|
|
28868
30186
|
const plistPath = launchdPlistPath2();
|
|
28869
|
-
if (!
|
|
30187
|
+
if (!existsSync41(plistPath))
|
|
28870
30188
|
return;
|
|
28871
30189
|
try {
|
|
28872
30190
|
runPrivileged2("launchctl", ["unload", "-w", plistPath]);
|
|
@@ -28883,7 +30201,7 @@ function daemonVersionSkew2() {
|
|
|
28883
30201
|
const recorded = readVersionFile()?.daemon;
|
|
28884
30202
|
if (!recorded)
|
|
28885
30203
|
return null;
|
|
28886
|
-
if (!
|
|
30204
|
+
if (!existsSync41(installedBinaryPath(recorded)))
|
|
28887
30205
|
return null;
|
|
28888
30206
|
return recorded === version2 ? null : { installed: recorded, expected: version2 };
|
|
28889
30207
|
}
|
|
@@ -28901,7 +30219,7 @@ function daemonServiceStatus2() {
|
|
|
28901
30219
|
if (!isDaemonSupportedPlatform2())
|
|
28902
30220
|
return "unsupported-platform";
|
|
28903
30221
|
if (process.platform === "linux") {
|
|
28904
|
-
if (!
|
|
30222
|
+
if (!existsSync41(systemdUnitPath2()))
|
|
28905
30223
|
return "not-installed";
|
|
28906
30224
|
try {
|
|
28907
30225
|
const out = execFileSync5("systemctl", ["is-active", systemdUnitName2()], {
|
|
@@ -28914,7 +30232,7 @@ function daemonServiceStatus2() {
|
|
|
28914
30232
|
}
|
|
28915
30233
|
}
|
|
28916
30234
|
const plistPath = launchdPlistPath2();
|
|
28917
|
-
if (!
|
|
30235
|
+
if (!existsSync41(plistPath))
|
|
28918
30236
|
return "not-installed";
|
|
28919
30237
|
try {
|
|
28920
30238
|
const root = typeof process.getuid === "function" && process.getuid() === 0;
|
|
@@ -28933,6 +30251,28 @@ function daemonServiceStatus2() {
|
|
|
28933
30251
|
return "stopped";
|
|
28934
30252
|
}
|
|
28935
30253
|
}
|
|
30254
|
+
function daemonStartedAtMs() {
|
|
30255
|
+
if (process.platform !== "linux")
|
|
30256
|
+
return null;
|
|
30257
|
+
if (!existsSync41(systemdUnitPath2()))
|
|
30258
|
+
return null;
|
|
30259
|
+
try {
|
|
30260
|
+
const raw = execFileSync5("systemctl", ["show", systemdUnitName2(), "-p", "ActiveEnterTimestampMonotonic", "--value"], { stdio: ["ignore", "pipe", "ignore"], timeout: SERVICE_CMD_TIMEOUT_MS2 }).toString().trim();
|
|
30261
|
+
return startedAtFromMonotonic(Number(raw), Math.floor(Number(process.hrtime.bigint()) / 1000), Date.now());
|
|
30262
|
+
} catch {
|
|
30263
|
+
return null;
|
|
30264
|
+
}
|
|
30265
|
+
}
|
|
30266
|
+
function startedAtFromMonotonic(activeEnterMonotonicUs, monotonicNowUs, nowMs) {
|
|
30267
|
+
if (!Number.isFinite(activeEnterMonotonicUs) || activeEnterMonotonicUs <= 0)
|
|
30268
|
+
return null;
|
|
30269
|
+
if (!Number.isFinite(monotonicNowUs) || monotonicNowUs <= 0)
|
|
30270
|
+
return null;
|
|
30271
|
+
const activeForMs = (monotonicNowUs - activeEnterMonotonicUs) / 1000;
|
|
30272
|
+
if (activeForMs < 0)
|
|
30273
|
+
return null;
|
|
30274
|
+
return Math.round(nowMs - activeForMs);
|
|
30275
|
+
}
|
|
28936
30276
|
var SERVICE_CMD_TIMEOUT_MS2 = 1e4, SERVICE_START_TIMEOUT_MS2 = 5000, SERVICE_START_POLL_MS2 = 100, SERVICE_SETTLE_MS2 = 750, SUDO_PROMPT_TIMEOUT_MS2 = 120000, UNIT_UNSAFE_RE2, DAEMON_PROBE_TIMEOUT_MS2 = 5000, DAEMON_PROBE_READY_TIMEOUT_MS2 = 1e4, DAEMON_PROBE_RETRY_MS2 = 150;
|
|
28937
30277
|
var init_daemon_service2 = __esm(() => {
|
|
28938
30278
|
init_hook_logger();
|
|
@@ -28949,7 +30289,7 @@ __export(exports_uninstall_cli, {
|
|
|
28949
30289
|
serviceStateLabel: () => serviceStateLabel,
|
|
28950
30290
|
runUninstallCommand: () => runUninstallCommand
|
|
28951
30291
|
});
|
|
28952
|
-
import { existsSync as
|
|
30292
|
+
import { existsSync as existsSync42, rmSync as rmSync16 } from "node:fs";
|
|
28953
30293
|
function survey(cwd) {
|
|
28954
30294
|
const clis = [];
|
|
28955
30295
|
for (const id of listInstallableIds()) {
|
|
@@ -28970,9 +30310,9 @@ function survey(cwd) {
|
|
|
28970
30310
|
return {
|
|
28971
30311
|
clis,
|
|
28972
30312
|
servicePath,
|
|
28973
|
-
serviceInstalled: !!servicePath &&
|
|
30313
|
+
serviceInstalled: !!servicePath && existsSync42(servicePath),
|
|
28974
30314
|
daemonConfigured,
|
|
28975
|
-
homeExists:
|
|
30315
|
+
homeExists: existsSync42(failproofaiHome())
|
|
28976
30316
|
};
|
|
28977
30317
|
}
|
|
28978
30318
|
async function runUninstallCommand(opts = {}) {
|
|
@@ -29070,7 +30410,7 @@ async function runUninstallCommand(opts = {}) {
|
|
|
29070
30410
|
} catch {}
|
|
29071
30411
|
try {
|
|
29072
30412
|
await uninstallDaemonService();
|
|
29073
|
-
if (found.servicePath &&
|
|
30413
|
+
if (found.servicePath && existsSync42(found.servicePath)) {
|
|
29074
30414
|
failures.push(`the service at ${found.servicePath} is still there (most often: no sudo). ` + `Remove it with the commands below.`);
|
|
29075
30415
|
} else {
|
|
29076
30416
|
lines.push(`✓ stopped and removed the daemon service`);
|
|
@@ -29082,7 +30422,7 @@ async function runUninstallCommand(opts = {}) {
|
|
|
29082
30422
|
}
|
|
29083
30423
|
if (opts.purge && found.homeExists) {
|
|
29084
30424
|
try {
|
|
29085
|
-
|
|
30425
|
+
rmSync16(failproofaiHome(), { recursive: true, force: true });
|
|
29086
30426
|
purged = true;
|
|
29087
30427
|
lines.push(`✓ deleted ${failproofaiHome()}`);
|
|
29088
30428
|
} catch (err) {
|
|
@@ -29093,7 +30433,7 @@ async function runUninstallCommand(opts = {}) {
|
|
|
29093
30433
|
lines.push(``, `Not everything could be removed:`);
|
|
29094
30434
|
for (const f of failures)
|
|
29095
30435
|
lines.push(` ✗ ${f}`);
|
|
29096
|
-
if (found.servicePath &&
|
|
30436
|
+
if (found.servicePath && existsSync42(found.servicePath)) {
|
|
29097
30437
|
lines.push(``, `To remove the service by hand:`, ...manualServiceRemoval(found.servicePath), ``, `(\`${daemonStatusCommand()}\` shows its current state.)`);
|
|
29098
30438
|
}
|
|
29099
30439
|
return { exitCode: 1, lines, planLines, purged };
|
|
@@ -29136,6 +30476,8 @@ var exports_tui = {};
|
|
|
29136
30476
|
__export(exports_tui, {
|
|
29137
30477
|
truncate: () => truncate2,
|
|
29138
30478
|
summarize: () => summarize2,
|
|
30479
|
+
stepOpen: () => stepOpen2,
|
|
30480
|
+
step: () => step2,
|
|
29139
30481
|
selectOne: () => selectOne2,
|
|
29140
30482
|
renderLaunchBanner: () => renderLaunchBanner2,
|
|
29141
30483
|
renderBrandLogo: () => renderBrandLogo2,
|
|
@@ -29172,11 +30514,11 @@ function truecolorEnabled2() {
|
|
|
29172
30514
|
}
|
|
29173
30515
|
function paint2(on) {
|
|
29174
30516
|
const tc = on && truecolorEnabled2();
|
|
29175
|
-
const mk = (h,
|
|
30517
|
+
const mk = (h, bold2 = false) => (s) => {
|
|
29176
30518
|
if (!on)
|
|
29177
30519
|
return s;
|
|
29178
30520
|
const code = tc ? `38;2;${h.rgb[0]};${h.rgb[1]};${h.rgb[2]}` : h.basic;
|
|
29179
|
-
return `${ESC2}[${
|
|
30521
|
+
return `${ESC2}[${bold2 ? "1;" : ""}${code}m${s}${ESC2}[0m`;
|
|
29180
30522
|
};
|
|
29181
30523
|
return {
|
|
29182
30524
|
bold: (s) => on ? `${ESC2}[1m${s}${ESC2}[0m` : s,
|
|
@@ -29282,19 +30624,40 @@ function intro2(message, stdout = process.stdout) {
|
|
|
29282
30624
|
}
|
|
29283
30625
|
function renderLaunchBanner2(version3, stdout = process.stdout) {
|
|
29284
30626
|
const c2 = paint2(colorsEnabled2(stdout));
|
|
29285
|
-
const
|
|
30627
|
+
const row2 = (label, value) => ` ${c2.guide(label.padEnd(9))}${value}`;
|
|
29286
30628
|
const header = stdout.isTTY ? renderBrandLogo2(stdout) : [" failproof ai", ` ${TAGLINE2}`];
|
|
29287
30629
|
return [
|
|
29288
30630
|
"",
|
|
29289
30631
|
...header,
|
|
29290
30632
|
"",
|
|
29291
|
-
|
|
29292
|
-
|
|
29293
|
-
|
|
29294
|
-
|
|
30633
|
+
row2("version", c2.pink(version3)),
|
|
30634
|
+
row2("star", c2.dim("https://github.com/failproofai/failproofai")),
|
|
30635
|
+
row2("docs", c2.dim("https://docs.befailproof.ai/introduction")),
|
|
30636
|
+
row2("discord", c2.dim("https://discord.befailproof.ai/")),
|
|
29295
30637
|
""
|
|
29296
30638
|
];
|
|
29297
30639
|
}
|
|
30640
|
+
function step2(message, summary, stdout = process.stdout) {
|
|
30641
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
30642
|
+
const rows = summary === undefined ? [] : Array.isArray(summary) ? summary : [summary];
|
|
30643
|
+
if (!stdout.isTTY) {
|
|
30644
|
+
if (rows.length)
|
|
30645
|
+
stdout.write(`${message}: ${rows.join(" ")}
|
|
30646
|
+
`);
|
|
30647
|
+
return;
|
|
30648
|
+
}
|
|
30649
|
+
const cols = stdout.columns || 80;
|
|
30650
|
+
const lines = [c2.dim(BAR2), truncate2(`${c2.dim(STEP_DONE2)} ${message}`, cols - 1)];
|
|
30651
|
+
for (const r of rows)
|
|
30652
|
+
lines.push(truncate2(`${c2.dim(BAR2)} ${c2.dim(r)}`, cols - 1));
|
|
30653
|
+
writeLines2(stdout, lines);
|
|
30654
|
+
}
|
|
30655
|
+
function stepOpen2(message, stdout = process.stdout) {
|
|
30656
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
30657
|
+
if (!stdout.isTTY)
|
|
30658
|
+
return;
|
|
30659
|
+
writeLines2(stdout, [c2.dim(BAR2), `${c2.guide(STEP_ACTIVE2)} ${c2.bold(message)}`]);
|
|
30660
|
+
}
|
|
29298
30661
|
function outro2(message, opts = {}, stdout = process.stdout) {
|
|
29299
30662
|
const c2 = paint2(colorsEnabled2(stdout));
|
|
29300
30663
|
const ok2 = opts.ok !== false;
|
|
@@ -29364,11 +30727,11 @@ function runPrompt2(p) {
|
|
|
29364
30727
|
lines.push(`${c2.dim(BAR2)} ${c2.dim(`↑ ${above} more`)}`);
|
|
29365
30728
|
const budget = Math.max(6, cols - nameCol - 10);
|
|
29366
30729
|
for (let ri = start;ri < end; ri++) {
|
|
29367
|
-
const
|
|
29368
|
-
if (
|
|
29369
|
-
lines.push(`${c2.dim(BAR2)} ${c2.dim(
|
|
30730
|
+
const row2 = rows[ri];
|
|
30731
|
+
if (row2.kind === "header") {
|
|
30732
|
+
lines.push(`${c2.dim(BAR2)} ${c2.dim(row2.text)}`);
|
|
29370
30733
|
} else {
|
|
29371
|
-
lines.push(`${c2.dim(BAR2)} ${p.renderRow(
|
|
30734
|
+
lines.push(`${c2.dim(BAR2)} ${p.renderRow(row2.index, row2.index === cursor2, budget)}`);
|
|
29372
30735
|
}
|
|
29373
30736
|
}
|
|
29374
30737
|
if (below > 0)
|
|
@@ -29387,7 +30750,7 @@ function runPrompt2(p) {
|
|
|
29387
30750
|
`${c2.dim(BAR2)} ${c2.dim(summary)}`
|
|
29388
30751
|
]);
|
|
29389
30752
|
};
|
|
29390
|
-
return new Promise((
|
|
30753
|
+
return new Promise((resolve31) => {
|
|
29391
30754
|
hideCursor2(stdout);
|
|
29392
30755
|
repaint2(stdout, region, build());
|
|
29393
30756
|
readline3.emitKeypressEvents(stdin);
|
|
@@ -29403,7 +30766,7 @@ function runPrompt2(p) {
|
|
|
29403
30766
|
const finish = (result) => {
|
|
29404
30767
|
cleanup();
|
|
29405
30768
|
collapse(result);
|
|
29406
|
-
|
|
30769
|
+
resolve31(result);
|
|
29407
30770
|
};
|
|
29408
30771
|
function onKey(_s, key) {
|
|
29409
30772
|
if (!key)
|
|
@@ -29533,7 +30896,7 @@ function promptText2(opts) {
|
|
|
29533
30896
|
return error ? { ok: false, error } : { ok: true, value };
|
|
29534
30897
|
};
|
|
29535
30898
|
if (!stdin.isTTY) {
|
|
29536
|
-
return new Promise((
|
|
30899
|
+
return new Promise((resolve31) => {
|
|
29537
30900
|
let buf = "";
|
|
29538
30901
|
const onData = (chunk) => {
|
|
29539
30902
|
buf += String(chunk);
|
|
@@ -29546,21 +30909,21 @@ function promptText2(opts) {
|
|
|
29546
30909
|
stdin.removeListener("end", done);
|
|
29547
30910
|
const r = accept(buf.split(`
|
|
29548
30911
|
`)[0] ?? "");
|
|
29549
|
-
|
|
30912
|
+
resolve31(r.ok ? r.value : null);
|
|
29550
30913
|
};
|
|
29551
30914
|
stdin.on("data", onData);
|
|
29552
30915
|
stdin.on("end", done);
|
|
29553
30916
|
});
|
|
29554
30917
|
}
|
|
29555
|
-
return new Promise((
|
|
30918
|
+
return new Promise((resolve31) => {
|
|
29556
30919
|
let value = "";
|
|
29557
30920
|
const draw = (error) => {
|
|
29558
30921
|
const cols = stdout.columns || 80;
|
|
29559
30922
|
const shown = opts.mask ? "•".repeat(value.length) : value;
|
|
29560
|
-
const hint = opts.hint ? ` ${c2.dim(opts.hint)}` : "";
|
|
29561
|
-
const line = truncate2(`${c2.bold(opts.message)} ${shown}${hint}`, cols - 1);
|
|
30923
|
+
const hint = opts.hint && value.length === 0 ? ` ${c2.dim(opts.hint)}` : "";
|
|
30924
|
+
const line = truncate2(`${opts.prefix ?? ""}${c2.bold(opts.message)} ${shown}${hint}`, cols - 1);
|
|
29562
30925
|
const err = error ? `
|
|
29563
|
-
${truncate2(c2.warn(error), cols - 3)}` : "";
|
|
30926
|
+
${opts.prefix ?? " "}${truncate2(c2.warn(error), cols - 3)}` : "";
|
|
29564
30927
|
stdout.write(`\r\x1B[2K${line}${err}`);
|
|
29565
30928
|
if (err)
|
|
29566
30929
|
stdout.write("\x1B[1A");
|
|
@@ -29582,14 +30945,14 @@ function promptText2(opts) {
|
|
|
29582
30945
|
return;
|
|
29583
30946
|
if (key.ctrl && (key.name === "c" || key.name === "d") || key.name === "escape") {
|
|
29584
30947
|
cleanup();
|
|
29585
|
-
|
|
30948
|
+
resolve31(null);
|
|
29586
30949
|
return;
|
|
29587
30950
|
}
|
|
29588
30951
|
if (key.name === "return" || key.name === "enter") {
|
|
29589
30952
|
const r = accept(value);
|
|
29590
30953
|
if (r.ok) {
|
|
29591
30954
|
cleanup();
|
|
29592
|
-
|
|
30955
|
+
resolve31(r.value);
|
|
29593
30956
|
} else {
|
|
29594
30957
|
draw(r.error);
|
|
29595
30958
|
}
|
|
@@ -29657,9 +31020,9 @@ __export(exports_manager, {
|
|
|
29657
31020
|
getSettingsPath: () => getSettingsPath2
|
|
29658
31021
|
});
|
|
29659
31022
|
import { execSync as execSync7 } from "node:child_process";
|
|
29660
|
-
import { existsSync as
|
|
29661
|
-
import { resolve as
|
|
29662
|
-
import { platform as platform5, arch as arch4, release as release3, hostname as
|
|
31023
|
+
import { existsSync as existsSync43 } from "node:fs";
|
|
31024
|
+
import { resolve as resolve31, basename as basename8 } from "node:path";
|
|
31025
|
+
import { platform as platform5, arch as arch4, release as release3, hostname as hostname6 } from "node:os";
|
|
29663
31026
|
function getSettingsPath2(scope, cwd) {
|
|
29664
31027
|
return claudeCode.getSettingsPath(scope, cwd);
|
|
29665
31028
|
}
|
|
@@ -29766,9 +31129,9 @@ async function installHooksImpl2(policyNames, scope = "user", cwd, includeBeta =
|
|
|
29766
31129
|
delete configToWrite.customPoliciesPath;
|
|
29767
31130
|
delete configToWrite.customPoliciesPaths;
|
|
29768
31131
|
} else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
|
|
29769
|
-
const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) =>
|
|
29770
|
-
const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) =>
|
|
29771
|
-
if (
|
|
31132
|
+
const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve31(path3));
|
|
31133
|
+
const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve31(path3)).filter((path3) => {
|
|
31134
|
+
if (existsSync43(path3))
|
|
29772
31135
|
return true;
|
|
29773
31136
|
console.log(`Dropping custom policies path (file no longer exists): ${path3}`);
|
|
29774
31137
|
return false;
|
|
@@ -29854,7 +31217,7 @@ Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}
|
|
|
29854
31217
|
platform: platform5(),
|
|
29855
31218
|
arch: arch4(),
|
|
29856
31219
|
os_release: release3(),
|
|
29857
|
-
hostname_hash: hashToId2(
|
|
31220
|
+
hostname_hash: hashToId2(hostname6()),
|
|
29858
31221
|
has_custom_hooks_path: !!(configToWrite.customPoliciesPaths?.length || configToWrite.customPoliciesPath),
|
|
29859
31222
|
has_policy_params: !!(configToWrite.policyParams && Object.keys(configToWrite.policyParams).length > 0),
|
|
29860
31223
|
param_policy_names: configToWrite.policyParams ? Object.keys(configToWrite.policyParams) : [],
|
|
@@ -29945,7 +31308,7 @@ async function removeHooks2(policyNames, scope = "user", cwd, opts) {
|
|
|
29945
31308
|
platform: platform5(),
|
|
29946
31309
|
arch: arch4(),
|
|
29947
31310
|
os_release: release3(),
|
|
29948
|
-
hostname_hash: hashToId2(
|
|
31311
|
+
hostname_hash: hashToId2(hostname6())
|
|
29949
31312
|
});
|
|
29950
31313
|
} catch {}
|
|
29951
31314
|
console.log(`Disabled ${policyNames.length - notEnabled.length} policy(ies).`);
|
|
@@ -29960,7 +31323,7 @@ async function removeHooks2(policyNames, scope = "user", cwd, opts) {
|
|
|
29960
31323
|
const scopesToRemove = scope === "all" ? [...integration.scopes] : integration.scopes.includes(scope) ? [scope] : [];
|
|
29961
31324
|
for (const s of scopesToRemove) {
|
|
29962
31325
|
const settingsPaths = settingsPathsFor(integration, s, cwd);
|
|
29963
|
-
const existing = settingsPaths.filter((p) =>
|
|
31326
|
+
const existing = settingsPaths.filter((p) => existsSync43(p));
|
|
29964
31327
|
if (existing.length === 0) {
|
|
29965
31328
|
if (scope !== "all" && selectedClis.length === 1) {
|
|
29966
31329
|
console.log("No settings file found. Nothing to remove.");
|
|
@@ -30010,7 +31373,7 @@ async function removeHooks2(policyNames, scope = "user", cwd, opts) {
|
|
|
30010
31373
|
platform: platform5(),
|
|
30011
31374
|
arch: arch4(),
|
|
30012
31375
|
os_release: release3(),
|
|
30013
|
-
hostname_hash: hashToId2(
|
|
31376
|
+
hostname_hash: hashToId2(hostname6())
|
|
30014
31377
|
});
|
|
30015
31378
|
} catch {}
|
|
30016
31379
|
if (scope === "all") {
|
|
@@ -30109,16 +31472,16 @@ Failproof AI Hook Policies
|
|
|
30109
31472
|
console.log(` ${"─".repeat(scopeHeaderWidth)}${"─".repeat(nameColWidth)}${"─".repeat(38)}`);
|
|
30110
31473
|
const printMultiScopeRow = (policy) => {
|
|
30111
31474
|
const enabled = enabledSet.has(policy.name);
|
|
30112
|
-
let
|
|
31475
|
+
let row2 = " ";
|
|
30113
31476
|
for (const _scope of installedScopes) {
|
|
30114
31477
|
if (enabled) {
|
|
30115
|
-
|
|
31478
|
+
row2 += `\x1B[32m✓ ON\x1B[0m` + " ".repeat(COL - 4);
|
|
30116
31479
|
} else {
|
|
30117
|
-
|
|
31480
|
+
row2 += " OFF" + " ".repeat(COL - 5);
|
|
30118
31481
|
}
|
|
30119
31482
|
}
|
|
30120
|
-
|
|
30121
|
-
console.log(
|
|
31483
|
+
row2 += policy.name.padEnd(nameColWidth) + policy.description;
|
|
31484
|
+
console.log(row2);
|
|
30122
31485
|
printParamsSummary(policy.name, ` ${" ".repeat(scopeHeaderWidth)}`);
|
|
30123
31486
|
};
|
|
30124
31487
|
for (const policy of regularPolicies)
|
|
@@ -30159,9 +31522,9 @@ Failproof AI Hook Policies
|
|
|
30159
31522
|
console.log(`
|
|
30160
31523
|
── Custom Policies ───────────────────────`);
|
|
30161
31524
|
for (const path3 of explicitPaths) {
|
|
30162
|
-
const absPath =
|
|
31525
|
+
const absPath = resolve31(findProjectConfigDir(cwd ?? process.cwd()), path3);
|
|
30163
31526
|
console.log(` ${absPath}`);
|
|
30164
|
-
if (!
|
|
31527
|
+
if (!existsSync43(absPath)) {
|
|
30165
31528
|
console.log(` \x1B[31m✗ File not found: ${absPath}\x1B[0m`);
|
|
30166
31529
|
continue;
|
|
30167
31530
|
}
|
|
@@ -30180,7 +31543,7 @@ Failproof AI Hook Policies
|
|
|
30180
31543
|
console.log();
|
|
30181
31544
|
}
|
|
30182
31545
|
const base = findProjectConfigDir(cwd ?? process.cwd());
|
|
30183
|
-
const projectDir =
|
|
31546
|
+
const projectDir = resolve31(base, ".failproofai", "policies");
|
|
30184
31547
|
const userDir = customPoliciesDir();
|
|
30185
31548
|
const sameDir = userDir === projectDir;
|
|
30186
31549
|
const conventionDirs = [
|
|
@@ -30393,32 +31756,32 @@ async function promptCliTargetSelection(detected, action = "install") {
|
|
|
30393
31756
|
cursorHidden = false;
|
|
30394
31757
|
}
|
|
30395
31758
|
}
|
|
30396
|
-
const { dim, bold, guide: teal, pink, pinkBold } = paint(!process.env.NO_COLOR);
|
|
31759
|
+
const { dim: dim2, bold: bold2, guide: teal, pink: pink2, pinkBold } = paint(!process.env.NO_COLOR);
|
|
30397
31760
|
const heading = action === "uninstall" ? "Remove failproofai hooks" : "Install failproofai hooks";
|
|
30398
31761
|
function render() {
|
|
30399
31762
|
const cols = process.stdout.columns || 100;
|
|
30400
31763
|
hideCursor3();
|
|
30401
|
-
const g =
|
|
30402
|
-
const lines = [g, `${teal(STEP_ACTIVE)} ${
|
|
30403
|
-
for (const
|
|
30404
|
-
if (
|
|
31764
|
+
const g = dim2(BAR);
|
|
31765
|
+
const lines = [g, `${teal(STEP_ACTIVE)} ${bold2(heading)}`];
|
|
31766
|
+
for (const row2 of buildDisplayRows()) {
|
|
31767
|
+
if (row2.kind === "blank") {
|
|
30405
31768
|
lines.push(g);
|
|
30406
31769
|
continue;
|
|
30407
31770
|
}
|
|
30408
|
-
if (
|
|
30409
|
-
const hint =
|
|
30410
|
-
lines.push(`${g} ${
|
|
31771
|
+
if (row2.kind === "header") {
|
|
31772
|
+
const hint = row2.hint ? ` ${dim2("· " + row2.hint)}` : "";
|
|
31773
|
+
lines.push(`${g} ${dim2(row2.title)}${hint}`);
|
|
30411
31774
|
continue;
|
|
30412
31775
|
}
|
|
30413
|
-
const opt =
|
|
30414
|
-
const isActive =
|
|
30415
|
-
const caret = isActive ?
|
|
30416
|
-
const marker = opt.isAll ? teal("★") : opt.detected ?
|
|
30417
|
-
const label = isActive ? pinkBold(opt.label) : opt.detected ? opt.label :
|
|
31776
|
+
const opt = row2.option;
|
|
31777
|
+
const isActive = row2.itemIndex === cursor2;
|
|
31778
|
+
const caret = isActive ? pink2("❯") : " ";
|
|
31779
|
+
const marker = opt.isAll ? teal("★") : opt.detected ? pink2("●") : dim2("○");
|
|
31780
|
+
const label = isActive ? pinkBold(opt.label) : opt.detected ? opt.label : dim2(opt.label);
|
|
30418
31781
|
lines.push(`${g} ${caret} ${marker} ${label}`);
|
|
30419
31782
|
}
|
|
30420
31783
|
lines.push(g);
|
|
30421
|
-
lines.push(`${g} ${
|
|
31784
|
+
lines.push(`${g} ${dim2("↑/↓ move · ↵ select · esc cancel")}`);
|
|
30422
31785
|
if (lastLineCount > 0) {
|
|
30423
31786
|
process.stdout.write(`\x1B[${lastLineCount}A\x1B[J`);
|
|
30424
31787
|
}
|
|
@@ -30428,7 +31791,7 @@ async function promptCliTargetSelection(detected, action = "install") {
|
|
|
30428
31791
|
lastLineCount = lines.length;
|
|
30429
31792
|
}
|
|
30430
31793
|
const itemCount = options.length;
|
|
30431
|
-
return new Promise((
|
|
31794
|
+
return new Promise((resolve32) => {
|
|
30432
31795
|
render();
|
|
30433
31796
|
readline4.emitKeypressEvents(process.stdin);
|
|
30434
31797
|
const wasRaw = process.stdin.isRaw;
|
|
@@ -30461,7 +31824,7 @@ async function promptCliTargetSelection(detected, action = "install") {
|
|
|
30461
31824
|
cleanup();
|
|
30462
31825
|
process.stdout.write(`
|
|
30463
31826
|
`);
|
|
30464
|
-
|
|
31827
|
+
resolve32(options[cursor2].value);
|
|
30465
31828
|
}
|
|
30466
31829
|
}
|
|
30467
31830
|
process.stdin.on("keypress", onKey);
|
|
@@ -30487,7 +31850,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
30487
31850
|
const WINDOW_SIZE = 10;
|
|
30488
31851
|
const useColor = !process.env.NO_COLOR;
|
|
30489
31852
|
const c2 = paint(useColor);
|
|
30490
|
-
const { dim, bold } = c2;
|
|
31853
|
+
const { dim: dim2, bold: bold2 } = c2;
|
|
30491
31854
|
const stepMark = c2.guide;
|
|
30492
31855
|
const activeName = c2.pinkBold;
|
|
30493
31856
|
const selectedMark = c2.pink;
|
|
@@ -30556,24 +31919,24 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
30556
31919
|
if (shown > 0 && cursor2 >= shown)
|
|
30557
31920
|
cursor2 = shown - 1;
|
|
30558
31921
|
const selectedCount = items.filter((i) => i.selected).length;
|
|
30559
|
-
const g =
|
|
31922
|
+
const g = dim2(BAR);
|
|
30560
31923
|
const lines = [];
|
|
30561
31924
|
lines.push(g);
|
|
30562
|
-
lines.push(`${stepMark(STEP_ACTIVE)} ${
|
|
31925
|
+
lines.push(`${stepMark(STEP_ACTIVE)} ${bold2("Choose the policies to enable")} ${dim2("·")} ` + `${stepMark(String(selectedCount))} ${dim2("selected")}`);
|
|
30563
31926
|
const blockCursor = useColor ? "\x1B[7m \x1B[0m" : "_";
|
|
30564
|
-
const countLabel = search ?
|
|
30565
|
-
lines.push(`${g} ${
|
|
31927
|
+
const countLabel = search ? dim2(`${shown}/${total} shown`) : dim2(`${total} policies`);
|
|
31928
|
+
lines.push(`${g} ${dim2("filter ›")} ${search}${blockCursor} ${countLabel}`);
|
|
30566
31929
|
lines.push(g);
|
|
30567
31930
|
if (shown === 0) {
|
|
30568
|
-
lines.push(`${g} ${
|
|
31931
|
+
lines.push(`${g} ${dim2(`no policies match “${search}”`)}`);
|
|
30569
31932
|
for (let i = 0;i < WINDOW_SIZE + 1; i++)
|
|
30570
31933
|
lines.push(g);
|
|
30571
31934
|
} else {
|
|
30572
31935
|
const displayRows3 = buildDisplayRows(filtered);
|
|
30573
31936
|
let cursorDisplayRow = 0;
|
|
30574
31937
|
for (let i = 0;i < displayRows3.length; i++) {
|
|
30575
|
-
const
|
|
30576
|
-
if (
|
|
31938
|
+
const row2 = displayRows3[i];
|
|
31939
|
+
if (row2.kind === "item" && row2.filteredIndex === cursor2) {
|
|
30577
31940
|
cursorDisplayRow = i;
|
|
30578
31941
|
break;
|
|
30579
31942
|
}
|
|
@@ -30583,30 +31946,30 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
30583
31946
|
windowStart = Math.min(windowStart, Math.max(0, displayRows3.length - WINDOW_SIZE));
|
|
30584
31947
|
const windowEnd = Math.min(displayRows3.length, windowStart + WINDOW_SIZE);
|
|
30585
31948
|
const aboveItems = displayRows3.slice(0, windowStart).filter((r) => r.kind === "item").length;
|
|
30586
|
-
lines.push(aboveItems > 0 ? `${g} ${
|
|
31949
|
+
lines.push(aboveItems > 0 ? `${g} ${dim2(`↑ ${aboveItems} more`)}` : g);
|
|
30587
31950
|
for (let i = windowStart;i < windowEnd; i++) {
|
|
30588
|
-
const
|
|
30589
|
-
if (
|
|
30590
|
-
lines.push(`${g} ${
|
|
31951
|
+
const row2 = displayRows3[i];
|
|
31952
|
+
if (row2.kind === "header") {
|
|
31953
|
+
lines.push(`${g} ${dim2(row2.category.toUpperCase())} ${dim2(`${row2.enabledCount}/${row2.totalCount}`)}`);
|
|
30591
31954
|
} else {
|
|
30592
|
-
const item =
|
|
30593
|
-
const active =
|
|
30594
|
-
const box = item.selected ? selectedMark(CHECK_ON) :
|
|
31955
|
+
const item = row2.item;
|
|
31956
|
+
const active = row2.filteredIndex === cursor2;
|
|
31957
|
+
const box = item.selected ? selectedMark(CHECK_ON) : dim2(CHECK_OFF);
|
|
30595
31958
|
const rawName = item.name.padEnd(NAME_COL);
|
|
30596
|
-
const name = active ? activeName(rawName) : item.selected ? rawName :
|
|
31959
|
+
const name = active ? activeName(rawName) : item.selected ? rawName : dim2(rawName);
|
|
30597
31960
|
const beta = item.beta ? ` ${betaTag("beta")}` : "";
|
|
30598
31961
|
const descWidth = Math.max(8, cols - NAME_COL - 8 - (item.beta ? 5 : 0));
|
|
30599
|
-
const desc =
|
|
31962
|
+
const desc = dim2(truncate(item.description, descWidth));
|
|
30600
31963
|
lines.push(`${g} ${box} ${name}${beta} ${desc}`);
|
|
30601
31964
|
}
|
|
30602
31965
|
}
|
|
30603
31966
|
for (let i = windowEnd - windowStart;i < WINDOW_SIZE; i++)
|
|
30604
31967
|
lines.push(g);
|
|
30605
31968
|
const belowItems = displayRows3.slice(windowEnd).filter((r) => r.kind === "item").length;
|
|
30606
|
-
lines.push(belowItems > 0 ? `${g} ${
|
|
31969
|
+
lines.push(belowItems > 0 ? `${g} ${dim2(`↓ ${belowItems} more`)}` : g);
|
|
30607
31970
|
}
|
|
30608
31971
|
lines.push(g);
|
|
30609
|
-
lines.push(`${g} ${
|
|
31972
|
+
lines.push(`${g} ${dim2("↑/↓ move · space select · ctrl+a all · ↵ save · type to filter · esc clear")}`);
|
|
30610
31973
|
if (lastLineCount > 0)
|
|
30611
31974
|
process.stdout.write(`\x1B[${lastLineCount}A\x1B[J`);
|
|
30612
31975
|
process.stdout.write(lines.map((l) => truncate(l, cols)).join(`
|
|
@@ -30614,7 +31977,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
30614
31977
|
`);
|
|
30615
31978
|
lastLineCount = lines.length;
|
|
30616
31979
|
}
|
|
30617
|
-
return new Promise((
|
|
31980
|
+
return new Promise((resolve32) => {
|
|
30618
31981
|
render();
|
|
30619
31982
|
process.stdin.setRawMode(true);
|
|
30620
31983
|
process.stdin.resume();
|
|
@@ -30647,11 +32010,11 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
30647
32010
|
const selected = items.filter((i) => i.selected).map((i) => i.name);
|
|
30648
32011
|
if (lastLineCount > 0)
|
|
30649
32012
|
process.stdout.write(`\x1B[${lastLineCount}A\x1B[J`);
|
|
30650
|
-
process.stdout.write(`${
|
|
30651
|
-
${
|
|
30652
|
-
${
|
|
32013
|
+
process.stdout.write(`${dim2(BAR)}
|
|
32014
|
+
${dim2(STEP_DONE)} Policies
|
|
32015
|
+
${dim2(BAR)} ${dim2(`${selected.length} selected`)}
|
|
30653
32016
|
`);
|
|
30654
|
-
|
|
32017
|
+
resolve32(selected);
|
|
30655
32018
|
} else if (key.name === "escape") {
|
|
30656
32019
|
search = "";
|
|
30657
32020
|
cursor2 = 0;
|
|
@@ -30696,10 +32059,43 @@ __export(exports_cli2, {
|
|
|
30696
32059
|
runScheduledAudit: () => runScheduledAudit2,
|
|
30697
32060
|
runPostSetupAudit: () => runPostSetupAudit2,
|
|
30698
32061
|
runAuditCli: () => runAuditCli2,
|
|
32062
|
+
helpText: () => helpText2,
|
|
30699
32063
|
buildSummary: () => buildSummary2,
|
|
30700
32064
|
EXIT_AUDIT_ALREADY_RUNNING: () => EXIT_AUDIT_ALREADY_RUNNING2,
|
|
30701
32065
|
AUDIT_STAGES: () => AUDIT_STAGES2
|
|
30702
32066
|
});
|
|
32067
|
+
function helpText2() {
|
|
32068
|
+
const row2 = (command, lines) => {
|
|
32069
|
+
const pad = " ".repeat(Math.max(1, HELP_DESC_COL2 - 2 - command.length));
|
|
32070
|
+
return lines.map((line, i) => i === 0 ? ` ${c2(CYAN2, command)}${pad}${line}` : `${" ".repeat(HELP_DESC_COL2)}${line}`).join(`
|
|
32071
|
+
`);
|
|
32072
|
+
};
|
|
32073
|
+
return [
|
|
32074
|
+
`${c2(BOLD2, "failproofai audit")} — review your agent CLIs for risky and wasteful patterns.`,
|
|
32075
|
+
c2(DIM2, "Everything runs on this machine; only a scheduled digest ever leaves it."),
|
|
32076
|
+
"",
|
|
32077
|
+
c2(BOLD2, "USAGE"),
|
|
32078
|
+
row2("failproofai audit", [
|
|
32079
|
+
"Scan your session history, then open",
|
|
32080
|
+
`http://localhost:${DASHBOARD_PORT2}/audit`
|
|
32081
|
+
]),
|
|
32082
|
+
row2("failproofai audit --schedule [days]", [
|
|
32083
|
+
"Scan on a timer and email the findings.",
|
|
32084
|
+
"Default 7 days, range 1-90.",
|
|
32085
|
+
"Signs you in the first time; add",
|
|
32086
|
+
"--email <address> to skip a prompt."
|
|
32087
|
+
]),
|
|
32088
|
+
row2("failproofai audit --no-schedule", ["Stop the timer. Leaves you signed in."]),
|
|
32089
|
+
row2("failproofai audit --status", [
|
|
32090
|
+
"Whether scheduling is on, where reports",
|
|
32091
|
+
"go, the daemon's state, and when the",
|
|
32092
|
+
"next scan is due."
|
|
32093
|
+
]),
|
|
32094
|
+
row2("failproofai audit -h, --help", ["Show this help."]),
|
|
32095
|
+
""
|
|
32096
|
+
].join(`
|
|
32097
|
+
`);
|
|
32098
|
+
}
|
|
30703
32099
|
function colorOn2() {
|
|
30704
32100
|
if (process.env.NO_COLOR && process.env.NO_COLOR !== "")
|
|
30705
32101
|
return false;
|
|
@@ -30874,6 +32270,13 @@ async function runScheduledAudit2() {
|
|
|
30874
32270
|
}
|
|
30875
32271
|
process.stdout.write(`failproofai: audit complete — ${num2(result.eventsScanned)} tool calls across ` + `${num2(result.transcripts.scanned)} sessions, ${num2(result.totals.hits)} hits
|
|
30876
32272
|
`);
|
|
32273
|
+
const outcome = await reportHarm(result);
|
|
32274
|
+
const line = describeOutcome2(outcome);
|
|
32275
|
+
if (line) {
|
|
32276
|
+
const stream = outcome.kind === "sent" ? process.stdout : process.stderr;
|
|
32277
|
+
stream.write(`${line}
|
|
32278
|
+
`);
|
|
32279
|
+
}
|
|
30877
32280
|
return 0;
|
|
30878
32281
|
} finally {
|
|
30879
32282
|
attempt.lock.release();
|
|
@@ -30929,7 +32332,7 @@ async function runPostSetupAudit2() {
|
|
|
30929
32332
|
}
|
|
30930
32333
|
async function runAuditCli2(args) {
|
|
30931
32334
|
if (args.includes("--help") || args.includes("-h")) {
|
|
30932
|
-
process.stdout.write(
|
|
32335
|
+
process.stdout.write(helpText2());
|
|
30933
32336
|
process.exit(0);
|
|
30934
32337
|
}
|
|
30935
32338
|
if (args.includes("--scheduled")) {
|
|
@@ -30938,15 +32341,71 @@ async function runAuditCli2(args) {
|
|
|
30938
32341
|
die2(`\`audit --scheduled\` takes no other arguments (got: ${extra}).`);
|
|
30939
32342
|
process.exit(await runScheduledAudit2());
|
|
30940
32343
|
}
|
|
32344
|
+
if (args.includes("--status")) {
|
|
32345
|
+
const extra = args.find((a) => a !== "--status");
|
|
32346
|
+
if (extra)
|
|
32347
|
+
die2(`\`audit --status\` takes no other arguments (got: ${extra}).`);
|
|
32348
|
+
const { runScheduleStatus: runScheduleStatus2 } = await Promise.resolve().then(() => (init_schedule_cli(), exports_schedule_cli));
|
|
32349
|
+
runScheduleStatus2();
|
|
32350
|
+
process.exit(0);
|
|
32351
|
+
}
|
|
32352
|
+
if (args.includes("--no-schedule")) {
|
|
32353
|
+
const extra = args.find((a) => a !== "--no-schedule");
|
|
32354
|
+
if (extra)
|
|
32355
|
+
die2(`\`audit --no-schedule\` takes no other arguments (got: ${extra}).`);
|
|
32356
|
+
const { runScheduleOff: runScheduleOff2 } = await Promise.resolve().then(() => (init_schedule_cli(), exports_schedule_cli));
|
|
32357
|
+
runScheduleOff2();
|
|
32358
|
+
process.exit(0);
|
|
32359
|
+
}
|
|
32360
|
+
const scheduleAt = args.indexOf("--schedule");
|
|
32361
|
+
if (scheduleAt !== -1) {
|
|
32362
|
+
let days;
|
|
32363
|
+
let email;
|
|
32364
|
+
for (let i = 0;i < args.length; i += 1) {
|
|
32365
|
+
const a = args[i];
|
|
32366
|
+
if (a === "--schedule") {
|
|
32367
|
+
const next = args[i + 1];
|
|
32368
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
32369
|
+
days = next;
|
|
32370
|
+
i += 1;
|
|
32371
|
+
}
|
|
32372
|
+
continue;
|
|
32373
|
+
}
|
|
32374
|
+
if (a === "--email" || a.startsWith("--email=")) {
|
|
32375
|
+
if (a.startsWith("--email=")) {
|
|
32376
|
+
email = a.slice("--email=".length);
|
|
32377
|
+
} else {
|
|
32378
|
+
email = args[i + 1];
|
|
32379
|
+
i += 1;
|
|
32380
|
+
}
|
|
32381
|
+
if (email === undefined || email.length === 0 || email.startsWith("-")) {
|
|
32382
|
+
die2("`--email` needs an address, e.g. `--email you@yourdomain.com`.");
|
|
32383
|
+
}
|
|
32384
|
+
continue;
|
|
32385
|
+
}
|
|
32386
|
+
die2(`\`audit --schedule\` does not take ${a}.`);
|
|
32387
|
+
}
|
|
32388
|
+
const { runScheduleOn: runScheduleOn2, ScheduleCliError: ScheduleCliError2 } = await Promise.resolve().then(() => (init_schedule_cli(), exports_schedule_cli));
|
|
32389
|
+
const { LoginError: LoginError2 } = await Promise.resolve().then(() => (init_cli_login(), exports_cli_login));
|
|
32390
|
+
try {
|
|
32391
|
+
await runScheduleOn2(days, email);
|
|
32392
|
+
} catch (err) {
|
|
32393
|
+
if (err instanceof ScheduleCliError2 || err instanceof LoginError2)
|
|
32394
|
+
die2(err.message);
|
|
32395
|
+
throw err;
|
|
32396
|
+
}
|
|
32397
|
+
process.exit(0);
|
|
32398
|
+
}
|
|
30941
32399
|
const stray = args.find((a) => a !== "--help" && a !== "-h");
|
|
30942
32400
|
if (stray) {
|
|
30943
|
-
die2(`\`audit\`
|
|
30944
|
-
|
|
32401
|
+
die2(`\`audit\` does not take ${stray}.
|
|
32402
|
+
Run \`failproofai audit\` to scan your history and open the dashboard,
|
|
32403
|
+
or \`failproofai audit --help\` for the scheduling commands.`);
|
|
30945
32404
|
}
|
|
30946
32405
|
const attempt = acquireAuditLock("cli");
|
|
30947
32406
|
if (!attempt.ok) {
|
|
30948
32407
|
process.stderr.write(`Error: ${heldByLine2(attempt.heldBy)}.
|
|
30949
|
-
|
|
32408
|
+
Two audits write the same cache files, so this one won't start. Try again shortly.
|
|
30950
32409
|
`);
|
|
30951
32410
|
process.exit(EXIT_AUDIT_ALREADY_RUNNING2);
|
|
30952
32411
|
}
|
|
@@ -30970,7 +32429,7 @@ async function runAuditCli2(args) {
|
|
|
30970
32429
|
if (result.eventsScanned === 0) {
|
|
30971
32430
|
process.stdout.write(`
|
|
30972
32431
|
${c2(DIM2, "no agent sessions found yet.")}
|
|
30973
|
-
|
|
32432
|
+
install hooks with ${c2(CYAN2, "failproofai policies --install")} ${c2(DIM2, "and come back after using your agent.")}
|
|
30974
32433
|
|
|
30975
32434
|
`);
|
|
30976
32435
|
process.exit(0);
|
|
@@ -30985,15 +32444,15 @@ async function runAuditCli2(args) {
|
|
|
30985
32444
|
const url = `http://localhost:${DASHBOARD_PORT2}/audit`;
|
|
30986
32445
|
process.stdout.write(`
|
|
30987
32446
|
${c2(DIM2, "starting the dashboard…")}
|
|
30988
|
-
|
|
30989
|
-
|
|
32447
|
+
${c2(PINK2, "✦")} ${c2(BOLD2, "here's your audit")} ${c2(DIM2, "→")} ${c2(CYAN2, url)}
|
|
32448
|
+
${c2(DIM2, "(opening in your browser — press Ctrl+C to stop the server)")}
|
|
30990
32449
|
|
|
30991
32450
|
`);
|
|
30992
32451
|
openWhenReady(DASHBOARD_PORT2, "/audit");
|
|
30993
32452
|
const { launch: launch2 } = await Promise.resolve().then(() => (init_launch(), exports_launch));
|
|
30994
32453
|
launch2("start");
|
|
30995
32454
|
}
|
|
30996
|
-
var DASHBOARD_PORT2 = 8020, EXIT_AUDIT_ALREADY_RUNNING2 = 75, AUDIT_STAGES2,
|
|
32455
|
+
var DASHBOARD_PORT2 = 8020, EXIT_AUDIT_ALREADY_RUNNING2 = 75, AUDIT_STAGES2, HELP_DESC_COL2 = 40, RESET2, DIM2, BOLD2, PINK2, GREEN2, CYAN2, SPINNER2;
|
|
30997
32456
|
var init_cli2 = __esm(() => {
|
|
30998
32457
|
init_audit();
|
|
30999
32458
|
init_audit_lock();
|
|
@@ -31002,6 +32461,7 @@ var init_cli2 = __esm(() => {
|
|
|
31002
32461
|
init_telemetry_id2();
|
|
31003
32462
|
init_telemetry_sanitize();
|
|
31004
32463
|
init_open_browser();
|
|
32464
|
+
init_report_harm();
|
|
31005
32465
|
init_tui();
|
|
31006
32466
|
AUDIT_STAGES2 = [
|
|
31007
32467
|
{ label: "discovering transcripts", detail: "walking ~/.claude, ~/.codex, ~/.cursor, …" },
|
|
@@ -31009,29 +32469,6 @@ var init_cli2 = __esm(() => {
|
|
|
31009
32469
|
{ label: "running policy checks", detail: "replaying through 30 builtin policies" },
|
|
31010
32470
|
{ label: "aggregating results", detail: "counting hits, ranking by frequency" }
|
|
31011
32471
|
];
|
|
31012
|
-
HELP2 = `
|
|
31013
|
-
failproofai audit — audit your AI agent's behavior, then open the dashboard
|
|
31014
|
-
|
|
31015
|
-
USAGE
|
|
31016
|
-
failproofai audit Scan your agent-CLI session history for risky and
|
|
31017
|
-
wasteful patterns, then open the audit dashboard.
|
|
31018
|
-
failproofai audit --help Show this help.
|
|
31019
|
-
|
|
31020
|
-
failproofai audit --scheduled
|
|
31021
|
-
Headless run: scan, refresh the dashboard's cached
|
|
31022
|
-
result, print one line, exit. No browser, no
|
|
31023
|
-
server. This is what a scheduled audit runs; exit
|
|
31024
|
-
75 means another audit already had the lock.
|
|
31025
|
-
|
|
31026
|
-
WHAT IT DOES
|
|
31027
|
-
1. Scans past sessions from every installed agent CLI (Claude, Codex, Cursor,
|
|
31028
|
-
Copilot, OpenCode, Pi) — entirely on your machine.
|
|
31029
|
-
2. Starts the local dashboard and opens
|
|
31030
|
-
http://localhost:${DASHBOARD_PORT2}/audit with your results.
|
|
31031
|
-
|
|
31032
|
-
Runs fully offline — no account or network required. Press Ctrl+C to stop the
|
|
31033
|
-
dashboard server when you're done.
|
|
31034
|
-
`.trimStart();
|
|
31035
32472
|
RESET2 = ANSI_RESET;
|
|
31036
32473
|
DIM2 = ANSI_DIM;
|
|
31037
32474
|
BOLD2 = ANSI_BOLD;
|
|
@@ -31051,10 +32488,10 @@ __export(exports_cloud_enrollment_cli, {
|
|
|
31051
32488
|
describeMachine: () => describeMachine,
|
|
31052
32489
|
connectionStatusLines: () => connectionStatusLines
|
|
31053
32490
|
});
|
|
31054
|
-
function describeMachine(machineId,
|
|
31055
|
-
if (!
|
|
32491
|
+
function describeMachine(machineId, machineLabel2, full = false) {
|
|
32492
|
+
if (!machineLabel2 || machineLabel2 === machineId)
|
|
31056
32493
|
return machineId;
|
|
31057
|
-
return full ? `${
|
|
32494
|
+
return full ? `${machineLabel2} (${machineId})` : `${machineLabel2} (${machineId.slice(0, 8)})`;
|
|
31058
32495
|
}
|
|
31059
32496
|
function daemonWarning(status) {
|
|
31060
32497
|
if (status === "running")
|
|
@@ -31113,13 +32550,13 @@ async function runConnectCommand(opts) {
|
|
|
31113
32550
|
if (!validated.ok)
|
|
31114
32551
|
return { exitCode: 1, lines: [validated.reason] };
|
|
31115
32552
|
const machineId = resolveMachineId(opts.machineId);
|
|
31116
|
-
const
|
|
31117
|
-
const shownAs = describeMachine(machineId,
|
|
32553
|
+
const machineLabel2 = resolveMachineLabel(opts.machineLabel ?? opts.defaultMachineId);
|
|
32554
|
+
const shownAs = describeMachine(machineId, machineLabel2);
|
|
31118
32555
|
const outcome = await connectToCloud({
|
|
31119
32556
|
url: validated.url,
|
|
31120
32557
|
token: opts.token,
|
|
31121
32558
|
machineId,
|
|
31122
|
-
machineLabel,
|
|
32559
|
+
machineLabel: machineLabel2,
|
|
31123
32560
|
sessions: opts.sessions,
|
|
31124
32561
|
environment: opts.environment,
|
|
31125
32562
|
verifyPolicy: opts.verify,
|
|
@@ -31365,8 +32802,8 @@ __export(exports_launch2, {
|
|
|
31365
32802
|
launch: () => launch2
|
|
31366
32803
|
});
|
|
31367
32804
|
import { spawn as spawn3 } from "child_process";
|
|
31368
|
-
import { realpathSync as realpathSync5, existsSync as
|
|
31369
|
-
import { resolve as
|
|
32805
|
+
import { realpathSync as realpathSync5, existsSync as existsSync44 } from "node:fs";
|
|
32806
|
+
import { resolve as resolve32, dirname as dirname23 } from "node:path";
|
|
31370
32807
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
31371
32808
|
import { createInterface as createInterface2 } from "node:readline";
|
|
31372
32809
|
function launch2(mode) {
|
|
@@ -31390,9 +32827,9 @@ function launch2(mode) {
|
|
|
31390
32827
|
process.env.PORT = port;
|
|
31391
32828
|
process.env.HOSTNAME = bindHost;
|
|
31392
32829
|
cmd = "node";
|
|
31393
|
-
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ??
|
|
31394
|
-
const serverJsPath =
|
|
31395
|
-
if (!
|
|
32830
|
+
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve32(dirname23(realpathSync5(fileURLToPath3(import.meta.url))), "..");
|
|
32831
|
+
const serverJsPath = resolve32(packageRoot, ".next/standalone/server.js");
|
|
32832
|
+
if (!existsSync44(serverJsPath)) {
|
|
31396
32833
|
let shadowMessage = null;
|
|
31397
32834
|
try {
|
|
31398
32835
|
const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
|
|
@@ -31489,17 +32926,17 @@ var init_cli_error2 = __esm(() => {
|
|
|
31489
32926
|
|
|
31490
32927
|
// bin/failproofai.mjs
|
|
31491
32928
|
import { realpathSync as realpathSync6 } from "fs";
|
|
31492
|
-
import { dirname as dirname24, resolve as
|
|
32929
|
+
import { dirname as dirname24, resolve as resolve33 } from "path";
|
|
31493
32930
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
31494
32931
|
// package.json
|
|
31495
|
-
var version = "1.0.
|
|
32932
|
+
var version = "1.0.1-beta.1";
|
|
31496
32933
|
|
|
31497
32934
|
// bin/failproofai.mjs
|
|
31498
32935
|
if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
|
31499
|
-
process.env.FAILPROOFAI_PACKAGE_ROOT =
|
|
32936
|
+
process.env.FAILPROOFAI_PACKAGE_ROOT = resolve33(dirname24(realpathSync6(fileURLToPath4(import.meta.url))), "..");
|
|
31500
32937
|
}
|
|
31501
32938
|
if (!process.env.FAILPROOFAI_DIST_PATH) {
|
|
31502
|
-
process.env.FAILPROOFAI_DIST_PATH =
|
|
32939
|
+
process.env.FAILPROOFAI_DIST_PATH = resolve33(dirname24(realpathSync6(fileURLToPath4(import.meta.url))), "..", "dist");
|
|
31503
32940
|
}
|
|
31504
32941
|
var args = process.argv.slice(2);
|
|
31505
32942
|
if (args[0] === "p")
|
|
@@ -31682,6 +33119,12 @@ COMMANDS
|
|
|
31682
33119
|
|
|
31683
33120
|
audit Audit your agent's behavior, then open the
|
|
31684
33121
|
dashboard at http://localhost:8020/audit
|
|
33122
|
+
audit --schedule [days] Audit on a timer (default 7 days) and email you
|
|
33123
|
+
what it finds. Signs you in the first time;
|
|
33124
|
+
--email <address> skips that question
|
|
33125
|
+
audit --no-schedule Stop auditing on a timer
|
|
33126
|
+
audit --status Whether scheduling is on, where reports go, and
|
|
33127
|
+
when the next scan is due
|
|
31685
33128
|
audit --help, -h Show this help for the audit command
|
|
31686
33129
|
|
|
31687
33130
|
backfill Re-send history the collector already read past
|
|
@@ -32069,8 +33512,8 @@ Run \`failproofai migrate --help\` for usage.`);
|
|
|
32069
33512
|
return;
|
|
32070
33513
|
}
|
|
32071
33514
|
const run = runMigrations3(from);
|
|
32072
|
-
for (const
|
|
32073
|
-
console.log(`${
|
|
33515
|
+
for (const step3 of run.steps) {
|
|
33516
|
+
console.log(`${step3.ok ? "migrated" : "FAILED "} layout ${step3.from} \u2192 ${step3.to}`);
|
|
32074
33517
|
}
|
|
32075
33518
|
if (run.backedUp.length > 0) {
|
|
32076
33519
|
console.log(`Saved first: ${run.backedUp.join(", ")}`);
|
|
@@ -32140,8 +33583,8 @@ Run \`failproofai update --help\` for usage.`);
|
|
|
32140
33583
|
const run = runMigrations3(state.found);
|
|
32141
33584
|
migrationsRan = run.steps.length;
|
|
32142
33585
|
migrationFailed = Boolean(run.failed);
|
|
32143
|
-
for (const
|
|
32144
|
-
console.log(`${
|
|
33586
|
+
for (const step3 of run.steps) {
|
|
33587
|
+
console.log(`${step3.ok ? "migrated" : "FAILED "} layout ${step3.from} \u2192 ${step3.to}`);
|
|
32145
33588
|
}
|
|
32146
33589
|
if (run.backedUp.length > 0)
|
|
32147
33590
|
console.log(`Saved first: ${run.backedUp.join(", ")}`);
|
|
@@ -32678,14 +34121,14 @@ PAUSING ENFORCEMENT (one session, always time-boxed)
|
|
|
32678
34121
|
const { runDisconnectCommand: runDisconnectCommand2 } = await Promise.resolve().then(() => (init_cloud_enrollment_cli(), exports_cloud_enrollment_cli));
|
|
32679
34122
|
result2 = runDisconnectCommand2();
|
|
32680
34123
|
} else {
|
|
32681
|
-
const { hostname:
|
|
34124
|
+
const { hostname: hostname7 } = await import("os");
|
|
32682
34125
|
const { runConnectCommand: runConnectCommand2 } = await Promise.resolve().then(() => (init_cloud_enrollment_cli(), exports_cloud_enrollment_cli));
|
|
32683
34126
|
result2 = await runConnectCommand2({
|
|
32684
34127
|
url: valueAfter("--connect"),
|
|
32685
34128
|
token: valueAfter("--token"),
|
|
32686
34129
|
machineId: valueAfter("--machine-id"),
|
|
32687
34130
|
machineLabel: valueAfter("--machine-label"),
|
|
32688
|
-
defaultMachineId:
|
|
34131
|
+
defaultMachineId: hostname7(),
|
|
32689
34132
|
sessions: !args.includes("--no-transcripts")
|
|
32690
34133
|
});
|
|
32691
34134
|
}
|