failproofai 1.0.1 → 1.0.2-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 +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]__0l4748x._.js +3 -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]__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/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bwu160._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0u5zauv._.js → _0ns6k3a._.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/{3c808qrfcq0im.js → 01u1cf8js2yd6.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0k5050ezjvv49.js → 0izgu7nqh2zp-.js} +1 -1
- package/.next/standalone/.next/static/chunks/0u4-org_e7zde.js +1 -0
- package/.next/standalone/.next/static/chunks/1mfzsja6ftim5.css +2 -0
- package/.next/standalone/.next/static/chunks/{2zpp1we2jv6tb.js → 1tynnrrh711lr.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1ide1wl2f7zi1.js → 22rqvlr7p_1mv.js} +13 -13
- package/.next/standalone/.next/static/chunks/{31s1smp-g7gks.js → 2whxqyskpirbk.js} +1 -1
- package/.next/standalone/.next/static/chunks/31mwk8k0q3_3g.js +6 -0
- package/.next/standalone/.next/static/chunks/3ms6gxfm82t6s.js +1 -0
- package/.next/standalone/.next/static/chunks/{37fcpc36x9gpe.js → 3tbbs_d8xrvyp.js} +1 -1
- package/.next/standalone/.next/static/chunks/43nlc236oo898.js +1 -0
- 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 +163 -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 -15
- 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 +27 -21
- 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 +579 -306
- package/dist/cli.mjs +6499 -3662
- package/dist/index.js +5 -5
- package/dist/worker.mjs +2390 -1978
- package/package.json +27 -21
- 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 +56 -369
- package/src/hooks/custom-hooks-loader.ts +89 -6
- package/src/hooks/fp-home.ts +92 -4
- package/src/hooks/fp-reset.ts +34 -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 +424 -172
- package/src/hooks/migrations.ts +2 -2
- package/src/hooks/pack-cli.ts +885 -0
- package/src/hooks/pack-failclosed.ts +189 -0
- package/src/hooks/pack-manifest.ts +352 -0
- package/src/hooks/pack-store.ts +891 -0
- package/src/hooks/policy-catalog.ts +535 -0
- package/src/hooks/policy-evaluator.ts +24 -16
- 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 +695 -39
- 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]__07tbbf3._.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/server/chunks/ssr/_0lvyuv4._.js +0 -23
- package/.next/standalone/.next/static/chunks/2_2lhuojq2xop.js +0 -1
- package/.next/standalone/.next/static/chunks/36fw6yk1s6x_f.js +0 -1
- package/.next/standalone/.next/static/chunks/3k7mxbp9hiom0.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/src/hooks/policy-presets.ts +0 -135
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_ssgManifest.js +0 -0
package/src/hooks/manager.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Install/remove/list failproofai hooks for one or more agent
|
|
2
|
+
* Install/remove/list failproofai hooks for one or more agent harnesses.
|
|
3
3
|
*
|
|
4
4
|
* Per-CLI path resolution and settings I/O live in `./integrations` (one
|
|
5
5
|
* `Integration` impl per CLI). This module orchestrates: validation, policy
|
|
@@ -26,6 +26,23 @@ import { CliError } from "../cli-error";
|
|
|
26
26
|
import { hookLogWarn } from "./hook-logger";
|
|
27
27
|
import { customPoliciesDir, globalPolicyConfigFile } from "./fp-home";
|
|
28
28
|
import { readActiveCloudManagedPolicies } from "./cloud-managed-policies";
|
|
29
|
+
import { CORE_SOURCE, addPack, setPackPolicyEnabled } from "./pack-store";
|
|
30
|
+
import type { ResolvedPack } from "./pack-manifest";
|
|
31
|
+
import { hasInstalledPacks, readInstalledPacks } from "./pack-manifest";
|
|
32
|
+
import {
|
|
33
|
+
chip,
|
|
34
|
+
note,
|
|
35
|
+
nextStep,
|
|
36
|
+
optsFor,
|
|
37
|
+
printBlock,
|
|
38
|
+
rule,
|
|
39
|
+
stack,
|
|
40
|
+
table,
|
|
41
|
+
title,
|
|
42
|
+
warning,
|
|
43
|
+
type ChipState,
|
|
44
|
+
type TableRow,
|
|
45
|
+
} from "./tui";
|
|
29
46
|
|
|
30
47
|
const VALID_POLICY_NAMES = new Set(BUILTIN_POLICIES.map((p) => p.name));
|
|
31
48
|
|
|
@@ -63,13 +80,167 @@ function resolveFailproofaiBinary(): string {
|
|
|
63
80
|
}
|
|
64
81
|
}
|
|
65
82
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
83
|
+
/** One policy of one installed pack, resolved from a name the user typed. */
|
|
84
|
+
interface PackPolicyRef {
|
|
85
|
+
packId: string;
|
|
86
|
+
packVersion: string;
|
|
87
|
+
name: string;
|
|
88
|
+
/** The `disabledCustomPolicies` entry the dashboard writes for it. */
|
|
89
|
+
disabledKey: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Split names the user typed into builtins and installed-pack policies.
|
|
94
|
+
*
|
|
95
|
+
* Without this, every name went through `validatePolicyNames`, whose set is the
|
|
96
|
+
* compiled builtins — so `policies --disable block-big-refund` on a pack the
|
|
97
|
+
* user had just installed answered "Unknown policy name" and listed 39 names
|
|
98
|
+
* that were not the one they meant. A pack could be installed and then not
|
|
99
|
+
* managed at all.
|
|
100
|
+
*
|
|
101
|
+
* A builtin wins a bare name, because that is the name people have typed for a
|
|
102
|
+
* year and a third-party pack must not be able to capture it. Two packs
|
|
103
|
+
* declaring the same name is the one case that cannot be guessed, so it is
|
|
104
|
+
* refused with the qualified `<pack-id>:<name>` form spelled out.
|
|
105
|
+
*/
|
|
106
|
+
function resolvePolicyNames(names: string[]): { builtins: string[]; packs: PackPolicyRef[] } {
|
|
107
|
+
const builtins: string[] = [];
|
|
108
|
+
const packs: PackPolicyRef[] = [];
|
|
109
|
+
const unknown: string[] = [];
|
|
110
|
+
|
|
111
|
+
let installed: ResolvedPack[] = [];
|
|
112
|
+
try {
|
|
113
|
+
installed = readInstalledPacks().packs;
|
|
114
|
+
} catch {
|
|
115
|
+
// No packs, or an unreadable manifest: names simply resolve as builtins and
|
|
116
|
+
// an unknown one gets the ordinary error. A listing-adjacent command must
|
|
117
|
+
// not fail because a pack manifest is corrupt.
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const refsFor = (packId: string | null, policyName: string): PackPolicyRef[] =>
|
|
121
|
+
installed
|
|
122
|
+
.filter((pack) => (packId === null || pack.id === packId))
|
|
123
|
+
.filter((pack) => pack.policies.some((p) => p.name === policyName))
|
|
124
|
+
.map((pack) => ({
|
|
125
|
+
packId: pack.id,
|
|
126
|
+
packVersion: pack.version,
|
|
127
|
+
name: policyName,
|
|
128
|
+
disabledKey: `pack:${pack.id}@${pack.version}:${policyName}`,
|
|
129
|
+
}));
|
|
130
|
+
|
|
131
|
+
for (const raw of names) {
|
|
132
|
+
// A PACK first, then the compiled set. This order is the whole fix for
|
|
133
|
+
// `policy remove block-sudo` printing "Disabled 0" while `block-sudo` kept
|
|
134
|
+
// denying: the name resolved to a builtin, the command edited
|
|
135
|
+
// `enabledPolicies`, and `enabledPolicies` stopped deciding anything when
|
|
136
|
+
// this build stopped registering builtins. The pack is where the switch is.
|
|
137
|
+
const direct = refsFor(null, raw);
|
|
138
|
+
if (direct.length === 1) {
|
|
139
|
+
packs.push(direct[0]);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (direct.length > 1) {
|
|
143
|
+
throw new CliError(
|
|
144
|
+
`"${raw}" is declared by ${direct.length} installed packs.\n` +
|
|
145
|
+
`Name the one you mean:\n` +
|
|
146
|
+
direct.map((m) => ` ${m.packId}:${m.name}`).join("\n"),
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
// No pack carries it. Falls back to the compiled name set, which is what a
|
|
150
|
+
// machine still running on the migration shim has.
|
|
151
|
+
if (VALID_POLICY_NAMES.has(raw)) {
|
|
152
|
+
builtins.push(raw);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
// `acme/finance:block-big-refund` — a pack id holds a slash, never a colon,
|
|
156
|
+
// so the last colon separates them unambiguously.
|
|
157
|
+
const colon = raw.lastIndexOf(":");
|
|
158
|
+
const qualified = colon > 0
|
|
159
|
+
? { packId: raw.slice(0, colon), name: raw.slice(colon + 1) }
|
|
160
|
+
: null;
|
|
161
|
+
const matches = qualified
|
|
162
|
+
? refsFor(qualified.packId, qualified.name)
|
|
163
|
+
: refsFor(null, raw);
|
|
164
|
+
|
|
165
|
+
if (matches.length === 1) {
|
|
166
|
+
packs.push(matches[0]);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (matches.length > 1) {
|
|
170
|
+
throw new CliError(
|
|
171
|
+
`"${raw}" is declared by ${matches.length} installed packs.\n` +
|
|
172
|
+
`Name the one you mean:\n` +
|
|
173
|
+
matches.map((m) => ` ${m.packId}:${m.name}`).join("\n"),
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
unknown.push(raw);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (unknown.length > 0) {
|
|
180
|
+
const packNames = installed.flatMap((pack) =>
|
|
181
|
+
pack.policies.map((p) => `${pack.id}:${p.name}`),
|
|
182
|
+
);
|
|
70
183
|
throw new CliError(
|
|
71
|
-
`Unknown policy name(s): ${
|
|
72
|
-
|
|
184
|
+
`Unknown policy name(s): ${unknown.join(", ")}\n` +
|
|
185
|
+
`Valid policies: ${[...VALID_POLICY_NAMES].join(", ")}` +
|
|
186
|
+
(packNames.length > 0 ? `\nFrom installed packs: ${packNames.join(", ")}` : ""),
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
return { builtins, packs };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Turn pack policies on or off, and say what happened. */
|
|
193
|
+
function applyPackPolicies(
|
|
194
|
+
refs: PackPolicyRef[],
|
|
195
|
+
on: boolean,
|
|
196
|
+
scope: HookScope,
|
|
197
|
+
cwd?: string,
|
|
198
|
+
): void {
|
|
199
|
+
if (refs.length === 0) return;
|
|
200
|
+
for (const ref of refs) {
|
|
201
|
+
const result = setPackPolicyEnabled(ref.packId, ref.name, on);
|
|
202
|
+
if (!result.ok) {
|
|
203
|
+
throw new CliError(`Could not ${on ? "enable" : "disable"} ${ref.name}: ${result.reason}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (on) {
|
|
207
|
+
// Clearing the dashboard's key too. The selection and the disabled key are
|
|
208
|
+
// two different switches for one policy, and leaving the second one set
|
|
209
|
+
// would report the policy enabled while it stayed off.
|
|
210
|
+
const config = readScopedHooksConfig(scope, cwd);
|
|
211
|
+
const keys = new Set(refs.map((r) => r.disabledKey));
|
|
212
|
+
const remaining = (config.disabledCustomPolicies ?? []).filter((k) => !keys.has(k));
|
|
213
|
+
if (remaining.length !== (config.disabledCustomPolicies ?? []).length) {
|
|
214
|
+
const next: HooksConfig = { ...config, disabledCustomPolicies: remaining };
|
|
215
|
+
if (remaining.length === 0) delete next.disabledCustomPolicies;
|
|
216
|
+
writeScopedHooksConfig(next, scope, cwd);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
for (const ref of refs) {
|
|
220
|
+
console.log(
|
|
221
|
+
`${on ? "Enabled" : "Disabled"} ${ref.name} from pack ${ref.packId}@${ref.packVersion}.`,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Refuse to "disable" a policy that will register anyway.
|
|
228
|
+
*
|
|
229
|
+
* Removing an `alwaysOn` name from `enabledPolicies` succeeds at the file level
|
|
230
|
+
* and changes nothing at the enforcement level, so without this the CLI reports
|
|
231
|
+
* a policy disabled while it keeps denying — the operator's mental model and the
|
|
232
|
+
* machine's behaviour diverge silently, which is the failure this policy exists
|
|
233
|
+
* to prevent in the first place.
|
|
234
|
+
*/
|
|
235
|
+
function rejectAlwaysOnPolicies(names: string[]): void {
|
|
236
|
+
const alwaysOn = new Set(BUILTIN_POLICIES.filter((p) => p.alwaysOn).map((p) => p.name));
|
|
237
|
+
const refused = names.filter((n) => alwaysOn.has(n));
|
|
238
|
+
if (refused.length > 0) {
|
|
239
|
+
throw new CliError(
|
|
240
|
+
`Cannot disable: ${refused.join(", ")}\n` +
|
|
241
|
+
`This policy stops an agent from switching off failproofai itself, so it ` +
|
|
242
|
+
`is always on and ships with the package. A guard the agent can disable ` +
|
|
243
|
+
`by the means it is meant to prevent is not a guard.`
|
|
73
244
|
);
|
|
74
245
|
}
|
|
75
246
|
}
|
|
@@ -151,8 +322,21 @@ async function installHooksImpl(
|
|
|
151
322
|
// Validate user input first before any system checks
|
|
152
323
|
if (policyNames !== undefined && policyNames.length > 0) {
|
|
153
324
|
const nonAllNames = policyNames.filter((n) => n !== "all");
|
|
154
|
-
// Check unknown names first (most actionable error for the user)
|
|
155
|
-
|
|
325
|
+
// Check unknown names first (most actionable error for the user). Pack
|
|
326
|
+
// policies are applied here and taken out of the list: the rest of this
|
|
327
|
+
// function writes `enabledPolicies`, which is a builtin-only set.
|
|
328
|
+
if (nonAllNames.length > 0) {
|
|
329
|
+
const resolved = resolvePolicyNames(nonAllNames);
|
|
330
|
+
applyPackPolicies(resolved.packs, true, scope, cwd);
|
|
331
|
+
if (resolved.packs.length > 0) {
|
|
332
|
+
policyNames = policyNames.filter((n) => n === "all" || resolved.builtins.includes(n));
|
|
333
|
+
// Named ONLY pack policies: the work is done. Carrying on would resolve
|
|
334
|
+
// the failproofai binary and rewrite every CLI's settings to enable a
|
|
335
|
+
// set of builtins nobody asked about — and would fail outright on a
|
|
336
|
+
// machine where the binary is not on PATH, AFTER the pack change landed.
|
|
337
|
+
if (policyNames.length === 0) return;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
156
340
|
// Then check if "all" is mixed with valid specific names
|
|
157
341
|
if (policyNames.includes("all") && nonAllNames.length > 0) {
|
|
158
342
|
throw new CliError(
|
|
@@ -290,6 +474,55 @@ async function installHooksImpl(
|
|
|
290
474
|
}
|
|
291
475
|
}
|
|
292
476
|
writeScopedHooksConfig(configToWrite, scope, cwd);
|
|
477
|
+
|
|
478
|
+
// Choosing policies IS selecting from the pack now. Nothing registers these
|
|
479
|
+
// names from this build any more, so writing `enabledPolicies` and stopping
|
|
480
|
+
// would leave a freshly set-up machine enforcing nothing at all — which is
|
|
481
|
+
// the failure mode this whole product exists to prevent.
|
|
482
|
+
//
|
|
483
|
+
// Fetched from the pack's GitHub release: there is no copy in this package,
|
|
484
|
+
// cannot fail behind a proxy, and the machine is guarded the moment it is
|
|
485
|
+
// configured rather than the moment it next reaches github.com.
|
|
486
|
+
// The always-on guard is excluded: a pack may not declare `alwaysOn`, so the
|
|
487
|
+
// pack does not carry it and asking for it by name is a selection the pack
|
|
488
|
+
// cannot satisfy. It ships compiled in and registers regardless.
|
|
489
|
+
const alwaysOnNames = new Set(BUILTIN_POLICIES.filter((p) => p.alwaysOn).map((p) => p.name));
|
|
490
|
+
const fromPack = selectedPolicies.filter((name) => !alwaysOnNames.has(name));
|
|
491
|
+
if (fromPack.length > 0) {
|
|
492
|
+
// ONLY when there is no pack yet. Fetching the core pack with
|
|
493
|
+
// `only: <selection>` REPLACES whatever the machine had chosen, and
|
|
494
|
+
// `selectedPolicies` is derived from `enabledPolicies` — which is empty on a
|
|
495
|
+
// machine whose pack came from `pack add core`. `policy add block-rm-rf`
|
|
496
|
+
// therefore took a machine from ten guards to one, silently, and nine
|
|
497
|
+
// policies went from denying to allowing.
|
|
498
|
+
//
|
|
499
|
+
// With a pack already installed the names are switched on individually
|
|
500
|
+
// instead, which is additive and touches nothing else.
|
|
501
|
+
if (!hasInstalledPacks()) {
|
|
502
|
+
// Fetched, not unpacked from this package: there is no copy in here any
|
|
503
|
+
// more. That makes this the one path in `policies --install` that needs
|
|
504
|
+
// the network, so its failure is reported rather than thrown — the names
|
|
505
|
+
// are already written to config, and the no-pack fallback enforces them
|
|
506
|
+
// until a pack arrives.
|
|
507
|
+
try {
|
|
508
|
+
await addPack(CORE_SOURCE, { only: fromPack });
|
|
509
|
+
} catch (err) {
|
|
510
|
+
console.log(
|
|
511
|
+
`\nWarning: could not fetch the policy pack (${err instanceof Error ? err.message : String(err)}).\n` +
|
|
512
|
+
`Run \`failproofai policies add core\` once you are online.`,
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
} else {
|
|
516
|
+
for (const name of fromPack) {
|
|
517
|
+
for (const pack of readInstalledPacks().packs) {
|
|
518
|
+
if (pack.policies.some((p) => p.name === name)) {
|
|
519
|
+
setPackPolicyEnabled(pack.id, name, true);
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
293
526
|
console.log(`\nEnabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}\n`);
|
|
294
527
|
if (removeCustomHooks) {
|
|
295
528
|
console.log("Custom hooks path cleared.");
|
|
@@ -434,7 +667,18 @@ export async function removeHooks(policyNames?: string[], scope: HookScope | "al
|
|
|
434
667
|
|
|
435
668
|
// Remove specific policies from config (keep hooks installed)
|
|
436
669
|
if (policyNames && policyNames.length > 0 && !(policyNames.length === 1 && policyNames[0] === "all")) {
|
|
437
|
-
|
|
670
|
+
const resolved = resolvePolicyNames(policyNames);
|
|
671
|
+
applyPackPolicies(resolved.packs, false, configScope, cwd);
|
|
672
|
+
policyNames = resolved.builtins;
|
|
673
|
+
rejectAlwaysOnPolicies(policyNames);
|
|
674
|
+
// Named ONLY pack policies: they are off now and there is nothing else to
|
|
675
|
+
// do. Falling through would reach the hook-removal path below with an empty
|
|
676
|
+
// name list, which is the "remove failproofai from every CLI" branch — so
|
|
677
|
+
// `--uninstall <a-pack-policy>` would have torn out every hook on the
|
|
678
|
+
// machine.
|
|
679
|
+
if (resolved.packs.length > 0 && policyNames.length === 0) return;
|
|
680
|
+
}
|
|
681
|
+
if (policyNames && policyNames.length > 0 && !(policyNames.length === 1 && policyNames[0] === "all")) {
|
|
438
682
|
const config = readScopedHooksConfig(configScope, cwd);
|
|
439
683
|
const removeSet = new Set(policyNames);
|
|
440
684
|
const remaining = config.enabledPolicies.filter((p) => !removeSet.has(p));
|
|
@@ -593,135 +837,88 @@ export async function removeHooks(policyNames?: string[], scope: HookScope | "al
|
|
|
593
837
|
*/
|
|
594
838
|
export async function listHooks(cwd?: string): Promise<void> {
|
|
595
839
|
const config = readMergedHooksConfig(cwd);
|
|
596
|
-
const enabledSet = new Set(config.enabledPolicies);
|
|
597
840
|
const disabledCustomSet = new Set(config.disabledCustomPolicies ?? []);
|
|
841
|
+
const opts = optsFor(process.stdout);
|
|
598
842
|
|
|
599
843
|
// Determine which scopes have hooks installed (deduplicate when paths overlap, e.g. cwd === home)
|
|
600
844
|
const uniqueScopes = deduplicateScopes(HOOK_SCOPES, cwd);
|
|
601
845
|
const installedScopes = uniqueScopes.filter((s) => hooksInstalledInSettings(s, cwd));
|
|
602
846
|
|
|
603
|
-
//
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
// All known builtin policy names (for unknown policyParams key detection)
|
|
611
|
-
const builtinPolicyNames = new Set(BUILTIN_POLICIES.map((p) => p.name));
|
|
612
|
-
|
|
613
|
-
// Helper: print params summary lines beneath a policy row
|
|
614
|
-
const printParamsSummary = (policyName: string, indent: string) => {
|
|
615
|
-
const params = config.policyParams?.[policyName];
|
|
616
|
-
if (!params) return;
|
|
617
|
-
for (const [key, val] of Object.entries(params)) {
|
|
618
|
-
console.log(`${indent} ${key}: ${JSON.stringify(val)}`);
|
|
847
|
+
// Names a `policyParams` key may legitimately use: every policy an installed
|
|
848
|
+
// pack carries. Previously the compiled catalog, which no longer describes
|
|
849
|
+
// what runs.
|
|
850
|
+
const knownPolicyNames = new Set<string>();
|
|
851
|
+
try {
|
|
852
|
+
for (const pack of readInstalledPacks().packs) {
|
|
853
|
+
for (const policy of pack.policies) knownPolicyNames.add(policy.name);
|
|
619
854
|
}
|
|
620
|
-
}
|
|
855
|
+
} catch {
|
|
856
|
+
// Unreadable manifest: skip the typo warning rather than invent one.
|
|
857
|
+
}
|
|
621
858
|
|
|
622
|
-
const
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
for (const policy of betaPolicies) printRow(policy);
|
|
859
|
+
const groups: Array<string[] | null> = [];
|
|
860
|
+
const packCount = (() => {
|
|
861
|
+
try {
|
|
862
|
+
return readInstalledPacks().packs.reduce(
|
|
863
|
+
(n, pack) => n + (pack.enabled ?? pack.policies.map((p) => p.name)).length,
|
|
864
|
+
0,
|
|
865
|
+
);
|
|
866
|
+
} catch {
|
|
867
|
+
return 0;
|
|
632
868
|
}
|
|
633
|
-
};
|
|
869
|
+
})();
|
|
870
|
+
groups.push(
|
|
871
|
+
title(
|
|
872
|
+
"failproofai policies",
|
|
873
|
+
installedScopes.length === 0
|
|
874
|
+
? "not installed"
|
|
875
|
+
: `${installedScopes.join(" + ")} · ${packCount} on`,
|
|
876
|
+
opts,
|
|
877
|
+
),
|
|
878
|
+
);
|
|
634
879
|
|
|
635
880
|
if (installedScopes.length === 0) {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
console.log(`\n Config: ${globalPolicyConfigFile()}\n`);
|
|
663
|
-
} else {
|
|
664
|
-
// State C: Multiple scopes — column table
|
|
665
|
-
const COL = 9;
|
|
666
|
-
const scopeLabelMap: Record<HookScope, string> = {
|
|
667
|
-
user: "User",
|
|
668
|
-
project: "Project",
|
|
669
|
-
local: "Local",
|
|
670
|
-
};
|
|
671
|
-
|
|
672
|
-
console.log("\nFailproof AI Hook Policies\n");
|
|
673
|
-
|
|
674
|
-
// Header with only installed scope columns + separator
|
|
675
|
-
const buildScopePrefix = () => {
|
|
676
|
-
let s = " ";
|
|
677
|
-
for (const sc of installedScopes) s += scopeLabelMap[sc].padEnd(COL);
|
|
678
|
-
return s;
|
|
679
|
-
};
|
|
680
|
-
const scopeHeaderWidth = installedScopes.length * COL;
|
|
681
|
-
console.log(`${buildScopePrefix()}${"Name".padEnd(nameColWidth)}Description`);
|
|
682
|
-
console.log(` ${"\u2500".repeat(scopeHeaderWidth)}${"\u2500".repeat(nameColWidth)}${"\u2500".repeat(38)}`);
|
|
683
|
-
|
|
684
|
-
const printMultiScopeRow = (policy: { name: string; description: string }) => {
|
|
685
|
-
const enabled = enabledSet.has(policy.name);
|
|
686
|
-
let row = " ";
|
|
687
|
-
for (const _scope of installedScopes) {
|
|
688
|
-
if (enabled) {
|
|
689
|
-
row += `\x1B[32m\u2713 ON\x1B[0m` + " ".repeat(COL - 4);
|
|
690
|
-
} else {
|
|
691
|
-
row += " OFF" + " ".repeat(COL - 5);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
row += policy.name.padEnd(nameColWidth) + policy.description;
|
|
695
|
-
console.log(row);
|
|
696
|
-
printParamsSummary(policy.name, ` ${" ".repeat(scopeHeaderWidth)}`);
|
|
697
|
-
};
|
|
698
|
-
|
|
699
|
-
for (const policy of regularPolicies) printMultiScopeRow(policy);
|
|
700
|
-
|
|
701
|
-
if (betaPolicies.length > 0) {
|
|
702
|
-
console.log(`\n \x1B[2m\u2500\u2500 Beta \u2500\u2500\x1B[0m`);
|
|
703
|
-
for (const policy of betaPolicies) printMultiScopeRow(policy);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
console.log(`\n Config: ${globalPolicyConfigFile()}`);
|
|
707
|
-
|
|
708
|
-
// Multi-scope warning
|
|
709
|
-
const scopeNames = installedScopes.join(", ");
|
|
710
|
-
console.log();
|
|
711
|
-
console.log(`\x1B[33m\u26A0 Hooks in multiple scopes (${scopeNames}).\x1B[0m`);
|
|
712
|
-
console.log(" Consider keeping one. Remove with: failproofai policies --uninstall --scope <scope>\n");
|
|
881
|
+
groups.push(
|
|
882
|
+
nextStep(
|
|
883
|
+
"failproofai policies --install",
|
|
884
|
+
config.enabledPolicies.length > 0
|
|
885
|
+
? "These are configured but NOT installed — no hook is running them:"
|
|
886
|
+
: "Nothing is installed yet. Get started with:",
|
|
887
|
+
opts,
|
|
888
|
+
),
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
// Held back to the end rather than printed here. Everything below this point
|
|
892
|
+
// is another section of the same listing, and a footer in the middle of it
|
|
893
|
+
// reads as the end of the output — while a warning at the very end is the one
|
|
894
|
+
// a reader scrolling back from their prompt actually sees.
|
|
895
|
+
const footer: Array<string[] | null> = [note(`Config: ${globalPolicyConfigFile()}`, opts)];
|
|
896
|
+
|
|
897
|
+
if (installedScopes.length > 1) {
|
|
898
|
+
footer.push(
|
|
899
|
+
warning(
|
|
900
|
+
[
|
|
901
|
+
`Hooks in multiple scopes (${installedScopes.join(", ")}).`,
|
|
902
|
+
"Consider keeping one. Remove with: failproofai policies --uninstall --scope <scope>",
|
|
903
|
+
],
|
|
904
|
+
opts,
|
|
905
|
+
),
|
|
906
|
+
);
|
|
713
907
|
}
|
|
714
908
|
|
|
715
909
|
// Warn about unknown policyParams keys
|
|
716
910
|
if (config.policyParams) {
|
|
717
911
|
const unknownKeys: string[] = [];
|
|
718
912
|
for (const key of Object.keys(config.policyParams)) {
|
|
719
|
-
if (!
|
|
720
|
-
console.log(` \x1B[33mWarning: unknown policyParams key "${key}" — possible typo\x1B[0m`);
|
|
721
|
-
unknownKeys.push(key);
|
|
722
|
-
}
|
|
913
|
+
if (knownPolicyNames.size > 0 && !knownPolicyNames.has(key)) unknownKeys.push(key);
|
|
723
914
|
}
|
|
724
915
|
if (unknownKeys.length > 0) {
|
|
916
|
+
footer.push(
|
|
917
|
+
warning(
|
|
918
|
+
unknownKeys.map((key) => `unknown policyParams key "${key}" — possible typo`),
|
|
919
|
+
opts,
|
|
920
|
+
),
|
|
921
|
+
);
|
|
725
922
|
try {
|
|
726
923
|
await trackHookEvent(getInstanceId(), "policy_params_validation_warning", {
|
|
727
924
|
unknown_keys_count: unknownKeys.length,
|
|
@@ -734,30 +931,38 @@ export async function listHooks(cwd?: string): Promise<void> {
|
|
|
734
931
|
// Explicit Custom Policies section
|
|
735
932
|
const explicitPaths = configuredCustomPolicyPaths(config);
|
|
736
933
|
if (explicitPaths.length > 0) {
|
|
737
|
-
|
|
934
|
+
groups.push(rule("Custom Policies", opts));
|
|
738
935
|
for (const path of explicitPaths) {
|
|
739
936
|
// Enforcement resolves configured paths from the project config root.
|
|
740
937
|
// Use the same canonical path here so the ID checked by the CLI exactly
|
|
741
938
|
// matches the ID written by the dashboard.
|
|
742
939
|
const absPath = resolve(findProjectConfigDir(cwd ?? process.cwd()), path);
|
|
743
|
-
|
|
940
|
+
groups.push(note(absPath, opts));
|
|
744
941
|
if (!existsSync(absPath)) {
|
|
745
|
-
|
|
942
|
+
groups.push(warning([`file not found: ${absPath}`], opts));
|
|
746
943
|
continue;
|
|
747
944
|
}
|
|
748
945
|
const hooks = await loadCustomHooks(absPath);
|
|
749
946
|
if (hooks.length === 0) {
|
|
750
|
-
|
|
947
|
+
groups.push(
|
|
948
|
+
warning(["failed to load (check ~/.failproofai/logs/hooks.log)"], opts),
|
|
949
|
+
);
|
|
751
950
|
} else {
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
951
|
+
groups.push(
|
|
952
|
+
table(
|
|
953
|
+
{
|
|
954
|
+
head: ["", "Name", "Description"],
|
|
955
|
+
rows: hooks.map((hook) => [
|
|
956
|
+
chip(disabledCustomSet.has(`custom:${absPath}:${hook.name}`) ? "off" : "on", opts),
|
|
957
|
+
hook.name,
|
|
958
|
+
hook.description ?? "",
|
|
959
|
+
]),
|
|
960
|
+
},
|
|
961
|
+
opts,
|
|
962
|
+
),
|
|
963
|
+
);
|
|
758
964
|
}
|
|
759
965
|
}
|
|
760
|
-
console.log();
|
|
761
966
|
}
|
|
762
967
|
|
|
763
968
|
// Convention Policies section (.failproofai/policies/*policies.{js,mjs,ts})
|
|
@@ -801,43 +1006,82 @@ export async function listHooks(cwd?: string): Promise<void> {
|
|
|
801
1006
|
for (const t of targets) discovered[t].push({ file, hooks });
|
|
802
1007
|
};
|
|
803
1008
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
// straight into the filename with no space
|
|
808
|
-
// (`enforce-bengaluru-event-links-policies.mjs1 hook(s)`). Widen to fit the
|
|
809
|
-
// files actually being printed, keeping a two-space gutter.
|
|
810
|
-
const colWidth = Math.max(nameColWidth, ...files.map((f) => basename(f).length + 2));
|
|
1009
|
+
groups.push(rule(`Convention Policies — ${label}`, opts));
|
|
1010
|
+
groups.push(note(dir, opts));
|
|
1011
|
+
const rows: TableRow[] = [];
|
|
811
1012
|
for (const file of files) {
|
|
1013
|
+
const filename = basename(file);
|
|
812
1014
|
try {
|
|
813
1015
|
const hooks = await loadCustomHooks(file);
|
|
814
|
-
const filename = basename(file);
|
|
815
1016
|
record(filename, hooks.map((h) => h.name));
|
|
816
1017
|
if (hooks.length === 0) {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
const hookStates = hooks.map((hook) => ({
|
|
820
|
-
hook,
|
|
821
|
-
disabled: disabledCustomSet.has(`convention:${policyScope}:${filename}:${hook.name}`),
|
|
822
|
-
}));
|
|
823
|
-
const disabledCount = hookStates.filter((entry) => entry.disabled).length;
|
|
824
|
-
const status = disabledCount === 0
|
|
825
|
-
? "\x1B[32m\u2713 ON\x1B[0m"
|
|
826
|
-
: disabledCount === hooks.length
|
|
827
|
-
? "\x1B[2m OFF\x1B[0m"
|
|
828
|
-
: "\x1B[33m\u25D0 MIXED\x1B[0m";
|
|
829
|
-
const hookSummary = hookStates
|
|
830
|
-
.map(({ hook, disabled }) => `${hook.name}${disabled ? " (OFF)" : ""}`)
|
|
831
|
-
.join(", ");
|
|
832
|
-
console.log(` ${status} ${filename.padEnd(colWidth)}${hooks.length} hook(s): ${hookSummary}`);
|
|
1018
|
+
rows.push({ cells: [chip("failed", opts), filename, "failed to load"] });
|
|
1019
|
+
continue;
|
|
833
1020
|
}
|
|
1021
|
+
const hookStates = hooks.map((hook) => ({
|
|
1022
|
+
hook,
|
|
1023
|
+
disabled: disabledCustomSet.has(`convention:${policyScope}:${filename}:${hook.name}`),
|
|
1024
|
+
}));
|
|
1025
|
+
const disabledCount = hookStates.filter((entry) => entry.disabled).length;
|
|
1026
|
+
const state: ChipState =
|
|
1027
|
+
disabledCount === 0 ? "on" : disabledCount === hooks.length ? "off" : "mixed";
|
|
1028
|
+
const hookSummary = hookStates
|
|
1029
|
+
.map(({ hook, disabled }) => `${hook.name}${disabled ? " (OFF)" : ""}`)
|
|
1030
|
+
.join(", ");
|
|
1031
|
+
rows.push({
|
|
1032
|
+
cells: [chip(state, opts), filename, `${hooks.length} hook(s): ${hookSummary}`],
|
|
1033
|
+
});
|
|
834
1034
|
} catch {
|
|
835
|
-
const filename = basename(file);
|
|
836
1035
|
record(filename, []);
|
|
837
|
-
|
|
1036
|
+
rows.push({ cells: [chip("failed", opts), filename, "error"] });
|
|
838
1037
|
}
|
|
839
1038
|
}
|
|
840
|
-
|
|
1039
|
+
groups.push(table({ head: ["", "File", "Hooks"], rows }, opts));
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// Installed packs. They enforce on this machine exactly like every section
|
|
1043
|
+
// above, and until now the only way to see one was `failproofai policies` —
|
|
1044
|
+
// so the command that answers "what is enforcing here?" answered it with a
|
|
1045
|
+
// subset, for the one source a person had to go out of their way to install.
|
|
1046
|
+
try {
|
|
1047
|
+
const { packs, errors } = readInstalledPacks();
|
|
1048
|
+
for (const pack of packs) {
|
|
1049
|
+
const taken = pack.enabled ?? pack.policies.map((p) => p.name);
|
|
1050
|
+
groups.push(rule(`Pack — ${pack.id}@${pack.version}`, opts));
|
|
1051
|
+
groups.push(
|
|
1052
|
+
table(
|
|
1053
|
+
{
|
|
1054
|
+
head: ["", "Name", "Description"],
|
|
1055
|
+
rows: pack.policies.map((policy) => {
|
|
1056
|
+
const disabled = disabledCustomSet.has(
|
|
1057
|
+
`pack:${pack.id}@${pack.version}:${policy.name}`,
|
|
1058
|
+
);
|
|
1059
|
+
// `observe` evaluates and discards its verdict, so a row reading
|
|
1060
|
+
// ON would claim enforcement the pack deliberately is not doing.
|
|
1061
|
+
const state: ChipState =
|
|
1062
|
+
pack.effect === "observe"
|
|
1063
|
+
? "observe"
|
|
1064
|
+
: !taken.includes(policy.name) || disabled
|
|
1065
|
+
? "off"
|
|
1066
|
+
: "pack";
|
|
1067
|
+
return [chip(state, opts), policy.name, policy.description];
|
|
1068
|
+
}),
|
|
1069
|
+
},
|
|
1070
|
+
opts,
|
|
1071
|
+
),
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
if (errors.length > 0) {
|
|
1075
|
+
groups.push(
|
|
1076
|
+
warning(
|
|
1077
|
+
errors.map((err) => `pack ${err.id ?? "(unnamed)"} will not load: ${err.reason}`),
|
|
1078
|
+
opts,
|
|
1079
|
+
),
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
} catch {
|
|
1083
|
+
// Same rule as the cloud section below: a listing must not be the thing
|
|
1084
|
+
// that turns an unreadable manifest into a broken command.
|
|
841
1085
|
}
|
|
842
1086
|
|
|
843
1087
|
// Cloud-managed policies. These enforce on this machine exactly like the two
|
|
@@ -852,21 +1096,27 @@ export async function listHooks(cwd?: string): Promise<void> {
|
|
|
852
1096
|
try {
|
|
853
1097
|
const cloud = readActiveCloudManagedPolicies();
|
|
854
1098
|
if (cloud.length > 0) {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
1099
|
+
groups.push(rule(`Cloud-managed — deployment ${cloud[0].deployment}`, opts));
|
|
1100
|
+
groups.push(
|
|
1101
|
+
table(
|
|
1102
|
+
{
|
|
1103
|
+
head: ["", "Policy", "Version"],
|
|
1104
|
+
rows: cloud.map((artifact) => [
|
|
1105
|
+
// `observe` is evaluated and then has its verdict discarded, so a
|
|
1106
|
+
// row that read "ON" would claim enforcement this policy
|
|
1107
|
+
// deliberately is not doing.
|
|
1108
|
+
chip(artifact.effect === "observe" ? "observe" : "cloud", opts),
|
|
1109
|
+
artifact.id,
|
|
1110
|
+
`v${artifact.version}`,
|
|
1111
|
+
]),
|
|
1112
|
+
flex: 1,
|
|
1113
|
+
},
|
|
1114
|
+
opts,
|
|
1115
|
+
),
|
|
1116
|
+
);
|
|
1117
|
+
groups.push(
|
|
1118
|
+
note("Managed from the dashboard — not switchable with `failproofai policies`.", opts),
|
|
858
1119
|
);
|
|
859
|
-
const colWidth = Math.max(nameColWidth, ...cloud.map((c) => c.id.length + 2));
|
|
860
|
-
for (const artifact of cloud) {
|
|
861
|
-
// `observe` is evaluated and then has its verdict discarded, so a row
|
|
862
|
-
// that read "ON" would claim enforcement this policy deliberately is
|
|
863
|
-
// not doing.
|
|
864
|
-
const status =
|
|
865
|
-
artifact.effect === "observe" ? "\x1B[33m\u25D0 OBS\x1B[0m" : "\x1B[32m\u2713 ON\x1B[0m";
|
|
866
|
-
console.log(` ${status} ${artifact.id.padEnd(colWidth)}v${artifact.version}`);
|
|
867
|
-
}
|
|
868
|
-
console.log("\n Managed from the dashboard \u2014 not switchable with `failproofai policies`.");
|
|
869
|
-
console.log();
|
|
870
1120
|
}
|
|
871
1121
|
} catch {
|
|
872
1122
|
// A machine with no deployment, or an unreadable manifest, simply has no
|
|
@@ -874,6 +1124,8 @@ export async function listHooks(cwd?: string): Promise<void> {
|
|
|
874
1124
|
// the thing that turns a bad manifest into a broken command.
|
|
875
1125
|
}
|
|
876
1126
|
|
|
1127
|
+
printBlock(process.stdout, stack(...groups, ...footer));
|
|
1128
|
+
|
|
877
1129
|
// Mirror what was just listed into the USER config. Safe here because
|
|
878
1130
|
// `failproofai policies` is a one-shot command — never do this on the hook
|
|
879
1131
|
// path (see the HooksConfig.conventionPolicies doc comment).
|