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,492 @@
1
+ """Typer application: global options + command registration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ import time
7
+ from typing import Optional
8
+
9
+ import typer
10
+
11
+ from . import _click_compat as click # the Click Typer is running; see _click_compat
12
+ from . import _context
13
+ from . import analytics
14
+ from . import config as cfgmod
15
+ from . import output
16
+ from . import orgs as orgsmod
17
+ from ._context import AppState
18
+ from ._version import __version__
19
+ from .commands import (
20
+ agent_cmds,
21
+ alerts_cmds,
22
+ audits_cmds,
23
+ auth_cmds,
24
+ errors_cmds,
25
+ evals_cmds,
26
+ events_cmds,
27
+ fleet_cmds,
28
+ guardrails_cmds,
29
+ incidents_cmds,
30
+ keys_cmds,
31
+ list_cmds,
32
+ orgs_cmds,
33
+ policies_cmds,
34
+ queries_cmds,
35
+ sessions_cmds,
36
+ settings_cmds,
37
+ usage_cmds,
38
+ users_cmds,
39
+ )
40
+
41
+ _HELP = """\
42
+ Query FailproofAI Cloud — **sessions, events, and evaluation results** — from your terminal.
43
+
44
+ Every data command accepts `--json` for stable, machine-readable output: data on **stdout**, and
45
+ on failure a `{"error": …, "exit_code": …}` object on **stdout** too; human status/progress goes to
46
+ **stderr**. So it is safe to script and easy for AI agents to parse.
47
+
48
+ **Getting started**
49
+
50
+ * `fp --base-url https://cloud.example.com login` — email a one-time code, then paste it
51
+ * `fp whoami` — confirm who you are and your permissions
52
+ * `fp --json sessions --since 24h` — run a query (results are newest-first)
53
+
54
+ **Global options come before the command.** `fp --json --base-url URL events` is correct;
55
+ `fp events --json` is not. The globals are `--json`, `--base-url`, `--org`, `--token`,
56
+ `--api-key`, `--insecure`/`--secure`, `--timeout`, `--quiet`, and `--no-color`.
57
+
58
+ **In CI, authenticate with an API key** — `--api-key <key>` or `FP_API_KEY` — instead of
59
+ a session. The key is never written to disk, and the commands that need a *human* session
60
+ (`login`, `logout`, `orgs`, `agent`, `keys update`, and the whole `policies`/`fleet`/`guardrails`
61
+ group) exit `2` rather than half-run. Pass `--org
62
+ <slug>` if the key can act for more than one org; nothing is inherited from a saved login.
63
+
64
+ **Destructive commands confirm only on a terminal.** Anything that creates, updates or
65
+ deletes prompts before acting when it is attached to a TTY — but under `--json`, or with
66
+ stdin redirected, it proceeds **without asking**, so scripts and agents never hang on a
67
+ prompt nobody can answer. Pass `--yes`/`-y` to say so explicitly, and prefer it in
68
+ automation: it makes the intent visible at the call site instead of resting on how stdin
69
+ happened to be wired.
70
+
71
+ **Multi-tenant:** if you belong to more than one org, pick the active tenant at login
72
+ (`fp login --org <slug>`) or per command with `--org <slug>` (or `FP_ORG`).
73
+ `fp orgs` lists the orgs you can access; `fp orgs switch <slug>` sets the default.
74
+
75
+ **Exit codes** (stable, for scripting): `0` success · `1` unexpected error (e.g. an unhandled
76
+ server status) · `2` usage error · `3` cannot reach the dashboard · `4` not signed in or session
77
+ expired (run `fp login`) · `5` missing the required permission · `6` resource not found.
78
+
79
+ Run `fp COMMAND -h` (or `--help`) for each command's filters, value formats, and JSON shape.
80
+ """
81
+
82
+ # Each example is its own paragraph (blank line between) on purpose: Typer collapses single
83
+ # newlines within an epilog paragraph into spaces, which would mash a normal Markdown list onto
84
+ # one line. Paragraph breaks survive as the single newlines Markdown needs for a clean list.
85
+ _EPILOG = """\
86
+ **Examples**
87
+
88
+ * `fp --json sessions --since 7d --status error` — runs that errored in the past week
89
+
90
+ * `fp --json evals --score helpfulness:..0.5` — evaluations scoring ≤ 0.5 on helpfulness
91
+
92
+ * `fp evals --aggregate --since 7d --env prod` — rolled-up eval health for the last week in prod
93
+
94
+ * `fp --json errors --since 24h --env prod` — error summary for the last day in prod
95
+
96
+ * `fp --json events --session-id run-001 --all | jq '.events[].payload'` — every event payload for one session, ready to pipe into a script
97
+ """
98
+
99
+ # -h is an alias for --help everywhere (Click reads this from the context).
100
+ _CTX = {"help_option_names": ["-h", "--help"]}
101
+
102
+ app = typer.Typer(
103
+ no_args_is_help=True,
104
+ add_completion=False,
105
+ rich_markup_mode="markdown",
106
+ context_settings=_CTX,
107
+ help=_HELP,
108
+ epilog=_EPILOG,
109
+ # Typer renders local variables into its pretty traceback, and the frames on
110
+ # any escaped exception hold `ClientContext(token=…, api_key=…)` and
111
+ # `CliConfig(session_token=…)`. The default is True for every typer from
112
+ # 0.13.0 to 0.23.x — the whole declared range below 0.24 — so an ordinary
113
+ # unhandled server-shape error dumped the session token onto stderr, into
114
+ # terminal scrollback, CI job logs and pasted bug reports. Verified on
115
+ # 0.13.0: `fp whoami` against a malformed `/api/keys` body printed the token
116
+ # three times. This package goes to real lengths to protect that credential
117
+ # (0600 file, atomic rename, symlink refusal, an allowlisted telemetry
118
+ # payload) and this bypassed all of it. Set explicitly rather than relying on
119
+ # a library default for a security property, since `uv.lock` happening to
120
+ # pin 0.27.1 is the only reason CI never saw it.
121
+ pretty_exceptions_show_locals=False,
122
+ )
123
+
124
+
125
+ # Render every ClickException as a clean one-line error (`✗ message`) instead of
126
+ # Typer's heavy red panel, so failures match the rest of the CLI's line aesthetic
127
+ # (`✓` / `○` / `›`). Usage errors keep a `try '… -h'` nudge. Patches the single
128
+ # function Typer calls for this; falls back silently if its internals change.
129
+ try: # pragma: no cover - exercised via real command output, not unit assertions
130
+ import typer.rich_utils as _rich_utils
131
+
132
+ import re as _re
133
+
134
+ from .errors import FpCliError as _FpCliError
135
+
136
+ # Global options live BEFORE the command; putting one after it ("sessions --json")
137
+ # is the single most common mistake, so detect it and nudge toward the right form.
138
+ _GLOBAL_FLAGS = frozenset({
139
+ "--json", "--base-url", "--org", "--token", "--api-key", "--timeout",
140
+ "--quiet", "--no-color", "--insecure", "--secure",
141
+ })
142
+
143
+ def _flag_placement_hint(message: str) -> "Optional[str]":
144
+ m = _re.search(r"No such option:? '?(--[a-z-]+)'?", message)
145
+ if m and m.group(1) in _GLOBAL_FLAGS:
146
+ return "global options go before the command, e.g. 'fp --json <command>'"
147
+ return None
148
+
149
+ def _wants_json() -> bool:
150
+ # output.is_json() is set by the group callback. Errors raised BEFORE it runs (an unknown
151
+ # command, a bad global option) never reached configure(), so fall back to the raw flag/env
152
+ # — otherwise `fp --json <typo>` would print a human box instead of a JSON envelope.
153
+ if output.is_json():
154
+ return True
155
+ import os
156
+ if os.environ.get("FP_JSON", "").strip().lower() in ("1", "true", "yes", "on"):
157
+ return True
158
+ return "--json" in sys.argv
159
+
160
+ def _format_error_as_line(error: click.ClickException) -> None:
161
+ # Our typed errors carry a clean message; the HTTP status (for ApiError) rides in the JSON
162
+ # envelope's `status` field instead of being appended to the human text. Other Click
163
+ # exceptions (usage etc.) use their normal rendering.
164
+ if isinstance(error, _FpCliError):
165
+ message = error.message
166
+ else:
167
+ message = error.format_message() if hasattr(error, "format_message") else str(error)
168
+ # Click/Typer sometimes doubles the suggestion ("Did you mean 'x'? Did you
169
+ # mean 'x'?"); collapse the repeat so the one-line error stays clean.
170
+ message = _re.sub(r"(Did you mean [^?]+\?)(\s*\1)+", r"\1", message)
171
+ # A bare group invocation (`fp`, `fp keys`) already printed its help via
172
+ # no_args_is_help; Click then raises a message-less UsageError. Don't render an empty
173
+ # "✗" box (or an `{"error": ""}` envelope) on top of the help — just let Click exit.
174
+ if not message.strip():
175
+ return
176
+ # Hint precedence: an explicit hint on the exception > the global-flag-placement
177
+ # nudge (more useful than the generic -h line) > the usage "try '… -h'" fallback.
178
+ hint = getattr(error, "hint", None)
179
+ if hint is None:
180
+ hint = _flag_placement_hint(message)
181
+ ctx = getattr(error, "ctx", None)
182
+ if hint is None and isinstance(error, click.UsageError) and ctx is not None:
183
+ hint = f"try '{ctx.command_path} -h' for help"
184
+ # Under --json, every failure is a machine-readable object on stdout (data channel),
185
+ # mirroring the success contract — never a Rich box. Click then exits with the same code.
186
+ if _wants_json():
187
+ code = getattr(error, "exit_code", None)
188
+ if not isinstance(code, int):
189
+ code = 2 if isinstance(error, click.UsageError) else 1
190
+ payload = {"error": message, "exit_code": code}
191
+ status = getattr(error, "status", None)
192
+ if isinstance(status, int):
193
+ payload["status"] = status
194
+ # Keep the server's request-id (set on ApiError) so an agent can correlate a failure
195
+ # with server logs — it's no longer appended to the human message.
196
+ request_id = getattr(error, "request_id", None)
197
+ if request_id:
198
+ payload["request_id"] = request_id
199
+ if hint:
200
+ payload["hint"] = hint
201
+ output.emit_json(payload)
202
+ else:
203
+ output.cli_error(message, hint=hint)
204
+
205
+ _rich_utils.rich_format_error = _format_error_as_line
206
+
207
+ # Replace the TOP-LEVEL help (bare `fp`, `fp --help`/`-h`) with the grouped
208
+ # commands screen; every subcommand's `--help` keeps Typer's default rendering. All three
209
+ # top-level paths flow through `rich_format_help`, so one override covers them.
210
+ _orig_rich_format_help = _rich_utils.rich_format_help
211
+
212
+ def _format_help(*, obj, ctx, markup_mode): # noqa: ANN001
213
+ if getattr(ctx, "parent", None) is None: # the root `fp` command
214
+ output.render_top_level_help()
215
+ else:
216
+ _orig_rich_format_help(obj=obj, ctx=ctx, markup_mode=markup_mode)
217
+
218
+ _rich_utils.rich_format_help = _format_help
219
+ except Exception:
220
+ pass
221
+
222
+
223
+ def _version_callback(value: bool) -> None:
224
+ if value:
225
+ print(__version__)
226
+ raise typer.Exit()
227
+
228
+
229
+ def _same_origin(a: str, b: str) -> bool:
230
+ """Same scheme+host+port, ignoring path and trailing slashes.
231
+
232
+ Used to decide whether a saved `--insecure` applies to the URL in front of us.
233
+ A string compare would treat `https://dash.internal` and
234
+ `https://dash.internal/` as different hosts and re-prompt for no reason.
235
+ """
236
+ from urllib.parse import urlsplit
237
+
238
+ try:
239
+ pa, pb = urlsplit((a or "").strip()), urlsplit((b or "").strip())
240
+ except ValueError:
241
+ return False
242
+ return bool(pa.scheme) and (pa.scheme, pa.netloc) == (pb.scheme, pb.netloc)
243
+
244
+
245
+ def _from_command_line(ctx: typer.Context, param: str) -> bool:
246
+ """True when ``param`` was typed on the command line (not read from its env var).
247
+
248
+ `--api-key` and `--token` have DIFFERENT precedence as flags than as env vars, and
249
+ by the time Typer hands us the value the two are indistinguishable. Click records
250
+ the source; ask it. Defaults to False if a Click without the API is ever in play,
251
+ which degrades to "treat it as env" — the conservative direction, since the only
252
+ thing it can cost is the both-flags usage error, never a wrong credential.
253
+ """
254
+ try:
255
+ source = ctx.get_parameter_source(param)
256
+ except Exception: # pragma: no cover - only if Click drops the API
257
+ return False
258
+ return getattr(source, "name", None) == "COMMANDLINE"
259
+
260
+
261
+ @app.callback()
262
+ def main(
263
+ ctx: typer.Context,
264
+ json_output: bool = typer.Option(
265
+ False, "--json", envvar="FP_JSON", help="Emit JSON to stdout instead of a table."
266
+ ),
267
+ base_url: Optional[str] = typer.Option(
268
+ None,
269
+ "--base-url",
270
+ envvar="FP_DASHBOARD_URL",
271
+ metavar="URL",
272
+ help="Dashboard base URL. Defaults to https://app.befailproof.ai; "
273
+ "override for a self-hosted or dev instance (or set FP_DASHBOARD_URL).",
274
+ ),
275
+ org: Optional[str] = typer.Option(
276
+ None,
277
+ "--org",
278
+ envvar="FP_ORG",
279
+ metavar="SLUG",
280
+ help="Active org/tenant slug. Required if you belong to more than one org; "
281
+ "set it once at login (`login --org <slug>`) or here per command.",
282
+ ),
283
+ token: Optional[str] = typer.Option(
284
+ None, "--token", envvar="FP_TOKEN", help="Session token override (for CI/agents)."
285
+ ),
286
+ # `FP_API_KEY`, deliberately NOT the two names that already exist:
287
+ # * `AGENTEYE_KEY` is the collector's INGEST key, normally `events:add` only —
288
+ # picking it up here would make every read command 403 for no visible reason.
289
+ # * `AGENTEYE_API_KEY` is the dashboard service's own admin-grade key. Silently
290
+ # promoting an operator credential to "the CLI's identity" is a privilege
291
+ # surprise, and on a dashboard host both variables are typically already set.
292
+ # It also keeps the CLI's own `FP_TOKEN` / `FP_JSON` namespace.
293
+ api_key: Optional[str] = typer.Option(
294
+ None,
295
+ "--api-key",
296
+ envvar="FP_API_KEY",
297
+ metavar="KEY",
298
+ help="Authenticate as an API key against the versioned API instead of a user "
299
+ "session (for CI). Wins over --token's env var; passing both flags is an error. "
300
+ "Never saved to disk.",
301
+ ),
302
+ timeout: float = typer.Option(30.0, "--timeout", help="HTTP timeout in seconds."),
303
+ no_color: bool = typer.Option(
304
+ False, "--no-color", envvar="NO_COLOR", help="Disable coloured output."
305
+ ),
306
+ quiet: bool = typer.Option(False, "--quiet", "-q", help="Suppress status messages on stderr."),
307
+ insecure: Optional[bool] = typer.Option(
308
+ None,
309
+ "--insecure/--secure",
310
+ envvar="FP_INSECURE",
311
+ help="Skip TLS certificate verification (for self-signed/internal dashboards). "
312
+ "Saved at login and honoured only for the base URL it was saved for; "
313
+ "--secure disables it for this invocation (pass it to `login` to save that).",
314
+ ),
315
+ version: bool = typer.Option(
316
+ False, "--version", callback=_version_callback, is_eager=True, hidden=True,
317
+ help="Show version and exit.",
318
+ ),
319
+ ) -> None:
320
+ """Resolve global options into the per-invocation AppState (flag > env > config)."""
321
+ if timeout <= 0:
322
+ raise click.BadParameter(
323
+ "must be a positive number of seconds.", param_hint="--timeout"
324
+ )
325
+ cfg = cfgmod.load_config()
326
+ output.configure(no_color=no_color, quiet=quiet, json=json_output)
327
+ # Resolve the credential BEFORE telemetry starts: the distinct-id depends on it.
328
+ # `get_parameter_source` is what separates a flag from its env var — the values
329
+ # alone cannot, and the precedence rules differ between the two (see resolve_auth).
330
+ auth_mode, resolved_api_key, resolved_token = _context.resolve_auth(
331
+ api_key=api_key,
332
+ api_key_on_cli=_from_command_line(ctx, "api_key"),
333
+ token=token,
334
+ token_on_cli=_from_command_line(ctx, "token"),
335
+ saved_token=cfg.session_token,
336
+ )
337
+ # In key mode force the ANONYMOUS distinct id. `_resolve_distinct_id` reads the
338
+ # SAVED config, so a CI box (or a laptop) where a human happens to be logged in
339
+ # would otherwise attribute every key-mode command to that person — an identity
340
+ # the key has nothing to do with.
341
+ analytics.init_analytics(cfg, force_anonymous=auth_mode is _context.AuthMode.API_KEY)
342
+ analytics.note_command(ctx.invoked_subcommand, json_output, auth_mode=auth_mode)
343
+ # Active tenant: flag > FP_ORG env > saved config. Validate the shape early
344
+ # so a typo is a clean usage error rather than a confusing server rejection.
345
+ resolved_org = org or cfg.org
346
+ if resolved_org and not orgsmod.is_valid_org_slug(resolved_org):
347
+ raise click.BadParameter(
348
+ f"'{resolved_org}' is not a valid org slug "
349
+ "(lowercase letters, digits and single hyphens).",
350
+ param_hint="--org",
351
+ )
352
+ # Base-URL resolution: explicit flag/env > saved config > the public default
353
+ # (`config.DEFAULT_BASE_URL`). An explicit empty string (`--base-url ""`, e.g.
354
+ # an unset CI var) is treated as "unset" and falls through, so a script never
355
+ # errors for lack of a URL — it lands on the hosted product. `or` (not
356
+ # `is not None`) is deliberate here: unlike `--token`, an empty base-url is a
357
+ # public endpoint choice, not an identity, so collapsing "" to the default is
358
+ # safe.
359
+ resolved_base = (base_url or None) or cfg.base_url or cfgmod.DEFAULT_BASE_URL
360
+ # A base-url without a scheme breaks httpx's cookie handling with a raw urllib
361
+ # ValueError (an uncaught traceback) — reject it up front as a clean usage error,
362
+ # mirroring the --org shape check above. (The default always has a scheme, so
363
+ # this only ever fires on a user-supplied value.)
364
+ if not resolved_base.lower().startswith(("http://", "https://")):
365
+ raise click.BadParameter(
366
+ f"'{resolved_base}' must start with http:// or https://.",
367
+ param_hint="--base-url",
368
+ )
369
+ # TLS verification, resolved AFTER the base URL because the saved preference
370
+ # is only honoured for the URL it was granted for.
371
+ #
372
+ # `insecure` is stored per MACHINE and was applied to every base URL, so one
373
+ # `fp --base-url https://dash.internal --insecure login` against a self-signed
374
+ # dev box — the use its own help text names — left verification off for the
375
+ # next `fp --base-url https://app.befailproof.ai login`, which is the request
376
+ # that carries the OTP and receives the session token, and for `fp keys
377
+ # create`, which carries a freshly minted API key in the body. `clear_token`
378
+ # deliberately preserves `insecure` across a logout, so it outlived the
379
+ # session it was set for. An explicit flag still wins, for both values.
380
+ if insecure is not None:
381
+ resolved_insecure = insecure
382
+ elif cfg.insecure and _same_origin(resolved_base, cfg.base_url):
383
+ resolved_insecure = True
384
+ else:
385
+ resolved_insecure = False
386
+ if cfg.insecure:
387
+ output.cli_warn(
388
+ f"⚠ ignoring the saved --insecure preference: it was granted for "
389
+ f"{cfg.base_url}, not {resolved_base}. Pass --insecure explicitly "
390
+ f"if you mean it here too."
391
+ )
392
+ if resolved_insecure and ctx.invoked_subcommand not in ("version", "help", None):
393
+ # NOT `output.warn`, which `--quiet` suppresses along with cosmetic status
394
+ # chrome. This one is a security property: `fp --quiet …` on a machine
395
+ # where someone once ran `login --insecure` performed every request with
396
+ # verification off and said nothing at all.
397
+ output.cli_warn("⚠ TLS verification disabled (--insecure).")
398
+ # `resolve_auth` above already applied the whole precedence ladder, including the
399
+ # rule that an EXPLICIT empty `--token ""` / `--api-key ""` (e.g. an unset CI var)
400
+ # means "no override" and must NOT silently fall back to the saved session — so a
401
+ # script can't unknowingly act as the stored identity. `base_url` intentionally
402
+ # differs (see `resolved_base` above): an empty URL is a public endpoint, not an
403
+ # identity, so it defaults.
404
+ #
405
+ # `api_key` is carried on the in-memory AppState ONLY. `CliConfig` has no field for
406
+ # it and must not grow one: a session token expires in ~24h, which bounds a leaked
407
+ # `cli.json`; an API key is valid until revoked, and nothing here could revoke it
408
+ # (`keys disable` needs `keys:disable`, which a scoped CI key will not hold).
409
+ ctx.obj = AppState(
410
+ json=json_output,
411
+ base_url=resolved_base,
412
+ token=resolved_token,
413
+ timeout=timeout,
414
+ config=cfg,
415
+ insecure=resolved_insecure,
416
+ org=resolved_org,
417
+ # `org` here is the global --org flag or FP_ORG env (None if neither) —
418
+ # the explicit choice, distinct from the saved-config fallback in `resolved_org`.
419
+ # Key mode sends ONLY this one (see `_context._org_header`).
420
+ org_explicit=org,
421
+ api_key=resolved_api_key,
422
+ auth_mode=auth_mode,
423
+ )
424
+
425
+
426
+ @app.command()
427
+ def version(ctx: typer.Context) -> None:
428
+ """Show the CLI version in a small branded box.
429
+
430
+ Follows the global option format — the **global** `--json` (before the command) makes it
431
+ emit `{"version": "<x.y.z>"}` and nothing else: `fp --json version`. For a bare,
432
+ unboxed string use `fp --version`.
433
+ """
434
+ if getattr(ctx.obj, "json", False):
435
+ output.emit_json({"version": __version__})
436
+ else:
437
+ output.version_banner(__version__)
438
+
439
+
440
+ @app.command("help")
441
+ def help_cmd(ctx: typer.Context) -> None:
442
+ """Show this help and the available commands."""
443
+ output.render_top_level_help()
444
+
445
+
446
+ auth_cmds.register(app)
447
+ orgs_cmds.register(app)
448
+ events_cmds.register(app)
449
+ sessions_cmds.register(app)
450
+ evals_cmds.register(app)
451
+ errors_cmds.register(app)
452
+ usage_cmds.register(app)
453
+ list_cmds.register(app)
454
+ keys_cmds.register(app)
455
+ queries_cmds.register(app)
456
+ users_cmds.register(app)
457
+ settings_cmds.register(app)
458
+ alerts_cmds.register(app)
459
+ audits_cmds.register(app)
460
+ incidents_cmds.register(app)
461
+ agent_cmds.register(app)
462
+ policies_cmds.register(app)
463
+ fleet_cmds.register(app)
464
+ guardrails_cmds.register(app)
465
+
466
+
467
+ def _elapsed_ms(start: float) -> int:
468
+ return int((time.monotonic() - start) * 1000)
469
+
470
+
471
+ def main_entry() -> None:
472
+ """Console-script entry point: run the CLI, then emit one telemetry event.
473
+
474
+ Wraps ``app()`` so a single ``command_executed`` event (carrying the exit code and
475
+ duration) is captured however the command ends, then flushed. The original exit
476
+ code is preserved exactly — standalone Click has already printed any error and
477
+ raised ``SystemExit`` with the right status (see ``errors.py``). Tests invoke
478
+ ``app`` directly via ``CliRunner``, so they bypass this wrapper entirely.
479
+ """
480
+ start = time.monotonic()
481
+ code = 0
482
+ try:
483
+ app()
484
+ except SystemExit as exc: # normal path: Click exits with its status code
485
+ code = exc.code if isinstance(exc.code, int) else (0 if exc.code is None else 1)
486
+ except BaseException: # escaped Click (e.g. KeyboardInterrupt): record, then re-raise unchanged
487
+ analytics.capture_command(1, _elapsed_ms(start), sys.argv[1:])
488
+ analytics.shutdown()
489
+ raise
490
+ analytics.capture_command(code, _elapsed_ms(start), sys.argv[1:])
491
+ analytics.shutdown()
492
+ sys.exit(code)
@@ -0,0 +1,160 @@
1
+ """Email-OTP device login against the dashboard.
2
+
3
+ Flow: ``/api/auth/otp/request`` (sends a code) then ``/api/auth/otp/verify``.
4
+ The verify response carries the session token in the ``ae_session`` **Set-Cookie**
5
+ header (not the JSON body), so we read it from ``response.cookies``.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from datetime import datetime, timedelta, timezone
11
+ from typing import Any, Dict, Optional, Tuple
12
+
13
+ import httpx
14
+
15
+ from .config import CliConfig, save_config
16
+ from .errors import ApiError, AuthError, NetworkError
17
+
18
+
19
+ def _iso(dt: datetime) -> str:
20
+ return dt.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
21
+
22
+
23
+ def _network_error(base_url: str, exc: Exception) -> NetworkError:
24
+ return NetworkError(f"Cannot reach FailproofAI Cloud at {base_url}: {exc}")
25
+
26
+
27
+ def request_otp(
28
+ base_url: str,
29
+ email: str,
30
+ *,
31
+ timeout: float = 30.0,
32
+ transport: Optional[httpx.BaseTransport] = None,
33
+ verify: bool = True,
34
+ ) -> None:
35
+ """Ask the dashboard to email a login code. Always succeeds quietly for a
36
+ valid request (the server returns 200 even for unknown emails)."""
37
+ try:
38
+ with httpx.Client(
39
+ base_url=base_url.rstrip("/"), timeout=timeout, transport=transport, verify=verify
40
+ ) as client:
41
+ # Mark this as a CLI login so the server emails the paste-into-terminal
42
+ # OTP template (with no "open the dashboard" button) instead of the
43
+ # browser one.
44
+ response = client.post(
45
+ "/api/auth/otp/request",
46
+ json={"email": email},
47
+ headers={"X-AgentEye-Client": "cli"},
48
+ )
49
+ except httpx.RequestError as exc:
50
+ raise _network_error(base_url, exc)
51
+ if response.status_code >= 400:
52
+ raise ApiError(
53
+ f"Failed to request a login code (HTTP {response.status_code}).",
54
+ status=response.status_code,
55
+ )
56
+
57
+
58
+ def verify_otp(
59
+ base_url: str,
60
+ email: str,
61
+ code: str,
62
+ *,
63
+ timeout: float = 30.0,
64
+ transport: Optional[httpx.BaseTransport] = None,
65
+ verify: bool = True,
66
+ ) -> Tuple[str, int, Dict[str, Any]]:
67
+ """Exchange the code for a session token. Returns ``(token, expires_in_secs, user)``."""
68
+ try:
69
+ with httpx.Client(
70
+ base_url=base_url.rstrip("/"), timeout=timeout, transport=transport, verify=verify
71
+ ) as client:
72
+ response = client.post(
73
+ "/api/auth/otp/verify", json={"email": email, "code": code}
74
+ )
75
+ except httpx.RequestError as exc:
76
+ raise _network_error(base_url, exc)
77
+
78
+ if response.status_code == 401:
79
+ raise AuthError("That code didn't match or has expired. Sign in again with fp login.")
80
+ if response.status_code == 429:
81
+ raise ApiError(
82
+ "Too many attempts — wait a bit, then run fp login again.",
83
+ status=429,
84
+ )
85
+ if response.status_code >= 400:
86
+ # The dashboard proxy collapses the server's wrong/expired-code 401 into a 500 (its
87
+ # `await res.json()` throws on the server's empty 401 body). So a non-401 4xx/5xx at the
88
+ # verify step is, in practice, a bad/expired code — surface it as a clean auth failure,
89
+ # not a raw "HTTP 500". (Real unreachability is a NetworkError, handled above.)
90
+ raise AuthError("That code didn't match or has expired. Sign in again with fp login.")
91
+
92
+ token = response.cookies.get("ae_session")
93
+ if not token:
94
+ raise AuthError("The dashboard did not return a session token.")
95
+
96
+ try:
97
+ body = response.json()
98
+ except Exception:
99
+ body = {}
100
+ if not isinstance(body, dict):
101
+ body = {}
102
+
103
+ try:
104
+ expires_in = int(body.get("expires_in_secs"))
105
+ except (TypeError, ValueError):
106
+ expires_in = 86400
107
+
108
+ user = body.get("user") or {}
109
+ if not isinstance(user, dict):
110
+ user = {}
111
+
112
+ return token, expires_in, user
113
+
114
+
115
+ def persist_session(
116
+ cfg: CliConfig,
117
+ base_url: str,
118
+ token: str,
119
+ expires_in_secs: int,
120
+ user: Dict[str, Any],
121
+ *,
122
+ insecure: bool = False,
123
+ org: Optional[str] = None,
124
+ now: Optional[datetime] = None,
125
+ ) -> CliConfig:
126
+ now = now or datetime.now(timezone.utc)
127
+ cfg.base_url = base_url
128
+ cfg.session_token = token
129
+ cfg.expires_at = _iso(now + timedelta(seconds=expires_in_secs))
130
+ cfg.email = (user or {}).get("email") or cfg.email
131
+ cfg.user_id = (user or {}).get("id") or cfg.user_id
132
+ cfg.insecure = insecure
133
+ if org is not None:
134
+ cfg.org = org # active tenant chosen at login
135
+ save_config(cfg)
136
+ return cfg
137
+
138
+
139
+ def logout(
140
+ base_url: str,
141
+ token: Optional[str],
142
+ *,
143
+ timeout: float = 30.0,
144
+ transport: Optional[httpx.BaseTransport] = None,
145
+ verify: bool = True,
146
+ ) -> None:
147
+ """Best-effort server-side session revocation; never raises."""
148
+ if not token:
149
+ return
150
+ try:
151
+ with httpx.Client(
152
+ base_url=base_url.rstrip("/"),
153
+ cookies={"ae_session": token},
154
+ timeout=timeout,
155
+ transport=transport,
156
+ verify=verify,
157
+ ) as client:
158
+ client.post("/api/auth/logout")
159
+ except httpx.RequestError:
160
+ pass