failproofai 1.0.7 → 1.0.8
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 +3 -3
- 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]__02npjtd._.js → [root-of-the-server]__056wjo4._.js} +3 -3
- 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]__0p-5p8u._.js → [root-of-the-server]__0n0xg95._.js} +3 -3
- 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]__013jr2b._.js → [root-of-the-server]__0rwtwpm._.js} +3 -3
- 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]__0da85px._.js → [root-of-the-server]__11mayhe._.js} +3 -3
- 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]__13t1zkw._.js +3 -0
- 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]__0ftmoxc._.js → [root-of-the-server]__1pprgri._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1p2otjt._.js → [root-of-the-server]__1q4p5b8._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qiz0e4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_166t73i._.js → _0-vcssj._.js} +1 -1
- 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/_0uyu3jf._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_13kfn90._.js +23 -0
- 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_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/06dnzbolj00mc.js +6 -0
- package/.next/standalone/.next/static/chunks/0bhidk90e-07f.css +2 -0
- package/.next/standalone/.next/static/chunks/{2mdh397ghgnvv.js → 0m-9d6yn9hx4j.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0__8a7m868fvf.js → 0wbjy0zo-m7is.js} +1 -1
- package/.next/standalone/.next/static/chunks/16f3fa-lx38hk.js +1 -0
- package/.next/standalone/.next/static/chunks/{0o6qlkgubtoex.js → 21uv-uusw329x.js} +1 -1
- package/.next/standalone/.next/static/chunks/2g2tki08kdhie.js +1 -0
- package/.next/standalone/.next/static/chunks/2qdpj67x6ifk_.js +69 -0
- package/.next/standalone/.next/static/chunks/2qv4hshejedtx.css +1 -0
- package/.next/standalone/.next/static/chunks/3-nbtkhg9y-1j.js +1 -0
- package/.next/standalone/.next/static/chunks/355km0ihuqo1p.js +1 -0
- package/.next/standalone/.next/static/chunks/{13i7-9is-vhys.js → 3c3qbmosdjl6w.js} +1 -1
- 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/CHANGELOG.md +18 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package-lock.json +0 -274
- 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 +18599 -9478
- package/dist/index.js +19 -1
- package/dist/worker.mjs +10500 -3773
- 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 +513 -0
- package/src/hooks/semantic/envelope.ts +1255 -0
- package/src/hooks/semantic/evaluator.ts +556 -0
- package/src/hooks/semantic/facts.ts +387 -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 +162 -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]__01wy8d-._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bd3mje._.js +0 -3
- 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]__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]__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/_1-i_gzc._.js +0 -23
- 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 → 5yKM8NoTOpUI0MoQl8eo5}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{PgeWCHmyVbjRznv2VO7KF → 5yKM8NoTOpUI0MoQl8eo5}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{PgeWCHmyVbjRznv2VO7KF → 5yKM8NoTOpUI0MoQl8eo5}/_ssgManifest.js +0 -0
|
@@ -35,14 +35,23 @@ import {
|
|
|
35
35
|
writeCloudCredentials,
|
|
36
36
|
type CloudCredentials,
|
|
37
37
|
} from "./cloud-enrollment";
|
|
38
|
-
import {
|
|
38
|
+
import { credentialsFile } from "./fp-home";
|
|
39
|
+
import {
|
|
40
|
+
clearJevCloudCredential,
|
|
41
|
+
updateConfig,
|
|
42
|
+
readCredentials,
|
|
43
|
+
writeCredentials,
|
|
44
|
+
writeJevCloudCredential,
|
|
45
|
+
} from "./fp-config";
|
|
39
46
|
import {
|
|
40
47
|
introspectKey,
|
|
41
48
|
hasPermission,
|
|
42
49
|
describeOrg,
|
|
43
50
|
PERMISSION_EVENTS,
|
|
51
|
+
PERMISSION_JEV,
|
|
44
52
|
PERMISSION_POLICIES,
|
|
45
53
|
} from "./cloud-introspect";
|
|
54
|
+
import type { CloudJevConfigWrite } from "./jev-cloud-connection";
|
|
46
55
|
import {
|
|
47
56
|
ingestPath,
|
|
48
57
|
validateIngestKey,
|
|
@@ -139,12 +148,50 @@ export interface CapabilityOutcome {
|
|
|
139
148
|
export interface ConnectOutcome {
|
|
140
149
|
policy: CapabilityOutcome & { policyCount?: number; deployment?: number };
|
|
141
150
|
ingest: CapabilityOutcome;
|
|
151
|
+
/**
|
|
152
|
+
* Jev through FailproofAI Cloud. Absent when nothing connected, and when the
|
|
153
|
+
* server has no introspect endpoint to say whether the key carries it.
|
|
154
|
+
*/
|
|
155
|
+
jev?: JevConnectOutcome;
|
|
142
156
|
/** Which organisation the key belongs to. Absent on a pre-introspect server. */
|
|
143
157
|
org?: { id?: string; slug?: string; name?: string };
|
|
144
158
|
/** True when at least one capability was configured. */
|
|
145
159
|
anyConfigured: boolean;
|
|
146
160
|
}
|
|
147
161
|
|
|
162
|
+
export interface JevConnectOutcome {
|
|
163
|
+
/** The key carries `jev:evaluate`, and it was stored for Jev. */
|
|
164
|
+
ok: boolean;
|
|
165
|
+
reason?: string;
|
|
166
|
+
/** What became of `jev.json`. Present when `ok`, unless `optIn`. */
|
|
167
|
+
config?: CloudJevConfigWrite;
|
|
168
|
+
/**
|
|
169
|
+
* `ok`, but Jev was deliberately NOT switched on: the connection opted out
|
|
170
|
+
* of transcripts (`sessions !== true`), and there is no `jev.json`. The key
|
|
171
|
+
* is stored; `jev setup --provider failproofai` switches Jev on.
|
|
172
|
+
*/
|
|
173
|
+
optIn?: true;
|
|
174
|
+
/**
|
|
175
|
+
* A `--no-transcripts` connection found a `jev.json` already in place that
|
|
176
|
+
* keeps Jev running through FailproofAI Cloud, in this mode. It is left
|
|
177
|
+
* alone like any existing file (never overwritten), but a connection that
|
|
178
|
+
* asked for decisions only is told that Jev still sends more than that.
|
|
179
|
+
*/
|
|
180
|
+
stillOn?: "shadow" | "enforce";
|
|
181
|
+
/**
|
|
182
|
+
* Introspect gave no answer about this key's permissions — the server could
|
|
183
|
+
* not be asked (`unreachable`) or predates the endpoint (`unsupported`) — so
|
|
184
|
+
* whether it carries `jev:evaluate` is unknown, and the Jev slot was:
|
|
185
|
+
*
|
|
186
|
+
* - `kept`: it already held THIS key for THIS origin, and was left as it
|
|
187
|
+
* was — the machine's connection is the same one it belongs to;
|
|
188
|
+
* - `cleared`: it held another key or origin — the previous connection's,
|
|
189
|
+
* possibly another org's budget — and was removed;
|
|
190
|
+
* - `none`: there was no slot, and there still is none.
|
|
191
|
+
*/
|
|
192
|
+
unconfirmed?: "kept" | "cleared" | "none";
|
|
193
|
+
}
|
|
194
|
+
|
|
148
195
|
/**
|
|
149
196
|
* Verify both capabilities, then write only what actually works.
|
|
150
197
|
*
|
|
@@ -282,9 +329,146 @@ writeCloudCredentials(creds);
|
|
|
282
329
|
});
|
|
283
330
|
}
|
|
284
331
|
|
|
332
|
+
// ── Jev, last ─────────────────────────────────────────────────────────────
|
|
333
|
+
//
|
|
334
|
+
// Only from introspect's answer. `jev:evaluate` spends the org's budget on
|
|
335
|
+
// every gated tool call, so it is never inferred from a probe succeeding the
|
|
336
|
+
// way the other two capabilities are on a server without introspect — and a
|
|
337
|
+
// machine that connected to nothing gets nothing.
|
|
338
|
+
//
|
|
339
|
+
// There is no Jev-only path: the server refuses a key that carries
|
|
340
|
+
// `jev:evaluate` without `events:add` and `policies:pull`, so a key that gets
|
|
341
|
+
// here with Jev has already configured the other two.
|
|
342
|
+
if (outcome.anyConfigured) {
|
|
343
|
+
if (known && hasPermission(known, PERMISSION_JEV)) {
|
|
344
|
+
// The ORIGIN the key was verified against. `jev-config.ts` sends the key
|
|
345
|
+
// only to a base URL on this origin, so a jev.json pointing anywhere else
|
|
346
|
+
// cannot borrow it.
|
|
347
|
+
writeJevCloudCredential({ url: new URL(input.url).origin, key: input.token });
|
|
348
|
+
// Loaded here and nowhere earlier: a connect whose key does not carry Jev
|
|
349
|
+
// pulls in none of the Jev modules.
|
|
350
|
+
const { cloudJevRunningMode, existingJevConfig, writeCloudJevConfigIfAbsent } = await import("./jev-cloud-connection");
|
|
351
|
+
if (input.sessions === true) {
|
|
352
|
+
outcome.jev = { ok: true, config: writeCloudJevConfigIfAbsent(input.url) };
|
|
353
|
+
} else {
|
|
354
|
+
// `--no-transcripts` never switches Jev on (the user's decision). Jev
|
|
355
|
+
// sends each checked tool call and the recent prompt to FailproofAI
|
|
356
|
+
// Cloud, and someone who just asked for decisions only has not asked
|
|
357
|
+
// for that. The key is stored all the same, so opting in later is one
|
|
358
|
+
// command with no key to paste; jev.json is not written. One that is
|
|
359
|
+
// already there is somebody's decision and is reported as ever — and
|
|
360
|
+
// when it keeps Jev on through FailproofAI Cloud, that is said too, or
|
|
361
|
+
// "Decisions only." would be the last word while Jev keeps sending.
|
|
362
|
+
const existing = existingJevConfig(input.url);
|
|
363
|
+
const stillOn = existing ? cloudJevRunningMode() : null;
|
|
364
|
+
outcome.jev = existing
|
|
365
|
+
? { ok: true, config: existing, ...(stillOn ? { stillOn } : {}) }
|
|
366
|
+
: { ok: true, optIn: true };
|
|
367
|
+
}
|
|
368
|
+
} else if (known) {
|
|
369
|
+
// Introspect ANSWERED, and the key does not carry Jev. This connection
|
|
370
|
+
// replaces the last one, and the last key's Jev slot describes a
|
|
371
|
+
// connection this machine no longer has — possibly another org's budget.
|
|
372
|
+
// Dropped, so Jev never spends on a key it was not just handed. (A Cloud
|
|
373
|
+
// jev.json then reads `key-lacks-jev`: off, and said.)
|
|
374
|
+
clearJevCloudCredential();
|
|
375
|
+
outcome.jev = { ok: false, reason: missing(PERMISSION_JEV) };
|
|
376
|
+
} else {
|
|
377
|
+
// No answer about this key: a 502 or a network blip on introspect
|
|
378
|
+
// (`unreachable`), or a server with no introspect at all (`unsupported`).
|
|
379
|
+
// Neither says the key LACKS Jev, so neither is a reason to switch Jev
|
|
380
|
+
// off — a reconnect during a transient outage used to do exactly that,
|
|
381
|
+
// silently.
|
|
382
|
+
//
|
|
383
|
+
// But neither says it HAS Jev, and the slot is only ever spent against.
|
|
384
|
+
// So it is kept only when it already holds this very key for this very
|
|
385
|
+
// origin: then the connection just verified IS the one it belongs to,
|
|
386
|
+
// and keeping it changes nothing about whose budget Jev spends. A slot
|
|
387
|
+
// holding any other key or origin is the previous connection's, and the
|
|
388
|
+
// machine is not connected with that key any more — the rule above,
|
|
389
|
+
// which wins whenever the answer is not "yes".
|
|
390
|
+
//
|
|
391
|
+
// `unsupported` is decided the same way, and conservatively: a server
|
|
392
|
+
// that predates introspect predates the Jev route too, so a kept slot
|
|
393
|
+
// there can only fall back with `http-404` — never spend — and clearing
|
|
394
|
+
// it would turn Jev off on no evidence. It gets no line when there was
|
|
395
|
+
// no slot, because on such a server there is nothing to say about Jev.
|
|
396
|
+
const previous = readCredentials().jev;
|
|
397
|
+
if (!previous) {
|
|
398
|
+
if (identity.kind === "unreachable") outcome.jev = { ok: false, unconfirmed: "none" };
|
|
399
|
+
} else if (previous.key === input.token && previous.url === new URL(input.url).origin) {
|
|
400
|
+
outcome.jev = { ok: false, unconfirmed: "kept" };
|
|
401
|
+
} else {
|
|
402
|
+
clearJevCloudCredential();
|
|
403
|
+
outcome.jev = { ok: false, unconfirmed: "cleared" };
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
285
408
|
return outcome;
|
|
286
409
|
}
|
|
287
410
|
|
|
411
|
+
/**
|
|
412
|
+
* The Jev line(s) for `describeOutcome`: whether FailproofAI Cloud runs Jev for
|
|
413
|
+
* this machine now, and — the one case worth a second line — why an existing
|
|
414
|
+
* `jev.json` was not touched.
|
|
415
|
+
*/
|
|
416
|
+
function jevLines(outcome: ConnectOutcome): string[] {
|
|
417
|
+
const jev = outcome.jev;
|
|
418
|
+
if (!jev) return [];
|
|
419
|
+
if (jev.unconfirmed === "cleared") {
|
|
420
|
+
return [
|
|
421
|
+
" Jev could not confirm this key's Jev permission, and the Jev key stored before was a different one, so it was removed: Jev through FailproofAI Cloud is off. Run `failproofai config --token <key>` again to retry.",
|
|
422
|
+
];
|
|
423
|
+
}
|
|
424
|
+
if (jev.unconfirmed) return [" Jev could not confirm the key's Jev permission; left as it was."];
|
|
425
|
+
if (!jev.ok) {
|
|
426
|
+
return [` Jev not through FailproofAI Cloud: ${jev.reason ?? `that key does not carry \`${PERMISSION_JEV}\``}.`];
|
|
427
|
+
}
|
|
428
|
+
if (jev.optIn) {
|
|
429
|
+
// One line, and nothing that reads as "on": this connection asked for
|
|
430
|
+
// decisions only, and Jev would send more than that.
|
|
431
|
+
return [
|
|
432
|
+
" Jev available on this key, not switched on (--no-transcripts): it sends each checked tool call and the recent prompt to FailproofAI Cloud for evaluation. To switch it on: `failproofai jev setup --provider failproofai`.",
|
|
433
|
+
];
|
|
434
|
+
}
|
|
435
|
+
const config = jev.config;
|
|
436
|
+
if (!config || config.status === "written") {
|
|
437
|
+
// Plain http (loopback only) is accepted in shadow mode and nowhere else, so
|
|
438
|
+
// the enforce command would be refused there, by the CLI and the dashboard.
|
|
439
|
+
const plainHttp = config?.status === "written" && new URL(config.baseUrl).protocol === "http:";
|
|
440
|
+
return [
|
|
441
|
+
` Jev on through FailproofAI Cloud, in shadow mode: logged, not enforced (${config?.path ?? "jev.json"}).`,
|
|
442
|
+
plainHttp
|
|
443
|
+
? " Enforce needs an https FailproofAI Cloud URL: reconnect with `failproofai config --token <key> --url https://…`. Over plain http Jev stays in shadow mode."
|
|
444
|
+
: " Enforce it with `failproofai jev setup --mode enforce`, or from the dashboard.",
|
|
445
|
+
];
|
|
446
|
+
}
|
|
447
|
+
if (config.status === "kept") {
|
|
448
|
+
const lines = [` Jev key stored; ${config.path} already exists and was left as configured.`];
|
|
449
|
+
if (config.otherOrigin) {
|
|
450
|
+
lines.push(
|
|
451
|
+
config.jevOff?.why === "off"
|
|
452
|
+
? ` It points at ${config.otherOrigin} and has Jev switched off (mode off), so Jev is off. To point it here and turn it on: \`failproofai jev setup --provider failproofai --mode shadow\`.`
|
|
453
|
+
: ` It points at ${config.otherOrigin}, so Jev stays off until you run \`failproofai jev setup --provider failproofai\`.`,
|
|
454
|
+
);
|
|
455
|
+
} else if (config.jevOff?.why === "refused") {
|
|
456
|
+
lines.push(
|
|
457
|
+
` It was refused (${config.jevOff.problem}), so Jev is off. ${config.jevOff.fix ? `Fix: \`${config.jevOff.fix}\`.` : "Write a valid one: `failproofai jev setup --provider failproofai`."}`,
|
|
458
|
+
);
|
|
459
|
+
} else if (config.jevOff?.why === "off") {
|
|
460
|
+
lines.push(" It has Jev switched off (mode off), so Jev is off. To turn it on: `failproofai jev setup --mode shadow`.");
|
|
461
|
+
}
|
|
462
|
+
if (jev.stillOn) {
|
|
463
|
+
lines.push(
|
|
464
|
+
` Jev is still on through FailproofAI Cloud (${jev.stillOn} mode): it sends each checked tool call and the recent prompt to FailproofAI Cloud. To switch it off: \`failproofai jev setup --mode off\`.`,
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
return lines;
|
|
468
|
+
}
|
|
469
|
+
return [` Jev key stored, but Jev was not turned on: ${config.problem}.`];
|
|
470
|
+
}
|
|
471
|
+
|
|
288
472
|
/**
|
|
289
473
|
* How to describe the outcome to someone at a terminal.
|
|
290
474
|
*
|
|
@@ -311,6 +495,7 @@ export function describeOutcome(outcome: ConnectOutcome, machineId: string, url:
|
|
|
311
495
|
const n = outcome.policy.policyCount ?? 0;
|
|
312
496
|
lines.push(` Policy ${n} polic${n === 1 ? "y" : "ies"} assigned (deployment ${outcome.policy.deployment ?? 0}).`);
|
|
313
497
|
lines.push(` Dashboard hook activity will be sent to ${ingestUrlFor(url)}.`);
|
|
498
|
+
lines.push(...jevLines(outcome));
|
|
314
499
|
return lines;
|
|
315
500
|
}
|
|
316
501
|
|
|
@@ -322,6 +507,7 @@ export function describeOutcome(outcome: ConnectOutcome, machineId: string, url:
|
|
|
322
507
|
lines.push("");
|
|
323
508
|
lines.push(" Enforcement works. Nothing will appear in the dashboard until this");
|
|
324
509
|
lines.push(" key also carries `events:add`, or you re-run --connect with one that does.");
|
|
510
|
+
lines.push(...jevLines(outcome));
|
|
325
511
|
return lines;
|
|
326
512
|
}
|
|
327
513
|
|
|
@@ -332,6 +518,7 @@ export function describeOutcome(outcome: ConnectOutcome, machineId: string, url:
|
|
|
332
518
|
lines.push("");
|
|
333
519
|
lines.push(" This machine keeps enforcing its LOCAL policies and will report what");
|
|
334
520
|
lines.push(" they decide, but will not receive centrally-managed ones.");
|
|
521
|
+
lines.push(...jevLines(outcome));
|
|
335
522
|
return lines;
|
|
336
523
|
}
|
|
337
524
|
|
|
@@ -346,6 +533,8 @@ export function configuredPaths(outcome: ConnectOutcome): string[] {
|
|
|
346
533
|
const paths: string[] = [];
|
|
347
534
|
if (outcome.policy.ok) paths.push(cloudCredentialPath());
|
|
348
535
|
if (outcome.ingest.ok) paths.push(ingestPath());
|
|
536
|
+
// The Jev slot is always in credentials.json, never behind the JSON override.
|
|
537
|
+
if (outcome.jev?.ok) paths.push(credentialsFile());
|
|
349
538
|
// Deduplicated because layout 2 consolidated both credentials into
|
|
350
539
|
// `credentials.toml`, so the two capabilities now name the SAME file and the
|
|
351
540
|
// closing note read "stored in <path> and <path>" — the same path, twice.
|
|
@@ -20,9 +20,13 @@ import {
|
|
|
20
20
|
daemonVersionSkew,
|
|
21
21
|
} from "./daemon-service";
|
|
22
22
|
import { clearActiveCloudManagedPolicies } from "./cloud-managed-policies";
|
|
23
|
-
import { optsFor, rows as kitRows, stack, warning } from "./tui";
|
|
23
|
+
import { TOKEN_ON_ARGV, optsFor, rows as kitRows, stack, warning } from "./tui";
|
|
24
24
|
import { deliveryHealth, deliveryHealthLine } from "./delivery-health";
|
|
25
|
-
import { readVersionFile, readCredentials } from "./fp-config";
|
|
25
|
+
import { clearJevCloudCredential, readVersionFile, readCredentials } from "./fp-config";
|
|
26
|
+
// A CLI command's import, never the hook path's: `jev-cloud-connection` reaches
|
|
27
|
+
// `semantic/jev-config`, which a machine without jev.json must not load while
|
|
28
|
+
// evaluating a tool call. `config --disconnect` is not that.
|
|
29
|
+
import { removeCloudJevConfig } from "./jev-cloud-connection";
|
|
26
30
|
import { version as cliVersion } from "../../package.json";
|
|
27
31
|
import { daemonSocketPresent } from "./daemon-client";
|
|
28
32
|
import {
|
|
@@ -174,16 +178,26 @@ function daemonWarning(status: ReturnType<typeof daemonServiceStatus>): string[]
|
|
|
174
178
|
];
|
|
175
179
|
}
|
|
176
180
|
|
|
181
|
+
/**
|
|
182
|
+
* What `config --token` (and `config --connect … --token`) ends with, connected
|
|
183
|
+
* or not: every Jev remedy sends people to that spelling, and the key is in
|
|
184
|
+
* history either way. `jev setup --token` says the same first line.
|
|
185
|
+
*/
|
|
186
|
+
export const CONFIG_TOKEN_HISTORY_WARNING = [
|
|
187
|
+
TOKEN_ON_ARGV,
|
|
188
|
+
"Next time, run `failproofai config` with the key in FAILPROOFAI_CLOUD_TOKEN (from a secret store, or `read -rs`) instead of --token — and rotate this one if it matters.",
|
|
189
|
+
];
|
|
190
|
+
|
|
177
191
|
export async function runConnectCommand(opts: ConnectOptions): Promise<CommandResult> {
|
|
178
192
|
if (!opts.url) return { exitCode: 1, lines: ["--connect needs a URL, e.g. --connect https://be.failproof.ai"] };
|
|
179
193
|
if (!opts.token) {
|
|
180
194
|
return {
|
|
181
195
|
exitCode: 1,
|
|
182
196
|
lines: [
|
|
183
|
-
"--connect needs
|
|
197
|
+
"--connect needs this machine's key: set FAILPROOFAI_CLOUD_TOKEN (preferred), or pass --token <key>.",
|
|
184
198
|
"",
|
|
185
|
-
"Create
|
|
186
|
-
"
|
|
199
|
+
"Create it with the \"machine\" preset on the dashboard's Keys page",
|
|
200
|
+
"(events:add, policies:pull, jev:evaluate). Do not use an admin key.",
|
|
187
201
|
],
|
|
188
202
|
};
|
|
189
203
|
}
|
|
@@ -324,6 +338,13 @@ export function runDisconnectCommand(): CommandResult {
|
|
|
324
338
|
// leave the machine still shipping activity to a cloud the user believes
|
|
325
339
|
// they have left — the one outcome nobody expects from this command.
|
|
326
340
|
const removedIngest = clearIngestCredential();
|
|
341
|
+
// And spending: the Jev key goes with the rest, so no tool call after this
|
|
342
|
+
// one is charged to an org this machine has left. The Cloud's own jev.json
|
|
343
|
+
// goes too — it names a route that now has no key — unless it is switched
|
|
344
|
+
// off, the owner's opt-out that must outlive a reconnect; a BYOK one stays,
|
|
345
|
+
// because it never depended on the Cloud and is not the Cloud's to delete.
|
|
346
|
+
const removedJevKey = clearJevCloudCredential();
|
|
347
|
+
const jevConfig = removeCloudJevConfig();
|
|
327
348
|
// Stop ENFORCING them too, not merely refreshing them. Clearing the
|
|
328
349
|
// credential ends the daemon's polling; every artifact already on disk stayed
|
|
329
350
|
// referenced by `active.json` and kept being loaded on every tool call — so a
|
|
@@ -337,8 +358,35 @@ export function runDisconnectCommand(): CommandResult {
|
|
|
337
358
|
// machine is provably silent instead of silent-by-happenstance.
|
|
338
359
|
updateConfig({ mode: "oss" });
|
|
339
360
|
|
|
340
|
-
|
|
341
|
-
|
|
361
|
+
const jevLines =
|
|
362
|
+
jevConfig.status === "removed"
|
|
363
|
+
? [` Jev through FailproofAI Cloud is off: removed ${jevConfig.path}. Hooks run the regex policies.`]
|
|
364
|
+
: jevConfig.status === "error"
|
|
365
|
+
? [
|
|
366
|
+
removedJevKey
|
|
367
|
+
? ` ! The FailproofAI Cloud key for Jev is gone, so Jev is off — but ${jevConfig.problem}.`
|
|
368
|
+
: ` ! ${jevConfig.problem}.`,
|
|
369
|
+
...(jevConfig.setAside ? [` To put it back: mv ${jevConfig.setAside} ${jevConfig.path}`] : []),
|
|
370
|
+
]
|
|
371
|
+
: jevConfig.status === "kept-off"
|
|
372
|
+
? [` Jev stays switched off: ${jevConfig.path} (mode off) was kept, so connecting again leaves it off.`]
|
|
373
|
+
: jevConfig.status === "kept" && jevConfig.provider !== null
|
|
374
|
+
? [` ${jevConfig.path} (provider ${jevConfig.provider}) is your own Jev setup and was left in place.`]
|
|
375
|
+
: jevConfig.status === "set-aside"
|
|
376
|
+
? [
|
|
377
|
+
` ! ${jevConfig.path} was being checked when another jev.json was written in its place. Nothing was deleted: the new one is at ${jevConfig.path},`,
|
|
378
|
+
` and the one that was there (provider ${jevConfig.provider ?? "unreadable"}, not FailproofAI Cloud's) is kept at ${jevConfig.setAside}.`,
|
|
379
|
+
]
|
|
380
|
+
: removedJevKey
|
|
381
|
+
? [" Jev through FailproofAI Cloud is off: its key was removed."]
|
|
382
|
+
: [];
|
|
383
|
+
|
|
384
|
+
if (!removed && !existing && !hadIngest && !stoppedManaged && !removedJevKey && jevConfig.status !== "removed") {
|
|
385
|
+
// Nothing was connected — but the jev.json check ran all the same, and a
|
|
386
|
+
// file it could not put back where it was (a bring-your-own-key setup,
|
|
387
|
+
// moved aside) is not nothing: left unsaid, that Jev is simply gone.
|
|
388
|
+
const unsettled = jevConfig.status === "error" || jevConfig.status === "set-aside";
|
|
389
|
+
return { exitCode: 0, lines: ["This machine is not connected to FailproofAI Cloud.", ...(unsettled ? jevLines : [])] };
|
|
342
390
|
}
|
|
343
391
|
|
|
344
392
|
const lines = [
|
|
@@ -348,6 +396,7 @@ export function runDisconnectCommand(): CommandResult {
|
|
|
348
396
|
? " Cloud-managed policies stop being enforced and stop being refreshed.\n" +
|
|
349
397
|
" Local builtin, custom and convention policies are unaffected."
|
|
350
398
|
: " Local builtin, custom and convention policies are unaffected.",
|
|
399
|
+
...jevLines,
|
|
351
400
|
];
|
|
352
401
|
if (removedIngest) {
|
|
353
402
|
// Named honestly. The collector manager starts once for the daemon's
|
|
@@ -377,7 +426,7 @@ export function versionStatusLines(): string[] {
|
|
|
377
426
|
const daemon = recorded?.daemon ?? "not installed";
|
|
378
427
|
return [
|
|
379
428
|
`CLI ${cliVersion} · daemon ${daemon}${skew ? " (STALE)" : ""} · layout ${recorded?.layout ?? "-"}`,
|
|
380
|
-
...(skew ? [" Run `failproofai
|
|
429
|
+
...(skew ? [" Run `failproofai update` to update the daemon."] : []),
|
|
381
430
|
];
|
|
382
431
|
}
|
|
383
432
|
|
|
@@ -135,6 +135,12 @@ export async function introspectKey(
|
|
|
135
135
|
/** The permission each capability needs. Named so the messages can quote them. */
|
|
136
136
|
export const PERMISSION_EVENTS = "events:add";
|
|
137
137
|
export const PERMISSION_POLICIES = "policies:pull";
|
|
138
|
+
/**
|
|
139
|
+
* Jev through FailproofAI Cloud, charged to the key's org. The server refuses a
|
|
140
|
+
* key that carries it without both of the above, so a key with it always
|
|
141
|
+
* connects for policy and reporting too — there is no Jev-only connection.
|
|
142
|
+
*/
|
|
143
|
+
export const PERMISSION_JEV = "jev:evaluate";
|
|
138
144
|
|
|
139
145
|
export function hasPermission(identity: KeyIdentity, permission: string): boolean {
|
|
140
146
|
return identity.permissions.includes(permission);
|
|
@@ -8,6 +8,8 @@ import { createHash } from "node:crypto";
|
|
|
8
8
|
import { existsSync, readFileSync, realpathSync, rmSync } from "node:fs";
|
|
9
9
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
10
10
|
import { cloudPoliciesDir } from "./fp-home";
|
|
11
|
+
import { authorityFieldsOf } from "./policy-authority";
|
|
12
|
+
import type { PolicyAuthority } from "./policy-types";
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Active-manifest schema versions this reader accepts.
|
|
@@ -44,6 +46,19 @@ export interface CloudManagedPolicyArtifact {
|
|
|
44
46
|
sha256: string;
|
|
45
47
|
path: string;
|
|
46
48
|
deployment: number;
|
|
49
|
+
/**
|
|
50
|
+
* Whether Jev may clear this policy's verdict once it is configured, as the
|
|
51
|
+
* ASSIGNMENT declares it — code inside the artifact cannot grant itself this.
|
|
52
|
+
* Absent means `hard`, which is what keeps Jev from weakening central
|
|
53
|
+
* enforcement by default: a cloud policy is only ever reviewable because
|
|
54
|
+
* whoever deployed it said so. Applies to every hook the artifact registers.
|
|
55
|
+
*
|
|
56
|
+
* Read and validated here; the daemon has to carry it into `active.json`
|
|
57
|
+
* (`ActivePolicy` in `cloud_policies.rs`) before a deployment can set it.
|
|
58
|
+
*/
|
|
59
|
+
authority?: PolicyAuthority;
|
|
60
|
+
/** The semantic policies that must all be asked and none answer `deny`; see `authority`. */
|
|
61
|
+
reviewedBy?: string[];
|
|
47
62
|
}
|
|
48
63
|
|
|
49
64
|
interface ActiveManifest {
|
|
@@ -55,6 +70,8 @@ interface ActiveManifest {
|
|
|
55
70
|
effect?: string;
|
|
56
71
|
sha256: string;
|
|
57
72
|
path: string;
|
|
73
|
+
authority?: unknown;
|
|
74
|
+
reviewedBy?: unknown;
|
|
58
75
|
}>;
|
|
59
76
|
}
|
|
60
77
|
|
|
@@ -206,6 +223,11 @@ export function readActiveCloudManagedPolicies(): CloudManagedPolicyArtifact[] {
|
|
|
206
223
|
sha256: policy.sha256,
|
|
207
224
|
path,
|
|
208
225
|
deployment: manifest.deployment,
|
|
226
|
+
// Unlike `effect`, a malformed authority is dropped rather than refused:
|
|
227
|
+
// dropping it makes this policy `hard`, the default that keeps enforcing,
|
|
228
|
+
// while refusing would take the whole deployment down over an optional
|
|
229
|
+
// field. Absent stays absent.
|
|
230
|
+
...authorityFieldsOf(policy as unknown as Record<string, unknown>),
|
|
209
231
|
};
|
|
210
232
|
});
|
|
211
233
|
}
|
|
@@ -1178,7 +1178,18 @@ export async function runConfigureWizard(
|
|
|
1178
1178
|
// did — it is not a trusted back door. http stays loopback-only, so a
|
|
1179
1179
|
// bearer token still cannot be exported onto the wire in clear by
|
|
1180
1180
|
// setting a variable.
|
|
1181
|
-
|
|
1181
|
+
//
|
|
1182
|
+
// `--url` comes first. It was read into `answers.url` and then only
|
|
1183
|
+
// ever used as a CONDITION above, never as the URL, so
|
|
1184
|
+
// `config --token <key> --url <X>` connected to the env value or the
|
|
1185
|
+
// hosted default and ignored X — reporting a machine somewhere its
|
|
1186
|
+
// operator had explicitly said not to. The CLI fills `answers.url` from
|
|
1187
|
+
// `--url`, falling back to FAILPROOFAI_CLOUD_URL, so the source named
|
|
1188
|
+
// on screen is whichever of the two the value actually is.
|
|
1189
|
+
const envOverride = process.env.FAILPROOFAI_CLOUD_URL?.trim();
|
|
1190
|
+
const flagOverride = answers.url?.trim();
|
|
1191
|
+
const override = flagOverride || envOverride;
|
|
1192
|
+
const source = flagOverride && flagOverride !== envOverride ? "--url" : "FAILPROOFAI_CLOUD_URL";
|
|
1182
1193
|
if (override) {
|
|
1183
1194
|
const validated = validateCloudUrl(cloudBaseFor(override));
|
|
1184
1195
|
if (!validated.ok) {
|
|
@@ -1186,7 +1197,7 @@ export async function runConfigureWizard(
|
|
|
1186
1197
|
// wants THAT endpoint, and quietly reporting a machine to the
|
|
1187
1198
|
// hosted service instead is the one outcome they did not ask for.
|
|
1188
1199
|
stdout.write(
|
|
1189
|
-
`\
|
|
1200
|
+
`\n${source === "--url" ? "--url is" : "FAILPROOFAI_CLOUD_URL is set to"} "${override}", which cannot be used: ` +
|
|
1190
1201
|
`${validated.reason}\n`,
|
|
1191
1202
|
);
|
|
1192
1203
|
return cancel();
|
|
@@ -1196,7 +1207,7 @@ export async function runConfigureWizard(
|
|
|
1196
1207
|
// matters and a machine reporting somewhere unexpected is exactly the
|
|
1197
1208
|
// thing nobody notices until they go looking for data that is not
|
|
1198
1209
|
// there.
|
|
1199
|
-
stdout.write(`\nUsing ${url} (from
|
|
1210
|
+
stdout.write(`\nUsing ${url} (from ${source}).\n`);
|
|
1200
1211
|
} else {
|
|
1201
1212
|
url = cloudBaseFor(DEFAULT_INGEST_URL);
|
|
1202
1213
|
}
|
|
@@ -1507,9 +1518,13 @@ export async function runConfigureWizard(
|
|
|
1507
1518
|
token: connect.token,
|
|
1508
1519
|
machineId: connect.machineId,
|
|
1509
1520
|
machineLabel: connect.machineLabel,
|
|
1510
|
-
// Both streams, as disclosed at the connect question
|
|
1511
|
-
// place that decision becomes
|
|
1512
|
-
|
|
1521
|
+
// Both streams, as disclosed at the connect question, unless the run
|
|
1522
|
+
// said `--no-transcripts`. This is the one place that decision becomes
|
|
1523
|
+
// a written setting — and it used to be a literal `true`, so the flag
|
|
1524
|
+
// was parsed, carried here in `answers`, and silently ignored: a fleet
|
|
1525
|
+
// that asked for decisions only shipped every prompt, file and command
|
|
1526
|
+
// output. The separate `--connect` path always honoured it.
|
|
1527
|
+
sessions: answers.noTranscripts !== true,
|
|
1513
1528
|
});
|
|
1514
1529
|
connected = outcome.anyConfigured;
|
|
1515
1530
|
// Show the human label with the id in parentheses when they differ.
|
|
@@ -1520,6 +1535,11 @@ export async function runConfigureWizard(
|
|
|
1520
1535
|
for (const line of describeOutcome(outcome, shownAs, connect.url)) {
|
|
1521
1536
|
stdout.write(`${line}\n`);
|
|
1522
1537
|
}
|
|
1538
|
+
// Said when it took effect, as `--connect` says it: the default carries
|
|
1539
|
+
// prompts and file contents, so the opt-out is worth confirming.
|
|
1540
|
+
if (outcome.ingest.ok && answers.noTranscripts === true) {
|
|
1541
|
+
stdout.write(" Session transcripts are NOT being sent (--no-transcripts). Decisions only.\n");
|
|
1542
|
+
}
|
|
1523
1543
|
// Never the key, the URL, or the count — only that it happened and which
|
|
1524
1544
|
// capabilities the server actually granted.
|
|
1525
1545
|
void emit("configure_connect", {
|