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,70 @@
1
+ """A 404 in key mode has two very different causes; the CLI must tell them apart.
2
+
3
+ Pointing `--base-url` at a dashboard whose front door does not forward `/v1` is
4
+ the likeliest first-run mistake. It used to arrive as a 3xx to `/login`, which
5
+ `_raise_for_status` names explicitly — but a dashboard that correctly declines to
6
+ auth-gate `/v1` (see `dashboard/proxy.ts`) returns its own 404 instead, and on
7
+ the status code alone that is indistinguishable from "no such record".
8
+
9
+ The tell is the content type: the API only ever answers JSON.
10
+ """
11
+
12
+ import httpx
13
+ import pytest
14
+ import respx
15
+
16
+ from fp_cli import client as api
17
+ from fp_cli.client import AuthMode, ClientContext
18
+ from fp_cli.errors import ApiError, NotFoundError
19
+
20
+ BASE = "http://server.test"
21
+
22
+
23
+ def key_ctx() -> ClientContext:
24
+ return ClientContext(base_url=BASE, api_key="ak_test", auth_mode=AuthMode.API_KEY)
25
+
26
+
27
+ @respx.mock
28
+ def test_html_404_names_the_routing_problem() -> None:
29
+ respx.get(f"{BASE}/v1/events").mock(
30
+ return_value=httpx.Response(
31
+ 404,
32
+ text="<!DOCTYPE html><html>404</html>",
33
+ headers={"content-type": "text/html; charset=utf-8"},
34
+ )
35
+ )
36
+ with pytest.raises(ApiError) as excinfo:
37
+ api.list_events(key_ctx())
38
+ assert "not routed" in str(excinfo.value)
39
+ # Must NOT degrade to the ordinary not-found error, which would send the
40
+ # reader hunting for a missing record instead of a misconfigured base URL.
41
+ assert not isinstance(excinfo.value, NotFoundError)
42
+
43
+
44
+ @respx.mock
45
+ def test_json_404_is_still_an_ordinary_not_found() -> None:
46
+ """The other half.
47
+
48
+ Without this, the guard above could 'pass' by relabelling every 404 as a
49
+ routing problem — worse than the bug it fixes, because then a genuinely
50
+ missing record would send people to check their base URL forever.
51
+ """
52
+ respx.get(f"{BASE}/v1/events").mock(
53
+ return_value=httpx.Response(404, json={"error": "nope"})
54
+ )
55
+ with pytest.raises(NotFoundError):
56
+ api.list_events(key_ctx())
57
+
58
+
59
+ @respx.mock
60
+ def test_session_mode_html_404_is_untouched() -> None:
61
+ """Cookie mode keeps its existing behaviour — this diagnostic is key-mode only."""
62
+ respx.get(f"{BASE}/api/events").mock(
63
+ return_value=httpx.Response(
64
+ 404,
65
+ text="<!DOCTYPE html><html>404</html>",
66
+ headers={"content-type": "text/html; charset=utf-8"},
67
+ )
68
+ )
69
+ with pytest.raises(NotFoundError):
70
+ api.list_events(ClientContext(base_url=BASE, token="tok"))
@@ -0,0 +1,214 @@
1
+ """Anti-drift guard for API-key mode's `/api/*` -> `/v1/*` translation.
2
+
3
+ Why this test is shaped the way it is
4
+ -------------------------------------
5
+ The CLI already had an anti-drift test that stayed green through an 85 -> 0 collapse
6
+ (`memory/2026-07-24-typer-026-vendored-click-breaks-cli-errors.md`): it walked the
7
+ command tree with the *same* broken predicate the production code used, so it
8
+ compared nothing to nothing and reported success. The lesson is not "write an
9
+ anti-drift test", it is "get the expectation from a source the production code does
10
+ not also consult".
11
+
12
+ So each leg below sources its input independently of `client.py`'s own logic:
13
+
14
+ 1. The set of paths comes from an **AST scan of client.py's string literals**, not
15
+ from any registry the translator reads. Add a call site and it appears here
16
+ whether or not anyone remembered a list.
17
+ 2. The unsupported commands are driven through a **real CliRunner**, asserting the
18
+ exit code AND that zero HTTP calls happened — the "fails before any network
19
+ call" half is the part a return-value assertion cannot see.
20
+
21
+ A third leg used to check every translated `/v1` path against the server router's
22
+ own `.route()` literals, read out of an AgentEye checkout. The server now lives in
23
+ a separate private repository, so that leg could only ever run when a checkout
24
+ happened to be on disk and skipped everywhere else — including all of CI, where a
25
+ skip reads as green. It was removed rather than left switched off. Nothing here
26
+ verifies that a translated path is a route the server actually registers; a rename
27
+ on the server side surfaces as a 404 at runtime.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import ast
33
+ import pathlib
34
+
35
+ import httpx
36
+ import pytest
37
+ import respx
38
+
39
+ from fp_cli import client as api
40
+ from fp_cli.app import app
41
+ from fp_cli.errors import KeyModeUnsupportedError
42
+
43
+ CLIENT_PY = pathlib.Path(api.__file__)
44
+ BASE = "http://dash.test"
45
+ KEY = "ak_live_abc123"
46
+
47
+
48
+ # --- leg 1: every /api/ literal in client.py is classified -------------------
49
+
50
+
51
+ def _api_path_literals(source: str) -> set:
52
+ """Every `/api/...` path the CLI can build, read straight out of the source.
53
+
54
+ f-strings are reconstructed as templates (`f"/api/keys/{key_id}/disable"` ->
55
+ `/api/keys/{}/disable`) so an interpolated id becomes a wildcard segment rather
56
+ than a fragment. The constant *pieces* of an f-string are skipped for exactly that
57
+ reason: `"/api/issues/"` on its own is not a path anyone requests.
58
+ """
59
+ tree = ast.parse(source)
60
+ inside_fstring = {
61
+ id(v)
62
+ for node in ast.walk(tree)
63
+ if isinstance(node, ast.JoinedStr)
64
+ for v in node.values
65
+ }
66
+ found = set()
67
+ for node in ast.walk(tree):
68
+ if isinstance(node, ast.JoinedStr):
69
+ text = "".join(
70
+ v.value if isinstance(v, ast.Constant) and isinstance(v.value, str) else "{}"
71
+ for v in node.values
72
+ )
73
+ elif (
74
+ isinstance(node, ast.Constant)
75
+ and isinstance(node.value, str)
76
+ and id(node) not in inside_fstring
77
+ ):
78
+ text = node.value
79
+ else:
80
+ continue
81
+ # `> len("/api/")` skips the `_API_PREFIX` constant itself: it is the rule, not
82
+ # a path, and no real request is ever made to a bare "/api/".
83
+ if text.startswith("/api/") and len(text) > len("/api/"):
84
+ found.add(text)
85
+ return found
86
+
87
+
88
+ def _buckets(path: str) -> list:
89
+ """Which classification(s) `path` falls into, computed from the DATA in client.py
90
+ rather than by calling its classifier — a catch-all `else` in the translator must
91
+ not be able to make this test pass."""
92
+ kinds = []
93
+ if path in api._V1_RENAMED:
94
+ kinds.append("override")
95
+ else:
96
+ # The rename is an exact-match override of the family rule, so it is only
97
+ # checked when the path is not itself renamed; that keeps the three buckets
98
+ # genuinely disjoint instead of "exactly one, if you squint".
99
+ family = path[len("/api/") :].split("/", 1)[0]
100
+ if family in api._V1_NO_EQUIVALENT:
101
+ kinds.append("no-v1")
102
+ if family in api._V1_MECHANICAL_FAMILIES:
103
+ kinds.append("mechanical")
104
+ return kinds
105
+
106
+
107
+ def test_every_api_literal_is_classified_exactly_once():
108
+ literals = _api_path_literals(CLIENT_PY.read_text())
109
+ assert literals, "the AST scan found no /api/ paths — it has stopped asking anything"
110
+ unclassified = sorted(p for p in literals if not _buckets(p))
111
+ assert not unclassified, (
112
+ "these /api/ paths have no /v1 classification — add the family to "
113
+ "_V1_MECHANICAL_FAMILIES, _V1_RENAMED or _V1_NO_EQUIVALENT in client.py "
114
+ f"(and check the server actually serves it): {unclassified}"
115
+ )
116
+ ambiguous = sorted(p for p in literals if len(_buckets(p)) > 1)
117
+ assert not ambiguous, f"/api paths matching more than one bucket: {ambiguous}"
118
+
119
+
120
+ def test_no_v1_paths_raise_instead_of_being_requested():
121
+ literals = _api_path_literals(CLIENT_PY.read_text())
122
+ excluded = [p for p in literals if _buckets(p) == ["no-v1"]]
123
+ assert excluded, "the excluded families vanished from client.py — verify on purpose"
124
+ for path in excluded:
125
+ with pytest.raises(KeyModeUnsupportedError):
126
+ api._v1_path(path)
127
+
128
+
129
+ def test_an_unknown_family_raises_loudly():
130
+ # The property the two lists above cannot prove about each other: a path in no
131
+ # bucket must fail, not pass through to a URL nobody chose.
132
+ with pytest.raises(Exception) as excinfo:
133
+ api._v1_path("/api/telepathy/read")
134
+ assert "/api/telepathy/read" in str(excinfo.value)
135
+
136
+
137
+ def test_the_score_keys_rename_is_applied():
138
+ # Hyphen -> underscore. It exists only in the dashboard proxy
139
+ # (dashboard/app/api/evaluations/score-keys/route.ts), so a blind s|^/api|/v1|
140
+ # 404s and the CLI reports a cheerful "Not found."
141
+ assert api._v1_path("/api/evaluations/score-keys") == "/v1/evaluations/score_keys"
142
+ assert api._FACET_PATHS["score_filters"] == "/api/evaluations/score-keys"
143
+
144
+
145
+ # --- leg 2: unsupported commands fail before any network call -----------------
146
+
147
+
148
+ UNSUPPORTED = [
149
+ ["login"],
150
+ ["logout"],
151
+ ["orgs", "list"],
152
+ ["orgs", "switch", "acme"],
153
+ ["orgs", "current"],
154
+ ["orgs", "perms"],
155
+ ["agent", "health"],
156
+ ["agent", "models"],
157
+ ["agent", "chats"],
158
+ ["agent", "ask", "hello"],
159
+ ["agent", "show", "abc123"],
160
+ ["agent", "rename", "abc123", "--title", "x"],
161
+ ["agent", "delete", "abc123", "--yes"],
162
+ ["keys", "update", "ci-bot", "--add", "keys:read"],
163
+ ]
164
+
165
+
166
+ @pytest.mark.parametrize("argv", UNSUPPORTED, ids=lambda a: " ".join(a[:2]))
167
+ def test_unsupported_command_exits_2_with_zero_http_calls(logged_in, runner, argv):
168
+ # `logged_in` seeds a saved session deliberately: the failure must come from the
169
+ # key, not from having nothing else to fall back on.
170
+ with respx.mock(assert_all_called=False) as mock:
171
+ catch_all = mock.route().mock(return_value=httpx.Response(200, json={}))
172
+ result = runner.invoke(app, ["--base-url", BASE, "--api-key", KEY, *argv])
173
+ assert result.exit_code == 2, f"{argv} -> {result.exit_code}: {result.output}"
174
+ assert catch_all.call_count == 0, f"{argv} opened a connection before failing"
175
+
176
+
177
+ def test_unsupported_command_says_why_and_what_to_do(logged_in, runner):
178
+ result = runner.invoke(app, ["--base-url", BASE, "--api-key", KEY, "--json", "orgs", "list"])
179
+ assert result.exit_code == 2, result.output
180
+ assert "API key" in result.stdout
181
+ assert "fp login" in result.stdout # the hint rides in the JSON envelope
182
+
183
+
184
+ def test_whoami_is_the_exception(logged_in, runner):
185
+ # Contractually "never errors" (cli/skill/SKILL.md leans on it as the pre-flight).
186
+ result = runner.invoke(app, ["--base-url", BASE, "--api-key", KEY, "whoami"])
187
+ assert result.exit_code == 0, result.output
188
+
189
+
190
+ SUPPORTED = [
191
+ (["keys", "list"], "/v1/keys", []),
192
+ (["sessions"], "/v1/sessions", {"sessions": [], "next_cursor": None}),
193
+ (["events"], "/v1/events/summary", {"events": [], "next_cursor": None}),
194
+ (["issues", "list"], "/v1/issues", []),
195
+ (["list", "envs"], "/v1/events/environments", []),
196
+ (["list", "score_filters"], "/v1/evaluations/score_keys", []),
197
+ (["query", "list"], "/v1/queries", {"queries": []}),
198
+ (["users", "list"], "/v1/users", []),
199
+ (["settings", "list"], "/v1/settings", {"settings": []}),
200
+ (["alerts", "list"], "/v1/alerts", []),
201
+ (["audits", "list"], "/v1/audits", []),
202
+ ]
203
+
204
+
205
+ @pytest.mark.parametrize("argv,path,body", SUPPORTED, ids=lambda a: " ".join(a[:2]) if isinstance(a, list) else "")
206
+ def test_supported_commands_reach_their_v1_route(logged_in, runner, argv, path, body):
207
+ """The other half of leg 3: the guard must not have swallowed the working set, and
208
+ each command must land on the `/v1` route it is supposed to (only this exact URL is
209
+ mocked, so a wrong path is a connection error, not a quiet pass)."""
210
+ with respx.mock(assert_all_called=False) as mock:
211
+ route = mock.get(f"{BASE}{path}").mock(return_value=httpx.Response(200, json=body))
212
+ result = runner.invoke(app, ["--base-url", BASE, "--api-key", KEY, "--json", *argv])
213
+ assert result.exit_code == 0, f"{argv} -> {result.exit_code}: {result.output}"
214
+ assert route.called
@@ -0,0 +1,104 @@
1
+ """`whoami` (logged in): the identity header + permissions/orgs panels, and the
2
+ permission grouping/coloring logic."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import httpx
7
+ import respx
8
+
9
+ from fp_cli import theme
10
+ from fp_cli.app import app
11
+ from fp_cli.output import _group_permissions
12
+
13
+ BASE = "http://dash.test"
14
+
15
+
16
+ # --- permission grouping / coloring -----------------------------------------
17
+
18
+
19
+ def test_group_permissions_orders_resources_and_actions_by_risk():
20
+ grouped = _group_permissions(
21
+ ["keys:delete", "keys:read", "keys:create", "dashboards:read", "agent:use"]
22
+ )
23
+ # resources follow the fixed priority order (dashboards < keys < agent)
24
+ assert [r for r, _ in grouped] == ["dashboards", "keys", "agent"]
25
+ by_res = dict(grouped)
26
+ # actions within a row ordered by risk: read → create → delete
27
+ assert [a for a, _ in by_res["keys"]] == ["read", "create", "delete"]
28
+ colors = dict(by_res["keys"])
29
+ assert colors["read"] == theme.PERM_READ
30
+ assert colors["create"] == theme.PERM_WRITE
31
+ assert colors["delete"] == theme.PERM_DANGER
32
+
33
+
34
+ def test_group_permissions_unknown_action_falls_back_neutral():
35
+ grouped = _group_permissions(["weird:frobnicate"])
36
+ assert grouped == [("weird", [("frobnicate", theme.DEFAULT_PERM_COLOR)])]
37
+
38
+
39
+ # --- human render ------------------------------------------------------------
40
+
41
+
42
+ def _session(memberships):
43
+ return {
44
+ "id": "62230791-4811-4f04-b388-ae57bdcb422e",
45
+ "email": "admin@local.host",
46
+ "is_instance_admin": True,
47
+ "memberships": memberships,
48
+ }
49
+
50
+
51
+ @respx.mock
52
+ def test_whoami_human_renders_header_and_panels(logged_in, runner):
53
+ respx.get(f"{BASE}/api/auth/session").mock(
54
+ return_value=httpx.Response(200, json=_session([
55
+ {"org_id": "o1", "org_slug": "globex", "org_name": "Globex Corp", "permission_set": "admin",
56
+ "permissions": ["dashboards:read", "dashboards:write", "dashboards:delete", "keys:read", "agent:use"]},
57
+ {"org_id": "o2", "org_slug": "acme", "org_name": "Acme Corp", "permission_set": "admin",
58
+ "permissions": ["events:read"]},
59
+ ]))
60
+ )
61
+ result = runner.invoke(app, ["--org", "globex", "whoami"])
62
+ assert result.exit_code == 0, result.output
63
+ out = result.stdout
64
+ # identity header: email, instance role, FULL user id, active org
65
+ assert "admin@local.host" in out
66
+ assert "62230791-4811-4f04-b388-ae57bdcb422e" in out
67
+ assert "instance admin" in out
68
+ # permissions panel
69
+ assert "permissions" in out
70
+ assert "dashboards" in out and "read" in out and "write" in out and "delete" in out
71
+ # orgs panel + switch hint to the non-active org
72
+ assert "your orgs" in out
73
+ assert "globex" in out and "acme" in out
74
+ assert "Globex Corp" in out # permissions title shows the active org NAME
75
+ assert "fp orgs switch <slug>" in out
76
+
77
+
78
+ @respx.mock
79
+ def test_whoami_single_org_has_no_switch_hint(logged_in, runner):
80
+ respx.get(f"{BASE}/api/auth/session").mock(
81
+ return_value=httpx.Response(200, json=_session([
82
+ {"org_id": "o", "org_slug": "acme", "org_name": "Acme", "permission_set": "admin",
83
+ "permissions": ["events:read"]},
84
+ ]))
85
+ )
86
+ result = runner.invoke(app, ["whoami"])
87
+ assert result.exit_code == 0, result.output
88
+ assert "your orgs" in result.stdout
89
+ assert "orgs switch" not in result.stdout # only one org → no switch hint
90
+
91
+
92
+ @respx.mock
93
+ def test_whoami_no_color_marks_destructive_actions(logged_in, runner):
94
+ respx.get(f"{BASE}/api/auth/session").mock(
95
+ return_value=httpx.Response(200, json=_session([
96
+ {"org_id": "o", "org_slug": "acme", "org_name": "Acme", "permission_set": "admin",
97
+ "permissions": ["keys:read", "keys:delete", "keys:disable"]},
98
+ ]))
99
+ )
100
+ result = runner.invoke(app, ["whoami"], env={"NO_COLOR": "1"})
101
+ assert result.exit_code == 0, result.output
102
+ out = result.stdout
103
+ assert "delete*" in out and "disable*" in out # destructive marked in plain text
104
+ assert "read" in out and "read*" not in out # non-destructive unmarked