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,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")