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.
Files changed (389) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +6 -6
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  11. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  12. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  16. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  21. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
  22. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  23. package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
  24. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/audit/run/route.js +5 -4
  26. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -2
  29. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +4 -3
  31. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/logout/route.js +4 -4
  33. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/auth/status/route.js +4 -4
  35. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  38. package/.next/standalone/.next/server/app/audit/page.js +2 -2
  39. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  41. package/.next/standalone/.next/server/app/index.html +1 -1
  42. package/.next/standalone/.next/server/app/index.rsc +15 -15
  43. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
  44. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  45. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  46. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  47. package/.next/standalone/.next/server/app/page.js +2 -2
  48. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  50. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +66 -22
  51. package/.next/standalone/.next/server/app/policies/page.js +5 -4
  52. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  55. package/.next/standalone/.next/server/app/project/[name]/page.js +2 -2
  56. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  58. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  59. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  60. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +2 -2
  61. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  64. package/.next/standalone/.next/server/app/projects/page.js +2 -2
  65. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  68. package/.next/standalone/.next/server/app/settings/page.js +1 -1
  69. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/chunks/{[externals]__1qzqcio._.js → [externals]__0e32-7c._.js} +1 -1
  72. package/.next/standalone/.next/server/chunks/[externals]__1s462yl._.js +3 -0
  73. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +3 -0
  74. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  75. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p8poxd._.js → [root-of-the-server]__0igxt32._.js} +2 -2
  76. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0neqlu-._.js +3 -0
  77. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +3 -0
  78. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t9oqnn._.js +3 -0
  79. package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +3 -0
  80. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +3 -0
  81. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +3 -0
  82. package/.next/standalone/.next/server/chunks/_185gyk9._.js +3 -0
  83. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +5 -6
  84. package/.next/standalone/.next/server/chunks/lib_telemetry-id_ts_1k8gj9o._.js +3 -0
  85. 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
  86. 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
  87. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/src_hooks_0xbw3bu._.js +12 -0
  89. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
  90. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
  91. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
  92. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
  93. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0spkm68._.js → [root-of-the-server]__07gm3zl._.js} +21 -20
  94. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
  95. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
  98. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +6 -0
  99. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__07tbbf3._.js → [root-of-the-server]__0zig5sv._.js} +2 -2
  100. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__192nhvh._.js +5 -0
  101. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
  102. package/.next/standalone/.next/server/chunks/ssr/{_0lvyuv4._.js → _00e9tnf._.js} +2 -2
  103. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/{_0u5zauv._.js → _12l4a_k._.js} +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
  106. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +13 -13
  108. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  109. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/ssr/src_hooks_08o7bqo._.js +10 -0
  112. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  113. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  114. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  115. package/.next/standalone/.next/server/middleware.js +1 -1
  116. package/.next/standalone/.next/server/pages/404.html +1 -1
  117. package/.next/standalone/.next/server/pages/500.html +1 -1
  118. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  119. package/.next/standalone/.next/server/server-reference-manifest.json +71 -27
  120. package/.next/standalone/.next/static/chunks/{37fcpc36x9gpe.js → 0eqky-7n7dgdm.js} +1 -1
  121. package/.next/standalone/.next/static/chunks/{1ide1wl2f7zi1.js → 0sa9v5mhc8861.js} +13 -13
  122. package/.next/standalone/.next/static/chunks/1mfzsja6ftim5.css +2 -0
  123. package/.next/standalone/.next/static/chunks/{3k7mxbp9hiom0.js → 1nuqytbta44af.js} +1 -1
  124. package/.next/standalone/.next/static/chunks/1sl2xmn9inx3y.js +6 -0
  125. package/.next/standalone/.next/static/chunks/{2_2lhuojq2xop.js → 2aawsjld9ied_.js} +1 -1
  126. package/.next/standalone/.next/static/chunks/2gn7ut4svofm2.js +1 -0
  127. package/.next/standalone/.next/static/chunks/{31s1smp-g7gks.js → 2hs7ycvsav7v2.js} +1 -1
  128. package/.next/standalone/.next/static/chunks/{0k5050ezjvv49.js → 2i2yt3hzjredm.js} +1 -1
  129. package/.next/standalone/.next/static/chunks/{3c808qrfcq0im.js → 35xg6retpe6rp.js} +1 -1
  130. package/.next/standalone/.next/static/chunks/{2zpp1we2jv6tb.js → 3y54nw0i7dsjl.js} +1 -1
  131. package/.next/standalone/SECURITY.md +5 -4
  132. package/.next/standalone/app/actions/get-hooks-config.ts +108 -17
  133. package/.next/standalone/app/actions/pack-actions.ts +153 -0
  134. package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
  135. package/.next/standalone/app/audit/_components/share-templates.ts +13 -13
  136. package/.next/standalone/app/policies/hooks-client.tsx +320 -14
  137. package/.next/standalone/components/navbar.tsx +2 -28
  138. package/.next/standalone/components/reach-developers.tsx +8 -1
  139. package/.next/standalone/docs-old/agenteye/python-sdk-skill.mdx +1 -1
  140. package/.next/standalone/docs-old/agenteye/python-sdk.mdx +59 -35
  141. package/.next/standalone/docs-old/i18n/README.ar.md +1 -0
  142. package/.next/standalone/docs-old/i18n/README.de.md +1 -0
  143. package/.next/standalone/docs-old/i18n/README.es.md +1 -0
  144. package/.next/standalone/docs-old/i18n/README.fr.md +1 -0
  145. package/.next/standalone/docs-old/i18n/README.he.md +1 -0
  146. package/.next/standalone/docs-old/i18n/README.hi.md +1 -0
  147. package/.next/standalone/docs-old/i18n/README.it.md +1 -0
  148. package/.next/standalone/docs-old/i18n/README.ja.md +1 -0
  149. package/.next/standalone/docs-old/i18n/README.ko.md +1 -0
  150. package/.next/standalone/docs-old/i18n/README.pt-br.md +1 -0
  151. package/.next/standalone/docs-old/i18n/README.ru.md +1 -0
  152. package/.next/standalone/docs-old/i18n/README.tr.md +1 -0
  153. package/.next/standalone/docs-old/i18n/README.vi.md +1 -0
  154. package/.next/standalone/docs-old/i18n/README.zh.md +1 -0
  155. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +431 -0
  156. package/.next/standalone/fp-cloud-cli/LICENSE +42 -0
  157. package/.next/standalone/fp-cloud-cli/MANIFEST.in +10 -0
  158. package/.next/standalone/fp-cloud-cli/README.md +259 -0
  159. package/.next/standalone/fp-cloud-cli/fp_cli/__init__.py +10 -0
  160. package/.next/standalone/fp-cloud-cli/fp_cli/__main__.py +4 -0
  161. package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +64 -0
  162. package/.next/standalone/fp-cloud-cli/fp_cli/_context.py +332 -0
  163. package/.next/standalone/fp-cloud-cli/fp_cli/_version.py +1 -0
  164. package/.next/standalone/fp-cloud-cli/fp_cli/analytics.py +432 -0
  165. package/.next/standalone/fp-cloud-cli/fp_cli/analytics_config.py +77 -0
  166. package/.next/standalone/fp-cloud-cli/fp_cli/analytics_registry.py +83 -0
  167. package/.next/standalone/fp-cloud-cli/fp_cli/app.py +492 -0
  168. package/.next/standalone/fp-cloud-cli/fp_cli/auth.py +160 -0
  169. package/.next/standalone/fp-cloud-cli/fp_cli/client.py +1694 -0
  170. package/.next/standalone/fp-cloud-cli/fp_cli/commands/__init__.py +0 -0
  171. package/.next/standalone/fp-cloud-cli/fp_cli/commands/_write.py +214 -0
  172. package/.next/standalone/fp-cloud-cli/fp_cli/commands/agent_cmds.py +407 -0
  173. package/.next/standalone/fp-cloud-cli/fp_cli/commands/alerts_cmds.py +445 -0
  174. package/.next/standalone/fp-cloud-cli/fp_cli/commands/audits_cmds.py +1054 -0
  175. package/.next/standalone/fp-cloud-cli/fp_cli/commands/auth_cmds.py +512 -0
  176. package/.next/standalone/fp-cloud-cli/fp_cli/commands/errors_cmds.py +190 -0
  177. package/.next/standalone/fp-cloud-cli/fp_cli/commands/evals_cmds.py +161 -0
  178. package/.next/standalone/fp-cloud-cli/fp_cli/commands/events_cmds.py +159 -0
  179. package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +416 -0
  180. package/.next/standalone/fp-cloud-cli/fp_cli/commands/guardrails_cmds.py +148 -0
  181. package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +472 -0
  182. package/.next/standalone/fp-cloud-cli/fp_cli/commands/keys_cmds.py +407 -0
  183. package/.next/standalone/fp-cloud-cli/fp_cli/commands/list_cmds.py +63 -0
  184. package/.next/standalone/fp-cloud-cli/fp_cli/commands/orgs_cmds.py +319 -0
  185. package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +499 -0
  186. package/.next/standalone/fp-cloud-cli/fp_cli/commands/queries_cmds.py +378 -0
  187. package/.next/standalone/fp-cloud-cli/fp_cli/commands/sessions_cmds.py +151 -0
  188. package/.next/standalone/fp-cloud-cli/fp_cli/commands/settings_cmds.py +150 -0
  189. package/.next/standalone/fp-cloud-cli/fp_cli/commands/usage_cmds.py +35 -0
  190. package/.next/standalone/fp-cloud-cli/fp_cli/commands/users_cmds.py +404 -0
  191. package/.next/standalone/fp-cloud-cli/fp_cli/config.py +330 -0
  192. package/.next/standalone/fp-cloud-cli/fp_cli/dates.py +78 -0
  193. package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +345 -0
  194. package/.next/standalone/fp-cloud-cli/fp_cli/errors.py +98 -0
  195. package/.next/standalone/fp-cloud-cli/fp_cli/models.py +891 -0
  196. package/.next/standalone/fp-cloud-cli/fp_cli/orgs.py +30 -0
  197. package/.next/standalone/fp-cloud-cli/fp_cli/output.py +6593 -0
  198. package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +208 -0
  199. package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +290 -0
  200. package/.next/standalone/fp-cloud-cli/fp_cli/py.typed +0 -0
  201. package/.next/standalone/fp-cloud-cli/fp_cli/select.py +322 -0
  202. package/.next/standalone/fp-cloud-cli/fp_cli/theme.py +53 -0
  203. package/.next/standalone/fp-cloud-cli/pyproject.toml +106 -0
  204. package/.next/standalone/fp-cloud-cli/skill/SKILL.md +302 -0
  205. package/.next/standalone/fp-cloud-cli/skill/agents/openai.yaml +8 -0
  206. package/.next/standalone/fp-cloud-cli/skill/references/commands.md +254 -0
  207. package/.next/standalone/fp-cloud-cli/tests/__init__.py +0 -0
  208. package/.next/standalone/fp-cloud-cli/tests/conftest.py +92 -0
  209. package/.next/standalone/fp-cloud-cli/tests/test_alerting.py +644 -0
  210. package/.next/standalone/fp-cloud-cli/tests/test_analytics.py +385 -0
  211. package/.next/standalone/fp-cloud-cli/tests/test_audits.py +950 -0
  212. package/.next/standalone/fp-cloud-cli/tests/test_auth.py +98 -0
  213. package/.next/standalone/fp-cloud-cli/tests/test_auth_mode.py +272 -0
  214. package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +118 -0
  215. package/.next/standalone/fp-cloud-cli/tests/test_client.py +299 -0
  216. package/.next/standalone/fp-cloud-cli/tests/test_commands.py +814 -0
  217. package/.next/standalone/fp-cloud-cli/tests/test_config.py +99 -0
  218. package/.next/standalone/fp-cloud-cli/tests/test_dashboards_agent.py +340 -0
  219. package/.next/standalone/fp-cloud-cli/tests/test_dates.py +76 -0
  220. package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +481 -0
  221. package/.next/standalone/fp-cloud-cli/tests/test_facets.py +170 -0
  222. package/.next/standalone/fp-cloud-cli/tests/test_failproofai_home.py +722 -0
  223. package/.next/standalone/fp-cloud-cli/tests/test_fp_home_contract.py +198 -0
  224. package/.next/standalone/fp-cloud-cli/tests/test_hardening.py +186 -0
  225. package/.next/standalone/fp-cloud-cli/tests/test_help_table_coverage.py +127 -0
  226. package/.next/standalone/fp-cloud-cli/tests/test_keys_queries.py +496 -0
  227. package/.next/standalone/fp-cloud-cli/tests/test_list.py +59 -0
  228. package/.next/standalone/fp-cloud-cli/tests/test_multivalue.py +183 -0
  229. package/.next/standalone/fp-cloud-cli/tests/test_no_customer_identifiers.py +168 -0
  230. package/.next/standalone/fp-cloud-cli/tests/test_operator.py +492 -0
  231. package/.next/standalone/fp-cloud-cli/tests/test_orgs.py +692 -0
  232. package/.next/standalone/fp-cloud-cli/tests/test_output.py +1732 -0
  233. package/.next/standalone/fp-cloud-cli/tests/test_policy_check.py +191 -0
  234. package/.next/standalone/fp-cloud-cli/tests/test_readme_matches_reality.py +125 -0
  235. package/.next/standalone/fp-cloud-cli/tests/test_review_fixes.py +161 -0
  236. package/.next/standalone/fp-cloud-cli/tests/test_telemetry_completeness.py +90 -0
  237. package/.next/standalone/fp-cloud-cli/tests/test_usage.py +115 -0
  238. package/.next/standalone/fp-cloud-cli/tests/test_v1_origin_diagnostic.py +70 -0
  239. package/.next/standalone/fp-cloud-cli/tests/test_v1_routing.py +214 -0
  240. package/.next/standalone/fp-cloud-cli/tests/test_whoami.py +104 -0
  241. package/.next/standalone/fp-cloud-cli/uv.lock +552 -0
  242. package/.next/standalone/package.json +29 -22
  243. package/.next/standalone/sdk/python/CHANGELOG.md +70 -0
  244. package/.next/standalone/sdk/python/LICENSE +42 -0
  245. package/.next/standalone/sdk/python/MANIFEST.in +21 -0
  246. package/.next/standalone/sdk/python/README.md +466 -0
  247. package/.next/standalone/sdk/python/docs/README.md +249 -0
  248. package/.next/standalone/sdk/python/docs/_shared/README.md +43 -0
  249. package/.next/standalone/sdk/python/docs/_shared/__init__.py +195 -0
  250. package/.next/standalone/sdk/python/docs/crewai/README.md +328 -0
  251. package/.next/standalone/sdk/python/docs/crewai/examples/quickstart.py +61 -0
  252. package/.next/standalone/sdk/python/docs/crewai/examples/research_crew.py +92 -0
  253. package/.next/standalone/sdk/python/docs/langgraph/README.md +481 -0
  254. package/.next/standalone/sdk/python/docs/langgraph/examples/quickstart.py +51 -0
  255. package/.next/standalone/sdk/python/docs/langgraph/examples/research_agent.py +86 -0
  256. package/.next/standalone/sdk/python/docs/langgraph/examples/supervisor_handoff.py +83 -0
  257. package/.next/standalone/sdk/python/docs/llama_index/README.md +332 -0
  258. package/.next/standalone/sdk/python/docs/llama_index/examples/quickstart.py +62 -0
  259. package/.next/standalone/sdk/python/docs/llama_index/examples/research_agent.py +93 -0
  260. package/.next/standalone/sdk/python/docs/manual/README.md +491 -0
  261. package/.next/standalone/sdk/python/docs/manual/examples/quickstart.py +57 -0
  262. package/.next/standalone/sdk/python/docs/manual/examples/research_agent.py +133 -0
  263. package/.next/standalone/sdk/python/docs/pydantic_ai/README.md +323 -0
  264. package/.next/standalone/sdk/python/docs/pydantic_ai/examples/quickstart.py +48 -0
  265. package/.next/standalone/sdk/python/docs/pydantic_ai/examples/research_agent.py +94 -0
  266. package/.next/standalone/sdk/python/failproofai_sdk/__init__.py +131 -0
  267. package/.next/standalone/sdk/python/failproofai_sdk/_context.py +218 -0
  268. package/.next/standalone/sdk/python/failproofai_sdk/_environment.py +78 -0
  269. package/.next/standalone/sdk/python/failproofai_sdk/_events.py +752 -0
  270. package/.next/standalone/sdk/python/failproofai_sdk/_resolver.py +118 -0
  271. package/.next/standalone/sdk/python/failproofai_sdk/_runtime.py +21 -0
  272. package/.next/standalone/sdk/python/failproofai_sdk/_schema.py +317 -0
  273. package/.next/standalone/sdk/python/failproofai_sdk/_scopes.py +422 -0
  274. package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -0
  275. package/.next/standalone/sdk/python/failproofai_sdk/_writer.py +750 -0
  276. package/.next/standalone/sdk/python/failproofai_sdk/integrations/__init__.py +274 -0
  277. package/.next/standalone/sdk/python/failproofai_sdk/integrations/_compat.py +247 -0
  278. package/.next/standalone/sdk/python/failproofai_sdk/integrations/_core.py +1076 -0
  279. package/.next/standalone/sdk/python/failproofai_sdk/integrations/crewai.py +1669 -0
  280. package/.next/standalone/sdk/python/failproofai_sdk/integrations/langchain.py +2151 -0
  281. package/.next/standalone/sdk/python/failproofai_sdk/integrations/llama_index.py +1588 -0
  282. package/.next/standalone/sdk/python/failproofai_sdk/integrations/pydantic_ai.py +977 -0
  283. package/.next/standalone/sdk/python/failproofai_sdk/py.typed +0 -0
  284. package/.next/standalone/sdk/python/pyproject.toml +92 -0
  285. package/.next/standalone/sdk/python/skill/SKILL.md +399 -0
  286. package/.next/standalone/sdk/python/skill/agents/openai.yaml +8 -0
  287. package/.next/standalone/sdk/python/skill/references/events.md +234 -0
  288. package/.next/standalone/sdk/python/skill/references/frameworks.md +314 -0
  289. package/.next/standalone/sdk/python/skill/references/install.md +100 -0
  290. package/.next/standalone/sdk/python/skill/references/integration.md +352 -0
  291. package/.next/standalone/sdk/python/tests/__init__.py +0 -0
  292. package/.next/standalone/sdk/python/tests/conftest.py +169 -0
  293. package/.next/standalone/sdk/python/tests/integrations/__init__.py +6 -0
  294. package/.next/standalone/sdk/python/tests/integrations/test_crewai.py +1299 -0
  295. package/.next/standalone/sdk/python/tests/integrations/test_langchain.py +2338 -0
  296. package/.next/standalone/sdk/python/tests/integrations/test_llama_index.py +1576 -0
  297. package/.next/standalone/sdk/python/tests/integrations/test_pydantic_ai.py +1102 -0
  298. package/.next/standalone/sdk/python/tests/test_context.py +410 -0
  299. package/.next/standalone/sdk/python/tests/test_docs.py +411 -0
  300. package/.next/standalone/sdk/python/tests/test_durability.py +1352 -0
  301. package/.next/standalone/sdk/python/tests/test_encoding.py +571 -0
  302. package/.next/standalone/sdk/python/tests/test_integrations.py +1127 -0
  303. package/.next/standalone/sdk/python/tests/test_no_customer_identifiers.py +214 -0
  304. package/.next/standalone/sdk/python/tests/test_packaging.py +185 -0
  305. package/.next/standalone/sdk/python/tests/test_resolver_umbrella.py +178 -0
  306. package/.next/standalone/sdk/python/tests/test_scopes.py +487 -0
  307. package/.next/standalone/sdk/python/tests/test_sdk.py +923 -0
  308. package/.next/standalone/sdk/python/tests/test_server_contract.py +650 -0
  309. package/.next/standalone/sdk/python/tests/test_site_docs.py +371 -0
  310. package/.next/standalone/sdk/python/tests/test_skill_snippets.py +165 -0
  311. package/.next/standalone/sdk/python/tests/test_spool_contract.py +287 -0
  312. package/.next/standalone/sdk/python/tests/test_spool_creation.py +360 -0
  313. package/.next/standalone/sdk/python/tests/test_wire_format.py +279 -0
  314. package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +378 -0
  315. package/.next/standalone/sdk/python/uv.lock +6005 -0
  316. package/.next/standalone/server.js +1 -1
  317. package/README.md +77 -21
  318. package/bin/failproofai.mjs +145 -11
  319. package/dist/cli.mjs +4482 -2205
  320. package/dist/index.js +5 -5
  321. package/dist/worker.mjs +2399 -1978
  322. package/package.json +29 -22
  323. package/policy-pack/SHA256SUMS +2 -0
  324. package/policy-pack/failproofai-pack.json +732 -0
  325. package/policy-pack/failproofai-pack.mjs +2017 -0
  326. package/scripts/build-policy-pack.mjs +121 -0
  327. package/scripts/changelog-section.py +77 -0
  328. package/scripts/prune-standalone.mjs +3 -0
  329. package/scripts/python-version.py +232 -0
  330. package/scripts/translate-docs/cli.ts +21 -0
  331. package/scripts/translate-docs/mintlify-nav.ts +85 -13
  332. package/scripts/translate-docs/translator.ts +66 -11
  333. package/src/audit/cache.ts +40 -3
  334. package/src/audit/cli.ts +1 -1
  335. package/src/audit/index.ts +1 -1
  336. package/src/audit/replay.ts +89 -3
  337. package/src/audit/report.ts +5 -5
  338. package/src/audit/schedule-cli.ts +49 -53
  339. package/src/hooks/builtin-policies.ts +217 -579
  340. package/src/hooks/cloud-enrollment-cli.ts +66 -27
  341. package/src/hooks/cloud-managed-policies.ts +6 -1
  342. package/src/hooks/configure-wizard.ts +1 -1
  343. package/src/hooks/custom-hooks-loader.ts +89 -6
  344. package/src/hooks/fp-home.ts +92 -4
  345. package/src/hooks/fp-reset.ts +26 -2
  346. package/src/hooks/handler.ts +181 -11
  347. package/src/hooks/harness-cli.ts +79 -25
  348. package/src/hooks/hook-activity-store.ts +13 -3
  349. package/src/hooks/install-prompt.ts +25 -3
  350. package/src/hooks/manager.ts +418 -172
  351. package/src/hooks/migrations.ts +2 -2
  352. package/src/hooks/pack-cli.ts +505 -0
  353. package/src/hooks/pack-failclosed.ts +189 -0
  354. package/src/hooks/pack-manifest.ts +344 -0
  355. package/src/hooks/pack-store.ts +930 -0
  356. package/src/hooks/policy-catalog.ts +535 -0
  357. package/src/hooks/policy-evaluator.ts +24 -16
  358. package/src/hooks/policy-presets.ts +4 -2
  359. package/src/hooks/policy-registry.ts +8 -2
  360. package/src/hooks/policy-types.ts +26 -0
  361. package/src/hooks/session-pause-cli.ts +33 -9
  362. package/src/hooks/tui.ts +570 -0
  363. package/src/hooks/types.ts +15 -1
  364. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_usx0j._.js +0 -3
  365. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_zk340._.js +0 -3
  366. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0agdw2a._.js +0 -3
  367. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d1u_na._.js +0 -3
  368. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +0 -3
  369. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +0 -3
  370. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +0 -3
  371. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +0 -3
  372. package/.next/standalone/.next/server/chunks/_1ger0a2._.js +0 -3
  373. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +0 -3
  374. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1iiwm5e._.js +0 -3
  375. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kc8_v5._.js +0 -4
  376. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kgy9da._.js +0 -4
  377. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mhqk5a._.js +0 -4
  378. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pltpn8._.js +0 -4
  379. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qkcyf-._.js +0 -4
  380. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w0x_ok._.js +0 -4
  381. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_dfocn._.js +0 -4
  382. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bfex62._.js +0 -5
  383. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kzapg5._.js +0 -6
  384. package/.next/standalone/.next/static/chunks/36fw6yk1s6x_f.js +0 -1
  385. package/.next/standalone/.next/static/chunks/3tzq0260n17ae.js +0 -6
  386. package/.next/standalone/.next/static/chunks/3wq9x0c-7ld5d.css +0 -2
  387. /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_buildManifest.js +0 -0
  388. /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_clientMiddlewareManifest.js +0 -0
  389. /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_ssgManifest.js +0 -0
@@ -0,0 +1,445 @@
1
+ """Alert definitions: alerts list / show / create / update / delete / test.
2
+
3
+ Alerts are referenced by their **name** (unique per org). An alert is a **trigger** (a condition,
4
+ shaped per ``trigger_kind`` — e.g. a metric threshold, a custom SQL count, an evaluation-score
5
+ rule) plus an **evaluation** cadence and a set of notification **channels**. The trigger body
6
+ (``trigger_spec``) + ``channels`` are opaque/union JSON, so create/update take them as JSON flags
7
+ or a full payload via ``--file``/stdin, with a few common scalar fields available as convenience
8
+ overrides layered on top.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ from typing import Any, Dict, Optional
15
+
16
+ import typer
17
+
18
+ from .. import _click_compat as click # the Click Typer is running; see _click_compat
19
+ from .. import client as api
20
+ from .. import output
21
+ from .._context import GLOBALS_EPILOG, AppState, require_auth
22
+ from ..models import Alert
23
+ from . import _write
24
+
25
+ _TRIGGER_KINDS = ("metric_threshold", "custom_sql", "evaluation_score", "eval_compound", "per_event")
26
+ _SEVERITIES = ("info", "warning", "critical")
27
+
28
+
29
+ def _load_file(file: Optional[str]) -> Dict[str, Any]:
30
+ if file is None:
31
+ return {}
32
+ raw = _write.read_text_arg(file)
33
+ try:
34
+ parsed = json.loads(raw)
35
+ except json.JSONDecodeError as exc:
36
+ raise typer.BadParameter(f"--file is not valid JSON: {exc}", param_hint="--file")
37
+ if not isinstance(parsed, dict):
38
+ raise typer.BadParameter("--file must contain a JSON object.", param_hint="--file")
39
+ return parsed
40
+
41
+
42
+ def _apply_overrides(body: Dict[str, Any], **overrides: Any) -> Dict[str, Any]:
43
+ for key, value in overrides.items():
44
+ if value is not None:
45
+ body[key] = value
46
+ return body
47
+
48
+
49
+ def _alert_to_body(alert: Alert) -> Dict[str, Any]:
50
+ """The writable AlertBody fields of an existing alert, used as the merge base for
51
+ a flag-only update.
52
+
53
+ The server's ``PUT /api/alerts/{id}`` is a **full replace** (it overwrites every
54
+ column), so a single-field edit must resend the whole alert. We seed the body from
55
+ the current alert and let the override flags change just the named fields — the
56
+ same read-merge ``users update`` already does."""
57
+ return {
58
+ "name": alert.name,
59
+ "description": alert.description,
60
+ "enabled": alert.enabled,
61
+ "trigger_kind": alert.trigger_kind,
62
+ "trigger_spec": alert.trigger_spec,
63
+ "min_breaches": alert.min_breaches,
64
+ "eval_window": alert.eval_window,
65
+ "eval_interval_secs": alert.eval_interval_secs,
66
+ "severity": alert.severity,
67
+ "channels": alert.channels,
68
+ }
69
+
70
+
71
+ def _validate_alert(body: Dict[str, Any], *, require_core: bool) -> None:
72
+ if require_core:
73
+ if not body.get("name"):
74
+ raise typer.BadParameter("alert 'name' is required (in --file or via --name).")
75
+ if not body.get("trigger_kind"):
76
+ raise typer.BadParameter("alert 'trigger_kind' is required (in --file or via --trigger-kind).")
77
+ if body.get("trigger_spec") is None:
78
+ raise typer.BadParameter("alert 'trigger_spec' is required (provide it in --file or --trigger-spec).")
79
+ if body.get("trigger_kind") and body["trigger_kind"] not in _TRIGGER_KINDS:
80
+ raise typer.BadParameter(f"trigger_kind must be one of: {', '.join(_TRIGGER_KINDS)}.")
81
+ if body.get("severity") and body["severity"] not in _SEVERITIES:
82
+ raise typer.BadParameter(f"severity must be one of: {', '.join(_SEVERITIES)}.")
83
+ mb, ew = body.get("min_breaches"), body.get("eval_window")
84
+ if isinstance(mb, int) and mb < 1:
85
+ raise typer.BadParameter("min_breaches must be >= 1.")
86
+ if isinstance(ew, int) and ew < 1:
87
+ raise typer.BadParameter("eval_window must be >= 1.")
88
+ if isinstance(mb, int) and isinstance(ew, int) and mb > ew:
89
+ raise typer.BadParameter("min_breaches cannot exceed eval_window.")
90
+ eis = body.get("eval_interval_secs")
91
+ if isinstance(eis, int) and not (30 <= eis <= 86400):
92
+ raise typer.BadParameter("eval_interval_secs must be between 30 and 86400.")
93
+
94
+
95
+ def _parse_json_opt(value: Optional[str], hint: str) -> Any:
96
+ if value is None:
97
+ return None
98
+ try:
99
+ return json.loads(value)
100
+ except json.JSONDecodeError as exc:
101
+ raise typer.BadParameter(f"{hint} is not valid JSON: {exc}", param_hint=hint)
102
+
103
+
104
+ def _refetch_alert(cctx, name: Optional[str]) -> Optional[Alert]:
105
+ """Re-read the canonical stored alert by name after a create/update (the server busts the
106
+ list cache on write, so this reflects the saved state incl. server-applied defaults). Returns
107
+ ``None`` on any miss so the caller can fall back to rendering from the request body."""
108
+ if not name:
109
+ return None
110
+ try:
111
+ for a in api.list_alerts(cctx):
112
+ if a.name == name:
113
+ return a
114
+ except Exception:
115
+ return None
116
+ return None
117
+
118
+
119
+ def _alert_from_body(body: Dict[str, Any], base: Optional[Alert] = None) -> Alert:
120
+ """Build an ``Alert`` view from a create/update request body (fallback when the canonical
121
+ re-fetch misses). ``base`` (the pre-update alert) supplies id/created/open-incidents context;
122
+ for a create there is none, so those default to empty/0."""
123
+ return Alert(
124
+ id=base.id if base else "",
125
+ name=body.get("name") or (base.name if base else ""),
126
+ description=body.get("description") if body.get("description") is not None else (base.description if base else None),
127
+ enabled=body.get("enabled", base.enabled if base else True),
128
+ trigger_kind=body.get("trigger_kind") or (base.trigger_kind if base else ""),
129
+ trigger_spec=body.get("trigger_spec") or (base.trigger_spec if base else {}),
130
+ min_breaches=body.get("min_breaches", base.min_breaches if base else 1),
131
+ eval_window=body.get("eval_window", base.eval_window if base else 1),
132
+ eval_interval_secs=body.get("eval_interval_secs", base.eval_interval_secs if base else 0),
133
+ severity=body.get("severity") or (base.severity if base else ""),
134
+ channels=body.get("channels") if body.get("channels") is not None else (base.channels if base else []),
135
+ created_by=base.created_by if base else "",
136
+ created_at=base.created_at if base else "",
137
+ updated_at="",
138
+ last_attempted_at=base.last_attempted_at if base else None,
139
+ open_incidents=base.open_incidents if base else 0,
140
+ )
141
+
142
+
143
+ def _resolve_alert_or_exit(state: AppState, alerts, handle: str) -> Alert:
144
+ """Resolve an alert by **name** (primary), falling back to an exact id match, raising a typed
145
+ error the central chokepoint renders: none → exit 6, several → exit 2."""
146
+ return _write.resolve_one(alerts, handle, kind="alert", list_cmd="alerts list")
147
+
148
+
149
+ def alerts_list(
150
+ ctx: typer.Context,
151
+ show_id: bool = typer.Option(False, "--show-id", help="Prepend a short alert-id column (the full id is always in --json)."),
152
+ ) -> None:
153
+ """List alert definitions in a boxed table, newest first.
154
+
155
+ Shows `created · name · by · trigger · severity · last alert` — `by` the creator's email,
156
+ severity colour-coded, and `last alert` the humanized age of the last evaluation (`never` if it
157
+ has never run, e.g. a disabled alert). Disabled alerts are dimmed; the on/off split is in the
158
+ footer. `name` is the handle action commands take; the raw id is hidden unless `--show-id`.
159
+ Needs `alerts:read`. With `--json`: `{"alerts": [{id, name, created_by, trigger_kind, severity,
160
+ enabled, last_attempted_at, created_at, open_incidents, ...}]}`.
161
+
162
+ Example:
163
+
164
+ * `fp alerts list`
165
+ """
166
+ state: AppState = ctx.obj
167
+ cctx = require_auth(state)
168
+ alerts = api.list_alerts(cctx)
169
+ if state.json:
170
+ output.emit_json({"alerts": alerts})
171
+ return
172
+ output.render_alerts(alerts, show_id=show_id)
173
+ output.alerts_footer(alerts)
174
+
175
+
176
+ def alerts_show(
177
+ ctx: typer.Context,
178
+ name: str = typer.Argument(..., metavar="NAME", help="Alert name (or id)."),
179
+ ) -> None:
180
+ """Show one alert as a stack of parsed cards — identity, trigger, evaluation, channels.
181
+
182
+ Referenced by alert **name** (a UUID-shaped id is also accepted). The `trigger_spec` is parsed
183
+ into a human sentence per `trigger_kind`, and `channels` shows default-vs-custom per channel.
184
+ Not-found → red `✗ no alert named "…"`, exit 6. Needs `alerts:read`. With `--json`: the full
185
+ raw `Alert` (untouched `trigger_spec` + `channels`).
186
+
187
+ Example:
188
+
189
+ * `fp alerts show metric-threshold-alert`
190
+ """
191
+ state: AppState = ctx.obj
192
+ cctx = require_auth(state)
193
+ alert = _resolve_alert_or_exit(state, api.list_alerts(cctx), name)
194
+ if state.json:
195
+ output.emit_json(alert)
196
+ return
197
+ output.render_alert_show(alert)
198
+
199
+
200
+ def _common_overrides(name, description, severity, trigger_kind, eval_interval_secs, min_breaches, eval_window, trigger_spec, channels):
201
+ return dict(
202
+ name=name,
203
+ description=description,
204
+ severity=severity,
205
+ trigger_kind=trigger_kind,
206
+ eval_interval_secs=eval_interval_secs,
207
+ min_breaches=min_breaches,
208
+ eval_window=eval_window,
209
+ trigger_spec=_parse_json_opt(trigger_spec, "--trigger-spec"),
210
+ channels=_parse_json_opt(channels, "--channels"),
211
+ )
212
+
213
+
214
+ def alerts_create(
215
+ ctx: typer.Context,
216
+ name: str = typer.Argument(..., metavar="NAME", help="Alert name (unique per org)."),
217
+ file: Optional[str] = typer.Option(None, "--file", help="Full alert JSON (AlertInput) to base it on, or `-` for stdin."),
218
+ description: Optional[str] = typer.Option(None, "--description", help="Description (overrides --file)."),
219
+ severity: Optional[str] = typer.Option(None, "--severity", help=f"Severity: {', '.join(_SEVERITIES)} (overrides --file)."),
220
+ trigger_kind: Optional[str] = typer.Option(None, "--trigger-kind", help=f"What kind of trigger: {', '.join(_TRIGGER_KINDS)} (overrides --file)."),
221
+ trigger_spec: Optional[str] = typer.Option(None, "--trigger-spec", help="Trigger spec as JSON — the condition, shaped per trigger kind (overrides --file)."),
222
+ channels: Optional[str] = typer.Option(None, "--channels", help="Channels as a JSON array, e.g. `[{\"kind\":\"email\"}]` (overrides --file)."),
223
+ eval_interval_secs: Optional[int] = typer.Option(None, "--eval-interval-secs", help="How often to evaluate, in seconds (30–86400)."),
224
+ min_breaches: Optional[int] = typer.Option(None, "--min-breaches", help="Breaches within the window required to fire."),
225
+ eval_window: Optional[int] = typer.Option(None, "--eval-window", help="Evaluation window size (number of intervals)."),
226
+ ) -> None:
227
+ """Create an alert and show it rendered as parsed cards.
228
+
229
+ Give the alert a **name** (positional), then define it inline with the flags or base it on a
230
+ full JSON payload via `--file` (flags layer on top). The core pieces are `--trigger-kind` +
231
+ `--trigger-spec` (the condition) and `--severity`; the rest have sensible server defaults. On
232
+ success the new alert renders the same way `alerts show` does (identity + trigger + evaluation
233
+ + channels) in a green "alert created" card. Creating isn't destructive, so there's no confirm.
234
+ A name collision is rejected up front. New alerts start **enabled**. Needs `alerts:write`. With
235
+ `--json`: `{id, created_at}`.
236
+
237
+ Examples:
238
+
239
+ * `fp alerts create high-errors --trigger-kind metric_threshold --severity warning \\
240
+ --trigger-spec '{"metric":"error_count","op":">","value":50,"window_secs":900}'`
241
+ * `fp alerts create high-errors --file alert.json` — base it on a saved AlertInput
242
+ * `fp alerts create high-errors --file alert.json --severity critical` — file + an override
243
+ """
244
+ state: AppState = ctx.obj
245
+ body = _load_file(file)
246
+ _apply_overrides(body, **_common_overrides(name, description, severity, trigger_kind, eval_interval_secs, min_breaches, eval_window, trigger_spec, channels))
247
+ _validate_alert(body, require_core=True)
248
+ cctx = require_auth(state)
249
+ if any(a.name == body.get("name") for a in api.list_alerts(cctx)): # names are the handle → unique
250
+ raise click.UsageError(f'an alert named "{body.get("name")}" already exists')
251
+ result = api.create_alert(cctx, body)
252
+ _write.record_action("alert_created", resource="alert", success=True, trigger_kind=body.get("trigger_kind"))
253
+ if state.json:
254
+ output.emit_json(result)
255
+ return
256
+ # Re-read the canonical stored alert (server defaults applied) for the rendered cards.
257
+ alert = _refetch_alert(cctx, body.get("name")) or _alert_from_body(body)
258
+ output.render_alert_created(alert)
259
+
260
+
261
+ def alerts_update(
262
+ ctx: typer.Context,
263
+ name: str = typer.Argument(..., metavar="NAME", help="Alert name (or id) to update."),
264
+ new_name: Optional[str] = typer.Option(None, "--name", help="Rename the alert."),
265
+ file: Optional[str] = typer.Option(None, "--file", help="Full alert JSON (AlertInput) to replace it with, or `-` for stdin."),
266
+ description: Optional[str] = typer.Option(None, "--description", help="New description."),
267
+ severity: Optional[str] = typer.Option(None, "--severity", help=f"New severity: {', '.join(_SEVERITIES)}."),
268
+ trigger_kind: Optional[str] = typer.Option(None, "--trigger-kind", help=f"New trigger kind: {', '.join(_TRIGGER_KINDS)}."),
269
+ trigger_spec: Optional[str] = typer.Option(None, "--trigger-spec", help="New trigger spec as JSON."),
270
+ channels: Optional[str] = typer.Option(None, "--channels", help="New channels as a JSON array."),
271
+ eval_interval_secs: Optional[int] = typer.Option(None, "--eval-interval-secs", help="New evaluation interval in seconds (30–86400)."),
272
+ min_breaches: Optional[int] = typer.Option(None, "--min-breaches", help="New breaches-to-fire."),
273
+ eval_window: Optional[int] = typer.Option(None, "--eval-window", help="New evaluation window size."),
274
+ 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."),
275
+ ) -> None:
276
+ """Update an alert, referenced by **name** (or a UUID-shaped id). Two ways to change it:
277
+
278
+ * **Tweak fields** with the override flags (e.g. `--severity critical`, `--name new-name`).
279
+ Because the server replaces the whole alert on update, the CLI re-sends the current alert
280
+ with your changes applied — so a flag-only edit needs `alerts:read` **and** `alerts:write`.
281
+ * **Replace wholesale** with `--file` (a complete AlertInput), optionally layering a few
282
+ override flags on top.
283
+
284
+ It confirms first (default no; `--yes` skips) and renders the new state as a green "alert
285
+ updated" card. With `--json`: `{id, updated_at}`.
286
+
287
+ Examples:
288
+
289
+ * `fp alerts update high-errors --severity critical` — change one field
290
+ * `fp alerts update high-errors --name critical-errors --yes` — rename, no prompt
291
+ * `fp alerts update high-errors --file alert.json --yes` — replace the whole definition
292
+ """
293
+ state: AppState = ctx.obj
294
+ overrides = _common_overrides(new_name, description, severity, trigger_kind, eval_interval_secs, min_breaches, eval_window, trigger_spec, channels)
295
+ cctx = require_auth(state)
296
+ existing = api.list_alerts(cctx)
297
+ alert = _resolve_alert_or_exit(state, existing, name)
298
+ if file is not None:
299
+ # An explicit full body is a straight replace (existing behaviour).
300
+ body = _load_file(file)
301
+ _apply_overrides(body, **overrides)
302
+ _validate_alert(body, require_core=False)
303
+ else:
304
+ # Flag-only edit: the server's PUT is a full replace, so seed the body from the resolved
305
+ # alert and overlay just the changed fields (read-merge, like `users update`).
306
+ body = _alert_to_body(alert)
307
+ _apply_overrides(body, **overrides)
308
+ _validate_alert(body, require_core=True)
309
+ final_name = body.get("name") or alert.name
310
+ if final_name != alert.name and any(a.name == final_name and a.id != alert.id for a in existing):
311
+ raise click.UsageError(f'an alert named "{final_name}" already exists')
312
+ if _write.should_prompt(state, yes):
313
+ if not output.confirm_alert_update(alert.name):
314
+ if state.json:
315
+ output.emit_json({"cancelled": True})
316
+ else:
317
+ output.print_cancelled("nothing changed")
318
+ return
319
+ result = api.update_alert(cctx, alert.id, body)
320
+ _write.record_action("alert_updated", resource="alert", success=True)
321
+ if state.json:
322
+ output.emit_json(result)
323
+ return
324
+ final_name = body.get("name") or alert.name
325
+ updated = _refetch_alert(cctx, final_name) or _alert_from_body(body, base=alert)
326
+ output.render_alert_updated(updated, old_name=alert.name)
327
+
328
+
329
+ def alerts_delete(
330
+ ctx: typer.Context,
331
+ name: str = typer.Argument(..., metavar="NAME", help="Alert name (or id) to delete."),
332
+ 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."),
333
+ ) -> None:
334
+ """Delete an alert, referenced by **name** (or a UUID-shaped id). This cannot be undone.
335
+
336
+ Shows an amber preview of the alert (incl. its open-incident count, which the delete orphans)
337
+ then confirms. Needs `alerts:write`. With `--json`: `{"deleted": true, "id", "name"}` (or
338
+ `{"cancelled": true}` on a declined prompt).
339
+
340
+ Example:
341
+
342
+ * `fp alerts delete old-test-alert`
343
+ """
344
+ state: AppState = ctx.obj
345
+ cctx = require_auth(state)
346
+ alert = _resolve_alert_or_exit(state, api.list_alerts(cctx), name)
347
+ if _write.should_prompt(state, yes):
348
+ output.render_alert_delete_preview(alert) # amber preview of what's about to go
349
+ if not output.confirm_alert_delete(alert.open_incidents):
350
+ if state.json:
351
+ output.emit_json({"cancelled": True})
352
+ else:
353
+ output.print_cancelled("nothing deleted")
354
+ return
355
+ api.delete_alert(cctx, alert.id)
356
+ _write.record_action("alert_deleted", resource="alert", success=True, destructive=True)
357
+ if state.json:
358
+ output.emit_json({"deleted": True, "id": alert.id, "name": alert.name})
359
+ else:
360
+ output.alert_deleted(alert.name)
361
+
362
+
363
+ def _test_channel_kinds(alert: Alert, override: Any) -> list:
364
+ """The channel kinds a test will dispatch to: the ``--channels`` override if given, else the
365
+ alert's saved channels, else (empty) the default set (slack/webhook/email)."""
366
+ chans = override if override is not None else (alert.channels or [])
367
+ if not chans:
368
+ return ["slack", "webhook", "email"]
369
+ kinds: list = []
370
+ for c in chans:
371
+ if isinstance(c, dict):
372
+ k = c.get("kind")
373
+ if k and k not in kinds:
374
+ kinds.append(k)
375
+ return kinds
376
+
377
+
378
+ def alerts_test(
379
+ ctx: typer.Context,
380
+ name: str = typer.Argument(..., metavar="NAME", help="Alert name (or id) to test."),
381
+ channels: Optional[str] = typer.Option(None, "--channels", help="Channels as a JSON array (else uses the alert's saved channels)."),
382
+ 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."),
383
+ ) -> None:
384
+ """Fire a test notification for an alert — really sends to its email/Slack/webhook channels.
385
+
386
+ Referenced by alert **name** (a UUID-shaped id is also accepted). Confirms first (it delivers
387
+ real notifications; `--yes` skips), then reports which channels it dispatched to. Note: the
388
+ server reports success as soon as it dispatches — actual delivery isn't confirmed. Needs
389
+ `alerts:write`. With `--json`: `{ok, synthetic_incident_id}` (or `{cancelled: true}`).
390
+
391
+ Example:
392
+
393
+ * `fp alerts test metric-threshold-alert`
394
+ """
395
+ state: AppState = ctx.obj
396
+ cctx = require_auth(state)
397
+ alert = _resolve_alert_or_exit(state, api.list_alerts(cctx), name)
398
+ override = _parse_json_opt(channels, "--channels")
399
+ if _write.should_prompt(state, yes):
400
+ if not output.confirm_alert_test(alert.name):
401
+ if state.json:
402
+ output.emit_json({"cancelled": True})
403
+ else:
404
+ output.print_cancelled("nothing sent")
405
+ return
406
+ result = api.test_alert(cctx, alert.id, override)
407
+ _write.record_action("alert_tested", resource="alert", success=True)
408
+ if state.json:
409
+ output.emit_json(result)
410
+ return
411
+ output.alert_test_sent(alert.name, _test_channel_kinds(alert, override))
412
+
413
+
414
+ _ALERTS_GROUP_HELP = """Manage alert definitions — list, inspect, create, edit, delete, and test-fire them.
415
+
416
+ Alerts are referenced by **name** (unique per org). Each is a **trigger** (a condition shaped per
417
+ `--trigger-kind`) + an evaluation cadence + notification **channels** (email / Slack / webhook).
418
+
419
+ **Subcommands:** `list` · `show` · `create` · `update` · `delete` · `test`
420
+
421
+ **Examples:**
422
+
423
+ * `fp alerts list` — all alerts, newest first
424
+ * `fp alerts show high-errors` — one alert's trigger / evaluation / channels
425
+ * `fp alerts create high-errors --trigger-kind metric_threshold --severity warning --trigger-spec '{"metric":"error_count","op":">","value":50,"window_secs":900}'`
426
+ * `fp alerts update high-errors --severity critical` — change a field
427
+ * `fp alerts test high-errors` — fire a sample notification to its channels
428
+ * `fp alerts delete high-errors` — remove it
429
+ """
430
+
431
+
432
+ def register(app: typer.Typer) -> None:
433
+ alerts_app = typer.Typer(
434
+ no_args_is_help=True,
435
+ rich_markup_mode="markdown",
436
+ context_settings={"help_option_names": ["-h", "--help"]},
437
+ help=_ALERTS_GROUP_HELP,
438
+ )
439
+ alerts_app.command("list", epilog=GLOBALS_EPILOG)(alerts_list)
440
+ alerts_app.command("show", epilog=GLOBALS_EPILOG)(alerts_show)
441
+ alerts_app.command("create", epilog=GLOBALS_EPILOG)(alerts_create)
442
+ alerts_app.command("update", epilog=GLOBALS_EPILOG)(alerts_update)
443
+ alerts_app.command("delete", epilog=GLOBALS_EPILOG)(alerts_delete)
444
+ alerts_app.command("test", epilog=GLOBALS_EPILOG)(alerts_test)
445
+ app.add_typer(alerts_app, name="alerts")