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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""The fleet: fleet list / show / deploy / diff / history / rollback / rename.
|
|
1
|
+
"""The fleet: fleet list / show / deploy / jev-mode / diff / history / rollback / rename.
|
|
2
2
|
|
|
3
3
|
What each machine is TOLD to enforce. Authoring the policies is `fp policies`;
|
|
4
4
|
what they actually did is `fp guardrails`.
|
|
@@ -26,14 +26,18 @@ from .. import output
|
|
|
26
26
|
from .._context import GLOBALS_EPILOG, AppState, deny_in_key_mode, require_auth
|
|
27
27
|
from .. import _click_compat as click # the Click Typer is running; see _click_compat
|
|
28
28
|
from ..enforcement import (
|
|
29
|
+
JEV_MODES,
|
|
29
30
|
RefError,
|
|
30
31
|
RefUsageError,
|
|
31
32
|
check_race,
|
|
32
33
|
disabled_ids,
|
|
34
|
+
jev_mode_after,
|
|
33
35
|
latest_versions,
|
|
34
36
|
plan_deploy,
|
|
37
|
+
version_kinds,
|
|
35
38
|
)
|
|
36
|
-
from ..errors import ApiError, NotFoundError
|
|
39
|
+
from ..errors import ApiError, AuthError, FpCliError, NotFoundError
|
|
40
|
+
from ..models import PolicyRef
|
|
37
41
|
from . import _write
|
|
38
42
|
|
|
39
43
|
_KEY_MODE_REASON = (
|
|
@@ -62,6 +66,8 @@ def fleet_list(ctx: typer.Context) -> None:
|
|
|
62
66
|
`intended` is the generation deployed, `applied` is the one the machine last
|
|
63
67
|
collected, and `seen` is when it last reported anything — a machine can be
|
|
64
68
|
in sync and dead, or alive and behind, and those are different problems.
|
|
69
|
+
`state` reads `N errors` when the machine reported policies it could not
|
|
70
|
+
apply (`fp fleet show <machine>` lists them).
|
|
65
71
|
|
|
66
72
|
A machine appears from its very first check-in, including the poll that
|
|
67
73
|
finds nothing deployed — that is exactly the machine you are usually looking
|
|
@@ -101,10 +107,22 @@ def fleet_show(
|
|
|
101
107
|
machine can be told to run a policy and not yet have it; the policy list
|
|
102
108
|
alone cannot tell you which, and that is usually the question.
|
|
103
109
|
|
|
110
|
+
Shows the Jev mode FailproofAI Cloud sets on the machine (`local` when it
|
|
111
|
+
sets none) and the policy errors the machine last reported: a policy it
|
|
112
|
+
could not load, a Jev mode it cannot act on (`jev_unconfigured`: its key
|
|
113
|
+
does not carry Jev; `transcripts_disabled`: it was connected with
|
|
114
|
+
`--no-transcripts`), FailproofAI Cloud Jev refusing or failing its calls
|
|
115
|
+
(`jev_rate_limited`: over the organization's Jev rate limit;
|
|
116
|
+
`jev_unavailable`: it skips a failing Cloud Jev for a minute at a time), an
|
|
117
|
+
installed pack's check left out for the question budget (`jev_budget`) or
|
|
118
|
+
because an organization Cloud check has its name (`jev_name_clash`).
|
|
119
|
+
Anything listed there is not enforcing, whatever the deployment says.
|
|
120
|
+
|
|
104
121
|
Needs `policies:read`. With `--json`: `{machine, deployment}` — the machine
|
|
105
|
-
record (including `appliedDeployment`, `drifted`, `lastSeen
|
|
106
|
-
fields,
|
|
107
|
-
nothing is
|
|
122
|
+
record (including `appliedDeployment`, `drifted`, `lastSeen`, both label
|
|
123
|
+
fields, `jevMode` and `policyErrors`/`policyErrorsAt` when reported, with raw
|
|
124
|
+
timestamps) and the deployment, or `deployment: null` when nothing is
|
|
125
|
+
deployed.
|
|
108
126
|
|
|
109
127
|
Example:
|
|
110
128
|
|
|
@@ -129,7 +147,8 @@ def fleet_show(
|
|
|
129
147
|
"deployment": dep.to_dict() if dep else None,
|
|
130
148
|
})
|
|
131
149
|
return
|
|
132
|
-
output.render_machine_policies(machine_id, dep, machine
|
|
150
|
+
output.render_machine_policies(machine_id, dep, machine,
|
|
151
|
+
agent_labels=api.agent_labels(cctx, machine_id))
|
|
133
152
|
|
|
134
153
|
|
|
135
154
|
def fleet_deploy(
|
|
@@ -137,17 +156,34 @@ def fleet_deploy(
|
|
|
137
156
|
machine_id: str = typer.Argument(..., help="Machine id."),
|
|
138
157
|
add: Optional[List[str]] = typer.Option(
|
|
139
158
|
None, "--add",
|
|
140
|
-
help="Add or update a policy: `id`, `id@version`, `id:effect` or `id@version:effect`."
|
|
159
|
+
help="Add or update a policy: `id`, `id@version`, `id:effect` or `id@version:effect`. "
|
|
160
|
+
"`observe` on a `both` policy observes its JavaScript and withholds its Jev checks.",
|
|
141
161
|
),
|
|
142
162
|
remove: Optional[List[str]] = typer.Option(None, "--remove", help="Remove a policy by id."),
|
|
143
163
|
replace: Optional[List[str]] = typer.Option(
|
|
144
164
|
None, "--set",
|
|
145
165
|
help="REPLACE the whole set with exactly these. Cannot be combined with --add/--remove.",
|
|
146
166
|
),
|
|
167
|
+
targets: Optional[List[str]] = typer.Option(
|
|
168
|
+
None, "--target",
|
|
169
|
+
help="Scope one assigned policy: POLICY=INTEGRATION or POLICY=INTEGRATION/agt_ID. Repeat to OR selectors.",
|
|
170
|
+
),
|
|
171
|
+
all_agents: Optional[List[str]] = typer.Option(
|
|
172
|
+
None, "--all-agents",
|
|
173
|
+
help="Clear one policy's targeting back to every agent on this machine.",
|
|
174
|
+
),
|
|
147
175
|
create: bool = typer.Option(
|
|
148
176
|
False, "--create",
|
|
149
177
|
help="Allow deploying to a machine id that has not checked in yet (pre-staging).",
|
|
150
178
|
),
|
|
179
|
+
jev_mode: Optional[str] = typer.Option(
|
|
180
|
+
None, "--jev-mode",
|
|
181
|
+
help="Set the machine's Jev mode from FailproofAI Cloud: off, observe, enforce (Jev's "
|
|
182
|
+
"checks BLOCK calls as well as clearing what they review) — or local to stop "
|
|
183
|
+
"overriding the machine's own. Jev checks run on FailproofAI Cloud; nothing is "
|
|
184
|
+
"installed on the machine. Machine-wide: installed packs' checks too. "
|
|
185
|
+
"Omitted: unchanged.",
|
|
186
|
+
),
|
|
151
187
|
yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirmation prompt. The prompt only appears on an interactive terminal: under --json, or with stdin redirected, this command proceeds without asking."),
|
|
152
188
|
) -> None:
|
|
153
189
|
"""Change what a machine enforces, showing the full resulting set first.
|
|
@@ -159,6 +195,24 @@ def fleet_deploy(
|
|
|
159
195
|
|
|
160
196
|
`--set` replaces everything — the only way to drop policies you do not name.
|
|
161
197
|
|
|
198
|
+
Any kind deploys the same way. Jev checks run on FailproofAI Cloud; nothing
|
|
199
|
+
is installed on the machine: a `both` policy sends the machine its
|
|
200
|
+
JavaScript, and the machine asks FailproofAI Cloud about each checked tool
|
|
201
|
+
call while its Jev mode is `observe` or `enforce`. An effect applies to
|
|
202
|
+
JavaScript only — an `observe` `both` policy observes its JavaScript and its
|
|
203
|
+
Jev checks are not asked — and a `jev` policy is watched through the Jev
|
|
204
|
+
MODE instead: `--jev-mode observe` asks Jev and logs what it says while the
|
|
205
|
+
regex policies decide; `enforce` lets Jev's checks BLOCK calls (every `jev`
|
|
206
|
+
policy's deny checks) as well as clear the regex verdicts they review; `off`
|
|
207
|
+
stops Jev on the machine; `local` hands the choice back to the machine. The
|
|
208
|
+
mode is the MACHINE's: it also governs the checks of any pack installed
|
|
209
|
+
there, and it overrides the machine's own mode and `jev.json`, a local
|
|
210
|
+
`off` included. Without `--jev-mode` it is left as it is.
|
|
211
|
+
|
|
212
|
+
`--jev-mode` with no change to the set changes ONLY the mode (the same as
|
|
213
|
+
`fp fleet jev-mode`): the policy set — disabled policies' assignments
|
|
214
|
+
included — is not read back and rewritten.
|
|
215
|
+
|
|
162
216
|
**Concurrency.** The write is a full replace with no server-side lock, so the
|
|
163
217
|
CLI records the generation it read and refuses if the result is not exactly
|
|
164
218
|
one higher: that means somebody else deployed in between and a replace does
|
|
@@ -170,17 +224,19 @@ def fleet_deploy(
|
|
|
170
224
|
* `fp fleet deploy ci-runner-01 --add no-force-push`
|
|
171
225
|
* `fp fleet deploy ci-runner-01 --add prod-guard@1:observe --remove old-rule`
|
|
172
226
|
* `fp fleet deploy ci-runner-01 --set no-force-push --set no-secret-echo`
|
|
227
|
+
* `fp fleet deploy ci-runner-01 --add prod-db-intent --jev-mode observe`
|
|
228
|
+
* `fp fleet deploy ci-runner-01 --jev-mode local`
|
|
173
229
|
"""
|
|
174
230
|
state: AppState = ctx.obj
|
|
175
231
|
deny_in_key_mode(state, "fleet deploy", _KEY_MODE_REASON)
|
|
176
232
|
cctx = require_auth(state)
|
|
177
233
|
|
|
178
|
-
if not add and not remove and replace is None:
|
|
234
|
+
if not add and not remove and replace is None and jev_mode is None and not targets and not all_agents:
|
|
179
235
|
# Exit 2 for the same reason `--set` with `--add` is: no flag
|
|
180
236
|
# combination was given that this command can act on. Both are the
|
|
181
237
|
# caller's command line, not the server's answer.
|
|
182
238
|
raise click.UsageError(
|
|
183
|
-
"nothing to do — pass --add, --remove, or --
|
|
239
|
+
"nothing to do — pass --add, --remove, --set, --target, --all-agents, or --jev-mode. "
|
|
184
240
|
"`fp fleet show <machine>` prints the current set."
|
|
185
241
|
)
|
|
186
242
|
|
|
@@ -212,6 +268,11 @@ def fleet_deploy(
|
|
|
212
268
|
replace=replace,
|
|
213
269
|
latest=latest,
|
|
214
270
|
disabled=disabled_ids(published),
|
|
271
|
+
jev_mode=jev_mode,
|
|
272
|
+
current_jev_mode=current.jev_mode if current else None,
|
|
273
|
+
kinds=version_kinds(published),
|
|
274
|
+
targets=targets or (),
|
|
275
|
+
all_agents=all_agents or (),
|
|
215
276
|
)
|
|
216
277
|
except RefUsageError as exc:
|
|
217
278
|
# Exit 2, like every other bad flag value in this CLI (`--since`,
|
|
@@ -237,6 +298,16 @@ def fleet_deploy(
|
|
|
237
298
|
output.deployment_unchanged(machine_id)
|
|
238
299
|
return
|
|
239
300
|
|
|
301
|
+
# The mode alone: its own route, which never touches the policy set. A
|
|
302
|
+
# deploy is a full replace of the set the CLI read, and that set leaves out
|
|
303
|
+
# disabled policies' assignments — so a mode change sent as a deploy deleted
|
|
304
|
+
# them, and reverted anything changed since the read (CONTRACT C9.1). A
|
|
305
|
+
# machine with no deployment yet has nothing to lose and no mode to change:
|
|
306
|
+
# its first deploy, carrying the mode, is how it gets one.
|
|
307
|
+
if plan.jev_mode is not None and not plan.set_changes and current is not None:
|
|
308
|
+
_apply_jev_modes(state, cctx, [(machine_id, current.jev_mode)], plan.jev_mode, yes=yes, plan=plan)
|
|
309
|
+
return
|
|
310
|
+
|
|
240
311
|
if not output.is_json():
|
|
241
312
|
output.render_deploy_plan(plan)
|
|
242
313
|
dropped = len(plan.removed)
|
|
@@ -252,7 +323,7 @@ def fleet_deploy(
|
|
|
252
323
|
output.print_cancelled()
|
|
253
324
|
return
|
|
254
325
|
|
|
255
|
-
result = api.deploy_policies(cctx, machine_id, plan.result)
|
|
326
|
+
result = api.deploy_policies(cctx, machine_id, plan.result, jev_mode=jev_mode)
|
|
256
327
|
check_race(plan.base, result.deployment)
|
|
257
328
|
|
|
258
329
|
if output.is_json():
|
|
@@ -265,6 +336,176 @@ def fleet_deploy(
|
|
|
265
336
|
output.deployment_applied(machine_id, result.deployment, len(result.policies))
|
|
266
337
|
|
|
267
338
|
|
|
339
|
+
def _jev_mode_consequence(mode: str, count: int) -> str:
|
|
340
|
+
what = f"{count} machine{'s' if count != 1 else ''}"
|
|
341
|
+
tail = {
|
|
342
|
+
"enforce": "; in enforce Jev's deny checks BLOCK calls, and it covers installed packs' checks too",
|
|
343
|
+
"observe": "; in observe Jev only records what it would have decided",
|
|
344
|
+
"off": "; Jev stops on them, a local jev.json included",
|
|
345
|
+
"local": "; each machine's own jev.json decides again",
|
|
346
|
+
}.get(mode, "")
|
|
347
|
+
return f"this sets the Jev mode to {mode} on {what} and leaves their policy sets untouched{tail}"
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _apply_jev_modes(
|
|
351
|
+
state: AppState,
|
|
352
|
+
cctx,
|
|
353
|
+
targets: List[tuple],
|
|
354
|
+
mode: str,
|
|
355
|
+
*,
|
|
356
|
+
yes: bool,
|
|
357
|
+
plan=None,
|
|
358
|
+
) -> None:
|
|
359
|
+
"""Set `mode` on each `(machine_id, current_mode)`, through the mode-only route.
|
|
360
|
+
|
|
361
|
+
Shared by `fleet jev-mode` and `fleet deploy --jev-mode` with no set change.
|
|
362
|
+
A machine already on `mode` is left alone; one that fails is reported and
|
|
363
|
+
the rest still go ahead (exit 1 at the end, naming them).
|
|
364
|
+
"""
|
|
365
|
+
after = jev_mode_after(mode)
|
|
366
|
+
rows = [
|
|
367
|
+
{"machineId": mid, "from": cur or "local", "to": after or "local", "changed": (cur or None) != after}
|
|
368
|
+
for mid, cur in targets
|
|
369
|
+
]
|
|
370
|
+
todo = [r for r in rows if r["changed"]]
|
|
371
|
+
if not todo:
|
|
372
|
+
if output.is_json():
|
|
373
|
+
out = {"mode": mode, "machines": rows, "applied": False}
|
|
374
|
+
if plan is not None:
|
|
375
|
+
out = {"plan": plan.to_dict(), "deployment": None, "applied": False}
|
|
376
|
+
output.emit_json(out)
|
|
377
|
+
return
|
|
378
|
+
output.jev_mode_unchanged([r["machineId"] for r in rows], mode)
|
|
379
|
+
return
|
|
380
|
+
|
|
381
|
+
if not output.is_json():
|
|
382
|
+
output.render_jev_mode_plan(rows)
|
|
383
|
+
if not _write.confirm_destructive(
|
|
384
|
+
state, "change the Jev mode on",
|
|
385
|
+
todo[0]["machineId"] if len(todo) == 1 else f"{len(todo)} machines",
|
|
386
|
+
consequence=_jev_mode_consequence(mode, len(todo)),
|
|
387
|
+
assume_yes=yes,
|
|
388
|
+
):
|
|
389
|
+
if output.is_json():
|
|
390
|
+
output.emit_json({"cancelled": True, "applied": False, "machines": rows})
|
|
391
|
+
else:
|
|
392
|
+
output.print_cancelled()
|
|
393
|
+
return
|
|
394
|
+
|
|
395
|
+
failed = []
|
|
396
|
+
last = None
|
|
397
|
+
for row in todo:
|
|
398
|
+
try:
|
|
399
|
+
result, changed = api.set_jev_mode(cctx, row["machineId"], mode)
|
|
400
|
+
except AuthError:
|
|
401
|
+
raise
|
|
402
|
+
except FpCliError as exc:
|
|
403
|
+
# One machine's refusal (a 404, a 403, a 422) is that machine's;
|
|
404
|
+
# the rest still go ahead, and the exit code says it was not all.
|
|
405
|
+
row["error"] = exc.format_message() if hasattr(exc, "format_message") else str(exc)
|
|
406
|
+
failed.append(row["machineId"])
|
|
407
|
+
continue
|
|
408
|
+
last = result
|
|
409
|
+
row["deployment"] = result.deployment
|
|
410
|
+
row["changed"] = changed
|
|
411
|
+
|
|
412
|
+
if output.is_json():
|
|
413
|
+
if plan is not None:
|
|
414
|
+
output.emit_json({
|
|
415
|
+
"plan": plan.to_dict(),
|
|
416
|
+
"deployment": last.to_dict() if last is not None else None,
|
|
417
|
+
"applied": last is not None,
|
|
418
|
+
"jevModeOnly": True,
|
|
419
|
+
})
|
|
420
|
+
else:
|
|
421
|
+
output.emit_json({"mode": mode, "machines": rows, "applied": not failed})
|
|
422
|
+
else:
|
|
423
|
+
output.jev_mode_applied(rows)
|
|
424
|
+
if failed:
|
|
425
|
+
raise ApiError(
|
|
426
|
+
f"the Jev mode could not be changed on {', '.join(failed)}"
|
|
427
|
+
+ ("" if len(failed) == len(todo) else " — the other machines were changed"),
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def fleet_jev_mode(
|
|
432
|
+
ctx: typer.Context,
|
|
433
|
+
args: Optional[List[str]] = typer.Argument(
|
|
434
|
+
None, metavar="[MACHINE]... MODE",
|
|
435
|
+
help="The machines, then the mode: off, observe, enforce or local. With --all, just the mode.",
|
|
436
|
+
),
|
|
437
|
+
all_machines: bool = typer.Option(
|
|
438
|
+
False, "--all", help="Every machine that has a deployment (machines with none are skipped).",
|
|
439
|
+
),
|
|
440
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirmation prompt. The prompt only appears on an interactive terminal: under --json, or with stdin redirected, this command proceeds without asking."),
|
|
441
|
+
) -> None:
|
|
442
|
+
"""Set the Jev mode FailproofAI Cloud gives machines, and nothing else.
|
|
443
|
+
|
|
444
|
+
`off`, `observe` or `enforce` override each machine's own mode and
|
|
445
|
+
`jev.json`, a local `off` included; `local` hands the choice back to the
|
|
446
|
+
machine. Jev checks run on FailproofAI Cloud; nothing is installed on the
|
|
447
|
+
machine: under `observe` or `enforce` it sends each checked tool call to
|
|
448
|
+
FailproofAI Cloud. `observe` asks Jev and records what it says while the
|
|
449
|
+
regex policies decide. `enforce` lets Jev's checks BLOCK calls — every `jev`
|
|
450
|
+
policy's deny checks — as well as clear the regex verdicts they review. The
|
|
451
|
+
mode is the machine's: it also governs the checks of every pack installed
|
|
452
|
+
there. A machine connected with `--no-transcripts` never asks, and reports
|
|
453
|
+
`transcripts_disabled`.
|
|
454
|
+
|
|
455
|
+
Only the mode changes: each machine's policy set, disabled policies'
|
|
456
|
+
assignments included, is left exactly as it is, and a machine already on
|
|
457
|
+
the mode is skipped. Each change is a new generation in `fp fleet history`.
|
|
458
|
+
A machine with no deployment has no mode to change — `fp fleet deploy
|
|
459
|
+
<machine> --jev-mode <mode>` gives it its first deployment with one.
|
|
460
|
+
|
|
461
|
+
Needs `policies:write`. With `--json`: `{mode, applied, machines:[{machineId,
|
|
462
|
+
from, to, changed, deployment?, error?}]}`.
|
|
463
|
+
|
|
464
|
+
Examples:
|
|
465
|
+
|
|
466
|
+
* `fp fleet jev-mode ci-runner-01 observe`
|
|
467
|
+
* `fp fleet jev-mode ci-runner-01 ci-runner-02 enforce`
|
|
468
|
+
* `fp fleet jev-mode --all local`
|
|
469
|
+
"""
|
|
470
|
+
state: AppState = ctx.obj
|
|
471
|
+
deny_in_key_mode(state, "fleet jev-mode", _KEY_MODE_REASON)
|
|
472
|
+
words = list(args or [])
|
|
473
|
+
if not words:
|
|
474
|
+
raise click.UsageError("pass the mode: off, observe, enforce or local")
|
|
475
|
+
mode, machine_ids = words[-1], list(dict.fromkeys(words[:-1]))
|
|
476
|
+
if mode not in JEV_MODES:
|
|
477
|
+
raise click.UsageError(f"{mode!r} is not a Jev mode — one of {', '.join(JEV_MODES)}")
|
|
478
|
+
if all_machines and machine_ids:
|
|
479
|
+
raise click.UsageError("--all sets every machine; do not name machines as well")
|
|
480
|
+
if not all_machines and not machine_ids:
|
|
481
|
+
raise click.UsageError("name at least one machine, or pass --all")
|
|
482
|
+
cctx = require_auth(state)
|
|
483
|
+
|
|
484
|
+
deployments = {d.machine_id: d for d in api.list_deployments(cctx)}
|
|
485
|
+
if all_machines:
|
|
486
|
+
targets = [(mid, deployments[mid].jev_mode) for mid in sorted(deployments)]
|
|
487
|
+
else:
|
|
488
|
+
known = {m.machine_id for m in api.list_machines(cctx)} | set(deployments)
|
|
489
|
+
unknown = [mid for mid in machine_ids if mid not in known]
|
|
490
|
+
if unknown:
|
|
491
|
+
raise NotFoundError(f"no machine {', '.join(repr(u) for u in unknown)} has checked in")
|
|
492
|
+
bare = [mid for mid in machine_ids if mid not in deployments]
|
|
493
|
+
if bare:
|
|
494
|
+
raise ApiError(
|
|
495
|
+
f"{', '.join(bare)} {'has' if len(bare) == 1 else 'have'} no deployment, so there is no Jev "
|
|
496
|
+
"mode to change",
|
|
497
|
+
hint=f"`fp fleet deploy <machine> --jev-mode {mode}` gives a machine its first deployment with the mode",
|
|
498
|
+
)
|
|
499
|
+
targets = [(mid, deployments[mid].jev_mode) for mid in machine_ids]
|
|
500
|
+
if not targets:
|
|
501
|
+
if output.is_json():
|
|
502
|
+
output.emit_json({"mode": mode, "machines": [], "applied": False})
|
|
503
|
+
else:
|
|
504
|
+
output.info("no machine has a deployment yet — nothing to set")
|
|
505
|
+
return
|
|
506
|
+
_apply_jev_modes(state, cctx, targets, mode, yes=yes)
|
|
507
|
+
|
|
508
|
+
|
|
268
509
|
def fleet_diff(
|
|
269
510
|
ctx: typer.Context,
|
|
270
511
|
machine_id: Optional[str] = typer.Argument(None, help="Machine id. Omit for the whole fleet."),
|
|
@@ -314,8 +555,11 @@ def fleet_history(
|
|
|
314
555
|
"""List a machine's deployment generations, newest first.
|
|
315
556
|
|
|
316
557
|
A reissue — the server rewriting a deployment because a policy was disabled
|
|
317
|
-
— appears as an ordinary entry
|
|
318
|
-
`
|
|
558
|
+
— appears as an ordinary entry, and so does a Jev mode change. Each
|
|
559
|
+
generation shows the Jev mode FailproofAI Cloud set with it (`local` when it
|
|
560
|
+
set none), which is the mode a rollback to it restores. Needs
|
|
561
|
+
`policies:read`. With `--json`: `{machineId, history:[{deployment, policies,
|
|
562
|
+
updatedAt, jevMode}]}`.
|
|
319
563
|
|
|
320
564
|
Example:
|
|
321
565
|
|
|
@@ -329,7 +573,8 @@ def fleet_history(
|
|
|
329
573
|
if output.is_json():
|
|
330
574
|
output.emit_json({"machineId": machine_id, "history": entries})
|
|
331
575
|
return
|
|
332
|
-
output.render_deployment_history(machine_id, entries
|
|
576
|
+
output.render_deployment_history(machine_id, entries,
|
|
577
|
+
agent_labels=api.agent_labels(cctx, machine_id))
|
|
333
578
|
|
|
334
579
|
|
|
335
580
|
def fleet_rollback(
|
|
@@ -344,8 +589,12 @@ def fleet_rollback(
|
|
|
344
589
|
counter, so history stays append-only. A generation containing a policy that
|
|
345
590
|
has since been disabled or deleted cannot be reinstated; the server says so.
|
|
346
591
|
|
|
347
|
-
|
|
348
|
-
|
|
592
|
+
It restores that generation's Jev mode too, `local` included: the prompt
|
|
593
|
+
and the result say `jev mode <now> → <then>` when that changes it.
|
|
594
|
+
|
|
595
|
+
Needs `policies:write`. With `--json`: the resulting deployment plus
|
|
596
|
+
`jevModeBefore` (the mode it replaced), or `{"cancelled": true}` if you
|
|
597
|
+
decline.
|
|
349
598
|
|
|
350
599
|
Example:
|
|
351
600
|
|
|
@@ -356,9 +605,22 @@ def fleet_rollback(
|
|
|
356
605
|
cctx = require_auth(state)
|
|
357
606
|
_require_machine(cctx, machine_id)
|
|
358
607
|
current = api.get_deployment(cctx, machine_id)
|
|
608
|
+
# The mode that generation carried comes back with it, NULL (`local`)
|
|
609
|
+
# included — a rollback past an `--jev-mode off` turns enforcement back on.
|
|
610
|
+
# Read it first, so the prompt says so rather than the fleet finding out.
|
|
611
|
+
target = next(
|
|
612
|
+
(e for e in api.deployment_history(cctx, machine_id) if e.get("deployment") == deployment), None)
|
|
613
|
+
mode_before = (current.jev_mode if current else None) or "local"
|
|
614
|
+
mode_target = (target.get("jevMode") or "local") if target is not None else None
|
|
615
|
+
consequence = "this REPLACES the machine's current set with the one from that generation"
|
|
616
|
+
if target is not None:
|
|
617
|
+
refs = [PolicyRef.from_dict(ref).label for ref in (target.get("policies") or [])]
|
|
618
|
+
consequence += "; assignments: " + ("; ".join(refs) if refs else "no policies")
|
|
619
|
+
if mode_target is not None and mode_target != mode_before:
|
|
620
|
+
consequence += f", and its Jev mode: jev mode {mode_before} → {mode_target}"
|
|
359
621
|
if not _write.confirm_destructive(
|
|
360
622
|
state, f"reinstate deployment #{deployment} on", machine_id,
|
|
361
|
-
consequence=
|
|
623
|
+
consequence=consequence,
|
|
362
624
|
assume_yes=yes,
|
|
363
625
|
):
|
|
364
626
|
if output.is_json():
|
|
@@ -369,9 +631,12 @@ def fleet_rollback(
|
|
|
369
631
|
result = api.rollback_deployment(cctx, machine_id, deployment)
|
|
370
632
|
check_race(current.deployment if current else None, result.deployment)
|
|
371
633
|
if output.is_json():
|
|
372
|
-
output.emit_json(result.to_dict())
|
|
634
|
+
output.emit_json({**result.to_dict(), "jevModeBefore": mode_before})
|
|
373
635
|
return
|
|
374
|
-
output.deployment_rolled_back(
|
|
636
|
+
output.deployment_rolled_back(
|
|
637
|
+
machine_id, deployment, result.deployment,
|
|
638
|
+
jev_from=mode_before, jev_to=result.jev_mode or "local",
|
|
639
|
+
)
|
|
375
640
|
|
|
376
641
|
|
|
377
642
|
def fleet_rename(
|
|
@@ -404,11 +669,12 @@ def register(app: typer.Typer) -> None:
|
|
|
404
669
|
no_args_is_help=True,
|
|
405
670
|
rich_markup_mode="markdown",
|
|
406
671
|
context_settings={"help_option_names": ["-h", "--help"]},
|
|
407
|
-
help="The fleet and what each machine enforces (list / show / deploy / diff / history / rollback / rename).",
|
|
672
|
+
help="The fleet and what each machine enforces (list / show / deploy / jev-mode / diff / history / rollback / rename).",
|
|
408
673
|
)
|
|
409
674
|
fleet_app.command("list", epilog=GLOBALS_EPILOG)(fleet_list)
|
|
410
675
|
fleet_app.command("show", epilog=GLOBALS_EPILOG)(fleet_show)
|
|
411
676
|
fleet_app.command("deploy", epilog=GLOBALS_EPILOG)(fleet_deploy)
|
|
677
|
+
fleet_app.command("jev-mode", epilog=GLOBALS_EPILOG)(fleet_jev_mode)
|
|
412
678
|
fleet_app.command("diff", epilog=GLOBALS_EPILOG)(fleet_diff)
|
|
413
679
|
fleet_app.command("history", epilog=GLOBALS_EPILOG)(fleet_history)
|
|
414
680
|
fleet_app.command("rollback", epilog=GLOBALS_EPILOG)(fleet_rollback)
|
|
@@ -10,6 +10,7 @@ single most surprising thing here, so every success path says so.
|
|
|
10
10
|
"""
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
+
import json
|
|
13
14
|
from typing import Optional
|
|
14
15
|
|
|
15
16
|
import typer
|
|
@@ -18,8 +19,8 @@ from .. import client as api
|
|
|
18
19
|
from .. import output, theme
|
|
19
20
|
from .._context import GLOBALS_EPILOG, AppState, deny_in_key_mode, require_auth
|
|
20
21
|
from .. import _click_compat as click # the Click Typer is running; see _click_compat
|
|
21
|
-
from ..enforcement import RefError, RefUsageError, read_source
|
|
22
|
-
from ..policy_check import check_syntax, run_policy
|
|
22
|
+
from ..enforcement import RefError, RefUsageError, parse_semantic, read_source, resolve_kind
|
|
23
|
+
from ..policy_check import SyntaxResult, check_syntax, run_policy
|
|
23
24
|
from ..errors import ApiError, NotFoundError
|
|
24
25
|
from . import _write
|
|
25
26
|
|
|
@@ -36,11 +37,15 @@ _KEY_MODE_REASON = (
|
|
|
36
37
|
def policies_list(ctx: typer.Context) -> None:
|
|
37
38
|
"""List every published policy version, newest of each policy first.
|
|
38
39
|
|
|
39
|
-
Shows `policy · version · state · description`, one row per VERSION —
|
|
40
|
+
Shows `policy · version · kind · state · description`, one row per VERSION —
|
|
40
41
|
versions are immutable and every one stays addressable, so a policy
|
|
41
42
|
published three times is three rows. The title counts distinct policies and
|
|
42
43
|
captions the version total, the way the dashboard's library does.
|
|
43
44
|
|
|
45
|
+
`kind` is `regex` (JavaScript), `jev` (Jev checks only) or `both`
|
|
46
|
+
(JavaScript its own Jev checks may clear); a `jev`/`both` row names its
|
|
47
|
+
checks and the characters they take of a machine's Jev question budget.
|
|
48
|
+
|
|
44
49
|
`state` is active, disabled (kept but not enforced) or archived (deleted;
|
|
45
50
|
machines already carrying it keep it until redeployed). Needs
|
|
46
51
|
`policies:read`. With `--json`: the server's policy list verbatim — also
|
|
@@ -64,9 +69,11 @@ def policies_show(
|
|
|
64
69
|
ctx: typer.Context,
|
|
65
70
|
policy_id: str = typer.Argument(..., help="Policy id."),
|
|
66
71
|
) -> None:
|
|
67
|
-
"""Show one policy, including its full source.
|
|
72
|
+
"""Show one policy, including its full source and its Jev checks.
|
|
68
73
|
|
|
69
|
-
Needs `policies:read`. With `--json`: the policy object with `source
|
|
74
|
+
Needs `policies:read`. With `--json`: the policy object with `source`,
|
|
75
|
+
`kind`, and for `jev`/`both` its `semantic` declarations, `semanticSha256`
|
|
76
|
+
and the server-derived `authority`/`reviewedBy`.
|
|
70
77
|
|
|
71
78
|
Example:
|
|
72
79
|
|
|
@@ -96,6 +103,8 @@ def policies_show(
|
|
|
96
103
|
source_bytes=len((match.source or "").encode("utf-8")))
|
|
97
104
|
if match.source:
|
|
98
105
|
output.info(match.source)
|
|
106
|
+
if match.semantic:
|
|
107
|
+
output.info(json.dumps(match.semantic, indent=2))
|
|
99
108
|
|
|
100
109
|
|
|
101
110
|
def policies_publish(
|
|
@@ -103,7 +112,19 @@ def policies_publish(
|
|
|
103
112
|
policy_id: str = typer.Argument(..., help="Policy id (letters, numbers, '.', '_', '-')."),
|
|
104
113
|
source: Optional[str] = typer.Argument(
|
|
105
114
|
None,
|
|
106
|
-
help="Path to the
|
|
115
|
+
help="Path to the JavaScript source, @path, or - for stdin. Omit to paste it. Same as --source.",
|
|
116
|
+
),
|
|
117
|
+
kind: Optional[str] = typer.Option(
|
|
118
|
+
None, "--kind",
|
|
119
|
+
help="regex (JavaScript), jev (Jev checks only) or both (JavaScript its own Jev checks may "
|
|
120
|
+
"clear). Default: follows from what you give — --semantic alone is jev, with source both.",
|
|
121
|
+
),
|
|
122
|
+
source_opt: Optional[str] = typer.Option(
|
|
123
|
+
None, "--source", help="The JavaScript source: a path, @path, or - for stdin.",
|
|
124
|
+
),
|
|
125
|
+
semantic_path: Optional[str] = typer.Option(
|
|
126
|
+
None, "--semantic",
|
|
127
|
+
help="A JSON file holding the Jev declarations (a JSON array): a path, @path, or - for stdin.",
|
|
107
128
|
),
|
|
108
129
|
description: str = typer.Option("", "--description", help="One-line description."),
|
|
109
130
|
no_verify: bool = typer.Option(
|
|
@@ -112,13 +133,29 @@ def policies_publish(
|
|
|
112
133
|
) -> None:
|
|
113
134
|
"""Publish a policy — mints a NEW VERSION; it never edits one in place.
|
|
114
135
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
136
|
+
A policy has a **kind**:
|
|
137
|
+
|
|
138
|
+
* `regex` — JavaScript (`--source`, or the positional source): what publish
|
|
139
|
+
has always done, and the default.
|
|
140
|
+
* `jev` — Jev checks only (`--semantic checks.json`): a JSON array of Jev
|
|
141
|
+
declarations, each `{name, title, appliesTo, mode, userCanOverride,
|
|
142
|
+
probes:[{id, instructions, criteria:{true,false}}], guidance, ...}` —
|
|
143
|
+
exactly a failproofai pack manifest's semantic entry. No JavaScript.
|
|
144
|
+
* `both` — the two together. FailproofAI Cloud makes the JavaScript's
|
|
145
|
+
verdict reviewable by exactly its own checks, and deploys them as one.
|
|
146
|
+
|
|
147
|
+
Jev checks run on FailproofAI Cloud; nothing is installed on the machine.
|
|
148
|
+
A machine gets a `both` policy's JavaScript, and asks FailproofAI Cloud
|
|
149
|
+
about each checked tool call while its Jev mode is `observe` or `enforce`.
|
|
150
|
+
|
|
151
|
+
The JavaScript is parse-checked with node before it is sent. Nothing
|
|
152
|
+
downstream does this: the server validates the id and a size ceiling, and a
|
|
153
|
+
broken policy otherwise fails on the machine at enforcement time.
|
|
154
|
+
`--no-verify` skips it; a host without node publishes with a warning rather
|
|
155
|
+
than a block. Jev fields in the JavaScript (`semanticPolicies.add`,
|
|
156
|
+
`authority: "reviewable"`) are refused regardless — put Jev checks in
|
|
157
|
+
`--semantic`. The server checks each declaration (limits, reserved
|
|
158
|
+
FailproofAI names, names unique in the org) and says which one it refused.
|
|
122
159
|
|
|
123
160
|
Source can come from a path, `@path`, a pipe, `-`, or an interactive paste
|
|
124
161
|
when you give none and stdin is a terminal.
|
|
@@ -134,21 +171,42 @@ def policies_publish(
|
|
|
134
171
|
* `fp policies publish no-force-push ./rule.mjs`
|
|
135
172
|
* `cat rule.mjs | fp policies publish no-force-push`
|
|
136
173
|
* `fp policies publish no-force-push -` — read stdin explicitly
|
|
174
|
+
* `fp policies publish prod-db-intent --kind jev --semantic ./checks.json`
|
|
175
|
+
* `fp policies publish no-prod-db --kind both --source ./rule.mjs --semantic ./checks.json`
|
|
137
176
|
"""
|
|
138
177
|
state: AppState = ctx.obj
|
|
139
178
|
deny_in_key_mode(state, "policies publish", _KEY_MODE_REASON)
|
|
140
179
|
cctx = require_auth(state)
|
|
141
180
|
|
|
181
|
+
if source is not None and source_opt is not None:
|
|
182
|
+
raise click.UsageError("give the JavaScript source once — positionally or with --source, not both")
|
|
183
|
+
source = source if source is not None else source_opt
|
|
184
|
+
if source == "-" and semantic_path == "-":
|
|
185
|
+
raise click.UsageError("only one of the JavaScript source and --semantic can come from stdin")
|
|
186
|
+
|
|
187
|
+
# Whether JavaScript is coming is decided WITHOUT reading stdin: a Jev-only
|
|
188
|
+
# publish must not sit waiting for a paste nobody is going to give.
|
|
189
|
+
wants_source = source is not None or kind in ("regex", "both") or (kind is None and semantic_path is None)
|
|
190
|
+
try:
|
|
191
|
+
resolved_kind = resolve_kind(kind, has_source=wants_source, has_semantic=semantic_path is not None)
|
|
192
|
+
except RefUsageError as exc:
|
|
193
|
+
raise click.UsageError(str(exc))
|
|
194
|
+
|
|
142
195
|
def _paste_prompt() -> None:
|
|
143
196
|
output.hint("paste the policy source, then press Ctrl-D")
|
|
144
197
|
|
|
198
|
+
text: Optional[str] = None
|
|
199
|
+
semantic = None
|
|
145
200
|
try:
|
|
146
|
-
|
|
201
|
+
if semantic_path is not None:
|
|
202
|
+
semantic = parse_semantic(read_source(semantic_path, isatty=False))
|
|
203
|
+
if wants_source:
|
|
204
|
+
text = read_source(source, prompt=_paste_prompt)
|
|
147
205
|
except RefUsageError as exc:
|
|
148
206
|
raise click.UsageError(str(exc))
|
|
149
207
|
except RefError as exc:
|
|
150
208
|
raise ApiError(str(exc))
|
|
151
|
-
if not text.strip():
|
|
209
|
+
if wants_source and not (text or "").strip():
|
|
152
210
|
raise ApiError("policy source is empty — nothing to publish")
|
|
153
211
|
|
|
154
212
|
# Nothing downstream parses this. The server checks the id and a size
|
|
@@ -156,7 +214,9 @@ def policies_publish(
|
|
|
156
214
|
# place for a syntax error to surface. `--no-verify` exists because a
|
|
157
215
|
# machine without node should still be able to publish, not because
|
|
158
216
|
# skipping is ever a good idea.
|
|
159
|
-
|
|
217
|
+
syn = SyntaxResult(ok=True, checked=False,
|
|
218
|
+
message="not checked" if text else "no JavaScript source")
|
|
219
|
+
if text and not no_verify:
|
|
160
220
|
syn = check_syntax(text)
|
|
161
221
|
if not syn.ok:
|
|
162
222
|
raise ApiError(
|
|
@@ -167,7 +227,13 @@ def policies_publish(
|
|
|
167
227
|
if not syn.checked and not output.is_json():
|
|
168
228
|
output.warn(syn.message)
|
|
169
229
|
|
|
170
|
-
created = api.publish_policy(
|
|
230
|
+
created = api.publish_policy(
|
|
231
|
+
cctx, policy_id, text, description,
|
|
232
|
+
# Sent only when it says something the server's default does not, so a
|
|
233
|
+
# plain JavaScript publish is the same request it has always been.
|
|
234
|
+
kind=None if resolved_kind == "regex" and kind is None else resolved_kind,
|
|
235
|
+
semantic=semantic,
|
|
236
|
+
)
|
|
171
237
|
|
|
172
238
|
# Which machines already carry this policy, and at which version. Publishing
|
|
173
239
|
# deploys nothing, so this is the one thing the card must not guess at: it
|
|
@@ -191,7 +257,7 @@ def policies_publish(
|
|
|
191
257
|
output.emit_json({**created.to_dict(), "carriers": carriers, "syntax": syn.to_dict()})
|
|
192
258
|
return
|
|
193
259
|
output.render_policy_published(created, carriers=carriers,
|
|
194
|
-
source_bytes=len(text.encode("utf-8")))
|
|
260
|
+
source_bytes=len((text or "").encode("utf-8")))
|
|
195
261
|
|
|
196
262
|
|
|
197
263
|
def policies_enable(
|