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,472 @@
|
|
|
1
|
+
"""Incident triage: incidents list/count/show/ack/assign/resolve/comment*/subscribe*/open.
|
|
2
|
+
|
|
3
|
+
Incidents live under /api/issues but the triage workflow is distinct, so it
|
|
4
|
+
gets its own top-level group. The id IS the handle (incidents have no human name), so the
|
|
5
|
+
action commands take it directly; the boxed views show a short id + the alert label.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
from typing import List, Optional
|
|
12
|
+
|
|
13
|
+
import typer
|
|
14
|
+
|
|
15
|
+
from .. import client as api
|
|
16
|
+
from .. import output
|
|
17
|
+
from .._context import GLOBALS_EPILOG, AppState, require_auth, validate_limit
|
|
18
|
+
from ..errors import ApiError, ForbiddenError, NotFoundError
|
|
19
|
+
from . import _write
|
|
20
|
+
|
|
21
|
+
_SEVERITIES = ("info", "warning", "critical")
|
|
22
|
+
_STATES = ("firing", "acknowledged", "resolved")
|
|
23
|
+
_UUID_RE = re.compile(r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _validate_states(value: Optional[str]) -> None:
|
|
27
|
+
"""Reject an unknown ``--state`` value up front (exit 2) rather than letting the server
|
|
28
|
+
silently drop it and return a confusing set. Accepts a CSV of firing/acknowledged/resolved."""
|
|
29
|
+
if value is None:
|
|
30
|
+
return
|
|
31
|
+
for s in value.split(","):
|
|
32
|
+
s = s.strip()
|
|
33
|
+
if s and s not in _STATES:
|
|
34
|
+
raise typer.BadParameter(
|
|
35
|
+
f"'{s}' is not a valid state. Choose from: {', '.join(_STATES)} (CSV).",
|
|
36
|
+
param_hint="--state",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _fail(state: AppState, exc: Exception, *, incident_id: str = "") -> None:
|
|
41
|
+
"""Re-raise as a typed error for the central chokepoint to render (JSON envelope under
|
|
42
|
+
``--json`` on stdout, red box otherwise). A 404 — or a **malformed (non-UUID) id**, which the
|
|
43
|
+
server's path extractor answers with a 400 rather than a 404 — becomes the friendlier
|
|
44
|
+
``no issue <id>`` (exit 6); every other ApiError/ForbiddenError keeps the server's message
|
|
45
|
+
and exit code."""
|
|
46
|
+
# EXACTLY 400, not a range. This read `>= 500` on the belief that the server answers a
|
|
47
|
+
# malformed id with a 500, and it does not: axum's path extractor rejects it at 400 with a
|
|
48
|
+
# plain-text body, which the dashboard turns into the generic "upstream returned non-JSON
|
|
49
|
+
# response". So the remap never fired — `fp issues show not-a-uuid` exited 1 carrying that
|
|
50
|
+
# internal phrase while `fp audits show not-a-uuid` exited 6 with a usable message, and
|
|
51
|
+
# anything branching on exit 6 to mean not-found silently took the wrong arm.
|
|
52
|
+
#
|
|
53
|
+
# `>= 400` is the obvious fix and it is WRONG, which is why this is spelled out. Issue ids
|
|
54
|
+
# are not required to be UUIDs — `fp issues assign i1 --assignee ...` is a documented call —
|
|
55
|
+
# so a non-UUID id reaches real handlers and collects real 4xx answers. A 422 "a@x.com is
|
|
56
|
+
# not an operator" would then be rewritten as "no issue i1", replacing the one sentence that
|
|
57
|
+
# explains the failure with a claim that is false. Only 400 means "the router refused to
|
|
58
|
+
# parse this id"; every other 4xx got past the extractor and has something to say.
|
|
59
|
+
status = getattr(exc, "status", None) or 0
|
|
60
|
+
malformed = bool(incident_id) and not _UUID_RE.match(incident_id) and status == 400
|
|
61
|
+
if incident_id and (malformed or isinstance(exc, NotFoundError)):
|
|
62
|
+
raise NotFoundError(
|
|
63
|
+
f"no issue {incident_id}", hint="run `fp issues list` to see open issues"
|
|
64
|
+
)
|
|
65
|
+
raise exc
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def incidents_list(
|
|
69
|
+
ctx: typer.Context,
|
|
70
|
+
state_filter: Optional[str] = typer.Option(None, "--state", help="Filter by state(s): firing, acknowledged, resolved (CSV)."),
|
|
71
|
+
alert_id: Optional[str] = typer.Option(None, "--alert-id", help="Only incidents for this alert."),
|
|
72
|
+
limit: int = typer.Option(50, "--limit", "-n", help="Max incidents to return."),
|
|
73
|
+
show_id: bool = typer.Option(False, "--show-id", help="Show the full incident id instead of the short form (always full in --json)."),
|
|
74
|
+
) -> None:
|
|
75
|
+
"""List incidents in a boxed table (newest-opened first).
|
|
76
|
+
|
|
77
|
+
Shows `id · title · source · severity · state · opened · assignees` — the id is the handle the
|
|
78
|
+
action commands take (short by default, full with `--show-id`); `title` is the issue's own
|
|
79
|
+
identifying line (every issue has one, unlike `alert_name`); `source` is where it came from
|
|
80
|
+
(`manual`/`alert`/`audit`) and trails the alert name when there is one; severity is
|
|
81
|
+
colour-coded, state as `● firing`/`● acknowledged`/`○ resolved`, `opened` the compact age.
|
|
82
|
+
A footer breaks down the state distribution. Needs `issues:read`. With `--json`:
|
|
83
|
+
`{"issues": [{id, title, source, source_finding_id, alert_name, alert_severity, state,
|
|
84
|
+
opened_at, assignees, ...}]}`.
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
|
|
88
|
+
* `fp issues list --state firing`
|
|
89
|
+
"""
|
|
90
|
+
state: AppState = ctx.obj
|
|
91
|
+
_validate_states(state_filter)
|
|
92
|
+
validate_limit(limit)
|
|
93
|
+
cctx = require_auth(state)
|
|
94
|
+
incidents = api.list_incidents(cctx, state=state_filter, alert_id=alert_id, limit=limit)
|
|
95
|
+
if state.json:
|
|
96
|
+
output.emit_json({"issues": incidents})
|
|
97
|
+
return
|
|
98
|
+
output.render_incidents(incidents, show_id=show_id)
|
|
99
|
+
output.incidents_footer(incidents)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def incidents_count(
|
|
103
|
+
ctx: typer.Context,
|
|
104
|
+
state_filter: Optional[str] = typer.Option(None, "--state", help="Filter by state(s) (CSV)."),
|
|
105
|
+
) -> None:
|
|
106
|
+
"""Count incidents (optionally by state) as a compact stat card.
|
|
107
|
+
|
|
108
|
+
With no `--state` the server counts the open ones (firing + acknowledged). Needs
|
|
109
|
+
`issues:read`. With `--json`: `{"count": N}`.
|
|
110
|
+
|
|
111
|
+
Example:
|
|
112
|
+
|
|
113
|
+
* `fp issues count --state firing`
|
|
114
|
+
"""
|
|
115
|
+
state: AppState = ctx.obj
|
|
116
|
+
_validate_states(state_filter)
|
|
117
|
+
cctx = require_auth(state)
|
|
118
|
+
count = api.count_incidents(cctx, state=state_filter)
|
|
119
|
+
if state.json:
|
|
120
|
+
output.emit_json({"count": count})
|
|
121
|
+
else:
|
|
122
|
+
output.render_incident_count(count, state=state_filter)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def incidents_show(
|
|
126
|
+
ctx: typer.Context,
|
|
127
|
+
incident_id: str = typer.Argument(..., help="Incident id."),
|
|
128
|
+
) -> None:
|
|
129
|
+
"""Show one incident in full — a stack of cards: identity, comments, subscribers, activity.
|
|
130
|
+
|
|
131
|
+
The identity card is headed by the issue's own title and shows severity · state · source ·
|
|
132
|
+
opened, who acknowledged/is assigned, and the breach; empty sections are omitted. Not-found →
|
|
133
|
+
`✗ no incident <short id>`, exit 6. Needs `issues:read`. With `--json`: the full `Incident`
|
|
134
|
+
(including `title`, `source`, `source_finding_id`, and untouched
|
|
135
|
+
comments/subscribers/activity).
|
|
136
|
+
|
|
137
|
+
Example:
|
|
138
|
+
|
|
139
|
+
* `fp issues show <id>`
|
|
140
|
+
"""
|
|
141
|
+
state: AppState = ctx.obj
|
|
142
|
+
cctx = require_auth(state)
|
|
143
|
+
try:
|
|
144
|
+
incident = api.get_incident(cctx, incident_id)
|
|
145
|
+
except (ApiError, ForbiddenError, NotFoundError) as exc:
|
|
146
|
+
_fail(state, exc, incident_id=incident_id)
|
|
147
|
+
if state.json:
|
|
148
|
+
output.emit_json(incident)
|
|
149
|
+
return
|
|
150
|
+
output.render_incident_show(incident)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def incidents_ack(
|
|
154
|
+
ctx: typer.Context,
|
|
155
|
+
incident_id: str = typer.Argument(..., help="Incident id."),
|
|
156
|
+
) -> None:
|
|
157
|
+
"""Acknowledge an incident (no confirm — it isn't destructive).
|
|
158
|
+
|
|
159
|
+
Needs `issues:read` (ack rides on read). With `--json`: `{"acknowledged": true, "id": "<id>"}`.
|
|
160
|
+
|
|
161
|
+
Example:
|
|
162
|
+
|
|
163
|
+
* `fp issues ack <id>`
|
|
164
|
+
"""
|
|
165
|
+
state: AppState = ctx.obj
|
|
166
|
+
cctx = require_auth(state)
|
|
167
|
+
try:
|
|
168
|
+
api.ack_incident(cctx, incident_id)
|
|
169
|
+
except (ApiError, ForbiddenError, NotFoundError) as exc:
|
|
170
|
+
_fail(state, exc, incident_id=incident_id)
|
|
171
|
+
_write.record_action("incident_acked", resource="incident", success=True)
|
|
172
|
+
if state.json:
|
|
173
|
+
output.emit_json({"acknowledged": True, "id": incident_id})
|
|
174
|
+
else:
|
|
175
|
+
output.incident_acked(incident_id)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def incidents_assign(
|
|
179
|
+
ctx: typer.Context,
|
|
180
|
+
incident_id: str = typer.Argument(..., help="Incident id."),
|
|
181
|
+
assignee: Optional[List[str]] = typer.Option(None, "--assignee", help="Operator email to assign (repeatable; omit to clear all)."),
|
|
182
|
+
) -> None:
|
|
183
|
+
"""Set an incident's assignees (replaces the list; omit `--assignee` to clear).
|
|
184
|
+
|
|
185
|
+
Needs `issues:create`. The server rejects the whole call if any email is not an operator —
|
|
186
|
+
that's surfaced as a clean `✗ <message>`. With `--json`: `{"assignees": [...], "id": "<id>"}`.
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
|
|
190
|
+
* `fp issues assign <id> --assignee a@example.com --assignee b@example.com`
|
|
191
|
+
"""
|
|
192
|
+
state: AppState = ctx.obj
|
|
193
|
+
cctx = require_auth(state)
|
|
194
|
+
assignees = assignee or []
|
|
195
|
+
try:
|
|
196
|
+
api.assign_incident(cctx, incident_id, assignees)
|
|
197
|
+
except (ApiError, ForbiddenError, NotFoundError) as exc:
|
|
198
|
+
_fail(state, exc, incident_id=incident_id)
|
|
199
|
+
_write.record_action("incident_assigned", resource="incident", success=True)
|
|
200
|
+
if state.json:
|
|
201
|
+
output.emit_json({"assignees": assignees, "id": incident_id})
|
|
202
|
+
else:
|
|
203
|
+
output.incident_assigned(incident_id, assignees)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def incidents_resolve(
|
|
207
|
+
ctx: typer.Context,
|
|
208
|
+
incident_id: str = typer.Argument(..., help="Incident id."),
|
|
209
|
+
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."),
|
|
210
|
+
) -> None:
|
|
211
|
+
"""Resolve (close) an incident, after a calm confirm.
|
|
212
|
+
|
|
213
|
+
Needs `issues:close`. With `--json`: `{"resolved": true, "id": "<id>"}` (or `{cancelled: true}`
|
|
214
|
+
on a declined prompt).
|
|
215
|
+
|
|
216
|
+
Example:
|
|
217
|
+
|
|
218
|
+
* `fp issues resolve <id> --yes`
|
|
219
|
+
"""
|
|
220
|
+
state: AppState = ctx.obj
|
|
221
|
+
cctx = require_auth(state)
|
|
222
|
+
if _write.should_prompt(state, yes):
|
|
223
|
+
alert_name = None
|
|
224
|
+
try:
|
|
225
|
+
alert_name = api.get_incident(cctx, incident_id).alert_name
|
|
226
|
+
except NotFoundError as exc:
|
|
227
|
+
_fail(state, exc, incident_id=incident_id)
|
|
228
|
+
except (ApiError, ForbiddenError):
|
|
229
|
+
alert_name = None
|
|
230
|
+
if not output.confirm_incident_resolve(incident_id, alert_name):
|
|
231
|
+
if state.json:
|
|
232
|
+
output.emit_json({"cancelled": True})
|
|
233
|
+
else:
|
|
234
|
+
output.cancelled_plain("nothing changed")
|
|
235
|
+
return
|
|
236
|
+
try:
|
|
237
|
+
api.resolve_incident(cctx, incident_id)
|
|
238
|
+
except (ApiError, ForbiddenError, NotFoundError) as exc:
|
|
239
|
+
_fail(state, exc, incident_id=incident_id)
|
|
240
|
+
_write.record_action("incident_resolved", resource="incident", success=True)
|
|
241
|
+
if state.json:
|
|
242
|
+
output.emit_json({"resolved": True, "id": incident_id})
|
|
243
|
+
else:
|
|
244
|
+
output.incident_resolved(incident_id)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def incidents_comment_list(
|
|
248
|
+
ctx: typer.Context,
|
|
249
|
+
incident_id: str = typer.Argument(..., help="Incident id."),
|
|
250
|
+
) -> None:
|
|
251
|
+
"""List an incident's comments in a boxed table.
|
|
252
|
+
|
|
253
|
+
Shows `author · when · body` (the body wraps; a deleted comment shows a dim `(deleted)`).
|
|
254
|
+
Needs `issues:read`. With `--json`: `{"comments": [{id, author_email, body, created_at, ...}]}`.
|
|
255
|
+
"""
|
|
256
|
+
state: AppState = ctx.obj
|
|
257
|
+
cctx = require_auth(state)
|
|
258
|
+
try:
|
|
259
|
+
comments = api.list_incident_comments(cctx, incident_id)
|
|
260
|
+
except (ApiError, ForbiddenError, NotFoundError) as exc:
|
|
261
|
+
_fail(state, exc, incident_id=incident_id)
|
|
262
|
+
if state.json:
|
|
263
|
+
output.emit_json({"comments": comments})
|
|
264
|
+
return
|
|
265
|
+
output.render_incident_comments(comments)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def incidents_comment_add(
|
|
269
|
+
ctx: typer.Context,
|
|
270
|
+
incident_id: str = typer.Argument(..., help="Incident id."),
|
|
271
|
+
body: Optional[str] = typer.Option(None, "--body", help="Comment text (or use --file/-)."),
|
|
272
|
+
file: Optional[str] = typer.Option(None, "--file", help="Read the comment body from a file, or `-` for stdin."),
|
|
273
|
+
) -> None:
|
|
274
|
+
"""Add a comment to an incident (rendered as a green "comment added" card).
|
|
275
|
+
|
|
276
|
+
Needs `issues:read` (commenting rides on read). Provide exactly one of `--body` or `--file`/stdin. With `--json`: the
|
|
277
|
+
created comment.
|
|
278
|
+
|
|
279
|
+
Example:
|
|
280
|
+
|
|
281
|
+
* `fp issues comment-add <id> --body "looking into it"`
|
|
282
|
+
"""
|
|
283
|
+
state: AppState = ctx.obj
|
|
284
|
+
if (body is None) == (file is None):
|
|
285
|
+
raise typer.BadParameter("Provide exactly one of --body or --file.")
|
|
286
|
+
text = body if body is not None else _write.read_text_arg(file)
|
|
287
|
+
cctx = require_auth(state)
|
|
288
|
+
try:
|
|
289
|
+
comment = api.create_incident_comment(cctx, incident_id, text)
|
|
290
|
+
except (ApiError, ForbiddenError, NotFoundError) as exc:
|
|
291
|
+
_fail(state, exc, incident_id=incident_id)
|
|
292
|
+
_write.record_action("incident_comment_added", resource="incident", success=True)
|
|
293
|
+
if state.json:
|
|
294
|
+
output.emit_json(comment)
|
|
295
|
+
else:
|
|
296
|
+
output.render_incident_comment_added(comment)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def incidents_comment_delete(
|
|
300
|
+
ctx: typer.Context,
|
|
301
|
+
incident_id: str = typer.Argument(..., help="Incident id."),
|
|
302
|
+
comment_id: str = typer.Argument(..., help="Comment id."),
|
|
303
|
+
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."),
|
|
304
|
+
) -> None:
|
|
305
|
+
"""Delete an incident comment, after an amber preview + confirm.
|
|
306
|
+
|
|
307
|
+
Resolves the comment first (so an unknown comment id → `✗ no comment …`, exit 6), previews it,
|
|
308
|
+
then confirms. Needs `issues:read` to delete your own comment, `issues:close` to moderate others'. With `--json`: `{"deleted": true, "id": "<comment_id>"}`
|
|
309
|
+
(or `{cancelled: true}` on a declined prompt).
|
|
310
|
+
"""
|
|
311
|
+
state: AppState = ctx.obj
|
|
312
|
+
cctx = require_auth(state)
|
|
313
|
+
try:
|
|
314
|
+
comments = api.list_incident_comments(cctx, incident_id)
|
|
315
|
+
except (ApiError, ForbiddenError, NotFoundError) as exc:
|
|
316
|
+
_fail(state, exc, incident_id=incident_id)
|
|
317
|
+
match = next((c for c in comments if c.id == comment_id), None)
|
|
318
|
+
if match is None:
|
|
319
|
+
raise NotFoundError(f"no comment {comment_id}")
|
|
320
|
+
if _write.should_prompt(state, yes):
|
|
321
|
+
output.render_incident_comment_delete_preview(match)
|
|
322
|
+
if not output.confirm_incident_comment_delete():
|
|
323
|
+
if state.json:
|
|
324
|
+
output.emit_json({"cancelled": True})
|
|
325
|
+
else:
|
|
326
|
+
output.cancelled_plain("nothing deleted")
|
|
327
|
+
return
|
|
328
|
+
try:
|
|
329
|
+
api.delete_incident_comment(cctx, incident_id, comment_id)
|
|
330
|
+
except (ApiError, ForbiddenError, NotFoundError) as exc:
|
|
331
|
+
_fail(state, exc, incident_id=incident_id)
|
|
332
|
+
_write.record_action("incident_comment_deleted", resource="incident", success=True, destructive=True)
|
|
333
|
+
if state.json:
|
|
334
|
+
output.emit_json({"deleted": True, "id": comment_id})
|
|
335
|
+
else:
|
|
336
|
+
output.incident_comment_deleted()
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def incidents_subscribers(
|
|
340
|
+
ctx: typer.Context,
|
|
341
|
+
incident_id: str = typer.Argument(..., help="Incident id."),
|
|
342
|
+
) -> None:
|
|
343
|
+
"""List who is subscribed to an incident in a boxed table.
|
|
344
|
+
|
|
345
|
+
Shows `email · source · subscribed`. Needs `issues:read`. With `--json`:
|
|
346
|
+
`{"subscribers": [{email, source, subscribed_at, ...}]}`.
|
|
347
|
+
"""
|
|
348
|
+
state: AppState = ctx.obj
|
|
349
|
+
cctx = require_auth(state)
|
|
350
|
+
try:
|
|
351
|
+
subs = api.list_incident_subscribers(cctx, incident_id)
|
|
352
|
+
except (ApiError, ForbiddenError, NotFoundError) as exc:
|
|
353
|
+
_fail(state, exc, incident_id=incident_id)
|
|
354
|
+
if state.json:
|
|
355
|
+
output.emit_json({"subscribers": subs})
|
|
356
|
+
return
|
|
357
|
+
output.render_incident_subscribers(subs)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def incidents_subscribe(
|
|
361
|
+
ctx: typer.Context,
|
|
362
|
+
incident_id: str = typer.Argument(..., help="Incident id."),
|
|
363
|
+
email: Optional[str] = typer.Option(None, "--email", help="Email to subscribe (default: you)."),
|
|
364
|
+
) -> None:
|
|
365
|
+
"""Subscribe to an incident's notifications.
|
|
366
|
+
|
|
367
|
+
With `--json`: `{"subscribed": true, "id": "<id>"}`.
|
|
368
|
+
"""
|
|
369
|
+
state: AppState = ctx.obj
|
|
370
|
+
cctx = require_auth(state)
|
|
371
|
+
try:
|
|
372
|
+
api.subscribe_incident(cctx, incident_id, email)
|
|
373
|
+
except (ApiError, ForbiddenError, NotFoundError) as exc:
|
|
374
|
+
_fail(state, exc, incident_id=incident_id)
|
|
375
|
+
_write.record_action("incident_subscribed", resource="incident", success=True)
|
|
376
|
+
if state.json:
|
|
377
|
+
output.emit_json({"subscribed": True, "id": incident_id})
|
|
378
|
+
else:
|
|
379
|
+
output.incident_subscribed(incident_id, email)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def incidents_unsubscribe(
|
|
383
|
+
ctx: typer.Context,
|
|
384
|
+
incident_id: str = typer.Argument(..., help="Incident id."),
|
|
385
|
+
email: Optional[str] = typer.Option(None, "--email", help="Email to unsubscribe (default: you)."),
|
|
386
|
+
) -> None:
|
|
387
|
+
"""Unsubscribe from an incident's notifications.
|
|
388
|
+
|
|
389
|
+
With `--json`: `{"unsubscribed": true, "id": "<id>"}`.
|
|
390
|
+
"""
|
|
391
|
+
state: AppState = ctx.obj
|
|
392
|
+
cctx = require_auth(state)
|
|
393
|
+
try:
|
|
394
|
+
api.unsubscribe_incident(cctx, incident_id, email)
|
|
395
|
+
except (ApiError, ForbiddenError, NotFoundError) as exc:
|
|
396
|
+
_fail(state, exc, incident_id=incident_id)
|
|
397
|
+
_write.record_action("incident_unsubscribed", resource="incident", success=True)
|
|
398
|
+
if state.json:
|
|
399
|
+
output.emit_json({"unsubscribed": True, "id": incident_id})
|
|
400
|
+
else:
|
|
401
|
+
output.incident_unsubscribed(incident_id, email)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def incidents_open(
|
|
405
|
+
ctx: typer.Context,
|
|
406
|
+
summary: str = typer.Option(..., "--summary", help="Short description of the incident."),
|
|
407
|
+
title: Optional[str] = typer.Option(None, "--title", help="Short title. Required unless --alert-id is given."),
|
|
408
|
+
alert_id: Optional[str] = typer.Option(None, "--alert-id", help="Link to an alert (inherits its severity)."),
|
|
409
|
+
severity: Optional[str] = typer.Option(None, "--severity", help=f"Severity for a standalone incident: {', '.join(_SEVERITIES)}."),
|
|
410
|
+
) -> None:
|
|
411
|
+
"""Open a manual incident (standalone, or linked to an alert) — rendered as a green card.
|
|
412
|
+
|
|
413
|
+
Needs `issues:create`. `--title` is required for a standalone incident (there's no parent
|
|
414
|
+
alert whose name it could borrow) and optional with `--alert-id`, where it defaults to the
|
|
415
|
+
alert's name. A missing `--title` or an invalid `--severity` → exit 2. With `--json`:
|
|
416
|
+
`{id, newly_opened, state}`.
|
|
417
|
+
|
|
418
|
+
Examples:
|
|
419
|
+
|
|
420
|
+
* `fp issues open --title "checkout 500s" --summary "manual page" --severity critical`
|
|
421
|
+
* `fp issues open --alert-id <id> --summary "paging on this again"`
|
|
422
|
+
"""
|
|
423
|
+
state: AppState = ctx.obj
|
|
424
|
+
if severity and severity not in _SEVERITIES:
|
|
425
|
+
raise typer.BadParameter(f"severity must be one of: {', '.join(_SEVERITIES)}.")
|
|
426
|
+
if not alert_id and not (title or "").strip():
|
|
427
|
+
raise typer.BadParameter("--title is required for a standalone issue (or pass --alert-id).")
|
|
428
|
+
cctx = require_auth(state)
|
|
429
|
+
try:
|
|
430
|
+
result = api.open_incident(cctx, summary=summary, alert_id=alert_id, severity=severity, title=title)
|
|
431
|
+
except (ApiError, ForbiddenError, NotFoundError) as exc:
|
|
432
|
+
_fail(state, exc, incident_id=alert_id or "")
|
|
433
|
+
_write.record_action("incident_opened", resource="incident", success=True)
|
|
434
|
+
if state.json:
|
|
435
|
+
output.emit_json(result)
|
|
436
|
+
return
|
|
437
|
+
# The open response is minimal ({id, newly_opened, state}); re-fetch the canonical incident
|
|
438
|
+
# for a richer card (its real severity — esp. a linked incident inheriting the alert's).
|
|
439
|
+
inc = None
|
|
440
|
+
try:
|
|
441
|
+
inc = api.get_incident(cctx, str(result.get("id", "")))
|
|
442
|
+
except Exception:
|
|
443
|
+
inc = None
|
|
444
|
+
sev = (inc.alert_severity if inc else None) or severity or ""
|
|
445
|
+
st = (inc.state if inc else None) or str(result.get("state", "") or "")
|
|
446
|
+
# Prefer the server's stored title — on the linked path it may have been
|
|
447
|
+
# defaulted to the alert's name rather than anything we sent.
|
|
448
|
+
hero = (inc.title if inc else None) or str(result.get("title", "") or "") or (title or "")
|
|
449
|
+
output.render_incident_opened(summary=summary, severity=sev, state=st, title=hero)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def register(app: typer.Typer) -> None:
|
|
453
|
+
inc = typer.Typer(
|
|
454
|
+
no_args_is_help=True,
|
|
455
|
+
rich_markup_mode="markdown",
|
|
456
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
457
|
+
help="Triage issues (list / count / show / ack / assign / resolve / comment-* / subscribe* / open).",
|
|
458
|
+
)
|
|
459
|
+
inc.command("list", epilog=GLOBALS_EPILOG)(incidents_list)
|
|
460
|
+
inc.command("count", epilog=GLOBALS_EPILOG)(incidents_count)
|
|
461
|
+
inc.command("show", epilog=GLOBALS_EPILOG)(incidents_show)
|
|
462
|
+
inc.command("ack", epilog=GLOBALS_EPILOG)(incidents_ack)
|
|
463
|
+
inc.command("assign", epilog=GLOBALS_EPILOG)(incidents_assign)
|
|
464
|
+
inc.command("resolve", epilog=GLOBALS_EPILOG)(incidents_resolve)
|
|
465
|
+
inc.command("comment-list", epilog=GLOBALS_EPILOG)(incidents_comment_list)
|
|
466
|
+
inc.command("comment-add", epilog=GLOBALS_EPILOG)(incidents_comment_add)
|
|
467
|
+
inc.command("comment-delete", epilog=GLOBALS_EPILOG)(incidents_comment_delete)
|
|
468
|
+
inc.command("subscribers", epilog=GLOBALS_EPILOG)(incidents_subscribers)
|
|
469
|
+
inc.command("subscribe", epilog=GLOBALS_EPILOG)(incidents_subscribe)
|
|
470
|
+
inc.command("unsubscribe", epilog=GLOBALS_EPILOG)(incidents_unsubscribe)
|
|
471
|
+
inc.command("open", epilog=GLOBALS_EPILOG)(incidents_open)
|
|
472
|
+
app.add_typer(inc, name="issues")
|