failproofai 1.0.1 → 1.0.2-beta.1

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 (390) 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]__0l4748x._.js +3 -0
  97. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  98. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
  99. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +6 -0
  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/_08x1r5t._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/ssr/_0bwu160._.js +23 -0
  104. package/.next/standalone/.next/server/chunks/ssr/{_0u5zauv._.js → _0ns6k3a._.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/{3c808qrfcq0im.js → 01u1cf8js2yd6.js} +1 -1
  121. package/.next/standalone/.next/static/chunks/{0k5050ezjvv49.js → 0izgu7nqh2zp-.js} +1 -1
  122. package/.next/standalone/.next/static/chunks/0u4-org_e7zde.js +1 -0
  123. package/.next/standalone/.next/static/chunks/1mfzsja6ftim5.css +2 -0
  124. package/.next/standalone/.next/static/chunks/{2zpp1we2jv6tb.js → 1tynnrrh711lr.js} +1 -1
  125. package/.next/standalone/.next/static/chunks/{1ide1wl2f7zi1.js → 22rqvlr7p_1mv.js} +13 -13
  126. package/.next/standalone/.next/static/chunks/{31s1smp-g7gks.js → 2whxqyskpirbk.js} +1 -1
  127. package/.next/standalone/.next/static/chunks/31mwk8k0q3_3g.js +6 -0
  128. package/.next/standalone/.next/static/chunks/3ms6gxfm82t6s.js +1 -0
  129. package/.next/standalone/.next/static/chunks/{37fcpc36x9gpe.js → 3tbbs_d8xrvyp.js} +1 -1
  130. package/.next/standalone/.next/static/chunks/43nlc236oo898.js +1 -0
  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 +163 -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 -15
  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 +27 -21
  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 +579 -306
  319. package/dist/cli.mjs +6499 -3662
  320. package/dist/index.js +5 -5
  321. package/dist/worker.mjs +2390 -1978
  322. package/package.json +27 -21
  323. package/scripts/build-policy-pack.mjs +121 -0
  324. package/scripts/changelog-section.py +77 -0
  325. package/scripts/prune-standalone.mjs +3 -0
  326. package/scripts/python-version.py +232 -0
  327. package/scripts/translate-docs/cli.ts +21 -0
  328. package/scripts/translate-docs/mintlify-nav.ts +85 -13
  329. package/scripts/translate-docs/translator.ts +66 -11
  330. package/src/audit/cache.ts +40 -3
  331. package/src/audit/cli.ts +1 -1
  332. package/src/audit/index.ts +1 -1
  333. package/src/audit/replay.ts +89 -3
  334. package/src/audit/report.ts +5 -5
  335. package/src/audit/schedule-cli.ts +49 -53
  336. package/src/hooks/builtin-policies.ts +217 -579
  337. package/src/hooks/cloud-enrollment-cli.ts +66 -27
  338. package/src/hooks/cloud-managed-policies.ts +6 -1
  339. package/src/hooks/configure-wizard.ts +56 -369
  340. package/src/hooks/custom-hooks-loader.ts +89 -6
  341. package/src/hooks/fp-home.ts +92 -4
  342. package/src/hooks/fp-reset.ts +34 -2
  343. package/src/hooks/handler.ts +181 -11
  344. package/src/hooks/harness-cli.ts +79 -25
  345. package/src/hooks/hook-activity-store.ts +13 -3
  346. package/src/hooks/install-prompt.ts +25 -3
  347. package/src/hooks/manager.ts +424 -172
  348. package/src/hooks/migrations.ts +2 -2
  349. package/src/hooks/pack-cli.ts +885 -0
  350. package/src/hooks/pack-failclosed.ts +189 -0
  351. package/src/hooks/pack-manifest.ts +352 -0
  352. package/src/hooks/pack-store.ts +891 -0
  353. package/src/hooks/policy-catalog.ts +535 -0
  354. package/src/hooks/policy-evaluator.ts +24 -16
  355. package/src/hooks/policy-registry.ts +8 -2
  356. package/src/hooks/policy-types.ts +26 -0
  357. package/src/hooks/session-pause-cli.ts +33 -9
  358. package/src/hooks/tui.ts +695 -39
  359. package/src/hooks/types.ts +15 -1
  360. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_usx0j._.js +0 -3
  361. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_zk340._.js +0 -3
  362. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0agdw2a._.js +0 -3
  363. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d1u_na._.js +0 -3
  364. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +0 -3
  365. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +0 -3
  366. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +0 -3
  367. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +0 -3
  368. package/.next/standalone/.next/server/chunks/_1ger0a2._.js +0 -3
  369. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +0 -3
  370. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1iiwm5e._.js +0 -3
  371. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__07tbbf3._.js +0 -3
  372. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kc8_v5._.js +0 -4
  373. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kgy9da._.js +0 -4
  374. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mhqk5a._.js +0 -4
  375. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pltpn8._.js +0 -4
  376. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qkcyf-._.js +0 -4
  377. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w0x_ok._.js +0 -4
  378. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_dfocn._.js +0 -4
  379. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bfex62._.js +0 -5
  380. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kzapg5._.js +0 -6
  381. package/.next/standalone/.next/server/chunks/ssr/_0lvyuv4._.js +0 -23
  382. package/.next/standalone/.next/static/chunks/2_2lhuojq2xop.js +0 -1
  383. package/.next/standalone/.next/static/chunks/36fw6yk1s6x_f.js +0 -1
  384. package/.next/standalone/.next/static/chunks/3k7mxbp9hiom0.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/src/hooks/policy-presets.ts +0 -135
  388. /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_buildManifest.js +0 -0
  389. /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_clientMiddlewareManifest.js +0 -0
  390. /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_ssgManifest.js +0 -0
@@ -0,0 +1,198 @@
1
+ """The CLI writes into a home another component owns, so both halves must agree.
2
+
3
+ `~/.failproofai/` is a governed layout. `src/hooks/fp-home.ts` is its register —
4
+ "nothing outside this file may join a path onto the failproofai home" — and
5
+ `resettablePaths()` is a *filter over* `HOME_CLASSES`, so what protects the
6
+ CLI's credential from a reset is its entry in that table, not the fact that the
7
+ file happens to exist.
8
+
9
+ Until this file existed, nothing checked that the two sides agreed. `config.py`
10
+ said so itself, in a comment above `FPCLI_SUBDIR`: *"Mirrors ``fpcliDir`` in
11
+ ``src/hooks/fp-home.ts`` — change one, change the other; nothing checks."*
12
+ Verified by experiment: renaming `fpcliDir` to `fp-cloud-cli` in the TypeScript and
13
+ leaving Python alone left 53 TS tests and 59 Python tests all passing, with the
14
+ register describing a directory nothing writes and the real credential sitting
15
+ at a path the register had never heard of.
16
+
17
+ That is the same shape as the SDK's `tests/test_spool_contract.py` next door,
18
+ which reads the Rust and the TypeScript that define its spool root. This is the
19
+ CLI's version of it.
20
+
21
+ The assertions run against source text rather than a running Node, because
22
+ requiring a toolchain to test a Python package would mean this skips in every
23
+ environment that matters and guards nothing — the failure mode the SDK's own
24
+ contract test had for its whole life before it moved into this repo.
25
+ """
26
+ from __future__ import annotations
27
+
28
+ import os
29
+ import re
30
+ from pathlib import Path
31
+
32
+ import pytest
33
+
34
+ from fp_cli import config as cfg
35
+
36
+ # tests/ -> fp-cloud-cli/ -> repo root.
37
+ #
38
+ # Guarded: `parents[2]` raises IndexError on a shallower tree, and a shallower
39
+ # tree is exactly the packaged-sdist case `_read_source` below handles. Raising
40
+ # here would be a COLLECTION error, which aborts the whole suite instead of
41
+ # skipping the one file that needs the repository.
42
+ _HERE = Path(__file__).resolve()
43
+ REPO_ROOT = _HERE.parents[2] if len(_HERE.parents) > 2 else _HERE.parent
44
+ FP_HOME_TS = REPO_ROOT / "src" / "hooks" / "fp-home.ts"
45
+
46
+ #: Set by CI. Turns "the source I read is missing" from a skip into a failure.
47
+ REQUIRE = os.environ.get("FP_CLI_REQUIRE_CONTRACT", "").strip().lower() in {
48
+ "1",
49
+ "true",
50
+ "yes",
51
+ "on",
52
+ }
53
+
54
+
55
+ def _read_source() -> str:
56
+ """`fp-home.ts`, or skip/fail depending on ``REQUIRE``.
57
+
58
+ Missing means one of two things: this is an installed sdist (fine — there is
59
+ no repo to read), or the file moved (not fine). ``REQUIRE`` distinguishes
60
+ them, because from in here they look identical.
61
+ """
62
+ if FP_HOME_TS.is_file():
63
+ return FP_HOME_TS.read_text(encoding="utf-8")
64
+ message = (
65
+ f"{FP_HOME_TS} is missing. In a packaged sdist that is expected. In the "
66
+ "repository it means the layout register moved, and this contract is now "
67
+ "unguarded — re-point this test at the new location rather than deleting it."
68
+ )
69
+ if REQUIRE:
70
+ pytest.fail(message)
71
+ pytest.skip(message)
72
+
73
+
74
+ def _declared(pattern: str, source: str, what: str) -> str:
75
+ """The single capture of `pattern`, failing loudly if it matched 0 or 2+.
76
+
77
+ A regex over source that quietly matches nothing is worse than no test: it
78
+ passes forever while checking a file that has been rewritten around it.
79
+ """
80
+ matches = re.findall(pattern, source)
81
+ assert len(matches) == 1, (
82
+ f"expected exactly one declaration of {what} in fp-home.ts, found "
83
+ f"{len(matches)}. The register was restructured; re-anchor this test "
84
+ f"rather than loosening the pattern."
85
+ )
86
+ return matches[0]
87
+
88
+
89
+ # ─────────────────────────────────────────────────────────────────────────────
90
+ # The paths themselves
91
+ # ─────────────────────────────────────────────────────────────────────────────
92
+
93
+
94
+ def test_the_subdirectory_name_agrees():
95
+ """`fpcliDir` in TypeScript vs `FPCLI_SUBDIR` here."""
96
+ source = _read_source()
97
+ declared = _declared(
98
+ r'export const fpcliDir = \(home\?: string\) => atHome\(home, "([^"]+)"\)',
99
+ source,
100
+ "fpcliDir",
101
+ )
102
+ assert declared == cfg.FPCLI_SUBDIR, (
103
+ f"fp-home.ts registers the CLI directory as {declared!r} and this package "
104
+ f"writes {cfg.FPCLI_SUBDIR!r}. The credential would sit at a path the "
105
+ f"layout register has never heard of, so nothing protects it from a reset "
106
+ f"the moment somebody classifies its parent."
107
+ )
108
+
109
+
110
+ def test_the_credential_filename_agrees():
111
+ source = _read_source()
112
+ declared = _declared(
113
+ r'export const fpcliAuthFile = \(home\?: string\) => resolve\(fpcliDir\(home\), "([^"]+)"\)',
114
+ source,
115
+ "fpcliAuthFile",
116
+ )
117
+ assert declared == cfg.config_path().name
118
+
119
+
120
+ def test_the_home_directory_name_agrees():
121
+ """Both sides hardcode `.failproofai`; neither imports it from the other."""
122
+ source = _read_source()
123
+ declared = _declared(
124
+ r'return process\.env\.FAILPROOFAI_HOME \|\| resolve\(homedir\(\), "([^"]+)"\)',
125
+ source,
126
+ "the failproofai home",
127
+ )
128
+ home = cfg.base_dir().parent.name
129
+ assert declared == home, f"fp-home.ts says {declared!r}, this package writes {home!r}"
130
+
131
+
132
+ def test_the_shared_home_override_variable_agrees():
133
+ """`FAILPROOFAI_HOME` relocates the whole layout; the CLI must follow it."""
134
+ source = _read_source()
135
+ assert "process.env.FAILPROOFAI_HOME" in source
136
+ assert "FAILPROOFAI_HOME" in Path(cfg.__file__).read_text(encoding="utf-8")
137
+
138
+
139
+ # ─────────────────────────────────────────────────────────────────────────────
140
+ # The classification — what actually keeps a reset off the credential
141
+ # ─────────────────────────────────────────────────────────────────────────────
142
+
143
+
144
+ def test_the_credential_is_registered_as_user_typed():
145
+ """This entry, not the file's existence, is what survives `resettablePaths()`.
146
+
147
+ `resettablePaths()` filters `HOME_CLASSES`, so an unregistered path survives
148
+ only by accident — and only until someone lists its parent. `user-typed` is
149
+ the class that says "a person typed this; nothing regenerates it", which is
150
+ exactly a login.
151
+ """
152
+ source = _read_source()
153
+ assert re.search(r"\{\s*path:\s*fpcliAuthFile,\s*class:\s*\"user-typed\"\s*\}", source), (
154
+ "fpcliAuthFile is no longer classified `user-typed` in HOME_CLASSES. A "
155
+ "reset walks that table; the CLI's session is not regenerable and must "
156
+ "never be on the delete list."
157
+ )
158
+
159
+
160
+ def test_the_directory_itself_is_deliberately_unclassified():
161
+ """`auditDir`'s rule: classify the children, never the parent.
162
+
163
+ A `user-typed` parent would protect a cache added later; a `derived` parent
164
+ would delete the session beside it.
165
+ """
166
+ source = _read_source()
167
+ assert not re.search(r"\{\s*path:\s*fpcliDir,\s*class:", source), (
168
+ "fpcliDir is now classified as a whole. If the directory has grown a "
169
+ "second file, classify that file — one class cannot be right for a "
170
+ "credential and a cache at once."
171
+ )
172
+
173
+
174
+ # ─────────────────────────────────────────────────────────────────────────────
175
+ # The anchors, so none of the above can pass vacuously
176
+ # ─────────────────────────────────────────────────────────────────────────────
177
+
178
+
179
+ def test_the_register_still_contains_what_these_patterns_anchor_on():
180
+ """Every regex above reads source text, and source text gets rewritten.
181
+
182
+ If `fp-home.ts` is restructured so the patterns stop matching, the tests
183
+ above fail loudly via `_declared`. This one covers the rest of the file:
184
+ the names must still be exported and still be mentioned in the class table.
185
+ """
186
+ source = _read_source()
187
+ for anchor in (
188
+ "export const fpcliDir",
189
+ "export const fpcliAuthFile",
190
+ "export const HOME_CLASSES",
191
+ "fpcliAuthFile, class:",
192
+ ):
193
+ assert anchor in source, f"fp-home.ts no longer contains {anchor!r}"
194
+
195
+
196
+ def test_this_file_can_be_imported_outside_the_repository():
197
+ """A collection error here would abort the whole CLI suite, not just this file."""
198
+ assert REPO_ROOT.is_absolute()
@@ -0,0 +1,186 @@
1
+ """Regression tests for the cli-bug-fix hardening pass.
2
+
3
+ Covers the crash/robustness fixes: pagination cursor type-mix, guarded file reads,
4
+ the non-JSON read-body trap, 429 messaging, explicit ISO date validation, non-finite
5
+ score bounds, and the empty-token (no silent saved-session fallback) guard.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from fp_cli import _click_compat as click # the Click Typer is running
11
+ import httpx
12
+ import pytest
13
+ import respx
14
+
15
+ from fp_cli import client, config, dates
16
+ from fp_cli._context import validate_score_filters
17
+ from fp_cli.app import app
18
+ from fp_cli.client import ClientContext
19
+ from fp_cli.commands._write import read_text_arg
20
+ from fp_cli.errors import ApiError
21
+ from fp_cli.models import Page
22
+
23
+ BASE = "http://dash.test"
24
+
25
+
26
+ def _ctx() -> ClientContext:
27
+ return ClientContext(base_url=BASE, token="t", org="globex")
28
+
29
+
30
+ # --- paginate: int/str cursor mix must not crash (was a TypeError) ----------
31
+
32
+
33
+ def test_paginate_mixed_cursor_types_no_crash():
34
+ # start_cursor is a str (from --cursor); pages return int next_cursor.
35
+ pages = [Page(items=[1, 2], next_cursor=100), Page(items=[3], next_cursor=None)]
36
+ seq = iter(pages)
37
+
38
+ def fetch(cursor, limit):
39
+ return next(seq)
40
+
41
+ got = list(client.paginate(fetch, limit=10, page_size=2, start_cursor="9999999999999999"))
42
+ assert got == [1, 2, 3]
43
+
44
+
45
+ def test_paginate_stops_on_repeated_cursor():
46
+ # A server that returns the same cursor forever must not loop forever.
47
+ def fetch(cursor, limit):
48
+ return Page(items=["x"], next_cursor=42)
49
+
50
+ got = list(client.paginate(fetch, limit=1000, page_size=1, start_cursor=None))
51
+ assert 0 < len(got) < 1000 # bounded by the seen-cursor guard
52
+
53
+
54
+ # --- read_text_arg: missing file -> usage error, not a traceback -----------
55
+
56
+
57
+ def test_read_text_arg_missing_file_is_usage_error():
58
+ with pytest.raises(click.BadParameter):
59
+ read_text_arg("/no/such/file.json")
60
+
61
+
62
+ # --- _get_json: non-JSON 2xx body -> ApiError, not raw JSONDecodeError ------
63
+
64
+
65
+ @respx.mock
66
+ def test_get_json_non_json_body_raises_clean_apierror():
67
+ respx.get(f"{BASE}/api/events").mock(
68
+ return_value=httpx.Response(200, text="<html>not json</html>")
69
+ )
70
+ with pytest.raises(ApiError):
71
+ client._get_json(_ctx(), "/api/events")
72
+
73
+
74
+ @respx.mock
75
+ def test_429_includes_retry_after():
76
+ respx.get(f"{BASE}/api/events").mock(
77
+ return_value=httpx.Response(429, headers={"retry-after": "5"}, json={})
78
+ )
79
+ with pytest.raises(ApiError) as ei:
80
+ client._get_json(_ctx(), "/api/events")
81
+ assert "Retry after 5" in str(ei.value)
82
+
83
+
84
+ # --- dates: explicit --from/--to validation --------------------------------
85
+
86
+
87
+ def test_resolve_range_rejects_garbage_date():
88
+ with pytest.raises(ValueError):
89
+ dates.resolve_range(ts_from="not-a-date")
90
+
91
+
92
+ def test_resolve_range_rejects_date_only():
93
+ with pytest.raises(ValueError):
94
+ dates.resolve_range(ts_from="2026-06-01")
95
+
96
+
97
+ def test_resolve_range_accepts_full_iso():
98
+ frm, _ = dates.resolve_range(ts_from="2026-05-01T00:00:00Z")
99
+ assert frm == "2026-05-01T00:00:00Z"
100
+
101
+
102
+ # --- validate_score_filters: reject non-finite bounds ----------------------
103
+
104
+
105
+ @pytest.mark.parametrize("value", ["helpfulness:nan..", "x:0.0..inf", "y:-inf..1"])
106
+ def test_score_filter_rejects_non_finite(value):
107
+ with pytest.raises(click.BadParameter):
108
+ validate_score_filters([value])
109
+
110
+
111
+ def test_score_filter_accepts_valid_ranges():
112
+ validate_score_filters(["helpfulness:0.5..0.8", "x:..0.3", "y:0.9.."]) # no raise
113
+
114
+
115
+ @pytest.mark.parametrize("value", ["helpfulness:..", "x:..", "metric:.."])
116
+ def test_score_filter_rejects_both_bounds_empty(value):
117
+ # `KEY:..` (no min, no max) is meaningless — the server silently drops it and
118
+ # returns the UNFILTERED set, so it must be a clean client-side usage error.
119
+ with pytest.raises(click.BadParameter):
120
+ validate_score_filters([value])
121
+
122
+
123
+ # --- empty --token must NOT silently use the saved session -----------------
124
+
125
+
126
+ def test_empty_token_does_not_fall_back_to_saved_session(home, runner):
127
+ config.save_config(config.CliConfig(base_url=BASE, session_token="saved-tok"))
128
+ # `--token ""` (e.g. an unset CI var) is an explicit "no auth", not a fallback.
129
+ result = runner.invoke(app, ["--base-url", BASE, "--token", "", "events"])
130
+ assert result.exit_code == 4, result.output # not logged in (was: used saved-tok)
131
+
132
+
133
+ def test_negative_timeout_is_usage_error(home, runner):
134
+ result = runner.invoke(app, ["--base-url", BASE, "--timeout", "-1", "events"])
135
+ assert result.exit_code == 2, result.output
136
+
137
+
138
+ def test_a_saved_insecure_does_not_follow_you_to_another_dashboard(logged_in, runner, monkeypatch):
139
+ """`insecure` was stored per MACHINE and applied to every base URL.
140
+
141
+ One `fp --base-url https://dash.internal --insecure login` against a
142
+ self-signed dev box — the use its own help text names — left certificate
143
+ verification off for the next `fp --base-url https://app.befailproof.ai
144
+ login`, which is the request that carries the OTP and receives the session
145
+ token, and for `fp keys create`, which carries a freshly minted API key in
146
+ the body. `clear_token` deliberately preserves `insecure` across a logout, so
147
+ it outlived the session it was set for.
148
+ """
149
+ cfg = config.load_config()
150
+ cfg.insecure = True
151
+ cfg.base_url = "https://dash.internal"
152
+ config.save_config(cfg)
153
+
154
+ seen = {}
155
+
156
+ class _Recorder(httpx.Client):
157
+ def __init__(self, *a, **kw):
158
+ seen["verify"] = kw.get("verify")
159
+ super().__init__(*a, **kw)
160
+
161
+ monkeypatch.setattr(httpx, "Client", _Recorder)
162
+
163
+ with respx.mock(assert_all_called=False) as m:
164
+ m.get(url__regex=r".*").mock(return_value=httpx.Response(200, json={"keys": []}))
165
+ runner.invoke(app, ["--base-url", "https://app.befailproof.ai", "keys", "list"])
166
+
167
+ assert seen.get("verify") is not False, (
168
+ "a preference granted for dash.internal was applied to a different dashboard"
169
+ )
170
+
171
+
172
+ def test_the_tls_warning_survives_quiet(logged_in, runner, monkeypatch):
173
+ """`--quiet` shares one gate with cosmetic status chrome; this is not that.
174
+
175
+ `fp --quiet …` on a machine where someone once ran `login --insecure` ran
176
+ every request with verification off and printed nothing anywhere.
177
+ """
178
+ cfg = config.load_config()
179
+ cfg.insecure = True
180
+ config.save_config(cfg)
181
+
182
+ with respx.mock(assert_all_called=False) as m:
183
+ m.get(url__regex=r".*").mock(return_value=httpx.Response(200, json={"keys": []}))
184
+ result = runner.invoke(app, ["--quiet", "keys", "list"])
185
+
186
+ assert "TLS verification disabled" in result.stderr, result.stderr
@@ -0,0 +1,127 @@
1
+ """The top-level help screen is a HAND-MAINTAINED table, not Click's command tree.
2
+
3
+ `output.render_top_level_help` renders `_TOP_LEVEL_GROUPS`, a literal list. A command
4
+ registered on the Typer app but missing from that list works perfectly and is invisible
5
+ in `fp help` forever — there is no error, no warning, and no other test that looks.
6
+
7
+ These tests close that gap in both directions, and additionally assert the help text
8
+ carries the current command name, which is the thing a rename silently leaves stale
9
+ (Click derives its own `Usage:` line from argv[0], so the auto-generated half updates
10
+ itself and the hand-written half does not).
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import re
16
+
17
+ from typer.main import get_command
18
+
19
+ from fp_cli import output
20
+ from fp_cli.app import app
21
+
22
+
23
+ def _registered_commands() -> set[str]:
24
+ """Every command name Click actually knows about."""
25
+ cmd = get_command(app)
26
+ return set(cmd.commands) # type: ignore[attr-defined]
27
+
28
+
29
+ def _help_table_commands() -> set[str]:
30
+ """Every command name the hand-maintained help table advertises."""
31
+ names = set()
32
+ for _group, entries in output._TOP_LEVEL_GROUPS:
33
+ for entry in entries:
34
+ names.add(entry[0])
35
+ return names
36
+
37
+
38
+ def test_every_registered_command_appears_in_the_help_table():
39
+ missing = _registered_commands() - _help_table_commands()
40
+ assert not missing, (
41
+ f"these commands are registered but absent from output._TOP_LEVEL_GROUPS, so "
42
+ f"`fp help` will never mention them: {sorted(missing)}"
43
+ )
44
+
45
+
46
+ def test_the_help_table_never_advertises_a_command_that_does_not_exist():
47
+ extra = _help_table_commands() - _registered_commands()
48
+ assert not extra, (
49
+ f"output._TOP_LEVEL_GROUPS advertises commands that are not registered — "
50
+ f"`fp <name>` would be a usage error: {sorted(extra)}"
51
+ )
52
+
53
+
54
+ def test_the_help_table_is_not_empty():
55
+ """Guards the assertions above from passing vacuously if the structure changes."""
56
+ assert len(_help_table_commands()) >= 15
57
+
58
+
59
+ def test_help_chrome_carries_no_retired_product_name():
60
+ """A rename updates Click's generated usage for free and the prose not at all."""
61
+ rendered = "\n".join(
62
+ str(entry) for _group, entries in output._TOP_LEVEL_GROUPS for entry in entries
63
+ )
64
+ examples = "\n".join(f"{cmd} {why}" for cmd, why in output._TOP_LEVEL_EXAMPLES)
65
+ haystack = f"{rendered}\n{examples}".lower()
66
+ assert "agenteye" not in haystack, "retired product name still present in help chrome"
67
+
68
+
69
+ def test_the_help_examples_use_the_current_command_name():
70
+ cmds = [cmd for cmd, _why in output._TOP_LEVEL_EXAMPLES]
71
+ assert cmds, "no examples to check"
72
+ assert all(c.startswith("fp ") for c in cmds), (
73
+ f"help examples must invoke `fp`: {[c for c in cmds if not c.startswith('fp ')]}"
74
+ )
75
+
76
+
77
+ def test_no_module_advertises_the_retired_env_var_namespace():
78
+ """`FP_*` is this CLI's namespace. `AGENTEYE_*` names that remain must be explicit
79
+ references to OTHER components (the collector's ingest key, the dashboard's admin
80
+ key, the SDK/collector spool dir) and never something this CLI reads."""
81
+ import pathlib
82
+
83
+ pkg = pathlib.Path(output.__file__).resolve().parent
84
+ allowed = {"AGENTEYE_KEY", "AGENTEYE_API_KEY", "AGENTEYE_HOME"}
85
+ found = set()
86
+ for mod in pkg.rglob("*.py"):
87
+ for m in re.finditer(r"AGENTEYE" + r"_[A-Z_]+", mod.read_text()):
88
+ found.add(m.group(0))
89
+ assert found <= allowed, (
90
+ f"these retired env vars are still referenced in the package: {sorted(found - allowed)}"
91
+ )
92
+
93
+
94
+ def test_every_subcommand_the_hint_column_advertises_actually_exists():
95
+ """The THIRD element of each row was never checked against anything.
96
+
97
+ `_help_table_commands()` reads `entry[0]` only, so the ten standalone leaves
98
+ and the thirteen group names were guarded in both directions and every
99
+ advertised SUBCOMMAND was not. `issues` listed a bare `comment`, which is not
100
+ a command — the group exposes `comment-add`, `comment-list` and
101
+ `comment-delete` — so `fp help` told users to run something that exits 2 with
102
+ "No such command 'comment'".
103
+
104
+ A `*` suffix is a deliberate wildcard (`comment-*`, `context-*`); a leading
105
+ `-` is a flag, not a subcommand. Everything else must be real.
106
+ """
107
+ cmd = get_command(app)
108
+ bad = []
109
+ for _group, entries in output._TOP_LEVEL_GROUPS:
110
+ for entry in entries:
111
+ name, hint = entry[0], (entry[2] if len(entry) > 2 else "")
112
+ group = cmd.commands.get(name) # type: ignore[attr-defined]
113
+ subcommands = set(getattr(group, "commands", {}) or {})
114
+ if not subcommands:
115
+ continue # a standalone leaf; its hint is prose, not a command list
116
+ for token in hint.split():
117
+ if token.startswith("-"):
118
+ continue
119
+ if token.endswith("*"):
120
+ prefix = token[:-1]
121
+ if not any(sub.startswith(prefix) for sub in subcommands):
122
+ bad.append(f"{name}: '{token}' matches no subcommand")
123
+ continue
124
+ if token not in subcommands:
125
+ bad.append(f"{name}: '{token}' is not a subcommand of `fp {name}`")
126
+
127
+ assert not bad, "the help screen advertises commands that do not exist:\n " + "\n ".join(bad)