failproofai 1.0.1 → 1.0.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +6 -6
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +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 +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +5 -4
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -2
- 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 +4 -3
- 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 +4 -4
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +4 -4
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +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/server-reference-manifest.json +66 -22
- package/.next/standalone/.next/server/app/policies/page.js +5 -4
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +2 -2
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +2 -2
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
- package/.next/standalone/.next/server/app/settings/page.js +1 -1
- 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]__1qzqcio._.js → [externals]__0e32-7c._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[externals]__1s462yl._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p8poxd._.js → [root-of-the-server]__0igxt32._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0neqlu-._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t9oqnn._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +3 -0
- package/.next/standalone/.next/server/chunks/_185gyk9._.js +3 -0
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +5 -6
- package/.next/standalone/.next/server/chunks/lib_telemetry-id_ts_1k8gj9o._.js +3 -0
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_09d5c79._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_04op_ou._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_1d2g73o._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0dr20uz._.js} +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0xbw3bu._.js +12 -0
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0spkm68._.js → [root-of-the-server]__07gm3zl._.js} +21 -20
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +6 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__07tbbf3._.js → [root-of-the-server]__0zig5sv._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__192nhvh._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0lvyuv4._.js → _00e9tnf._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0u5zauv._.js → _12l4a_k._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +13 -13
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_08o7bqo._.js +10 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware.js +1 -1
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +71 -27
- package/.next/standalone/.next/static/chunks/{37fcpc36x9gpe.js → 0eqky-7n7dgdm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1ide1wl2f7zi1.js → 0sa9v5mhc8861.js} +13 -13
- package/.next/standalone/.next/static/chunks/1mfzsja6ftim5.css +2 -0
- package/.next/standalone/.next/static/chunks/{3k7mxbp9hiom0.js → 1nuqytbta44af.js} +1 -1
- package/.next/standalone/.next/static/chunks/1sl2xmn9inx3y.js +6 -0
- package/.next/standalone/.next/static/chunks/{2_2lhuojq2xop.js → 2aawsjld9ied_.js} +1 -1
- package/.next/standalone/.next/static/chunks/2gn7ut4svofm2.js +1 -0
- package/.next/standalone/.next/static/chunks/{31s1smp-g7gks.js → 2hs7ycvsav7v2.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0k5050ezjvv49.js → 2i2yt3hzjredm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3c808qrfcq0im.js → 35xg6retpe6rp.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2zpp1we2jv6tb.js → 3y54nw0i7dsjl.js} +1 -1
- package/.next/standalone/SECURITY.md +5 -4
- package/.next/standalone/app/actions/get-hooks-config.ts +108 -17
- package/.next/standalone/app/actions/pack-actions.ts +153 -0
- package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
- package/.next/standalone/app/audit/_components/share-templates.ts +13 -13
- package/.next/standalone/app/policies/hooks-client.tsx +320 -14
- package/.next/standalone/components/navbar.tsx +2 -28
- package/.next/standalone/components/reach-developers.tsx +8 -1
- package/.next/standalone/docs-old/agenteye/python-sdk-skill.mdx +1 -1
- package/.next/standalone/docs-old/agenteye/python-sdk.mdx +59 -35
- package/.next/standalone/docs-old/i18n/README.ar.md +1 -0
- package/.next/standalone/docs-old/i18n/README.de.md +1 -0
- package/.next/standalone/docs-old/i18n/README.es.md +1 -0
- package/.next/standalone/docs-old/i18n/README.fr.md +1 -0
- package/.next/standalone/docs-old/i18n/README.he.md +1 -0
- package/.next/standalone/docs-old/i18n/README.hi.md +1 -0
- package/.next/standalone/docs-old/i18n/README.it.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ja.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ko.md +1 -0
- package/.next/standalone/docs-old/i18n/README.pt-br.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ru.md +1 -0
- package/.next/standalone/docs-old/i18n/README.tr.md +1 -0
- package/.next/standalone/docs-old/i18n/README.vi.md +1 -0
- package/.next/standalone/docs-old/i18n/README.zh.md +1 -0
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +431 -0
- package/.next/standalone/fp-cloud-cli/LICENSE +42 -0
- package/.next/standalone/fp-cloud-cli/MANIFEST.in +10 -0
- package/.next/standalone/fp-cloud-cli/README.md +259 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/__init__.py +10 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/__main__.py +4 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +64 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_context.py +332 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_version.py +1 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics.py +432 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics_config.py +77 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics_registry.py +83 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/app.py +492 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/auth.py +160 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +1694 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/__init__.py +0 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/_write.py +214 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/agent_cmds.py +407 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/alerts_cmds.py +445 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/audits_cmds.py +1054 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/auth_cmds.py +512 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/errors_cmds.py +190 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/evals_cmds.py +161 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/events_cmds.py +159 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +416 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/guardrails_cmds.py +148 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +472 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/keys_cmds.py +407 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/list_cmds.py +63 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/orgs_cmds.py +319 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +499 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/queries_cmds.py +378 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/sessions_cmds.py +151 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/settings_cmds.py +150 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/usage_cmds.py +35 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/users_cmds.py +404 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/config.py +330 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/dates.py +78 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +345 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/errors.py +98 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/models.py +891 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/orgs.py +30 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +6593 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +208 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +290 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/py.typed +0 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/select.py +322 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/theme.py +53 -0
- package/.next/standalone/fp-cloud-cli/pyproject.toml +106 -0
- package/.next/standalone/fp-cloud-cli/skill/SKILL.md +302 -0
- package/.next/standalone/fp-cloud-cli/skill/agents/openai.yaml +8 -0
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +254 -0
- package/.next/standalone/fp-cloud-cli/tests/__init__.py +0 -0
- package/.next/standalone/fp-cloud-cli/tests/conftest.py +92 -0
- package/.next/standalone/fp-cloud-cli/tests/test_alerting.py +644 -0
- package/.next/standalone/fp-cloud-cli/tests/test_analytics.py +385 -0
- package/.next/standalone/fp-cloud-cli/tests/test_audits.py +950 -0
- package/.next/standalone/fp-cloud-cli/tests/test_auth.py +98 -0
- package/.next/standalone/fp-cloud-cli/tests/test_auth_mode.py +272 -0
- package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +118 -0
- package/.next/standalone/fp-cloud-cli/tests/test_client.py +299 -0
- package/.next/standalone/fp-cloud-cli/tests/test_commands.py +814 -0
- package/.next/standalone/fp-cloud-cli/tests/test_config.py +99 -0
- package/.next/standalone/fp-cloud-cli/tests/test_dashboards_agent.py +340 -0
- package/.next/standalone/fp-cloud-cli/tests/test_dates.py +76 -0
- package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +481 -0
- package/.next/standalone/fp-cloud-cli/tests/test_facets.py +170 -0
- package/.next/standalone/fp-cloud-cli/tests/test_failproofai_home.py +722 -0
- package/.next/standalone/fp-cloud-cli/tests/test_fp_home_contract.py +198 -0
- package/.next/standalone/fp-cloud-cli/tests/test_hardening.py +186 -0
- package/.next/standalone/fp-cloud-cli/tests/test_help_table_coverage.py +127 -0
- package/.next/standalone/fp-cloud-cli/tests/test_keys_queries.py +496 -0
- package/.next/standalone/fp-cloud-cli/tests/test_list.py +59 -0
- package/.next/standalone/fp-cloud-cli/tests/test_multivalue.py +183 -0
- package/.next/standalone/fp-cloud-cli/tests/test_no_customer_identifiers.py +168 -0
- package/.next/standalone/fp-cloud-cli/tests/test_operator.py +492 -0
- package/.next/standalone/fp-cloud-cli/tests/test_orgs.py +692 -0
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +1732 -0
- package/.next/standalone/fp-cloud-cli/tests/test_policy_check.py +191 -0
- package/.next/standalone/fp-cloud-cli/tests/test_readme_matches_reality.py +125 -0
- package/.next/standalone/fp-cloud-cli/tests/test_review_fixes.py +161 -0
- package/.next/standalone/fp-cloud-cli/tests/test_telemetry_completeness.py +90 -0
- package/.next/standalone/fp-cloud-cli/tests/test_usage.py +115 -0
- package/.next/standalone/fp-cloud-cli/tests/test_v1_origin_diagnostic.py +70 -0
- package/.next/standalone/fp-cloud-cli/tests/test_v1_routing.py +214 -0
- package/.next/standalone/fp-cloud-cli/tests/test_whoami.py +104 -0
- package/.next/standalone/fp-cloud-cli/uv.lock +552 -0
- package/.next/standalone/package.json +29 -22
- package/.next/standalone/sdk/python/CHANGELOG.md +70 -0
- package/.next/standalone/sdk/python/LICENSE +42 -0
- package/.next/standalone/sdk/python/MANIFEST.in +21 -0
- package/.next/standalone/sdk/python/README.md +466 -0
- package/.next/standalone/sdk/python/docs/README.md +249 -0
- package/.next/standalone/sdk/python/docs/_shared/README.md +43 -0
- package/.next/standalone/sdk/python/docs/_shared/__init__.py +195 -0
- package/.next/standalone/sdk/python/docs/crewai/README.md +328 -0
- package/.next/standalone/sdk/python/docs/crewai/examples/quickstart.py +61 -0
- package/.next/standalone/sdk/python/docs/crewai/examples/research_crew.py +92 -0
- package/.next/standalone/sdk/python/docs/langgraph/README.md +481 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/quickstart.py +51 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/research_agent.py +86 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/supervisor_handoff.py +83 -0
- package/.next/standalone/sdk/python/docs/llama_index/README.md +332 -0
- package/.next/standalone/sdk/python/docs/llama_index/examples/quickstart.py +62 -0
- package/.next/standalone/sdk/python/docs/llama_index/examples/research_agent.py +93 -0
- package/.next/standalone/sdk/python/docs/manual/README.md +491 -0
- package/.next/standalone/sdk/python/docs/manual/examples/quickstart.py +57 -0
- package/.next/standalone/sdk/python/docs/manual/examples/research_agent.py +133 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/README.md +323 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/examples/quickstart.py +48 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/examples/research_agent.py +94 -0
- package/.next/standalone/sdk/python/failproofai_sdk/__init__.py +131 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_context.py +218 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_environment.py +78 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_events.py +752 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_resolver.py +118 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_runtime.py +21 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_schema.py +317 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_scopes.py +422 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_writer.py +750 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/__init__.py +274 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/_compat.py +247 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/_core.py +1076 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/crewai.py +1669 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/langchain.py +2151 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/llama_index.py +1588 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/pydantic_ai.py +977 -0
- package/.next/standalone/sdk/python/failproofai_sdk/py.typed +0 -0
- package/.next/standalone/sdk/python/pyproject.toml +92 -0
- package/.next/standalone/sdk/python/skill/SKILL.md +399 -0
- package/.next/standalone/sdk/python/skill/agents/openai.yaml +8 -0
- package/.next/standalone/sdk/python/skill/references/events.md +234 -0
- package/.next/standalone/sdk/python/skill/references/frameworks.md +314 -0
- package/.next/standalone/sdk/python/skill/references/install.md +100 -0
- package/.next/standalone/sdk/python/skill/references/integration.md +352 -0
- package/.next/standalone/sdk/python/tests/__init__.py +0 -0
- package/.next/standalone/sdk/python/tests/conftest.py +169 -0
- package/.next/standalone/sdk/python/tests/integrations/__init__.py +6 -0
- package/.next/standalone/sdk/python/tests/integrations/test_crewai.py +1299 -0
- package/.next/standalone/sdk/python/tests/integrations/test_langchain.py +2338 -0
- package/.next/standalone/sdk/python/tests/integrations/test_llama_index.py +1576 -0
- package/.next/standalone/sdk/python/tests/integrations/test_pydantic_ai.py +1102 -0
- package/.next/standalone/sdk/python/tests/test_context.py +410 -0
- package/.next/standalone/sdk/python/tests/test_docs.py +411 -0
- package/.next/standalone/sdk/python/tests/test_durability.py +1352 -0
- package/.next/standalone/sdk/python/tests/test_encoding.py +571 -0
- package/.next/standalone/sdk/python/tests/test_integrations.py +1127 -0
- package/.next/standalone/sdk/python/tests/test_no_customer_identifiers.py +214 -0
- package/.next/standalone/sdk/python/tests/test_packaging.py +185 -0
- package/.next/standalone/sdk/python/tests/test_resolver_umbrella.py +178 -0
- package/.next/standalone/sdk/python/tests/test_scopes.py +487 -0
- package/.next/standalone/sdk/python/tests/test_sdk.py +923 -0
- package/.next/standalone/sdk/python/tests/test_server_contract.py +650 -0
- package/.next/standalone/sdk/python/tests/test_site_docs.py +371 -0
- package/.next/standalone/sdk/python/tests/test_skill_snippets.py +165 -0
- package/.next/standalone/sdk/python/tests/test_spool_contract.py +287 -0
- package/.next/standalone/sdk/python/tests/test_spool_creation.py +360 -0
- package/.next/standalone/sdk/python/tests/test_wire_format.py +279 -0
- package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +378 -0
- package/.next/standalone/sdk/python/uv.lock +6005 -0
- package/.next/standalone/server.js +1 -1
- package/README.md +77 -21
- package/bin/failproofai.mjs +145 -11
- package/dist/cli.mjs +4482 -2205
- package/dist/index.js +5 -5
- package/dist/worker.mjs +2399 -1978
- package/package.json +29 -22
- package/policy-pack/SHA256SUMS +2 -0
- package/policy-pack/failproofai-pack.json +732 -0
- package/policy-pack/failproofai-pack.mjs +2017 -0
- package/scripts/build-policy-pack.mjs +121 -0
- package/scripts/changelog-section.py +77 -0
- package/scripts/prune-standalone.mjs +3 -0
- package/scripts/python-version.py +232 -0
- package/scripts/translate-docs/cli.ts +21 -0
- package/scripts/translate-docs/mintlify-nav.ts +85 -13
- package/scripts/translate-docs/translator.ts +66 -11
- package/src/audit/cache.ts +40 -3
- package/src/audit/cli.ts +1 -1
- package/src/audit/index.ts +1 -1
- package/src/audit/replay.ts +89 -3
- package/src/audit/report.ts +5 -5
- package/src/audit/schedule-cli.ts +49 -53
- package/src/hooks/builtin-policies.ts +217 -579
- package/src/hooks/cloud-enrollment-cli.ts +66 -27
- package/src/hooks/cloud-managed-policies.ts +6 -1
- package/src/hooks/configure-wizard.ts +1 -1
- package/src/hooks/custom-hooks-loader.ts +89 -6
- package/src/hooks/fp-home.ts +92 -4
- package/src/hooks/fp-reset.ts +26 -2
- package/src/hooks/handler.ts +181 -11
- package/src/hooks/harness-cli.ts +79 -25
- package/src/hooks/hook-activity-store.ts +13 -3
- package/src/hooks/install-prompt.ts +25 -3
- package/src/hooks/manager.ts +418 -172
- package/src/hooks/migrations.ts +2 -2
- package/src/hooks/pack-cli.ts +505 -0
- package/src/hooks/pack-failclosed.ts +189 -0
- package/src/hooks/pack-manifest.ts +344 -0
- package/src/hooks/pack-store.ts +930 -0
- package/src/hooks/policy-catalog.ts +535 -0
- package/src/hooks/policy-evaluator.ts +24 -16
- package/src/hooks/policy-presets.ts +4 -2
- package/src/hooks/policy-registry.ts +8 -2
- package/src/hooks/policy-types.ts +26 -0
- package/src/hooks/session-pause-cli.ts +33 -9
- package/src/hooks/tui.ts +570 -0
- package/src/hooks/types.ts +15 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_usx0j._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_zk340._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0agdw2a._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d1u_na._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1ger0a2._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +0 -3
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1iiwm5e._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kc8_v5._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kgy9da._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mhqk5a._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pltpn8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qkcyf-._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w0x_ok._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_dfocn._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bfex62._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kzapg5._.js +0 -6
- package/.next/standalone/.next/static/chunks/36fw6yk1s6x_f.js +0 -1
- package/.next/standalone/.next/static/chunks/3tzq0260n17ae.js +0 -6
- package/.next/standalone/.next/static/chunks/3wq9x0c-7ld5d.css +0 -2
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
"""Cloud-managed policies: policies list / show / publish / enable / disable / delete.
|
|
2
|
+
|
|
3
|
+
Where a policy VERSION is written. Deploying one to a machine is `fp fleet`, and
|
|
4
|
+
seeing what it actually did is `fp guardrails` — three commands because they are
|
|
5
|
+
three jobs, done by different people at different times, exactly as the
|
|
6
|
+
dashboard splits them across three pages.
|
|
7
|
+
|
|
8
|
+
Publishing mints a new version and changes nothing on any machine. That is the
|
|
9
|
+
single most surprising thing here, so every success path says so.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
import typer
|
|
16
|
+
|
|
17
|
+
from .. import client as api
|
|
18
|
+
from .. import output, theme
|
|
19
|
+
from .._context import GLOBALS_EPILOG, AppState, deny_in_key_mode, require_auth
|
|
20
|
+
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
|
|
23
|
+
from ..errors import ApiError, NotFoundError
|
|
24
|
+
from . import _write
|
|
25
|
+
|
|
26
|
+
#: Every command here is session-only. These endpoints are ROOT-ONLY on the
|
|
27
|
+
#: server — deliberately absent from `/v1`, because `/v1` is internet-facing and
|
|
28
|
+
#: publish/deploy/rollback are operator writes. Failing here beats translating a
|
|
29
|
+
#: path that would 404 with no explanation.
|
|
30
|
+
_KEY_MODE_REASON = (
|
|
31
|
+
"cloud-managed policies are an operator surface and are not exposed on the "
|
|
32
|
+
"versioned API that an API key authenticates against"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def policies_list(ctx: typer.Context) -> None:
|
|
37
|
+
"""List every published policy version, newest of each policy first.
|
|
38
|
+
|
|
39
|
+
Shows `policy · version · state · description`, one row per VERSION —
|
|
40
|
+
versions are immutable and every one stays addressable, so a policy
|
|
41
|
+
published three times is three rows. The title counts distinct policies and
|
|
42
|
+
captions the version total, the way the dashboard's library does.
|
|
43
|
+
|
|
44
|
+
`state` is active, disabled (kept but not enforced) or archived (deleted;
|
|
45
|
+
machines already carrying it keep it until redeployed). Needs
|
|
46
|
+
`policies:read`. With `--json`: the server's policy list verbatim — also
|
|
47
|
+
every version, so deduplicate on `id` if you want one row per policy.
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
|
|
51
|
+
* `fp policies list`
|
|
52
|
+
"""
|
|
53
|
+
state: AppState = ctx.obj
|
|
54
|
+
deny_in_key_mode(state, "policies", _KEY_MODE_REASON)
|
|
55
|
+
cctx = require_auth(state)
|
|
56
|
+
items = api.list_policies(cctx)
|
|
57
|
+
if output.is_json():
|
|
58
|
+
output.emit_json({"policies": [p.to_dict() for p in items]})
|
|
59
|
+
return
|
|
60
|
+
output.render_policies(items)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def policies_show(
|
|
64
|
+
ctx: typer.Context,
|
|
65
|
+
policy_id: str = typer.Argument(..., help="Policy id."),
|
|
66
|
+
) -> None:
|
|
67
|
+
"""Show one policy, including its full source.
|
|
68
|
+
|
|
69
|
+
Needs `policies:read`. With `--json`: the policy object with `source`.
|
|
70
|
+
|
|
71
|
+
Example:
|
|
72
|
+
|
|
73
|
+
* `fp policies show no-force-push`
|
|
74
|
+
"""
|
|
75
|
+
state: AppState = ctx.obj
|
|
76
|
+
deny_in_key_mode(state, "policies show", _KEY_MODE_REASON)
|
|
77
|
+
cctx = require_auth(state)
|
|
78
|
+
# Explicitly the newest version, not the first one the server happened to
|
|
79
|
+
# list. `next(...)` returned whichever came back first, so the source shown
|
|
80
|
+
# was correct only for as long as the endpoint kept returning descending
|
|
81
|
+
# versions — and a stale source rendered identically to a current one.
|
|
82
|
+
versions = [p for p in api.list_policies(cctx) if p.id == policy_id]
|
|
83
|
+
if not versions:
|
|
84
|
+
raise NotFoundError(f"no policy named {policy_id}")
|
|
85
|
+
match = max(versions, key=lambda p: p.version)
|
|
86
|
+
if output.is_json():
|
|
87
|
+
output.emit_json(match.to_dict())
|
|
88
|
+
return
|
|
89
|
+
carriers = {
|
|
90
|
+
d.machine_id: ref.version
|
|
91
|
+
for d in api.list_deployments(cctx)
|
|
92
|
+
for ref in d.policies
|
|
93
|
+
if ref.id == policy_id
|
|
94
|
+
}
|
|
95
|
+
output.render_policy_published(match, carriers=carriers,
|
|
96
|
+
source_bytes=len((match.source or "").encode("utf-8")))
|
|
97
|
+
if match.source:
|
|
98
|
+
output.info(match.source)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def policies_publish(
|
|
102
|
+
ctx: typer.Context,
|
|
103
|
+
policy_id: str = typer.Argument(..., help="Policy id (letters, numbers, '.', '_', '-')."),
|
|
104
|
+
source: Optional[str] = typer.Argument(
|
|
105
|
+
None,
|
|
106
|
+
help="Path to the policy source, @path, or - for stdin. Omit to paste it.",
|
|
107
|
+
),
|
|
108
|
+
description: str = typer.Option("", "--description", help="One-line description."),
|
|
109
|
+
no_verify: bool = typer.Option(
|
|
110
|
+
False, "--no-verify", help="Skip the JavaScript syntax check before publishing."
|
|
111
|
+
),
|
|
112
|
+
) -> None:
|
|
113
|
+
"""Publish a policy — mints a NEW VERSION; it never edits one in place.
|
|
114
|
+
|
|
115
|
+
The source is parse-checked with node before it is sent. Nothing downstream
|
|
116
|
+
does this: the server validates the id and a size ceiling, and a broken
|
|
117
|
+
policy otherwise fails on the machine at enforcement time. `--no-verify`
|
|
118
|
+
skips it; a host without node publishes with a warning rather than a block.
|
|
119
|
+
|
|
120
|
+
Source can come from a path, `@path`, a pipe, `-`, or an interactive paste
|
|
121
|
+
when you give none and stdin is a terminal.
|
|
122
|
+
|
|
123
|
+
**Publishing deploys nothing.** A new version sits unused until
|
|
124
|
+
`fp fleet deploy` puts it on a machine. Needs `policies:write`.
|
|
125
|
+
With `--json`: the created version plus `carriers` — a map of machine id to
|
|
126
|
+
the version of this policy it currently runs, so a harness can tell what a
|
|
127
|
+
publish left behind without a second call.
|
|
128
|
+
|
|
129
|
+
Examples:
|
|
130
|
+
|
|
131
|
+
* `fp policies publish no-force-push ./rule.mjs`
|
|
132
|
+
* `cat rule.mjs | fp policies publish no-force-push`
|
|
133
|
+
* `fp policies publish no-force-push -` — read stdin explicitly
|
|
134
|
+
"""
|
|
135
|
+
state: AppState = ctx.obj
|
|
136
|
+
deny_in_key_mode(state, "policies publish", _KEY_MODE_REASON)
|
|
137
|
+
cctx = require_auth(state)
|
|
138
|
+
|
|
139
|
+
def _paste_prompt() -> None:
|
|
140
|
+
output.hint("paste the policy source, then press Ctrl-D")
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
text = read_source(source, prompt=_paste_prompt)
|
|
144
|
+
except RefUsageError as exc:
|
|
145
|
+
raise click.UsageError(str(exc))
|
|
146
|
+
except RefError as exc:
|
|
147
|
+
raise ApiError(str(exc))
|
|
148
|
+
if not text.strip():
|
|
149
|
+
raise ApiError("policy source is empty — nothing to publish")
|
|
150
|
+
|
|
151
|
+
# Nothing downstream parses this. The server checks the id and a size
|
|
152
|
+
# ceiling; the machines find out at enforcement time, which is the worst
|
|
153
|
+
# place for a syntax error to surface. `--no-verify` exists because a
|
|
154
|
+
# machine without node should still be able to publish, not because
|
|
155
|
+
# skipping is ever a good idea.
|
|
156
|
+
if not no_verify:
|
|
157
|
+
syn = check_syntax(text)
|
|
158
|
+
if not syn.ok:
|
|
159
|
+
raise ApiError(
|
|
160
|
+
f"{policy_id} is not parseable JavaScript — refusing to publish it:\n"
|
|
161
|
+
f"{syn.message}",
|
|
162
|
+
hint="fix the syntax, or pass --no-verify to publish it anyway",
|
|
163
|
+
)
|
|
164
|
+
if not syn.checked and not output.is_json():
|
|
165
|
+
output.warn(syn.message)
|
|
166
|
+
|
|
167
|
+
created = api.publish_policy(cctx, policy_id, text, description)
|
|
168
|
+
|
|
169
|
+
# Which machines already carry this policy, and at which version. Publishing
|
|
170
|
+
# deploys nothing, so this is the one thing the card must not guess at: it
|
|
171
|
+
# used to state "not deployed anywhere" unconditionally, which was wrong for
|
|
172
|
+
# every policy that already had a version in the field.
|
|
173
|
+
carriers = {
|
|
174
|
+
d.machine_id: ref.version
|
|
175
|
+
for d in api.list_deployments(cctx)
|
|
176
|
+
for ref in d.policies
|
|
177
|
+
if ref.id == policy_id
|
|
178
|
+
}
|
|
179
|
+
if output.is_json():
|
|
180
|
+
# `syntax` included, exactly as `policies test` does. `SyntaxResult.checked`
|
|
181
|
+
# exists so that "we did not look" can never render as "we looked and it
|
|
182
|
+
# passed" — and the only report of it was gated on `not output.is_json()`,
|
|
183
|
+
# so on the automated path an UNCHECKED publish was byte-identical to a
|
|
184
|
+
# verified one. A CI container without node published unparsed source,
|
|
185
|
+
# the harness read success, and the syntax error surfaced at enforcement
|
|
186
|
+
# time on the machines — the one place `policy_check`'s docstring exists
|
|
187
|
+
# to move it away from.
|
|
188
|
+
output.emit_json({**created.to_dict(), "carriers": carriers, "syntax": syn.to_dict()})
|
|
189
|
+
return
|
|
190
|
+
output.render_policy_published(created, carriers=carriers,
|
|
191
|
+
source_bytes=len(text.encode("utf-8")))
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def policies_enable(
|
|
195
|
+
ctx: typer.Context,
|
|
196
|
+
policy_id: str = typer.Argument(..., help="Policy id."),
|
|
197
|
+
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."),
|
|
198
|
+
) -> None:
|
|
199
|
+
"""Re-enable a disabled policy, restoring it to the machines that lost it.
|
|
200
|
+
|
|
201
|
+
The exact inverse of `disable`: the server puts the policy back into every
|
|
202
|
+
deployment it was removed from, advancing each machine's generation again.
|
|
203
|
+
Nothing needs redeploying by hand.
|
|
204
|
+
|
|
205
|
+
Needs `policies:write`. With `--json`: `{id, disabled, archived,
|
|
206
|
+
machinesUpdated}` — `machinesUpdated` counts the deployments rewritten, and
|
|
207
|
+
matches the count the preceding `disable` reported.
|
|
208
|
+
|
|
209
|
+
Example:
|
|
210
|
+
|
|
211
|
+
* `fp policies enable no-force-push --yes`
|
|
212
|
+
"""
|
|
213
|
+
state: AppState = ctx.obj
|
|
214
|
+
deny_in_key_mode(state, "policies enable", _KEY_MODE_REASON)
|
|
215
|
+
cctx = require_auth(state)
|
|
216
|
+
# Confirmed, like its exact inverse. `disable` took `--yes` and prompted;
|
|
217
|
+
# this fired the moment it was typed, with no prompt and no way to withhold
|
|
218
|
+
# one — on a fleet-wide mutation with the same blast radius in the opposite
|
|
219
|
+
# direction: re-arming a policy can start denying tool calls on every machine
|
|
220
|
+
# at its next poll. It was the only deployment-mutating command in the CLI
|
|
221
|
+
# with neither (`fleet deploy`, `fleet rollback`, `policies disable` and
|
|
222
|
+
# `policies delete` all confirm), and a mistyped id reached the server
|
|
223
|
+
# unchecked. Calm glyph rather than the destructive ⚠, matching `users
|
|
224
|
+
# enable`: this is restorative, not dangerous — but it is not a read.
|
|
225
|
+
if not _write.confirm_action(
|
|
226
|
+
state, "enable policy", policy_id,
|
|
227
|
+
consequence=("it is added back to every deployment it was removed from, "
|
|
228
|
+
"minting a new generation on each"),
|
|
229
|
+
assume_yes=yes, glyph="↑", color=theme.ACCENT,
|
|
230
|
+
):
|
|
231
|
+
if output.is_json():
|
|
232
|
+
output.emit_json({"cancelled": True})
|
|
233
|
+
else:
|
|
234
|
+
output.print_cancelled()
|
|
235
|
+
return
|
|
236
|
+
res = api.set_policy_enabled(cctx, policy_id, True)
|
|
237
|
+
if output.is_json():
|
|
238
|
+
output.emit_json(res)
|
|
239
|
+
return
|
|
240
|
+
output.policy_lifecycle_changed(policy_id, "enabled")
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def policies_disable(
|
|
244
|
+
ctx: typer.Context,
|
|
245
|
+
policy_id: str = typer.Argument(..., help="Policy id."),
|
|
246
|
+
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."),
|
|
247
|
+
) -> None:
|
|
248
|
+
"""Disable a policy. It is removed from every deployment carrying it.
|
|
249
|
+
|
|
250
|
+
Not just "machines stop enforcing it": the server reissues each affected
|
|
251
|
+
machine's deployment WITHOUT this policy, advancing that machine's
|
|
252
|
+
generation. `fp fleet history` shows the reissue as an ordinary entry.
|
|
253
|
+
|
|
254
|
+
`policies enable` is the exact inverse: it puts the policy back into every
|
|
255
|
+
deployment it was removed from, so nothing needs redeploying by hand.
|
|
256
|
+
|
|
257
|
+
Needs `policies:write`. With `--json`: `{id, disabled, archived,
|
|
258
|
+
machinesUpdated}` — `machinesUpdated` is how many deployments were rewritten
|
|
259
|
+
to drop it, and is the number to check if you expected this to be a no-op.
|
|
260
|
+
|
|
261
|
+
Example:
|
|
262
|
+
|
|
263
|
+
* `fp policies disable no-force-push --yes`
|
|
264
|
+
"""
|
|
265
|
+
state: AppState = ctx.obj
|
|
266
|
+
deny_in_key_mode(state, "policies disable", _KEY_MODE_REASON)
|
|
267
|
+
cctx = require_auth(state)
|
|
268
|
+
if not _write.confirm_destructive(
|
|
269
|
+
state, "disable policy", policy_id,
|
|
270
|
+
consequence=("it is REMOVED from every deployment carrying it, minting a new "
|
|
271
|
+
"generation on each; `policies enable` puts it back the same way"),
|
|
272
|
+
assume_yes=yes,
|
|
273
|
+
):
|
|
274
|
+
if output.is_json():
|
|
275
|
+
output.emit_json({"cancelled": True})
|
|
276
|
+
else:
|
|
277
|
+
output.print_cancelled()
|
|
278
|
+
return
|
|
279
|
+
res = api.set_policy_enabled(cctx, policy_id, False)
|
|
280
|
+
if output.is_json():
|
|
281
|
+
output.emit_json(res)
|
|
282
|
+
return
|
|
283
|
+
output.policy_lifecycle_changed(policy_id, "disabled")
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def policies_delete(
|
|
287
|
+
ctx: typer.Context,
|
|
288
|
+
policy_id: str = typer.Argument(..., help="Policy id."),
|
|
289
|
+
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."),
|
|
290
|
+
) -> None:
|
|
291
|
+
"""Archive a policy. This cannot be undone from the CLI.
|
|
292
|
+
|
|
293
|
+
Archiving hides it from `policies list` and from future deployments. A
|
|
294
|
+
machine already carrying it keeps enforcing it until something redeploys —
|
|
295
|
+
deleting is not a way to stop enforcement everywhere, and `disable` is.
|
|
296
|
+
|
|
297
|
+
Needs `policies:write`. With `--json`: `{id, disabled, archived,
|
|
298
|
+
machinesUpdated}`, or `{"cancelled": true}` if you decline.
|
|
299
|
+
|
|
300
|
+
Example:
|
|
301
|
+
|
|
302
|
+
* `fp policies delete old-rule --yes`
|
|
303
|
+
"""
|
|
304
|
+
state: AppState = ctx.obj
|
|
305
|
+
deny_in_key_mode(state, "policies delete", _KEY_MODE_REASON)
|
|
306
|
+
cctx = require_auth(state)
|
|
307
|
+
if not _write.confirm_destructive(
|
|
308
|
+
state, "archive policy", policy_id,
|
|
309
|
+
consequence=("machines already carrying it keep enforcing until redeployed — "
|
|
310
|
+
"`policies disable` is what stops enforcement"),
|
|
311
|
+
assume_yes=yes,
|
|
312
|
+
):
|
|
313
|
+
if output.is_json():
|
|
314
|
+
output.emit_json({"cancelled": True})
|
|
315
|
+
else:
|
|
316
|
+
output.print_cancelled()
|
|
317
|
+
return
|
|
318
|
+
res = api.delete_policy(cctx, policy_id)
|
|
319
|
+
if output.is_json():
|
|
320
|
+
output.emit_json(res)
|
|
321
|
+
return
|
|
322
|
+
output.policy_lifecycle_changed(policy_id, "archived")
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def policies_test(
|
|
326
|
+
ctx: typer.Context,
|
|
327
|
+
source: Optional[str] = typer.Argument(
|
|
328
|
+
None, help="Policy file, @path, or - for stdin. Omit to paste it."
|
|
329
|
+
),
|
|
330
|
+
tool: str = typer.Option("Bash", "--tool", help="Tool name the hook fired for."),
|
|
331
|
+
command: Optional[str] = typer.Option(None, "--command", help="Bash command to test against."),
|
|
332
|
+
file_path: Optional[str] = typer.Option(None, "--file", help="File path to test against."),
|
|
333
|
+
event: str = typer.Option("PreToolUse", "--event", help="Hook event type."),
|
|
334
|
+
expect: Optional[str] = typer.Option(
|
|
335
|
+
None, "--expect",
|
|
336
|
+
help="Assert the decision is allow/deny/instruct; exit 1 if it is not.",
|
|
337
|
+
),
|
|
338
|
+
) -> None:
|
|
339
|
+
"""Run a policy locally and print what it would decide. No server, no fleet.
|
|
340
|
+
|
|
341
|
+
Executes the real file — bare `import { deny } from "failproofai"` and all —
|
|
342
|
+
against a context you describe, and prints allow / deny / instruct per
|
|
343
|
+
registered policy. Nothing is published and nothing is installed.
|
|
344
|
+
|
|
345
|
+
Needs `node` on PATH. This is a dry run, not the enforcement path: it proves
|
|
346
|
+
the policy parses, registers and decides for the input given. It cannot
|
|
347
|
+
prove the daemon feeds it the same context.
|
|
348
|
+
|
|
349
|
+
With `--json`: `{ok, decision, policies:[{name, decision, reason}]}` — the
|
|
350
|
+
overall `decision` is the strictest any policy returned.
|
|
351
|
+
|
|
352
|
+
Examples:
|
|
353
|
+
|
|
354
|
+
* `fp policies test ./rule.mjs --command "git push --force"`
|
|
355
|
+
* `fp policies test ./rule.mjs --tool Write --file .env`
|
|
356
|
+
"""
|
|
357
|
+
# No `require_auth` and no `deny_in_key_mode`: this command talks to node,
|
|
358
|
+
# not to the dashboard, so it works logged out and under an API key alike.
|
|
359
|
+
|
|
360
|
+
def _paste_prompt() -> None:
|
|
361
|
+
output.hint("paste the policy source, then press Ctrl-D")
|
|
362
|
+
|
|
363
|
+
try:
|
|
364
|
+
text = read_source(source, prompt=_paste_prompt)
|
|
365
|
+
except RefUsageError as exc:
|
|
366
|
+
raise click.UsageError(str(exc))
|
|
367
|
+
except RefError as exc:
|
|
368
|
+
raise ApiError(str(exc))
|
|
369
|
+
if not text.strip():
|
|
370
|
+
raise ApiError("policy source is empty — nothing to test")
|
|
371
|
+
|
|
372
|
+
# Checked before the syntax check runs, so a bad --expect reports itself
|
|
373
|
+
# rather than being masked by whatever node says about the file. A usage
|
|
374
|
+
# error should never depend on the content of an argument.
|
|
375
|
+
if expect is not None and expect not in ("allow", "deny", "instruct"):
|
|
376
|
+
raise typer.BadParameter(
|
|
377
|
+
f"invalid --expect value {expect!r}; choose one of allow, deny, instruct"
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
syn = check_syntax(text)
|
|
381
|
+
if not syn.ok:
|
|
382
|
+
if output.is_json():
|
|
383
|
+
output.emit_json({"ok": False, "syntax": syn.to_dict(), "policies": []})
|
|
384
|
+
raise typer.Exit(1)
|
|
385
|
+
raise ApiError(f"the policy is not parseable JavaScript:\n{syn.message}")
|
|
386
|
+
|
|
387
|
+
run = run_policy(text, tool=tool, command=command, file_path=file_path, event=event)
|
|
388
|
+
|
|
389
|
+
# A policy that correctly denies is a SUCCESSFUL test, so the decision does
|
|
390
|
+
# not set the exit code on its own — otherwise `policies test` would fail
|
|
391
|
+
# whenever the policy worked. `--expect` is how CI asserts instead: it turns
|
|
392
|
+
# "what did it decide" into "did it decide what I meant".
|
|
393
|
+
met = expect is None or run.decision == expect
|
|
394
|
+
if output.is_json():
|
|
395
|
+
output.emit_json({**run.to_dict(), "syntax": syn.to_dict(),
|
|
396
|
+
"expected": expect, "met": met})
|
|
397
|
+
raise typer.Exit(0 if (run.ok and met) else 1)
|
|
398
|
+
if not run.ok:
|
|
399
|
+
raise ApiError(run.error)
|
|
400
|
+
output.render_policy_test(run, tool=tool, command=command, file_path=file_path,
|
|
401
|
+
expected=expect)
|
|
402
|
+
if not met:
|
|
403
|
+
raise typer.Exit(1)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def policies_compose(
|
|
407
|
+
ctx: typer.Context,
|
|
408
|
+
prompt: str = typer.Argument(..., help="What the policy should do, in plain English."),
|
|
409
|
+
out: Optional[str] = typer.Option(None, "--out", help="Write the draft to this file."),
|
|
410
|
+
publish_as: Optional[str] = typer.Option(
|
|
411
|
+
None, "--publish", help="Publish the draft immediately under this policy id."
|
|
412
|
+
),
|
|
413
|
+
) -> None:
|
|
414
|
+
"""Draft a policy from a description, using the Cloud assistant.
|
|
415
|
+
|
|
416
|
+
The assistant writes the source; **you** decide whether it ships. By default
|
|
417
|
+
the draft is printed and nothing else happens — a generated policy that
|
|
418
|
+
deploys itself is a generated policy nobody read.
|
|
419
|
+
|
|
420
|
+
`--out` saves it; `--publish <id>` publishes it, still syntax-checked first.
|
|
421
|
+
Needs `policies:write` — for the draft as well as the publish. The route is
|
|
422
|
+
`POST /api/agent/compose-policy`, which the dashboard exports as
|
|
423
|
+
`withAuth("policies:write", …)`; `agent:use` gates the assistant's other
|
|
424
|
+
routes (chat, answer, conversations) and is NOT checked on this one. So a
|
|
425
|
+
role holding only `agent:use` is refused here, and one holding
|
|
426
|
+
`policies:write` without it works. Session-only.
|
|
427
|
+
|
|
428
|
+
With `--json`: `{prompt, source, syntax, published}`.
|
|
429
|
+
|
|
430
|
+
Examples:
|
|
431
|
+
|
|
432
|
+
* `fp policies compose "block force pushes to main"`
|
|
433
|
+
* `fp policies compose "deny reading .env" --out env.mjs`
|
|
434
|
+
"""
|
|
435
|
+
state: AppState = ctx.obj
|
|
436
|
+
deny_in_key_mode(state, "policies compose", _KEY_MODE_REASON)
|
|
437
|
+
cctx = require_auth(state)
|
|
438
|
+
|
|
439
|
+
with output.thinking("drafting…", enabled=not output.is_json()):
|
|
440
|
+
res = api.compose_policy(cctx, prompt)
|
|
441
|
+
source = (res or {}).get("source") or (res or {}).get("policy") or ""
|
|
442
|
+
if not source.strip():
|
|
443
|
+
raise ApiError(
|
|
444
|
+
"the assistant returned no policy source",
|
|
445
|
+
hint="check `fp agent health` — the assistant may not be configured here",
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
syn = check_syntax(source)
|
|
449
|
+
|
|
450
|
+
# Saved BEFORE anything that can fail. `--out` used to run after the
|
|
451
|
+
# publish, so a publish that was refused — bad syntax, no `policies:write`,
|
|
452
|
+
# a network blip — threw away the draft the user had just paid an assistant
|
|
453
|
+
# to write, with no way to get that same text back.
|
|
454
|
+
if out:
|
|
455
|
+
try:
|
|
456
|
+
with open(out, "w", encoding="utf-8") as fh:
|
|
457
|
+
fh.write(source)
|
|
458
|
+
except OSError as exc:
|
|
459
|
+
raise ApiError(f"cannot write {out}: {exc.strerror or exc}")
|
|
460
|
+
|
|
461
|
+
published = None
|
|
462
|
+
if publish_as:
|
|
463
|
+
if not syn.ok:
|
|
464
|
+
raise ApiError(
|
|
465
|
+
f"the drafted policy is not parseable JavaScript — refusing to publish:\n"
|
|
466
|
+
f"{syn.message}",
|
|
467
|
+
hint=("fix it and publish it with `fp policies publish`"
|
|
468
|
+
if out else "save it with --out, fix it, then publish"),
|
|
469
|
+
)
|
|
470
|
+
published = api.publish_policy(cctx, publish_as, source, f"drafted: {prompt}"[:500])
|
|
471
|
+
|
|
472
|
+
if output.is_json():
|
|
473
|
+
output.emit_json({
|
|
474
|
+
"prompt": prompt, "source": source, "syntax": syn.to_dict(),
|
|
475
|
+
"published": published.to_dict() if published else None,
|
|
476
|
+
"savedTo": out,
|
|
477
|
+
})
|
|
478
|
+
return
|
|
479
|
+
output.render_composed_policy(prompt, source, syn, saved_to=out)
|
|
480
|
+
if published:
|
|
481
|
+
output.policy_published_brief(published)
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def register(app: typer.Typer) -> None:
|
|
485
|
+
policies_app = typer.Typer(
|
|
486
|
+
no_args_is_help=True,
|
|
487
|
+
rich_markup_mode="markdown",
|
|
488
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
489
|
+
help="Write and manage cloud-managed policies (list / show / publish / enable / disable / delete).",
|
|
490
|
+
)
|
|
491
|
+
policies_app.command("list", epilog=GLOBALS_EPILOG)(policies_list)
|
|
492
|
+
policies_app.command("show", epilog=GLOBALS_EPILOG)(policies_show)
|
|
493
|
+
policies_app.command("publish", epilog=GLOBALS_EPILOG)(policies_publish)
|
|
494
|
+
policies_app.command("enable", epilog=GLOBALS_EPILOG)(policies_enable)
|
|
495
|
+
policies_app.command("disable", epilog=GLOBALS_EPILOG)(policies_disable)
|
|
496
|
+
policies_app.command("delete", epilog=GLOBALS_EPILOG)(policies_delete)
|
|
497
|
+
policies_app.command("test", epilog=GLOBALS_EPILOG)(policies_test)
|
|
498
|
+
policies_app.command("compose", epilog=GLOBALS_EPILOG)(policies_compose)
|
|
499
|
+
app.add_typer(policies_app, name="policies")
|