failproofai 1.0.7 → 1.0.8-beta.0
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/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +4 -4
- package/.next/standalone/.next/required-server-files.json +1 -1
- 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 -4
- 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 -4
- 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.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +8 -7
- 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.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- 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.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 +9 -7
- 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 +6 -6
- 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 +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +11 -10
- 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 +8 -7
- 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 -6
- 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 +7 -6
- 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 +52 -8
- package/.next/standalone/.next/server/app/settings/page.js +12 -7
- 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/chunks/{[externals]__1msfs-h._.js → [externals]__19_pzeq._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1_bftcl._.js → [externals]__20kzpkf._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ttrwnd._.js +22 -0
- package/.next/standalone/.next/server/chunks/_09dz7xv._.js +21 -21
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +16 -16
- package/.next/standalone/.next/server/chunks/{_1c3k-8x._.js → _1q5i8mb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/lib_16xa545._.js +3 -0
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_1aveq0u._.js +5 -0
- package/.next/standalone/.next/server/chunks/src_hooks_1eem5a7._.js +3 -0
- package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3._.js +4 -2
- package/.next/standalone/.next/server/chunks/ssr/[externals]__0ohnuzs._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01bmjsj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04usis8._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__056wjo4._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__059yza8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06pflha._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0eip4_k._.js +22 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n0xg95._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qcb0mg._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rwtwpm._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0yrsbd_._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11mayhe._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12e7nhs._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__13d-wb6._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0bd3mje._.js → [root-of-the-server]__14o3ek1._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15578wp._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19evfi8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1dinjii._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1m_svbe._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0cxe_2_._.js → [root-of-the-server]__1mf3zp6._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pprgri._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1q4p5b8._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qiz0e4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_042cgl1._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_166t73i._.js → _0o4xkpl._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0uyu3jf._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_1-i_gzc._.js → _1-7sqrb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_1es2j7i._.js → _1gb0ifp._.js} +5 -5
- package/.next/standalone/.next/server/chunks/ssr/{_1_qswah._.js → _1q46vxx._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +13 -13
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9sni._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- 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_02tf1h4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_0aiy-os._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_0drixxt._.js → node_modules_next_dist_0w6mzq5._.js} +4 -4
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_18_d8l1._.js +151 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0-q0umm._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_06kzv9d._.js +12 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0g194sy._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4._.js +4 -2
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1kx9e0d._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_effective-reviewers_ts_1h4wtvo._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me65._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_semantic_pack-policies_ts_0gh_bu_._.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 +67 -23
- package/.next/standalone/.next/static/chunks/078gnymqoh3r4.js +1 -0
- package/.next/standalone/.next/static/chunks/{2mdh397ghgnvv.js → 0ahfwmbkpgfw4.js} +1 -1
- package/.next/standalone/.next/static/chunks/0bhidk90e-07f.css +2 -0
- package/.next/standalone/.next/static/chunks/1bu2-nv59ed6i.js +1 -0
- package/.next/standalone/.next/static/chunks/{0o6qlkgubtoex.js → 1v_tp3hm8wyhe.js} +1 -1
- package/.next/standalone/.next/static/chunks/2a405_e1o26ol.js +1 -0
- package/.next/standalone/.next/static/chunks/2qv4hshejedtx.css +1 -0
- package/.next/standalone/.next/static/chunks/2vo7qbdbvnc0o.js +69 -0
- package/.next/standalone/.next/static/chunks/{13i7-9is-vhys.js → 2z7i-yg59w4pn.js} +1 -1
- package/.next/standalone/.next/static/chunks/36uhh9el_oz8e.js +1 -0
- package/.next/standalone/.next/static/chunks/{0__8a7m868fvf.js → 3bgot5v6f6s3c.js} +1 -1
- package/.next/standalone/.next/static/chunks/3o3f1ibfci0p7.js +6 -0
- package/.next/standalone/PROBE-FOLLOWUP.md +186 -0
- package/.next/standalone/app/actions/get-jev-config.ts +604 -0
- package/.next/standalone/app/actions/pack-actions.ts +12 -0
- package/.next/standalone/app/actions/update-jev-config.ts +569 -0
- package/.next/standalone/app/components/jev-notices.tsx +96 -0
- package/.next/standalone/app/policies/hooks-client.tsx +14 -3
- package/.next/standalone/app/settings/jev-panel.tsx +630 -0
- package/.next/standalone/app/settings/page.tsx +20 -1
- package/.next/standalone/app/settings/settings-client.tsx +27 -1
- package/.next/standalone/app/settings/settings.css +79 -0
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +22 -4
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +14 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/keys_cmds.py +17 -3
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +3 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +22 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +169 -0
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +1 -1
- package/.next/standalone/fp-cloud-cli/tests/test_keys_queries.py +65 -0
- package/.next/standalone/fp-cloud-cli/tests/test_policy_check.py +61 -0
- package/.next/standalone/package.json +10 -10
- package/.next/standalone/sdk/python/CHANGELOG.md +7 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
- package/.next/standalone/sdk/typescript/scripts/release.mjs +30 -0
- package/.next/standalone/server.js +1 -1
- package/README.md +3 -2
- package/bin/failproofai.mjs +148 -4
- package/dist/cli.mjs +18537 -9492
- package/dist/index.js +19 -1
- package/dist/worker.mjs +10421 -3770
- package/package.json +10 -10
- package/pi-extension/index.ts +11 -0
- package/scripts/build-policy-pack.mjs +53 -2
- package/src/audit/features.ts +3 -2
- package/src/hooks/builtin-policies.ts +195 -8
- package/src/hooks/cloud-connection.ts +190 -1
- package/src/hooks/cloud-enrollment-cli.ts +57 -8
- package/src/hooks/cloud-introspect.ts +6 -0
- package/src/hooks/cloud-managed-policies.ts +22 -0
- package/src/hooks/configure-wizard.ts +26 -6
- package/src/hooks/custom-hooks-loader.ts +98 -10
- package/src/hooks/custom-hooks-registry.ts +45 -1
- package/src/hooks/effective-reviewers.ts +243 -0
- package/src/hooks/first-run-gate.ts +5 -0
- package/src/hooks/flush-cli.ts +35 -8
- package/src/hooks/fp-config.ts +266 -1
- package/src/hooks/fp-home.ts +23 -0
- package/src/hooks/fp-reset.ts +22 -4
- package/src/hooks/handler.ts +303 -7
- package/src/hooks/hook-activity-store.ts +118 -6
- package/src/hooks/hook-telemetry.ts +41 -0
- package/src/hooks/jev-activity.ts +385 -0
- package/src/hooks/jev-cli.ts +2172 -0
- package/src/hooks/jev-cloud-connection.ts +392 -0
- package/src/hooks/loader-utils.ts +6 -0
- package/src/hooks/manager.ts +45 -7
- package/src/hooks/pack-cli.ts +568 -37
- package/src/hooks/pack-failclosed.ts +3 -0
- package/src/hooks/pack-manifest.ts +489 -7
- package/src/hooks/pack-store.ts +194 -11
- package/src/hooks/policy-authority.ts +386 -0
- package/src/hooks/policy-catalog.ts +206 -0
- package/src/hooks/policy-evaluator.ts +963 -796
- package/src/hooks/policy-registry.ts +27 -1
- package/src/hooks/policy-reviewability.ts +258 -0
- package/src/hooks/policy-types.ts +128 -0
- package/src/hooks/semantic/combine.ts +581 -0
- package/src/hooks/semantic/compile.ts +176 -0
- package/src/hooks/semantic/decide.ts +408 -0
- package/src/hooks/semantic/envelope.ts +1255 -0
- package/src/hooks/semantic/evaluator.ts +556 -0
- package/src/hooks/semantic/facts.ts +298 -0
- package/src/hooks/semantic/intent.ts +1190 -0
- package/src/hooks/semantic/jev-client.ts +1155 -0
- package/src/hooks/semantic/jev-config.ts +1142 -0
- package/src/hooks/semantic/jev-review.ts +389 -0
- package/src/hooks/semantic/jev-stats.ts +289 -0
- package/src/hooks/semantic/jev-throttle.ts +427 -0
- package/src/hooks/semantic/pack-policies.ts +294 -0
- package/src/hooks/semantic/policies.ts +597 -0
- package/src/hooks/semantic/precondition-names.ts +60 -0
- package/src/hooks/semantic/preconditions.ts +58 -0
- package/src/hooks/semantic/redact.ts +2937 -0
- package/src/hooks/semantic/session-root.ts +116 -0
- package/src/hooks/semantic/types.ts +156 -0
- package/src/hooks/semver-precedence.ts +128 -0
- package/src/hooks/tui.ts +4 -0
- package/src/hooks/types.ts +1 -1
- package/src/hooks/worker-server.ts +121 -27
- package/src/index.ts +6 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cuho4x._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1_r2rbg._.js +0 -24
- package/.next/standalone/.next/server/chunks/src_hooks_0iu54mz._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-_ki57._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cg-bgc._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cpu_mj._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u3w0ll._.js +0 -22
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17d_ffl._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19d9tgz._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1jiwfsj._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1phc187._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_06imw3p._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/_0h_douw._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +0 -3
- package/.next/standalone/.next/static/chunks/094xgi4owxaqf.js +0 -1
- package/.next/standalone/.next/static/chunks/1rz20_pz828f3.js +0 -6
- package/.next/standalone/.next/static/chunks/2k9f4tyv04809.css +0 -1
- package/.next/standalone/.next/static/chunks/2klitrtzpaoe0.js +0 -1
- package/.next/standalone/.next/static/chunks/2rshywgeqsyzk.css +0 -2
- package/.next/standalone/.next/static/chunks/3pzx4chkhko9k.js +0 -1
- package/.next/standalone/.next/static/chunks/3rh5o7e16irrm.js +0 -69
- package/.next/standalone/.next/static/chunks/43ufqrz8qo3h-.js +0 -1
- /package/.next/standalone/.next/static/{PgeWCHmyVbjRznv2VO7KF → O_b5R1axf5cb4NxIXq2kS}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{PgeWCHmyVbjRznv2VO7KF → O_b5R1axf5cb4NxIXq2kS}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{PgeWCHmyVbjRznv2VO7KF → O_b5R1axf5cb4NxIXq2kS}/_ssgManifest.js +0 -0
package/src/hooks/fp-config.ts
CHANGED
|
@@ -22,7 +22,19 @@
|
|
|
22
22
|
* someone opens when something is wrong. That guidance now lives in
|
|
23
23
|
* `failproofai config --status` and the docs, which is a worse place for it.
|
|
24
24
|
*/
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
chmodSync,
|
|
27
|
+
closeSync,
|
|
28
|
+
constants as fsConstants,
|
|
29
|
+
existsSync,
|
|
30
|
+
fstatSync,
|
|
31
|
+
mkdirSync,
|
|
32
|
+
openSync,
|
|
33
|
+
readFileSync,
|
|
34
|
+
readSync,
|
|
35
|
+
statSync,
|
|
36
|
+
writeFileSync,
|
|
37
|
+
} from "node:fs";
|
|
26
38
|
import { dirname } from "node:path";
|
|
27
39
|
import { version as cliVersion } from "../../package.json";
|
|
28
40
|
import {
|
|
@@ -753,6 +765,26 @@ export interface FpCredentials {
|
|
|
753
765
|
org?: { id?: string; slug?: string; name?: string };
|
|
754
766
|
ingest?: { url: string; key: string };
|
|
755
767
|
auth?: { baseUrl?: string; sessionToken?: string; expiresAt?: number; email?: string };
|
|
768
|
+
/**
|
|
769
|
+
* The key Jev's FailproofAI Cloud route sends, and the Cloud ORIGIN it was
|
|
770
|
+
* verified against. Written by connect only when introspect reports
|
|
771
|
+
* `jev:evaluate`; cleared by disconnect. See {@link readJevCloudCredential}.
|
|
772
|
+
*
|
|
773
|
+
* A slot of its own, not a reuse of `[cloud]` or `[ingest]`, although it holds
|
|
774
|
+
* the same key today: those two are written under their own permission checks
|
|
775
|
+
* and can hold DIFFERENT keys (see `cloud-connection.ts`), and "which of the
|
|
776
|
+
* two backs Jev" is a question nobody reading the file should have to answer.
|
|
777
|
+
* One slot per capability keeps revoking one from ever silently re-routing
|
|
778
|
+
* another.
|
|
779
|
+
*/
|
|
780
|
+
jev?: JevCloudCredential;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/** The `jev` slot of `credentials.json`. */
|
|
784
|
+
export interface JevCloudCredential {
|
|
785
|
+
/** The Cloud origin the key was verified against, e.g. `https://app.befailproof.ai`. */
|
|
786
|
+
url: string;
|
|
787
|
+
key: string;
|
|
756
788
|
}
|
|
757
789
|
|
|
758
790
|
/**
|
|
@@ -775,6 +807,8 @@ const OWNED_CREDENTIAL_KEYS: readonly (readonly string[])[] = [
|
|
|
775
807
|
["org", "name"],
|
|
776
808
|
["ingest", "url"],
|
|
777
809
|
["ingest", "key"],
|
|
810
|
+
["jev", "url"],
|
|
811
|
+
["jev", "key"],
|
|
778
812
|
["auth", "base_url"],
|
|
779
813
|
["auth", "session_token"],
|
|
780
814
|
["auth", "expires_at"],
|
|
@@ -843,6 +877,8 @@ export function projectCredentials(parsed: Record<string, unknown>): FpCredentia
|
|
|
843
877
|
if (ingest && typeof ingest.url === "string" && typeof ingest.key === "string" && ingest.url && ingest.key) {
|
|
844
878
|
out.ingest = { url: ingest.url, key: ingest.key };
|
|
845
879
|
}
|
|
880
|
+
const jev = projectJevSlot(parsed.jev);
|
|
881
|
+
if (jev) out.jev = jev;
|
|
846
882
|
if (auth) {
|
|
847
883
|
out.auth = {
|
|
848
884
|
baseUrl: typeof auth.base_url === "string" ? auth.base_url : undefined,
|
|
@@ -876,6 +912,9 @@ export function writeCredentials(creds: FpCredentials, raw?: Record<string, unkn
|
|
|
876
912
|
if (creds.ingest) {
|
|
877
913
|
out.ingest = { url: creds.ingest.url, key: creds.ingest.key };
|
|
878
914
|
}
|
|
915
|
+
if (creds.jev) {
|
|
916
|
+
out.jev = { url: creds.jev.url, key: creds.jev.key };
|
|
917
|
+
}
|
|
879
918
|
if (creds.org && (creds.org.id || creds.org.slug || creds.org.name)) {
|
|
880
919
|
out.org = {
|
|
881
920
|
...(creds.org.id ? { id: creds.org.id } : {}),
|
|
@@ -920,6 +959,232 @@ export function hasCloudCredentials(): boolean {
|
|
|
920
959
|
return Boolean(c.cloud || c.ingest);
|
|
921
960
|
}
|
|
922
961
|
|
|
962
|
+
// ── The Jev slot ─────────────────────────────────────────────────────────────
|
|
963
|
+
//
|
|
964
|
+
// The one credential in this file that a HOOK reads. Everything else here is
|
|
965
|
+
// read by the daemon or a CLI command; this is read by `inspectJevConfig` on
|
|
966
|
+
// every gated tool call of a machine whose `jev.json` names the FailproofAI
|
|
967
|
+
// Cloud provider — and the key it yields is what lets Jev's answers clear a
|
|
968
|
+
// deny. So it is read the way `jev.json` itself is (`semantic/jev-config.ts`),
|
|
969
|
+
// not the way `readCredentials` reads: a file or directory other users could
|
|
970
|
+
// have written is REFUSED rather than read, because a routing credential for a
|
|
971
|
+
// tier that can clear denies must not be one somebody else chose.
|
|
972
|
+
//
|
|
973
|
+
// And only from `credentialsFile()`, never from `FAILPROOFAI_CLOUD_CREDENTIALS`.
|
|
974
|
+
// That override relocates the policy credential for the daemon and CI; honoured
|
|
975
|
+
// here it would be an environment variable choosing the key Jev spends, which a
|
|
976
|
+
// repository's `.claude/settings.json` can set for a session. `FAILPROOFAI_HOME`
|
|
977
|
+
// is not an exception to that rule for the reason `jev-config.ts` gives: it
|
|
978
|
+
// moves the whole layout, `jev.json` included.
|
|
979
|
+
//
|
|
980
|
+
// # A slot counts only while the connection it came with is still there
|
|
981
|
+
//
|
|
982
|
+
// The slot is written by `config --token` and cleared by `config --disconnect`
|
|
983
|
+
// — by THIS build. An older one (1.0.7-beta.2 and before) knows nothing about
|
|
984
|
+
// it: its disconnect clears `[cloud]` and `[ingest]` and carries the `jev`
|
|
985
|
+
// table over as a key it does not own. So downgrade, disconnect, upgrade, and
|
|
986
|
+
// a machine its owner disconnected would find a Cloud key and a Cloud
|
|
987
|
+
// `jev.json` both still in place, and Jev would start spending on an org the
|
|
988
|
+
// machine had left. So the slot is read as usable only when a FailproofAI Cloud
|
|
989
|
+
// connection on the SAME origin — a policy (`[cloud]`) or reporting
|
|
990
|
+
// (`[ingest]`) credential — is in the same file. A key carrying `jev:evaluate`
|
|
991
|
+
// always carries `events:add` and `policies:pull` too (the server refuses it
|
|
992
|
+
// otherwise), so a live Jev connection always has one of them.
|
|
993
|
+
//
|
|
994
|
+
// And that connection must hold the SAME KEY. The origin alone does not say
|
|
995
|
+
// whose connection it is: on hosted FailproofAI Cloud every organisation shares
|
|
996
|
+
// one origin. An older build's `config --token` with another org's key
|
|
997
|
+
// rewrites `[cloud]` and `[ingest]` to that key and carries the `jev` table
|
|
998
|
+
// over untouched, so the origin still matches while the machine now reports
|
|
999
|
+
// into the other org — and Jev would go on spending the first org's budget.
|
|
1000
|
+
// This build's connect writes one key to all three (a partial connect may
|
|
1001
|
+
// leave one of `[cloud]`/`[ingest]` holding an earlier key, never both), so a
|
|
1002
|
+
// live slot always has a same-origin credential holding its key.
|
|
1003
|
+
//
|
|
1004
|
+
// Only from this file, for the reason above: under `FAILPROOFAI_CLOUD_CREDENTIALS`
|
|
1005
|
+
// the policy credential lives elsewhere, and an environment variable must not
|
|
1006
|
+
// be able to re-arm a slot either. The reporting credential is always here,
|
|
1007
|
+
// and connecting a Jev key writes it.
|
|
1008
|
+
|
|
1009
|
+
/** Same bound `jev.json` gets; no legitimate `credentials.json` is near it. */
|
|
1010
|
+
const MAX_CREDENTIALS_BYTES = 64 * 1024;
|
|
1011
|
+
/**
|
|
1012
|
+
* Non-blocking, so a FIFO in the file's place cannot hang a hook in `open()`.
|
|
1013
|
+
* A function, not a module-level constant: this module is imported everywhere,
|
|
1014
|
+
* and reading `fs.constants` at import time would make every importer depend on
|
|
1015
|
+
* it — including test doubles of `node:fs` that never needed it before.
|
|
1016
|
+
*/
|
|
1017
|
+
const credentialsOpenFlags = (): number => fsConstants.O_RDONLY | (fsConstants.O_NONBLOCK ?? 0);
|
|
1018
|
+
/** Group- or world-WRITE bits on the directory: whoever has them can replace the file. */
|
|
1019
|
+
const DIR_WRITABLE_BY_OTHERS = 0o022;
|
|
1020
|
+
|
|
1021
|
+
export type JevCloudCredentialRead =
|
|
1022
|
+
| { status: "ok"; path: string; credential: JevCloudCredential }
|
|
1023
|
+
/**
|
|
1024
|
+
* No usable Jev credential: no file, no `jev` slot, or a slot no live
|
|
1025
|
+
* connection backs (see "A slot counts only while…" above).
|
|
1026
|
+
*/
|
|
1027
|
+
| {
|
|
1028
|
+
status: "absent";
|
|
1029
|
+
path: string;
|
|
1030
|
+
/**
|
|
1031
|
+
* This machine IS connected to FailproofAI Cloud — a policy or reporting
|
|
1032
|
+
* credential is stored — just not with a Jev key. Tells "connected, but
|
|
1033
|
+
* the key does not carry Jev" apart from "not connected at all".
|
|
1034
|
+
*/
|
|
1035
|
+
connected: boolean;
|
|
1036
|
+
/** A `jev` slot is there, but no connection on its origin holding its key is: it is ignored. */
|
|
1037
|
+
orphaned?: true;
|
|
1038
|
+
}
|
|
1039
|
+
| {
|
|
1040
|
+
status: "refused";
|
|
1041
|
+
path: string;
|
|
1042
|
+
/** The file's permission bits, when they are the reason. */
|
|
1043
|
+
mode: number | null;
|
|
1044
|
+
reason: "too-open" | "unreadable" | "too-large" | "not-json";
|
|
1045
|
+
problem: string;
|
|
1046
|
+
/** The command that fixes it, when one does. */
|
|
1047
|
+
fix?: string;
|
|
1048
|
+
};
|
|
1049
|
+
|
|
1050
|
+
/** A `jev` table with two non-empty strings, or null. Shape only; `jev-config.ts` validates the values. */
|
|
1051
|
+
function projectJevSlot(raw: unknown): JevCloudCredential | null {
|
|
1052
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
1053
|
+
const { url, key } = raw as Record<string, unknown>;
|
|
1054
|
+
return typeof url === "string" && url && typeof key === "string" && key ? { url, key } : null;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* The `jev` slot, read owner-only.
|
|
1059
|
+
*
|
|
1060
|
+
* The file is opened once and every check runs against that descriptor, so the
|
|
1061
|
+
* permissions checked are the permissions of the bytes read — the same shape
|
|
1062
|
+
* as `inspectJevConfig`, for the same reason. Never throws.
|
|
1063
|
+
*/
|
|
1064
|
+
export function readJevCloudCredential(): JevCloudCredentialRead {
|
|
1065
|
+
const path = credentialsFile();
|
|
1066
|
+
let fd: number;
|
|
1067
|
+
try {
|
|
1068
|
+
fd = openSync(path, credentialsOpenFlags());
|
|
1069
|
+
} catch (err) {
|
|
1070
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
1071
|
+
if (code === "ENOENT" || code === "ENOTDIR") return { status: "absent", path, connected: false };
|
|
1072
|
+
return { status: "refused", path, mode: null, reason: "unreadable", problem: `cannot open ${path} (${code ?? "error"})` };
|
|
1073
|
+
}
|
|
1074
|
+
let text: string;
|
|
1075
|
+
try {
|
|
1076
|
+
const st = fstatSync(fd);
|
|
1077
|
+
if (!st.isFile()) return { status: "refused", path, mode: null, reason: "unreadable", problem: `${path} is not a regular file` };
|
|
1078
|
+
// On Windows `mode` is synthesized and would refuse every file while
|
|
1079
|
+
// protecting nothing; Jev setup is refused there anyway.
|
|
1080
|
+
const meaningful = process.platform !== "win32";
|
|
1081
|
+
const mode = st.mode & 0o777;
|
|
1082
|
+
if (meaningful && (mode & 0o077) !== 0) {
|
|
1083
|
+
return {
|
|
1084
|
+
status: "refused",
|
|
1085
|
+
path,
|
|
1086
|
+
mode,
|
|
1087
|
+
reason: "too-open",
|
|
1088
|
+
problem:
|
|
1089
|
+
`${path} is ${mode.toString(8).padStart(4, "0")}; it holds the key Jev sends to FailproofAI Cloud, ` +
|
|
1090
|
+
`so it must be owner-only (chmod 600 ${path})`,
|
|
1091
|
+
fix: `chmod 600 ${path}`,
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
if (meaningful) {
|
|
1095
|
+
let dirMode: number | null = null;
|
|
1096
|
+
try {
|
|
1097
|
+
const m = statSync(dirname(path)).mode & 0o777;
|
|
1098
|
+
if ((m & DIR_WRITABLE_BY_OTHERS) !== 0) dirMode = m;
|
|
1099
|
+
} catch {
|
|
1100
|
+
// No permission to stat it: the read below reports what it can.
|
|
1101
|
+
}
|
|
1102
|
+
if (dirMode !== null) {
|
|
1103
|
+
return {
|
|
1104
|
+
status: "refused",
|
|
1105
|
+
path,
|
|
1106
|
+
mode,
|
|
1107
|
+
reason: "too-open",
|
|
1108
|
+
problem:
|
|
1109
|
+
`its directory ${dirname(path)} is ${dirMode.toString(8).padStart(4, "0")} — other users can write there, ` +
|
|
1110
|
+
`so they can put their own credentials in its place; it must be owner-only (chmod 700 ${dirname(path)})`,
|
|
1111
|
+
fix: `chmod 700 ${dirname(path)}`,
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
if (st.size > MAX_CREDENTIALS_BYTES) {
|
|
1116
|
+
return { status: "refused", path, mode, reason: "too-large", problem: `${path} is larger than ${MAX_CREDENTIALS_BYTES} bytes` };
|
|
1117
|
+
}
|
|
1118
|
+
const buf = Buffer.alloc(st.size);
|
|
1119
|
+
let off = 0;
|
|
1120
|
+
while (off < buf.length) {
|
|
1121
|
+
const n = readSync(fd, buf, off, buf.length - off, off);
|
|
1122
|
+
if (n === 0) break;
|
|
1123
|
+
off += n;
|
|
1124
|
+
}
|
|
1125
|
+
text = buf.subarray(0, off).toString("utf8");
|
|
1126
|
+
} catch (err) {
|
|
1127
|
+
return { status: "refused", path, mode: null, reason: "unreadable", problem: `cannot read ${path} (${(err as NodeJS.ErrnoException).code ?? "error"})` };
|
|
1128
|
+
} finally {
|
|
1129
|
+
try {
|
|
1130
|
+
closeSync(fd);
|
|
1131
|
+
} catch {
|
|
1132
|
+
// Nothing useful to do.
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
let parsed: unknown;
|
|
1136
|
+
try {
|
|
1137
|
+
parsed = JSON.parse(text);
|
|
1138
|
+
} catch {
|
|
1139
|
+
return { status: "refused", path, mode: null, reason: "not-json", problem: `${path} is not valid JSON` };
|
|
1140
|
+
}
|
|
1141
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1142
|
+
return { status: "refused", path, mode: null, reason: "not-json", problem: `${path} does not hold a JSON object` };
|
|
1143
|
+
}
|
|
1144
|
+
const fields = parsed as Record<string, unknown>;
|
|
1145
|
+
// The same projection `readCredentials` uses, so "a cloud block without a
|
|
1146
|
+
// token is not a cloud block" means the same thing here as everywhere else.
|
|
1147
|
+
const others = projectCredentials(fields);
|
|
1148
|
+
const connections = [
|
|
1149
|
+
others.cloud ? { origin: originOrNull(others.cloud.url), key: others.cloud.token } : null,
|
|
1150
|
+
others.ingest ? { origin: originOrNull(others.ingest.url), key: others.ingest.key } : null,
|
|
1151
|
+
].filter((c): c is { origin: string; key: string } => c !== null && c.origin !== null);
|
|
1152
|
+
const connected = connections.length > 0;
|
|
1153
|
+
const credential = projectJevSlot(fields.jev);
|
|
1154
|
+
if (!credential) return { status: "absent", path, connected };
|
|
1155
|
+
const slotOrigin = originOrNull(credential.url);
|
|
1156
|
+
// The same origin AND the same key: see "A slot counts only while…" above.
|
|
1157
|
+
const backed = slotOrigin !== null && connections.some((c) => c.origin === slotOrigin && c.key === credential.key);
|
|
1158
|
+
if (!backed) return { status: "absent", path, connected, orphaned: true };
|
|
1159
|
+
return { status: "ok", path, credential };
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/** A URL's origin, or null for a missing or unparseable one. */
|
|
1163
|
+
function originOrNull(url: string | undefined): string | null {
|
|
1164
|
+
if (!url) return null;
|
|
1165
|
+
try {
|
|
1166
|
+
const origin = new URL(url).origin;
|
|
1167
|
+
return origin === "null" ? null : origin;
|
|
1168
|
+
} catch {
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/** Store the `jev` slot (0600, owner-only home), keeping every other credential. */
|
|
1174
|
+
export function writeJevCloudCredential(credential: JevCloudCredential): string {
|
|
1175
|
+
writeCredentials({ ...readCredentials(), jev: { url: credential.url, key: credential.key } });
|
|
1176
|
+
return credentialsFile();
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/** Remove the `jev` slot. True if there was one. Every other credential stays. */
|
|
1180
|
+
export function clearJevCloudCredential(): boolean {
|
|
1181
|
+
const current = readCredentials();
|
|
1182
|
+
if (!current.jev) return false;
|
|
1183
|
+
const { jev: _dropped, ...rest } = current;
|
|
1184
|
+
writeCredentials(rest);
|
|
1185
|
+
return true;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
923
1188
|
function writeFileAt(path: string, contents: string): void {
|
|
924
1189
|
mkdirSync(dirname(path), { recursive: true });
|
|
925
1190
|
writeFileSync(path, contents, "utf8");
|
package/src/hooks/fp-home.ts
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
* VERSION layout / cli / daemon versions
|
|
52
52
|
* config.json 0644 non-secret: mode, daemon, collector prefs
|
|
53
53
|
* credentials.json 0600 every token
|
|
54
|
+
* jev.json 0600 the customer's own Jev endpoint + key (BYOK opt-in)
|
|
54
55
|
* policies-config.json the builtin enable/disable set + params
|
|
55
56
|
* bin/ downloaded daemon binaries, one per version
|
|
56
57
|
* policies/ every policy: the user's *.mjs sit directly here
|
|
@@ -148,6 +149,14 @@ export const configFile = (home?: string) => atHome(home, "config.json");
|
|
|
148
149
|
*/
|
|
149
150
|
export const credentialsFile = (home?: string) => atHome(home, "credentials.json");
|
|
150
151
|
|
|
152
|
+
/**
|
|
153
|
+
* The customer's own Jev endpoint and key (BYOK), owner-only. Its presence is
|
|
154
|
+
* the whole opt-in to the two-tier evaluator: absent, hooks run the regex
|
|
155
|
+
* engine exactly as before. GLOBAL only — a repository can never set it. See
|
|
156
|
+
* `src/hooks/semantic/jev-config.ts`.
|
|
157
|
+
*/
|
|
158
|
+
export const jevConfigFile = (home?: string) => atHome(home, "jev.json");
|
|
159
|
+
|
|
151
160
|
// ── Daemon binaries ──────────────────────────────────────────────────────────
|
|
152
161
|
|
|
153
162
|
export const binDir = (home?: string) => atHome(home, "bin");
|
|
@@ -389,6 +398,12 @@ export const failedDir = (home?: string) => resolve(stateDir(home), "failed");
|
|
|
389
398
|
export const collectorHealthFile = (home?: string) => resolve(stateDir(home), "collector-health.json");
|
|
390
399
|
/** Per-session enforcement pauses, keyed by a hash of the session id. */
|
|
391
400
|
export const sessionPauseDir = () => resolve(stateDir(), "sessions");
|
|
401
|
+
/**
|
|
402
|
+
* The semantic (Jev) evaluator's local state: `sessions/<id>.json` holds the
|
|
403
|
+
* human prompts recorded at `UserPromptSubmit`, and `verdicts.jsonl` one row
|
|
404
|
+
* per semantic evaluation. Written only when a Jev config (BYOK) is present.
|
|
405
|
+
*/
|
|
406
|
+
export const semanticDir = () => resolve(stateDir(), "semantic");
|
|
392
407
|
/**
|
|
393
408
|
* When the scheduled audit last ran, and when the next one is due.
|
|
394
409
|
*
|
|
@@ -554,6 +569,9 @@ export const HOME_CLASSES: readonly { path: (home?: string) => string; class: Da
|
|
|
554
569
|
// policy silently — it keeps enforcing whatever it last had, reports healthy,
|
|
555
570
|
// and never reconciles again. Nothing re-derives a token.
|
|
556
571
|
{ path: credentialsFile, class: "user-typed" },
|
|
572
|
+
// The Jev BYOK config. A person typed the key; nothing regenerates it, and
|
|
573
|
+
// losing it silently turns the two-tier evaluator off.
|
|
574
|
+
{ path: jevConfigFile, class: "user-typed" },
|
|
557
575
|
// Holds `daemon.configured` (the flag that makes the machine fail closed),
|
|
558
576
|
// the collector preferences, `[audit] auto`, the telemetry opt-out, and
|
|
559
577
|
// `collector.sources.*.extra_paths` — the entire output of `harness add-path`.
|
|
@@ -642,6 +660,11 @@ export const HOME_CLASSES: readonly { path: (home?: string) => string; class: Da
|
|
|
642
660
|
{ path: codexSessionPathsFile, class: "derived" },
|
|
643
661
|
{ path: shimsDir, class: "derived" },
|
|
644
662
|
{ path: sessionPauseDir, class: "derived" },
|
|
663
|
+
// Recorded prompts (a six-hour window that only ever lets Jev clear a
|
|
664
|
+
// reviewable policy) and a local diagnostic log of semantic verdicts that is
|
|
665
|
+
// never shipped. Losing either costs a clear and some history, never
|
|
666
|
+
// enforcement.
|
|
667
|
+
{ path: semanticDir, class: "derived" },
|
|
645
668
|
{ path: logsDir, class: "derived" },
|
|
646
669
|
{ path: lastVersionFile, class: "derived" },
|
|
647
670
|
{ path: launcherMarker, class: "derived" },
|
package/src/hooks/fp-reset.ts
CHANGED
|
@@ -97,6 +97,7 @@ import {
|
|
|
97
97
|
daemonVersionSkew,
|
|
98
98
|
isDaemonSupportedPlatform,
|
|
99
99
|
probeDaemonEndToEnd,
|
|
100
|
+
type DaemonServiceStatus,
|
|
100
101
|
} from "./daemon-service";
|
|
101
102
|
|
|
102
103
|
export interface ResetOutcome {
|
|
@@ -1094,7 +1095,24 @@ async function drainSpoolAfterMigrating(): Promise<number> {
|
|
|
1094
1095
|
}
|
|
1095
1096
|
}
|
|
1096
1097
|
|
|
1097
|
-
|
|
1098
|
+
/**
|
|
1099
|
+
* Injectable for tests, matching `refreshDaemonToCliVersion`'s shape in
|
|
1100
|
+
* `daemon-service.ts`.
|
|
1101
|
+
*
|
|
1102
|
+
* `healDaemonFlag` asks systemd whether the unit is installed, and on a machine
|
|
1103
|
+
* with no unit that call blocks for seconds before answering. Two tests here
|
|
1104
|
+
* set `daemon.configured` and so reach it, and they were timing out at vitest's
|
|
1105
|
+
* 5 s default against the real service manager — a check about what this command
|
|
1106
|
+
* PRINTS, failing on how long the host takes to say "not-installed". A
|
|
1107
|
+
* `vi.spyOn` cannot intercept it either: this module calls its own local
|
|
1108
|
+
* binding, so without a seam a stub is ignored and the test passes or fails for
|
|
1109
|
+
* the wrong reason.
|
|
1110
|
+
*/
|
|
1111
|
+
export interface LayoutCheckDeps {
|
|
1112
|
+
daemonStatus?: () => DaemonServiceStatus;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
export async function checkLayoutForCli(deps: LayoutCheckDeps = {}): Promise<LayoutCheck> {
|
|
1098
1116
|
const state = detectLayout();
|
|
1099
1117
|
|
|
1100
1118
|
if (state.kind === "future") {
|
|
@@ -1220,7 +1238,7 @@ export async function checkLayoutForCli(): Promise<LayoutCheck> {
|
|
|
1220
1238
|
state,
|
|
1221
1239
|
fatal: false,
|
|
1222
1240
|
didReset: false,
|
|
1223
|
-
lines: [...(await healDaemonFlag()), ...staleDaemonHint()],
|
|
1241
|
+
lines: [...(await healDaemonFlag(deps)), ...staleDaemonHint()],
|
|
1224
1242
|
};
|
|
1225
1243
|
}
|
|
1226
1244
|
|
|
@@ -1239,13 +1257,13 @@ export async function checkLayoutForCli(): Promise<LayoutCheck> {
|
|
|
1239
1257
|
* downgrade a healthy machine to the in-process path — trading a loud, correct
|
|
1240
1258
|
* failure for a quiet, wrong one.
|
|
1241
1259
|
*/
|
|
1242
|
-
async function healDaemonFlag(): Promise<string[]> {
|
|
1260
|
+
async function healDaemonFlag(deps: LayoutCheckDeps = {}): Promise<string[]> {
|
|
1243
1261
|
try {
|
|
1244
1262
|
const cfg = readConfig();
|
|
1245
1263
|
if (!cfg.daemon.configured) return [];
|
|
1246
1264
|
if (!isDaemonSupportedPlatform()) return [];
|
|
1247
1265
|
|
|
1248
|
-
const status = daemonServiceStatus();
|
|
1266
|
+
const status = (deps.daemonStatus ?? daemonServiceStatus)();
|
|
1249
1267
|
if (status === "not-installed") {
|
|
1250
1268
|
updateConfig({ daemon: { configured: false } });
|
|
1251
1269
|
return [
|