failproofai 1.0.11 → 1.0.12-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +23 -23
- 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/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
- 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 +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +3 -3
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +5 -6
- 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 +24 -24
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js +2 -2
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js +4 -5
- 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 +6 -7
- 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 +6 -7
- 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 +5 -6
- 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 +2 -2
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +4 -4
- 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 +3 -3
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +7 -7
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +7 -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/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +8 -8
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +3 -3
- 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/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/settings/page.js +10 -10
- 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]__0qkp92tjfvw8r._.js → [externals]__0vt9i6fqinjne._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[externals]__1x24dzcbbq6-g._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__02vous7mpi2xm._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__03i5o4a840guz._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0__a_je8fsn65._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_hsnj_a2mvl_._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1izwqj_ij_9x_._.js → [root-of-the-server]__0f_4g-vazrvxv._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l-oi4gdtmlo5._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0swiyzbmvja2d._.js → [root-of-the-server]__0t9ufnv5hi2hq._.js} +5 -5
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wg9dk8x8bnau._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__12kq3x-h7t7i1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1a5zkndjwlc13._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1r7kbfkiw2xmu._.js → [root-of-the-server]__1aaw59g-5uty7._.js} +12 -12
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1hqpg6vgmfexv._.js +3 -0
- package/.next/standalone/.next/server/chunks/_09dz7xva4ce_2._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0hf5q4xzyg40j._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0tovk6q0miud6._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3ycqv17cl._.js +1 -1
- package/.next/standalone/.next/server/chunks/_19011-u44143j._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68lnh2daur._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1vslkoe35wok6._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_1ixl-kxq_ctsa._.js → _1y590mjzpkbos._.js} +2 -2
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4vkpxxu7._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0_em82s16zfym._.js +5 -0
- package/.next/standalone/.next/server/chunks/src_hooks_builtin-policies_ts_1yq5ybgx1v98e._.js +3 -0
- package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3h94wnn._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_daemon-client_ts_0il_yu1zc0291._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn_4twyh._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01j58mdcdw0vn._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0588st-tpiuia._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08ch3staq3ejw._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_a8z2ldp2v3n._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0vcyntk4snf0f._.js → [root-of-the-server]__0b78b0j8iyv6l._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bigvy9xmv2ka._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ffu2-38y03ic._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g8b0kyorya-t._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h86bl2qf0k_o._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1_xbg2kx30wp-._.js → [root-of-the-server]__0naevg4bcoh54._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pia5h3p8re4c._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__075uro8t92s0_._.js → [root-of-the-server]__0pwj2h2efe5f-._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0zn1d4blhd3bu._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-ey9fgesfbc0._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15usvs-ms-0hf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16gb3_92zvk5x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17r4zrwanhp6r._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1avi2kypusyb_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1lnfv02ucky7m._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mc9-zlnh656_._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1r7drlqli-ado._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1s2-ck3k3rsoo._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1l1qgu5mfapkh._.js → [root-of-the-server]__1vx7o8ukefudh._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0eoura3_9gjb-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0pedwblqdoh58._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_128ebxr83nk6g._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1_ozgifqv1ssi._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1gb0ifpkowwub._.js → _1a3tubds7s7ow._.js} +10 -9
- package/.next/standalone/.next/server/chunks/ssr/_1djqs9i27e4kg._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo5_vm5f.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9snixb5fd-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47q0xq5w._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x33jqb6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpcz0uxur._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h47jmhhd._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_1f_ymmaonkksc.js → node_modules_html-to-image_es_index_1i8oid1gc7s1f.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/package_json_[json]_cjs_013tntikdft59._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_06kzv9ddabn3_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0agdmptetetdt._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0mdwgjisv4fub._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4l1tzrm._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_daemon-client_ts_0k41_i7jg6ndv._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me658zqmh_._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +23 -23
- package/.next/standalone/.next/server/middleware.js +2 -2
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/preview-props.json +3 -3
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
- package/.next/standalone/.next/static/chunks/{0frxv4_m-m2u5.js → 063al_q720fkj.js} +1 -1
- package/.next/standalone/.next/static/chunks/0_zfad4co-t2z.js +1 -0
- package/.next/standalone/.next/static/chunks/0jutamwqz8pg5.js +1 -0
- package/.next/standalone/.next/static/chunks/114hbolvrv94e.js +1 -0
- package/.next/standalone/.next/static/chunks/{37stoe-_yd852.js → 18433qz-8njv3.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1kwj8uqxuv85t.js → 1b32-afqacaen.js} +1 -1
- package/.next/standalone/.next/static/chunks/{44a7k2_62qc-9.js → 1p-hsi58i-6h0.js} +2 -2
- package/.next/standalone/.next/static/chunks/{302llgh97ce6w.js → 2-tf6_7u623kd.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1_c0dc7zqj1n7.js → 24w3x2da9sk49.js} +1 -1
- package/.next/standalone/.next/static/chunks/2tjfp9dirxnku.js +1 -0
- package/.next/standalone/.next/static/chunks/{16z6_d7sp61jm.js → 3-pku9qwv1yfu.js} +1 -1
- package/.next/standalone/.next/static/chunks/{15rm15u5a6dua.js → 42e8zab314654.js} +1 -1
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +41 -0
- package/.next/standalone/fp-cloud-cli/README.md +42 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +117 -12
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +285 -19
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +84 -18
- package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +170 -6
- package/.next/standalone/fp-cloud-cli/fp_cli/models.py +89 -6
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +259 -21
- package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +8 -5
- package/.next/standalone/fp-cloud-cli/skill/SKILL.md +2 -2
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +17 -9
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_jev.py +692 -0
- package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +46 -0
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +23 -0
- package/.next/standalone/fp-cloud-cli/uv.lock +6 -6
- package/.next/standalone/hermes-plugin/README.md +6 -4
- package/.next/standalone/hermes-plugin/__init__.py +16 -0
- package/.next/standalone/hermes-plugin/client.py +5 -1
- package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
- package/.next/standalone/node_modules/@next/env/package.json +1 -1
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/api/context.js +72 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/api/diag.js +84 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/api/metrics.js +50 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/api/propagation.js +80 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/api/trace.js +70 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/baggage/context-helpers.js +52 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/baggage/internal/baggage-impl.js +44 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/baggage/internal/symbol.js +12 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/baggage/utils.js +41 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/context/NoopContextManager.js +27 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/context/context.js +52 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/context-api.js +16 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/diag/ComponentLogger.js +47 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/diag/consoleLogger.js +73 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/diag/internal/logLevelLogger.js +34 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/diag/types.js +33 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/diag-api.js +20 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/index.js +71 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/internal/global-utils.js +60 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/internal/semver.js +111 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/metrics/Metric.js +18 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/metrics/NoopMeter.js +118 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/metrics/NoopMeterProvider.js +20 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/metrics-api.js +17 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/propagation/NoopTextMapPropagator.js +23 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/propagation/TextMapPropagator.js +36 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/propagation-api.js +17 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/NonRecordingSpan.js +58 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/NoopTracer.js +68 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/NoopTracerProvider.js +21 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/ProxyTracer.js +46 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/ProxyTracerProvider.js +46 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/SamplingResult.js +33 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/context-utils.js +71 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/internal/tracestate-impl.js +94 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/internal/tracestate-validators.js +35 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/internal/utils.js +16 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/invalid-span-constants.js +25 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/span_kind.js +38 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/spancontext-utils.js +66 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/status.js +29 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/trace_flags.js +18 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace-api.js +17 -0
- package/.next/standalone/node_modules/@opentelemetry/api/build/src/version.js +10 -0
- package/.next/standalone/node_modules/@opentelemetry/api/package.json +96 -0
- package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +2 -2
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +2 -2
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
- package/.next/standalone/node_modules/next/dist/server/app-render/async-local-storage.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/generate-agent-files.js +3 -3
- package/.next/standalone/node_modules/next/dist/server/lib/router-server.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/trace/tracer.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/typescript/rules/config.js +2 -2
- package/.next/standalone/node_modules/next/dist/server/use-cache/use-cache-wrapper.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
- package/.next/standalone/node_modules/next/package.json +11 -11
- package/.next/standalone/package.json +11 -11
- package/.next/standalone/sdk/python/CHANGELOG.md +7 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
- package/.next/standalone/sdk/python/pyproject.toml +19 -0
- package/.next/standalone/sdk/python/uv.lock +520 -301
- package/.next/standalone/sdk/typescript/CHANGELOG.md +13 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/package-lock.json +4 -13
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/package.json +2 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package-lock.json +4 -13
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package.json +3 -0
- package/.next/standalone/sdk/typescript/package-lock.json +178 -162
- package/.next/standalone/sdk/typescript/package.json +1 -1
- package/.next/standalone/sdk/typescript/src/version.ts +1 -1
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +17 -1
- package/dist/cli.mjs +2866 -1124
- package/dist/worker.mjs +1450 -145
- package/hermes-plugin/README.md +6 -4
- package/hermes-plugin/__init__.py +16 -0
- package/hermes-plugin/client.py +5 -1
- package/hermes-plugin/plugin.yaml +1 -1
- package/package.json +11 -11
- package/src/hooks/agent-roster.ts +172 -0
- package/src/hooks/agent-targets.ts +60 -0
- package/src/hooks/cloud-connection.ts +97 -27
- package/src/hooks/cloud-enrollment-cli.ts +7 -4
- package/src/hooks/cloud-managed-policies.ts +380 -20
- package/src/hooks/cloud-policy-errors.ts +481 -0
- package/src/hooks/config-status.ts +1 -1
- package/src/hooks/custom-hooks-loader.ts +26 -3
- package/src/hooks/daemon-client.ts +9 -4
- package/src/hooks/daemon-service.ts +2 -1
- package/src/hooks/effective-reviewers.ts +22 -1
- package/src/hooks/fp-home.ts +6 -0
- package/src/hooks/handler.ts +235 -18
- package/src/hooks/health-probe.ts +15 -0
- package/src/hooks/integrations.ts +18 -11
- package/src/hooks/jev-cli.ts +197 -0
- package/src/hooks/jev-cloud-connection.ts +92 -0
- package/src/hooks/manager.ts +122 -16
- package/src/hooks/pack-failclosed.ts +2 -0
- package/src/hooks/pack-manifest.ts +8 -1
- package/src/hooks/policy-evaluator.ts +5 -1
- package/src/hooks/policy-registry.ts +3 -2
- package/src/hooks/policy-reviewability.ts +21 -5
- package/src/hooks/semantic/cloud-jev-health.ts +227 -0
- package/src/hooks/semantic/cloud-jev.ts +677 -0
- package/src/hooks/semantic/jev-client.ts +18 -1
- package/src/hooks/semantic/jev-config.ts +173 -3
- package/src/hooks/semantic/jev-review.ts +129 -10
- package/src/hooks/semantic/pack-policies.ts +2 -1
- package/src/hooks/semantic/types.ts +8 -0
- package/src/hooks/session-pause-cli.ts +1 -1
- package/src/hooks/worker-server.ts +5 -1
- package/.next/standalone/.next/server/chunks/[externals]__0hjaw70fchx66._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__07l68zv0hqdiu._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0car70pylmvor._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0u3i025_setk_._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__10a2f6s636yc7._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qt5u58xrcuk6._.js +0 -3
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_077n_4ip5__fu._.js +0 -3
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_0mqvvxcn6v0op._.js +0 -3
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_0pb7ds5g0vzgn._.js +0 -3
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1gasm5xkeqmm3._.js +0 -3
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1q1heqmkhh8l4._.js +0 -3
- package/.next/standalone/.next/server/chunks/src_hooks_1aveq0uhs2t1b._.js +0 -5
- package/.next/standalone/.next/server/chunks/src_hooks_1eem5a7cyqlb2._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0888k4s6spfmh._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_bflu046ep5j._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_lbstb5x2t15._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0fdwgktjfhedo._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s4deza787asx._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__18qe-hu0gwqm4._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_p_9xe520fex._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1fmhm8_s9i4re._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1g76awtnmimd1._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pczevlpk5lxh._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0dc-of_bn0cim._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_effective-reviewers_ts_1h4wtvo55j16x._.js +0 -5
- package/.next/standalone/.next/static/chunks/0m-tv1y6ghhtk.js +0 -1
- package/.next/standalone/.next/static/chunks/0ol2gj4ntfn56.js +0 -1
- package/.next/standalone/.next/static/chunks/3hj5_7i3nw4zj.js +0 -1
- package/.next/standalone/.next/static/chunks/3iyoapeoq_hup.js +0 -1
- /package/.next/standalone/.next/static/{kEFP3ybXwZdriNEQmWwlH → xq8UgF5yFxty1VWxY2hyn}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{kEFP3ybXwZdriNEQmWwlH → xq8UgF5yFxty1VWxY2hyn}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{kEFP3ybXwZdriNEQmWwlH → xq8UgF5yFxty1VWxY2hyn}/_ssgManifest.js +0 -0
|
@@ -13,7 +13,7 @@ import json as _json
|
|
|
13
13
|
import math
|
|
14
14
|
import re
|
|
15
15
|
from datetime import datetime, timezone
|
|
16
|
-
from typing import Any, List, Optional, Sequence
|
|
16
|
+
from typing import Any, Dict, List, Optional, Sequence
|
|
17
17
|
|
|
18
18
|
import typer
|
|
19
19
|
from rich.box import ROUNDED, SIMPLE_HEAD
|
|
@@ -490,7 +490,7 @@ _TOP_LEVEL_GROUPS = [
|
|
|
490
490
|
]),
|
|
491
491
|
("ENFORCE", [
|
|
492
492
|
("policies", "Write cloud-managed policies.", "list show publish test compose enable disable delete"),
|
|
493
|
-
("fleet", "Deploy policies to machines.", "list show deploy diff history rollback rename"),
|
|
493
|
+
("fleet", "Deploy policies to machines.", "list show deploy jev-mode diff history rollback rename"),
|
|
494
494
|
("guardrails", "What enforcement actually blocked.", "summary timeline"),
|
|
495
495
|
]),
|
|
496
496
|
("MANAGE", [
|
|
@@ -5918,6 +5918,10 @@ def _policy_cell(ref: Any) -> Text:
|
|
|
5918
5918
|
return t
|
|
5919
5919
|
|
|
5920
5920
|
|
|
5921
|
+
#: A policy version's kind, as `fp policies list` colours it.
|
|
5922
|
+
_KIND_STYLE = {"regex": theme.TEXT_DIM, "jev": theme.ACCENT, "both": theme.AMBER}
|
|
5923
|
+
|
|
5924
|
+
|
|
5921
5925
|
def render_policies(items: Sequence[Any]) -> None:
|
|
5922
5926
|
"""``fp policies`` — every published VERSION, newest of each policy first.
|
|
5923
5927
|
|
|
@@ -5938,11 +5942,25 @@ def render_policies(items: Sequence[Any]) -> None:
|
|
|
5938
5942
|
state = Text("archived", style=theme.FAINT)
|
|
5939
5943
|
elif p.disabled:
|
|
5940
5944
|
state = Text("disabled", style=theme.AMBER)
|
|
5945
|
+
kind = getattr(p, "kind", None) or "regex"
|
|
5946
|
+
names = list(getattr(p, "jev_names", None) or [])
|
|
5947
|
+
desc = Text()
|
|
5948
|
+
if names:
|
|
5949
|
+
# The checks first: for a `jev` policy they ARE the policy, and a
|
|
5950
|
+
# long description would otherwise push them off the ellipsis.
|
|
5951
|
+
desc.append(", ".join(names), style=theme.ACCENT)
|
|
5952
|
+
chars = getattr(p, "jev_chars", None)
|
|
5953
|
+
if chars is not None:
|
|
5954
|
+
desc.append(f" {chars:,} chars", style=theme.LABEL)
|
|
5955
|
+
if p.description:
|
|
5956
|
+
desc.append(" · ", style=theme.FAINT)
|
|
5957
|
+
desc.append(p.description or "", style=theme.TEXT_DIM)
|
|
5941
5958
|
rows.append([
|
|
5942
5959
|
Text(p.id, style=theme.TEXT),
|
|
5943
5960
|
Text(f"v{p.version}", style=theme.TEXT_DIM),
|
|
5961
|
+
Text(kind, style=_KIND_STYLE.get(kind, theme.TEXT_DIM)),
|
|
5944
5962
|
state,
|
|
5945
|
-
|
|
5963
|
+
desc,
|
|
5946
5964
|
])
|
|
5947
5965
|
distinct = len({p.id for p in items})
|
|
5948
5966
|
title = Text()
|
|
@@ -5952,7 +5970,7 @@ def render_policies(items: Sequence[Any]) -> None:
|
|
|
5952
5970
|
if len(rows) != distinct:
|
|
5953
5971
|
title.append(" · ", style=theme.FAINT)
|
|
5954
5972
|
title.append(f"{len(rows)} versions", style=theme.LABEL)
|
|
5955
|
-
render_list_panel("policies", header=["policy", "version", "state", "description"],
|
|
5973
|
+
render_list_panel("policies", header=["policy", "version", "kind", "state", "description"],
|
|
5956
5974
|
rows=rows, days=set(), order=None,
|
|
5957
5975
|
empty_message="no policies published — `fp policies publish <id> <file>`",
|
|
5958
5976
|
last_col="ellipsis", title=title)
|
|
@@ -5974,17 +5992,41 @@ def render_policy_published(p: Any, *, carriers: Optional[dict] = None,
|
|
|
5974
5992
|
"""
|
|
5975
5993
|
line1 = Text(p.id, style=f"bold {theme.TEXT}")
|
|
5976
5994
|
line1.append(f" v{p.version}", style=f"bold {theme.ACCENT}")
|
|
5995
|
+
kind = getattr(p, "kind", None) or "regex"
|
|
5996
|
+
line1.append(f" {kind}", style=_KIND_STYLE.get(kind, theme.TEXT_DIM))
|
|
5977
5997
|
body = [line1]
|
|
5978
5998
|
if p.description:
|
|
5979
5999
|
body.append(Text(p.description, style=theme.TEXT))
|
|
6000
|
+
names = list(getattr(p, "jev_names", None) or [])
|
|
6001
|
+
if names:
|
|
6002
|
+
checks = Text("jev checks ", style=theme.LABEL)
|
|
6003
|
+
checks.append(", ".join(names), style=theme.ACCENT)
|
|
6004
|
+
chars = getattr(p, "jev_chars", None)
|
|
6005
|
+
if chars is not None:
|
|
6006
|
+
# What these checks take of the ONE Jev question budget a machine
|
|
6007
|
+
# has for every check it asks — Cloud's and its installed packs'.
|
|
6008
|
+
checks.append(" · ", style=theme.FAINT)
|
|
6009
|
+
checks.append(f"{chars:,} question chars", style=theme.LABEL)
|
|
6010
|
+
body.append(checks)
|
|
6011
|
+
if kind == "both":
|
|
6012
|
+
# Derived by the server from the kind, never sent: said so the author
|
|
6013
|
+
# knows what the JavaScript's verdict now is.
|
|
6014
|
+
body.append(Text("its JavaScript is reviewable by exactly these checks", style=theme.LABEL))
|
|
5980
6015
|
body.append(Text())
|
|
5981
6016
|
|
|
5982
6017
|
meta = Text()
|
|
5983
6018
|
if source_bytes:
|
|
5984
6019
|
meta.append(f"{source_bytes:,} bytes", style=theme.LABEL)
|
|
5985
6020
|
meta.append(" · ", style=theme.FAINT)
|
|
5986
|
-
|
|
5987
|
-
|
|
6021
|
+
if p.sha256:
|
|
6022
|
+
meta.append("sha256 ", style=theme.LABEL)
|
|
6023
|
+
meta.append((p.sha256 or "")[:12] + "…", style=theme.TEXT_DIM)
|
|
6024
|
+
semantic_sha = getattr(p, "semantic_sha256", None)
|
|
6025
|
+
if semantic_sha:
|
|
6026
|
+
if p.sha256:
|
|
6027
|
+
meta.append(" · ", style=theme.FAINT)
|
|
6028
|
+
meta.append("jev sha256 ", style=theme.LABEL)
|
|
6029
|
+
meta.append(semantic_sha[:12] + "…", style=theme.TEXT_DIM)
|
|
5988
6030
|
body.append(meta)
|
|
5989
6031
|
body.append(Text())
|
|
5990
6032
|
|
|
@@ -6053,8 +6095,7 @@ def render_fleet(machines: Sequence[Any]) -> None:
|
|
|
6053
6095
|
Text(seen or "never", style=theme.FAINT if stale else theme.TEXT_DIM),
|
|
6054
6096
|
Text(f"{m.event_count:,}" if m.event_count else "—",
|
|
6055
6097
|
style=theme.TEXT_DIM if m.event_count else theme.FAINT),
|
|
6056
|
-
|
|
6057
|
-
style=theme.AMBER if m.drifted else (theme.SUCCESS if m.deployed else theme.FAINT)),
|
|
6098
|
+
_machine_state(m),
|
|
6058
6099
|
])
|
|
6059
6100
|
title = Text()
|
|
6060
6101
|
title.append("fleet", style=f"bold {theme.ACCENT}")
|
|
@@ -6067,6 +6108,18 @@ def render_fleet(machines: Sequence[Any]) -> None:
|
|
|
6067
6108
|
empty_message="no machines have checked in yet", title=title)
|
|
6068
6109
|
|
|
6069
6110
|
|
|
6111
|
+
def _machine_state(m: Any) -> Text:
|
|
6112
|
+
"""`fp fleet list`'s state cell. Reported policy errors outrank drift: a
|
|
6113
|
+
machine that says it cannot apply part of its set is not enforcing it,
|
|
6114
|
+
whichever generation it is on."""
|
|
6115
|
+
errors = getattr(m, "policy_errors", None) or []
|
|
6116
|
+
if errors:
|
|
6117
|
+
n = len(errors)
|
|
6118
|
+
return Text(f"{n} error{'s' if n != 1 else ''}", style=theme.ERROR)
|
|
6119
|
+
return Text("drifted" if m.drifted else ("ok" if m.deployed else "—"),
|
|
6120
|
+
style=theme.AMBER if m.drifted else (theme.SUCCESS if m.deployed else theme.FAINT))
|
|
6121
|
+
|
|
6122
|
+
|
|
6070
6123
|
def _compact_age(ms: Optional[int]) -> str:
|
|
6071
6124
|
"""`5h`, `7d`, `just now` — the column form of `_epoch_age`.
|
|
6072
6125
|
|
|
@@ -6097,7 +6150,33 @@ def _epoch_age(ms: Optional[int]) -> str:
|
|
|
6097
6150
|
return _relative_age(datetime.fromtimestamp(ms / 1000, tz=timezone.utc).isoformat())
|
|
6098
6151
|
|
|
6099
6152
|
|
|
6100
|
-
def
|
|
6153
|
+
def _agent_target_label(targets: Any, labels: Optional[Dict[str, str]] = None,
|
|
6154
|
+
*, with_id: bool = False) -> str:
|
|
6155
|
+
"""One assignment's scope, unscoped only when the field is absent.
|
|
6156
|
+
|
|
6157
|
+
``labels`` maps an exact profile's ``agt_…`` id to the profile label the
|
|
6158
|
+
machine reported (`.claude-work`), so the target reads as the profile a
|
|
6159
|
+
person recognises. ``with_id`` keeps the id beside it, for the views a
|
|
6160
|
+
selector is copied from. An id with no label stays the bare id.
|
|
6161
|
+
"""
|
|
6162
|
+
if not targets:
|
|
6163
|
+
return "all agents"
|
|
6164
|
+
labels = labels or {}
|
|
6165
|
+
|
|
6166
|
+
def one(target: Dict[str, Any]) -> str:
|
|
6167
|
+
instance = target.get("instanceId")
|
|
6168
|
+
if not instance:
|
|
6169
|
+
return f"all {target['integration']}"
|
|
6170
|
+
name = labels.get(instance)
|
|
6171
|
+
if not name:
|
|
6172
|
+
return f"{target['integration']}/{instance}"
|
|
6173
|
+
return f"{target['integration']}/{name}" + (f" ({instance})" if with_id else "")
|
|
6174
|
+
|
|
6175
|
+
return ", ".join(one(target) for target in targets)
|
|
6176
|
+
|
|
6177
|
+
|
|
6178
|
+
def render_machine_policies(machine_id: str, dep: Any, machine: Any = None,
|
|
6179
|
+
agent_labels: Optional[Dict[str, str]] = None) -> None:
|
|
6101
6180
|
"""``fp fleet show`` — what a machine is told to run, and whether it has it.
|
|
6102
6181
|
|
|
6103
6182
|
The first version printed the id, the generation and the policy list, which
|
|
@@ -6148,6 +6227,27 @@ def render_machine_policies(machine_id: str, dep: Any, machine: Any = None) -> N
|
|
|
6148
6227
|
act.append(f" · {machine.event_count} events", style=theme.LABEL)
|
|
6149
6228
|
field("last seen", act)
|
|
6150
6229
|
|
|
6230
|
+
mode = (getattr(dep, "jev_mode", None) if dep is not None else None) or (
|
|
6231
|
+
getattr(machine, "jev_mode", None) if machine is not None else None
|
|
6232
|
+
)
|
|
6233
|
+
jev = Text(mode or "local", style=theme.ACCENT if mode else theme.TEXT_DIM)
|
|
6234
|
+
jev.append(" · set by FailproofAI Cloud" if mode else " · the machine's own", style=theme.LABEL)
|
|
6235
|
+
field("jev mode", jev)
|
|
6236
|
+
|
|
6237
|
+
errors = (getattr(machine, "policy_errors", None) or []) if machine is not None else []
|
|
6238
|
+
if errors:
|
|
6239
|
+
body.append(Text())
|
|
6240
|
+
head = Text(f"{len(errors)} policy error{'s' if len(errors) != 1 else ''}", style=f"bold {theme.ERROR}")
|
|
6241
|
+
head.append(" · reported by the machine — these are not enforcing", style=theme.LABEL)
|
|
6242
|
+
body.append(head)
|
|
6243
|
+
for e in errors:
|
|
6244
|
+
line = Text(" ")
|
|
6245
|
+
ver = e.get("version")
|
|
6246
|
+
line.append(f"{e.get('id', '?')}{f' v{ver}' if ver is not None else ''}", style=theme.TEXT)
|
|
6247
|
+
line.append(f" {e.get('kind', '')}", style=theme.FAINT)
|
|
6248
|
+
line.append(f" {e.get('message', '')}", style=theme.TEXT_DIM)
|
|
6249
|
+
body.append(line)
|
|
6250
|
+
|
|
6151
6251
|
body.append(Text())
|
|
6152
6252
|
pols = sorted(dep.policies, key=lambda x: x.id) if dep is not None else []
|
|
6153
6253
|
if pols:
|
|
@@ -6156,13 +6256,15 @@ def render_machine_policies(machine_id: str, dep: Any, machine: Any = None) -> N
|
|
|
6156
6256
|
# padded to the header's width — otherwise the effect column steps left
|
|
6157
6257
|
# by one on every row and the table reads as misaligned.
|
|
6158
6258
|
vwidth = max(3, max(len(f"v{p.version}") for p in pols))
|
|
6159
|
-
head = Text(f" {'policy'.ljust(width)} {'ver'.ljust(vwidth)} effect", style=theme.LABEL)
|
|
6259
|
+
head = Text(f" {'policy'.ljust(width)} {'ver'.ljust(vwidth)} effect agents", style=theme.LABEL)
|
|
6160
6260
|
body.append(head)
|
|
6161
6261
|
for p in pols:
|
|
6162
6262
|
row = Text(" ")
|
|
6163
6263
|
row.append(p.id.ljust(width), style=theme.TEXT)
|
|
6164
6264
|
row.append(f" {f'v{p.version}'.ljust(vwidth)} ", style=theme.TEXT_DIM)
|
|
6165
6265
|
row.append_text(_effect(p.effect))
|
|
6266
|
+
row.append(" " + _agent_target_label(getattr(p, "agent_targets", None), agent_labels,
|
|
6267
|
+
with_id=True), style=theme.TEXT_DIM)
|
|
6166
6268
|
body.append(row)
|
|
6167
6269
|
else:
|
|
6168
6270
|
body.append(Text(" no policies deployed", style=theme.FAINT))
|
|
@@ -6183,18 +6285,24 @@ def render_deploy_plan(plan: Any, *, applied: bool = False) -> None:
|
|
|
6183
6285
|
lines = []
|
|
6184
6286
|
for p in plan.added:
|
|
6185
6287
|
t = Text(" + ", style=theme.SUCCESS); t.append_text(_policy_cell(p))
|
|
6186
|
-
t.append(" "); t.append_text(_effect(p.effect))
|
|
6288
|
+
t.append(" "); t.append_text(_effect(p.effect))
|
|
6289
|
+
t.append(" " + _agent_target_label(getattr(p, "agent_targets", None)), style=theme.TEXT_DIM)
|
|
6290
|
+
lines.append(t)
|
|
6187
6291
|
for was, now in plan.changed:
|
|
6188
6292
|
t = Text(" ~ ", style=theme.AMBER); t.append_text(_policy_cell(now))
|
|
6189
6293
|
t.append(" "); t.append_text(_effect(now.effect))
|
|
6190
|
-
t.append(
|
|
6294
|
+
t.append(" " + _agent_target_label(getattr(now, "agent_targets", None)), style=theme.TEXT_DIM)
|
|
6295
|
+
t.append(f" (was v{was.version} {was.effect}; {_agent_target_label(getattr(was, 'agent_targets', None))})", style=theme.FAINT)
|
|
6296
|
+
lines.append(t)
|
|
6191
6297
|
for p in plan.removed:
|
|
6192
6298
|
t = Text(" - ", style=theme.ERROR)
|
|
6193
6299
|
t.append(p.id, style=theme.TEXT_DIM); t.append(f" v{p.version}", style=theme.FAINT)
|
|
6194
6300
|
lines.append(t)
|
|
6195
6301
|
for p in plan.unchanged:
|
|
6196
6302
|
t = Text(" = ", style=theme.FAINT); t.append_text(_policy_cell(p))
|
|
6197
|
-
t.append(" "); t.append_text(_effect(p.effect))
|
|
6303
|
+
t.append(" "); t.append_text(_effect(p.effect))
|
|
6304
|
+
t.append(" " + _agent_target_label(getattr(p, "agent_targets", None)), style=theme.TEXT_DIM)
|
|
6305
|
+
lines.append(t)
|
|
6198
6306
|
if not lines:
|
|
6199
6307
|
lines = [Text(" (no policies)", style=theme.FAINT)]
|
|
6200
6308
|
|
|
@@ -6204,6 +6312,16 @@ def render_deploy_plan(plan: Any, *, applied: bool = False) -> None:
|
|
|
6204
6312
|
footer.append(f"polic{'y' if n == 1 else 'ies'} after this change", style=theme.LABEL)
|
|
6205
6313
|
lines.append(Text())
|
|
6206
6314
|
lines.append(footer)
|
|
6315
|
+
if getattr(plan, "jev_mode", None) is not None:
|
|
6316
|
+
before = plan.jev_mode_before or "local"
|
|
6317
|
+
after = plan.jev_mode_after or "local"
|
|
6318
|
+
jev = Text(" jev mode ", style=theme.LABEL)
|
|
6319
|
+
if plan.jev_mode_changes:
|
|
6320
|
+
jev.append(f"{before} → ", style=theme.FAINT)
|
|
6321
|
+
jev.append(after, style=f"bold {theme.ACCENT}")
|
|
6322
|
+
else:
|
|
6323
|
+
jev.append(f"{after} (unchanged)", style=theme.TEXT_DIM)
|
|
6324
|
+
lines.append(jev)
|
|
6207
6325
|
|
|
6208
6326
|
head = Text(plan.machine_id, style=f"bold {theme.TEXT}")
|
|
6209
6327
|
if plan.base is not None:
|
|
@@ -6333,7 +6451,18 @@ def deployment_applied(machine_id: str, generation: int, count: int) -> None:
|
|
|
6333
6451
|
_notice_box(body, color=theme.SUCCESS, title="deployed")
|
|
6334
6452
|
|
|
6335
6453
|
|
|
6336
|
-
def deployment_rolled_back(
|
|
6454
|
+
def deployment_rolled_back(
|
|
6455
|
+
machine_id: str,
|
|
6456
|
+
restored: int,
|
|
6457
|
+
generation: int,
|
|
6458
|
+
*,
|
|
6459
|
+
jev_from: Optional[str] = None,
|
|
6460
|
+
jev_to: Optional[str] = None,
|
|
6461
|
+
) -> None:
|
|
6462
|
+
"""``✓ restored the set from #R on <machine> · minted as #G``, plus the Jev
|
|
6463
|
+
mode the rollback brought back when it changed — a rollback restores the
|
|
6464
|
+
generation's mode too, and saying nothing is how `off` silently became
|
|
6465
|
+
`enforce` again."""
|
|
6337
6466
|
body = Text()
|
|
6338
6467
|
body.append("✓ ", style=theme.SUCCESS)
|
|
6339
6468
|
body.append("restored the set from ", style=theme.TEXT)
|
|
@@ -6342,9 +6471,76 @@ def deployment_rolled_back(machine_id: str, restored: int, generation: int) -> N
|
|
|
6342
6471
|
body.append(machine_id, style=theme.ACCENT)
|
|
6343
6472
|
body.append(" · ", style=theme.FAINT)
|
|
6344
6473
|
body.append(f"minted as deployment #{generation}", style=theme.LABEL)
|
|
6474
|
+
if jev_from is not None and jev_to is not None:
|
|
6475
|
+
body.append(" · ", style=theme.FAINT)
|
|
6476
|
+
if jev_from != jev_to:
|
|
6477
|
+
body.append("jev mode ", style=theme.LABEL)
|
|
6478
|
+
body.append(f"{jev_from} → ", style=theme.FAINT)
|
|
6479
|
+
body.append(jev_to, style=f"bold {theme.AMBER}")
|
|
6480
|
+
else:
|
|
6481
|
+
body.append(f"jev mode {jev_to} (unchanged)", style=theme.LABEL)
|
|
6345
6482
|
_notice_box(body, color=theme.SUCCESS, title="rolled back")
|
|
6346
6483
|
|
|
6347
6484
|
|
|
6485
|
+
def render_jev_mode_plan(rows: Sequence[dict]) -> None:
|
|
6486
|
+
"""The mode-only change, per machine: ``from → to``, the set untouched."""
|
|
6487
|
+
out = []
|
|
6488
|
+
for r in rows:
|
|
6489
|
+
changed = bool(r.get("changed"))
|
|
6490
|
+
mode = Text()
|
|
6491
|
+
if changed:
|
|
6492
|
+
mode.append(f"{r.get('from')} → ", style=theme.FAINT)
|
|
6493
|
+
mode.append(str(r.get("to")), style=f"bold {theme.ACCENT}")
|
|
6494
|
+
else:
|
|
6495
|
+
mode.append(f"{r.get('to')} (unchanged)", style=theme.TEXT_DIM)
|
|
6496
|
+
out.append([Text(str(r.get("machineId")), style=theme.TEXT), mode])
|
|
6497
|
+
n = sum(1 for r in rows if r.get("changed"))
|
|
6498
|
+
title = Text()
|
|
6499
|
+
title.append("jev mode", style=f"bold {theme.ACCENT}")
|
|
6500
|
+
title.append(" · ", style=theme.FAINT)
|
|
6501
|
+
title.append(f"{n} of {len(rows)} to change", style=theme.LABEL)
|
|
6502
|
+
title.append(" · policy sets untouched", style=theme.FAINT)
|
|
6503
|
+
render_list_panel("jev mode", header=["machine", "jev mode"], rows=out, days=set(), order=None,
|
|
6504
|
+
empty_message="no machines", title=title)
|
|
6505
|
+
|
|
6506
|
+
|
|
6507
|
+
def jev_mode_applied(rows: Sequence[dict]) -> None:
|
|
6508
|
+
"""``✓ jev mode observe → enforce on N machines``, and any that failed."""
|
|
6509
|
+
done = [r for r in rows if r.get("changed") and not r.get("error")]
|
|
6510
|
+
failed = [r for r in rows if r.get("error")]
|
|
6511
|
+
for r in done:
|
|
6512
|
+
body = Text()
|
|
6513
|
+
body.append("✓ ", style=theme.SUCCESS)
|
|
6514
|
+
body.append("jev mode ", style=theme.TEXT)
|
|
6515
|
+
body.append(f"{r.get('from')} → ", style=theme.FAINT)
|
|
6516
|
+
body.append(str(r.get("to")), style=f"bold {theme.ACCENT}")
|
|
6517
|
+
body.append(" on ", style=theme.TEXT)
|
|
6518
|
+
body.append(str(r.get("machineId")), style=theme.ACCENT)
|
|
6519
|
+
if r.get("deployment") is not None:
|
|
6520
|
+
body.append(" · ", style=theme.FAINT)
|
|
6521
|
+
body.append(f"now on deployment #{r.get('deployment')}", style=theme.LABEL)
|
|
6522
|
+
_notice_box(body, color=theme.SUCCESS, title="jev mode")
|
|
6523
|
+
for r in failed:
|
|
6524
|
+
body = Text()
|
|
6525
|
+
body.append("✗ ", style=theme.ERROR)
|
|
6526
|
+
body.append(str(r.get("machineId")), style=theme.ACCENT)
|
|
6527
|
+
body.append(" · ", style=theme.FAINT)
|
|
6528
|
+
body.append(str(r.get("error")), style=theme.TEXT)
|
|
6529
|
+
_notice_box(body, color=theme.ERROR, title="jev mode not changed")
|
|
6530
|
+
|
|
6531
|
+
|
|
6532
|
+
def jev_mode_unchanged(machine_ids: Sequence[str], mode: str) -> None:
|
|
6533
|
+
"""Every machine already has the mode. Calm, like a no-op deploy."""
|
|
6534
|
+
body = Text()
|
|
6535
|
+
body.append("= ", style=theme.FAINT)
|
|
6536
|
+
body.append(", ".join(machine_ids) if len(machine_ids) <= 3 else f"{len(machine_ids)} machines",
|
|
6537
|
+
style=theme.ACCENT)
|
|
6538
|
+
body.append(f" already {'has' if len(machine_ids) == 1 else 'have'} jev mode {mode}", style=theme.TEXT)
|
|
6539
|
+
body.append(" · ", style=theme.FAINT)
|
|
6540
|
+
body.append("nothing changed", style=theme.LABEL)
|
|
6541
|
+
_notice_box(body, color=theme.ACCENT, title="no change")
|
|
6542
|
+
|
|
6543
|
+
|
|
6348
6544
|
def machine_renamed(machine_id: str, label: str) -> None:
|
|
6349
6545
|
body = Text()
|
|
6350
6546
|
body.append("✓ ", style=theme.SUCCESS)
|
|
@@ -6581,7 +6777,8 @@ def policy_published_brief(p: Any) -> None:
|
|
|
6581
6777
|
_notice_box(body, color=theme.SUCCESS, title="published")
|
|
6582
6778
|
|
|
6583
6779
|
|
|
6584
|
-
def render_deployment_history(machine_id: str, entries: Sequence[dict]
|
|
6780
|
+
def render_deployment_history(machine_id: str, entries: Sequence[dict],
|
|
6781
|
+
agent_labels: Optional[Dict[str, str]] = None) -> None:
|
|
6585
6782
|
"""``fp fleet history`` — one row per generation, newest first.
|
|
6586
6783
|
|
|
6587
6784
|
Was a bare `print` per line, which put an unaligned wall of timestamps and
|
|
@@ -6595,6 +6792,7 @@ def render_deployment_history(machine_id: str, entries: Sequence[dict]) -> None:
|
|
|
6595
6792
|
"""
|
|
6596
6793
|
rows = []
|
|
6597
6794
|
prev = None
|
|
6795
|
+
prev_mode = "local"
|
|
6598
6796
|
# oldest first so each row can be diffed against the one before it, then
|
|
6599
6797
|
# reversed for display — newest first is how you read a history.
|
|
6600
6798
|
ordered = sorted(entries, key=lambda e: e.get("deployment") or 0)
|
|
@@ -6605,17 +6803,28 @@ def render_deployment_history(machine_id: str, entries: Sequence[dict]) -> None:
|
|
|
6605
6803
|
# that STOPPED BLOCKING, and it rendered as "no change". It also split a
|
|
6606
6804
|
# version bump into a "+x" and a "-x" for the same policy, which reads
|
|
6607
6805
|
# as removed-and-re-added rather than moved.
|
|
6608
|
-
cur = {p.get("id"): (p.get("version"), p.get("effect")
|
|
6806
|
+
cur = {p.get("id"): (p.get("version"), p.get("effect"),
|
|
6807
|
+
tuple(sorted(
|
|
6808
|
+
(t.get("integration") or "", t.get("instanceId") or "")
|
|
6809
|
+
for t in (p.get("agentTargets") or [])
|
|
6810
|
+
)))
|
|
6609
6811
|
for p in (e.get("policies") or [])}
|
|
6812
|
+
scope = {p.get("id"): _agent_target_label(p.get("agentTargets"), agent_labels)
|
|
6813
|
+
for p in (e.get("policies") or [])}
|
|
6814
|
+
mode = e.get("jevMode") or "local"
|
|
6610
6815
|
if prev is None:
|
|
6611
|
-
diffs[e.get("deployment")] = [("+", i) for i in sorted(cur)]
|
|
6816
|
+
diffs[e.get("deployment")] = [("+", f"{i} → {scope[i]}") for i in sorted(cur)]
|
|
6612
6817
|
else:
|
|
6613
6818
|
diffs[e.get("deployment")] = (
|
|
6614
|
-
[("+", i) for i in sorted(set(cur) - set(prev))]
|
|
6819
|
+
[("+", f"{i} → {scope[i]}") for i in sorted(set(cur) - set(prev))]
|
|
6615
6820
|
+ [("-", i) for i in sorted(set(prev) - set(cur))]
|
|
6616
|
-
+ [("~", i) for i in sorted(set(cur) & set(prev)) if cur[i] != prev[i]]
|
|
6821
|
+
+ [("~", f"{i} → {scope[i]}") for i in sorted(set(cur) & set(prev)) if cur[i] != prev[i]]
|
|
6822
|
+
# A mode-only generation (`fp fleet jev-mode`) read as "no
|
|
6823
|
+
# change", which is exactly the change a rollback brings back.
|
|
6824
|
+
+ ([("~", f"jev {prev_mode}→{mode}")] if mode != prev_mode else [])
|
|
6617
6825
|
)
|
|
6618
6826
|
prev = cur
|
|
6827
|
+
prev_mode = mode
|
|
6619
6828
|
|
|
6620
6829
|
newest_first = sorted(entries, key=lambda e: e.get("deployment") or 0, reverse=True)
|
|
6621
6830
|
# The shared time column: clock time, with the date folded in only when the
|
|
@@ -6636,10 +6845,14 @@ def render_deployment_history(machine_id: str, entries: Sequence[dict]) -> None:
|
|
|
6636
6845
|
change.append(ref, style=theme.TEXT_DIM)
|
|
6637
6846
|
if not change.plain:
|
|
6638
6847
|
change = Text("no change", style=theme.FAINT)
|
|
6848
|
+
mode = e.get("jevMode") or "local"
|
|
6639
6849
|
rows.append([
|
|
6640
6850
|
Text(f"#{gen}", style=theme.TEXT),
|
|
6641
6851
|
Text(tcell or (e.get("updatedAt", "") or "-"), style=theme.TEXT_DIM),
|
|
6642
6852
|
Text(str(len(pols)), style=theme.TEXT_DIM if pols else theme.FAINT),
|
|
6853
|
+
# The mode FailproofAI Cloud set with this generation — what a
|
|
6854
|
+
# rollback to it restores.
|
|
6855
|
+
Text(mode, style=theme.FAINT if mode == "local" else theme.TEXT),
|
|
6643
6856
|
change,
|
|
6644
6857
|
Text(", ".join(pols) or "(none)", style=theme.TEXT_DIM if pols else theme.FAINT),
|
|
6645
6858
|
])
|
|
@@ -6647,9 +6860,34 @@ def render_deployment_history(machine_id: str, entries: Sequence[dict]) -> None:
|
|
|
6647
6860
|
title.append(machine_id, style=f"bold {theme.ACCENT}")
|
|
6648
6861
|
title.append(" · ", style=theme.FAINT)
|
|
6649
6862
|
title.append(f"{len(rows)} generations", style=theme.LABEL)
|
|
6650
|
-
|
|
6863
|
+
header = ["gen", "when", "n", "jev", "change", "policies"]
|
|
6864
|
+
# Fit the panel to the terminal by giving up the expendable columns first.
|
|
6865
|
+
# Every cell is no_wrap, so a table wider than the terminal had Rich shave
|
|
6866
|
+
# ALL columns at once — and at 80 columns `gen`, `when` and `jev` were the
|
|
6867
|
+
# ones that vanished, though `gen` is the argument `fp fleet rollback` takes.
|
|
6868
|
+
# `policies` goes first (`n` still counts it, `change` says what moved),
|
|
6869
|
+
# then `change` wraps rather than squeezing the four short columns.
|
|
6870
|
+
last_col, last_col_max = "ellipsis", None
|
|
6871
|
+
if rows:
|
|
6872
|
+
def natural(i: int) -> int:
|
|
6873
|
+
return max([len(header[i])] + [r[i].cell_len for r in rows])
|
|
6874
|
+
# indent 2 + border 2 + panel padding 2, two spaces between columns,
|
|
6875
|
+
# and a little slack for Rich's rounding.
|
|
6876
|
+
chrome = 6 + 2
|
|
6877
|
+
fixed = sum(natural(i) for i in range(4)) + 2 * 4 + chrome
|
|
6878
|
+
room = _stdout.width - fixed
|
|
6879
|
+
change_w, policies_w = natural(4), natural(5)
|
|
6880
|
+
if change_w + 2 + policies_w > room:
|
|
6881
|
+
if room - change_w - 2 >= 12:
|
|
6882
|
+
last_col_max = room - change_w - 2
|
|
6883
|
+
else:
|
|
6884
|
+
header = header[:5]
|
|
6885
|
+
rows = [r[:5] for r in rows]
|
|
6886
|
+
last_col = "wrap"
|
|
6887
|
+
render_list_panel("history", header=header,
|
|
6651
6888
|
rows=rows, days=days, order=None,
|
|
6652
|
-
empty_message="no deployment history", last_col=
|
|
6889
|
+
empty_message="no deployment history", last_col=last_col,
|
|
6890
|
+
last_col_max=last_col_max, title=title)
|
|
6653
6891
|
|
|
6654
6892
|
|
|
6655
6893
|
def render_fleet_diff(rows: Sequence[dict]) -> None:
|
|
@@ -446,14 +446,17 @@ def cloud_publish_problem(source: str) -> Optional[str]:
|
|
|
446
446
|
if not source or len(source) > _MAX_SCAN:
|
|
447
447
|
return None
|
|
448
448
|
if _find_call(source, _SEMANTIC_CALL) != -1:
|
|
449
|
-
return ("semanticPolicies.add does nothing in a cloud policy: Jev reads semantic
|
|
450
|
-
"from a failproofai pack
|
|
451
|
-
"`
|
|
449
|
+
return ("semanticPolicies.add does nothing in a cloud policy's JavaScript: Jev reads semantic "
|
|
450
|
+
"checks only from a failproofai pack or from the policy's own Jev declarations. Put the "
|
|
451
|
+
"check in a JSON file and publish with `--kind both --semantic checks.json` (or `--kind "
|
|
452
|
+
"jev`), or ship the file with `failproofai publish`.")
|
|
452
453
|
# Every registration: any of them can carry a reviewable declaration.
|
|
453
454
|
at = _find_call(source, _ADD_CALL)
|
|
454
455
|
while at != -1:
|
|
455
456
|
if _direct_props(source, at).get("authority") == "reviewable":
|
|
456
|
-
return ("a cloud policy is always hard
|
|
457
|
-
"
|
|
457
|
+
return ("a cloud policy's JavaScript is always hard as written: its authority comes from "
|
|
458
|
+
"its kind, never from the source, so authority/reviewedBy here are ignored. Remove "
|
|
459
|
+
"them, and publish with `--kind both --semantic checks.json` to make it reviewable "
|
|
460
|
+
"by its own Jev checks.")
|
|
458
461
|
at = _find_call(source, _ADD_CALL, at + len(_ADD))
|
|
459
462
|
return None
|
|
@@ -196,8 +196,8 @@ you need a flag you don't already know.
|
|
|
196
196
|
- `audits list|show|create|edit|delete|run|runs` — scheduled sweeps, referenced by **name**; `audits findings|finding` + the triage verbs `ack|mute|dismiss|resolve|reopen|assign` act on a finding **id**. `audits run <name>` only *queues* a run (poll `audits runs <name>` for completion). See §8.
|
|
197
197
|
|
|
198
198
|
**Enforce (cloud-managed policy, session-only — see §2):**
|
|
199
|
-
- `policies list|show|publish|enable|disable|delete|test|compose` — policy versions. `publish` mints a version
|
|
200
|
-
- `fleet list|show|deploy|diff|history|rollback|rename` — which machines run which policies. **`deploy` REPLACES a machine's whole set** (`--add`/`--remove` amend it, `--set` replaces, `--create` mints a deployment); it prints the plan and asks **only on an interactive terminal without `--json`** — under `--json` or with stdin redirected it applies immediately, so read `fleet show` first if you want review.
|
|
199
|
+
- `policies list|show|publish|enable|disable|delete|test|compose` — policy versions. `publish` mints a version of a kind — `regex` (a local `.mjs`), `jev` (`--semantic checks.json`: Jev declarations, no JavaScript) or `both` (the two, the JavaScript reviewable by its own checks); list/show print the kind and the Jev check names; `test` runs one against a synthetic context locally (it applies each policy's `match` filter, so a policy that does not cover the `--event`/`--tool` you pass is reported `skipped`, not run). `enable`/`disable`/`delete` take `--yes`.
|
|
200
|
+
- `fleet list|show|deploy|jev-mode|diff|history|rollback|rename` — which machines run which policies, and the policy errors each machine reports. **`deploy` REPLACES a machine's whole set** (`--add`/`--remove` amend it, `--set` replaces, `--create` mints a deployment, `--jev-mode off|observe|enforce|local` sets the machine's Jev mode from FailproofAI Cloud — overriding its own, machine-wide, and left alone when omitted; in `enforce` Jev's checks BLOCK calls as well as clear). `jev-mode <machine…|--all> <mode>` changes only the mode, never the set; `rollback` restores the generation's Jev mode too; it prints the plan and asks **only on an interactive terminal without `--json`** — under `--json` or with stdin redirected it applies immediately, so read `fleet show` first if you want review.
|
|
201
201
|
- `guardrails summary|timeline` — what enforcement actually did; `--since 1h|6h|24h|7d`, `--machine`.
|
|
202
202
|
|
|
203
203
|
**Analytics & assistant:**
|
|
@@ -220,22 +220,29 @@ Built-in assistant. Chats referenced by a **short chat-id** (first 8 hex; prefix
|
|
|
220
220
|
Cloud-managed enforcement, split the way the dashboard splits it: `policies` writes a version, `fleet` decides which machines run it, `guardrails` reports what it blocked. Needs `policies:read` to read, `policies:write` to change anything.
|
|
221
221
|
|
|
222
222
|
### policies
|
|
223
|
-
- `policies list` — one row per published VERSION (versions are immutable and all stay addressable), newest of each policy first; the title counts distinct policies and captions the version total. `state` is active / disabled / archived. JSON `{policies:[…]}` — also every version, so deduplicate on `id` for one row per policy
|
|
224
|
-
- `policies show <id>` — the NEWEST version,
|
|
225
|
-
- `policies publish <id> [SOURCE] [--description "…"] [--no-verify]` — mints a **new version**; never edits one.
|
|
223
|
+
- `policies list` — one row per published VERSION (versions are immutable and all stay addressable), newest of each policy first; the title counts distinct policies and captions the version total. `kind` is `regex` / `jev` / `both`, and a `jev`/`both` row names its Jev checks before the description. `state` is active / disabled / archived. JSON `{policies:[…]}` — also every version, so deduplicate on `id` for one row per policy; each carries `kind`, and for `jev`/`both` `semantic`, `semanticSha256`, and the server-derived `authority`/`reviewedBy`.
|
|
224
|
+
- `policies show <id>` — the NEWEST version: its kind, its Jev check names, the full `source`, and the Jev declarations.
|
|
225
|
+
- `policies publish <id> [SOURCE] [--kind regex|jev|both] [--source FILE] [--semantic FILE] [--description "…"] [--no-verify]` — mints a **new version**; never edits one. A policy's **kind** says what it carries:
|
|
226
|
+
- `regex` — JavaScript only (SOURCE or `--source`): what publish has always done, and the default.
|
|
227
|
+
- `jev` — Jev checks only (`--semantic checks.json`, no JavaScript): a JSON array (or `{"semantic": [...]}`) of 1–24 Jev declarations, each exactly a failproofai pack manifest's semantic entry — `{name, title, appliesTo:[shell|write|read|network|other], mode:deny|instruct, userCanOverride, probes:[{id, instructions, criteria:{true,false}}] (≤6), guidance, precondition?, exempt?}`.
|
|
228
|
+
- `both` — JavaScript plus its own Jev checks, deployed and toggled as one. FailproofAI Cloud makes the JavaScript's verdict `reviewable` by exactly its own checks (`authority`/`reviewedBy` are derived by the server, never sent).
|
|
229
|
+
|
|
230
|
+
Without `--kind` it follows from what you give (`--semantic` alone is `jev`, with source `both`); a kind that contradicts its inputs is **exit 2**, before anything is sent. `fp` checks only the shape of the file (a non-empty list of named objects, ≤24); the server checks the rest — the limits, FailproofAI's 16 reserved check names (**422 `reserved_jev_name`**), and names unique across the org — and names the declaration it refused. SOURCE is a path, `@path`, `-`, a pipe, or omitted to paste on a TTY (Ctrl-D ends); `--semantic` takes a path, `@path` or `-` (only one of the two may be stdin). The JavaScript is **parse-checked with node** before it is sent; nothing downstream does this, and a broken policy otherwise fails on the machine at enforcement time. `--no-verify` skips it, and a host without node publishes with a warning rather than a block. JavaScript carrying Jev fields — `semanticPolicies.add(...)`, or `authority: "reviewable"` on a registration — is refused before sending (**exit 1**, not skipped by `--no-verify`; commented-out code does not count): put Jev checks in `--semantic` (`--kind both`), where they take effect. **Publishing deploys nothing** — the version is unused until `fleet deploy` puts it on a machine.
|
|
226
231
|
- `policies enable <id>` · `policies disable <id> [-y]` — **disable REMOVES the policy from every deployment carrying it**, reissuing each affected machine at a new generation (visible in `fleet history`). `enable` is the exact inverse — it puts the policy back into every deployment it was removed from, reissuing those machines again. Nothing needs redeploying by hand, and `machinesUpdated` in the JSON reports the count for both directions.
|
|
227
232
|
- `policies test [SOURCE] [--tool Bash] [--command "…"] [--file PATH] [--event PreToolUse] [--expect allow|deny|instruct]` — run a policy LOCALLY and print what it decides. Executes the real file (bare `import { deny } from "failproofai"` and all) against a synthetic context; nothing is published, nothing installed. Needs `node`. `--expect` asserts the decision and exits 1 when it differs — a correct `deny` is a PASSING test, so the decision alone never sets the exit code. JSON `{ok, decision, policies:[{name,decision,reason}], expected, met}`; `decision` is the strictest any registered policy returned.
|
|
228
233
|
- `policies compose "<description>" [--out FILE] [--publish ID]` — the assistant drafts policy source from plain English. Prints it and stops by default: a generated policy that deploys itself is one nobody read. `--publish` still syntax-checks first. Needs `policies:write` for BOTH the draft and the publish — `POST /api/agent/compose-policy` is `withAuth("policies:write")`; `agent:use` gates the assistant's other routes and is not checked on this one. The composer has a **30s server-side limit** — a long or vague description simply does not finish, and raising `--timeout` does not help because the cut is not client-side. Retry with something shorter and more specific.
|
|
229
234
|
- `policies delete <id> [-y]` — archives. **A machine already carrying the policy keeps enforcing it** until redeployed; `disable` is what stops enforcement everywhere.
|
|
230
235
|
|
|
231
236
|
### fleet
|
|
232
|
-
- `fleet list` — `machine · label · pol · intended · applied · seen · events · state`. `intended` is the generation deployed, `applied` what the machine last collected (they differ until it polls), and `seen` how long since it last reported anything — a machine can be in sync and dead, or alive and behind, which are different problems. JSON `{machines, deployments}` with raw epoch-ms timestamps plus the computed `drifted
|
|
233
|
-
- `fleet show <machine>` — the set the machine is told to run, **and whether it has collected it**. Reads both the deployment and the machine record, so it reports `not yet collected` / `machine is on #N` / `collected` alongside who deployed it, when, and last-seen. A machine can be told to run a policy it has never picked up; the policy list alone cannot tell you which. JSON `{machine, deployment}` with raw timestamps and both label fields; `deployment: null` when nothing is deployed.
|
|
234
|
-
- `fleet deploy <machine> [--add REF]… [--remove ID]… [--set REF]… [--create] [-y]`
|
|
237
|
+
- `fleet list` — `machine · label · pol · intended · applied · seen · events · state`. `intended` is the generation deployed, `applied` what the machine last collected (they differ until it polls), and `seen` how long since it last reported anything — a machine can be in sync and dead, or alive and behind, which are different problems. `state` reads `N errors` when the machine reported policies it could not apply. JSON `{machines, deployments}` with raw epoch-ms timestamps plus the computed `drifted`; a machine carries `policyErrors`/`policyErrorsAt` and `jevMode` when the server has them.
|
|
238
|
+
- `fleet show <machine>` — the set the machine is told to run, **and whether it has collected it**. Reads both the deployment and the machine record, so it reports `not yet collected` / `machine is on #N` / `collected` alongside who deployed it, when, and last-seen. A machine can be told to run a policy it has never picked up; the policy list alone cannot tell you which. Also the **Jev mode** FailproofAI Cloud sets on it (`local` when none) and the **policy errors** the machine last reported — `[{id, version, kind: regex|jev|both|daemon, message}]`: a policy that did not load, `jev_unconfigured` (a Jev mode, and the machine's key does not carry Jev), `transcripts_disabled` (a Jev mode on a machine connected with `--no-transcripts`, which never asks), `jev_budget: dropped <name>` (an installed pack's check FailproofAI Cloud left out of a request for the question budget). Whatever is listed there is not enforcing. JSON `{machine, deployment}` with raw timestamps and both label fields; `deployment: null` when nothing is deployed.
|
|
239
|
+
- `fleet deploy <machine> [--add REF]… [--remove ID]… [--set REF]… [--jev-mode off|observe|enforce|local] [--create] [-y]`
|
|
235
240
|
|
|
236
241
|
**A deploy REPLACES the whole set.** The endpoint takes the full list and does not merge. `--add`/`--remove` are a read-modify-write: the CLI reads the current set, applies the delta, prints the complete result, writes that. `--set` replaces everything and is refused alongside `--add`/`--remove`.
|
|
237
242
|
|
|
238
|
-
REF is `id`, `id@version`, `id:effect`, or `id@version:effect`. Effect is `enforce` (default) or `observe`. A bare `--add` of an already-deployed policy **keeps its pinned version** — pass `id@version` to move it.
|
|
243
|
+
REF is `id`, `id@version`, `id:effect`, or `id@version:effect`. Effect is `enforce` (default) or `observe`. A bare `--add` of an already-deployed policy **keeps its pinned version** — pass `id@version` to move it. Every kind deploys the same way (a `both` policy's JavaScript and its checks go together); the effect applies to JavaScript only, so `observe` on a `jev` policy is refused (**exit 2**) — watch Jev through the mode instead — and an `observe` `both` policy sends only its (observed) JavaScript: its Jev checks are not asked while it is observed.
|
|
244
|
+
|
|
245
|
+
`--jev-mode` sets the machine's Jev mode from FailproofAI Cloud and **overrides the machine's own, a local `off` included**: `observe` asks Jev and logs it while the regex policies decide; `enforce` lets Jev's checks **block** calls (every `jev` policy's deny checks) as well as clear the regex verdicts they review; `off` stops Jev there; `local` hands the choice back to the machine. The mode is the **machine's**: it also governs the checks of every pack installed there. Omitted, it is left as it is (the request carries no `jevMode`). `--jev-mode` with no change to the set changes **only the mode**, through its own route (like `fleet jev-mode`), so disabled policies' assignments and any concurrent edit are untouched; the plan shows `jev mode <from> → <to>` and the JSON adds `jevModeOnly: true`. On a machine with no deployment yet, it deploys the (empty) set with the mode. Jev checks run on FailproofAI Cloud; nothing is installed on the machine. Under `observe`/`enforce` the machine sends each checked tool call to FailproofAI Cloud on its Cloud Jev credential (its `jev.json` is not used); without one it reports `jev_unconfigured`, and connected with `--no-transcripts` it never asks and reports `transcripts_disabled`.
|
|
239
246
|
|
|
240
247
|
Deploying to an id that has never checked in is refused (a typo would mint a machine); `--create` allows it for pre-staging.
|
|
241
248
|
|
|
@@ -245,9 +252,10 @@ Cloud-managed enforcement, split the way the dashboard splits it: `policies` wri
|
|
|
245
252
|
|
|
246
253
|
**Exit codes.** A malformed ref (`bad ref!!`, `id:banana`, empty), or `--set` combined with `--add`/`--remove`, is a usage error → **exit 2**, like every other bad flag value. A ref that parses but names something that does not exist (`--add ghost-policy`, an unpublished `@version`) is **exit 1**; an unknown machine is **exit 6**. Branch on these rather than on the message.
|
|
247
254
|
|
|
248
|
-
JSON `{plan:{result,added,removed,changed,unchanged,noop}, deployment, applied}` — the plan is included so a harness does not recompute the diff.
|
|
255
|
+
JSON `{plan:{result,added,removed,changed,unchanged,noop[,jevMode:{from,to,changed}]}, deployment, applied}` — the plan is included so a harness does not recompute the diff.
|
|
256
|
+
- `fleet jev-mode <machine>… <mode>` · `fleet jev-mode --all <mode> [-y]` — set the Jev mode FailproofAI Cloud gives machines (`off|observe|enforce|local`) and **nothing else**: the policy set is never read back or rewritten. A machine already on the mode is skipped; `--all` covers every machine with a deployment and skips the rest; a named machine with no deployment is refused before anything is written (`fleet deploy <machine> --jev-mode <mode>` gives it its first). Each change is a new generation in `fleet history`. One machine's failure does not stop the others; the exit code is 1 and the JSON row carries `error`. A bad mode, or `--all` with machine ids, is **exit 2**. JSON `{mode, applied, machines:[{machineId, from, to, changed, deployment?, error?}]}`.
|
|
249
257
|
- `fleet diff [machine]` — intent vs delivery per machine, with a `drifted` flag. A machine id nobody has reported under is refused (exit 6), not rendered as an empty fleet.
|
|
250
|
-
- `fleet history <machine>` · `fleet rollback <machine> <generation> [-y]` — rollback mints a NEW generation carrying the old set; history stays append-only. The `change` column uses the deploy plan's vocabulary: `+` added, `-` removed, `~` same policy at a different version or effect (an enforce → observe flip is a policy that stopped blocking, so it is never "no change").
|
|
258
|
+
- `fleet history <machine>` · `fleet rollback <machine> <generation> [-y]` — rollback mints a NEW generation carrying the old set; history stays append-only. The `change` column uses the deploy plan's vocabulary: `+` added, `-` removed, `~` same policy at a different version or effect (an enforce → observe flip is a policy that stopped blocking, so it is never "no change"), and `~ jev <from>→<to>` for a Jev mode change. The `jev` column is the mode FailproofAI Cloud set with each generation (`local` = none). **A rollback restores that generation's Jev mode too**, `local` included: the prompt and the result say `jev mode <now> → <then>`, and the JSON adds `jevModeBefore`.
|
|
251
259
|
- `fleet rename <machine> "<label>"` — a human label; the id never changes. The server stores it as an override beside the machine's self-asserted label. An empty label **clears** the override (the machine falls back to its own label, else its id) and the CLI says so rather than reporting a rename to nothing. A machine that has never checked in cannot be renamed → exit 6.
|
|
252
260
|
|
|
253
261
|
### guardrails
|