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,385 @@
1
+ """Tests for CLI telemetry.
2
+
3
+ The whole suite runs with telemetry **off** (``is_dev_or_test`` sees
4
+ ``PYTEST_CURRENT_TEST``), so nothing here ever touches the network. Tests that need
5
+ the capture path force ``resolve_config`` to ``enabled`` and swap in a fake client
6
+ that records calls — mirroring the transport-fake style used elsewhere in the suite.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+
13
+ import pytest
14
+
15
+ from fp_cli import analytics
16
+ from fp_cli import analytics_config as acfg
17
+ from fp_cli import app as appmod
18
+ from fp_cli import config
19
+ from fp_cli._version import __version__
20
+
21
+
22
+ class FakeClient:
23
+ """Stand-in for ``posthog.Posthog`` that records calls instead of sending."""
24
+
25
+ def __init__(self, api_key=None, **kwargs):
26
+ self.api_key = api_key
27
+ self.kwargs = kwargs
28
+ self.events = [] # list of (distinct_id, event, properties)
29
+ self.aliases = [] # list of (previous_id, distinct_id)
30
+ self.flushed = 0
31
+ self.shutdowns = 0
32
+
33
+ # Keyword-only after `event`, mirroring the installed posthog>=7 signature
34
+ # (`capture(self, event, **kwargs)`). It was the posthog-3 positional shape,
35
+ # so every assertion in this file and in test_telemetry_completeness.py was
36
+ # validated against a stand-in the real client no longer matched — a 380-line
37
+ # privacy suite proving nothing about what would actually go over the wire.
38
+ def capture(self, event=None, *, distinct_id=None, properties=None, **_):
39
+ self.events.append((distinct_id, event, properties))
40
+
41
+ def alias(self, previous_id=None, distinct_id=None, **_):
42
+ self.aliases.append((previous_id, distinct_id))
43
+
44
+ def flush(self):
45
+ self.flushed += 1
46
+
47
+ def shutdown(self):
48
+ self.shutdowns += 1
49
+
50
+
51
+ @pytest.fixture(autouse=True)
52
+ def _clean_analytics_state():
53
+ """Reset the module singleton around every test."""
54
+ analytics._client = None
55
+ analytics._distinct_id = None
56
+ analytics._command = None
57
+ analytics._json_output = False
58
+ analytics._auth_mode = "none"
59
+ analytics._force_anonymous = False
60
+ analytics._init_done = False # client is built lazily; reset the one-shot guard
61
+ analytics._pending_conf = None
62
+ yield
63
+ analytics.shutdown()
64
+ analytics._client = None
65
+ analytics._init_done = False
66
+ analytics._pending_conf = None
67
+ analytics._force_anonymous = False
68
+
69
+
70
+ @pytest.fixture
71
+ def force_enabled(monkeypatch):
72
+ """Force telemetry 'enabled' despite the pytest gate, backed by a fake client."""
73
+ monkeypatch.setattr(
74
+ acfg,
75
+ "resolve_config",
76
+ lambda: acfg.AnalyticsConfig(enabled=True, api_key="phc_test", host="http://ph.test"),
77
+ )
78
+ import posthog
79
+
80
+ monkeypatch.setattr(posthog, "Posthog", FakeClient)
81
+ return FakeClient
82
+
83
+
84
+ # --- gating / opt-out ------------------------------------------------------------
85
+
86
+
87
+ def test_disabled_under_pytest(monkeypatch):
88
+ for var in ("FP_ANALYTICS_DISABLED", "DO_NOT_TRACK", "FP_CLI_DEV"):
89
+ monkeypatch.delenv(var, raising=False)
90
+ # PYTEST_CURRENT_TEST is set by pytest, so the dev/test gate alone disables us.
91
+ assert acfg.is_dev_or_test() is True
92
+ assert acfg.resolve_config().enabled is False
93
+ analytics.init_analytics(config.CliConfig())
94
+ analytics._ensure_client() # force the (deferred) build — stays off because disabled
95
+ assert analytics._client is None
96
+
97
+
98
+ @pytest.mark.parametrize("var", ["FP_ANALYTICS_DISABLED", "DO_NOT_TRACK"])
99
+ @pytest.mark.parametrize("val", ["1", "true", "TRUE", "yes", "Yes"])
100
+ def test_opt_out_truthy(monkeypatch, var, val):
101
+ monkeypatch.setenv(var, val)
102
+ assert acfg.is_disabled() is True
103
+
104
+
105
+ @pytest.mark.parametrize("val", ["0", "false", "no", "", " "])
106
+ def test_opt_out_falsey(monkeypatch, val):
107
+ monkeypatch.delenv("DO_NOT_TRACK", raising=False)
108
+ monkeypatch.setenv("FP_ANALYTICS_DISABLED", val)
109
+ assert acfg.is_disabled() is False
110
+
111
+
112
+ def test_init_noop_when_opted_out(monkeypatch):
113
+ # Even with the pytest gate bypassed, the opt-out env var keeps us off.
114
+ monkeypatch.setattr(acfg, "is_dev_or_test", lambda: False)
115
+ monkeypatch.setenv("FP_ANALYTICS_DISABLED", "1")
116
+ analytics.init_analytics(config.CliConfig())
117
+ analytics._ensure_client() # force the (deferred) build — stays off because opted out
118
+ assert analytics._client is None
119
+
120
+
121
+ # --- distinct id / identity ------------------------------------------------------
122
+
123
+
124
+ def test_anonymous_id_generated_and_persisted(home, force_enabled):
125
+ analytics.init_analytics(config.load_config())
126
+ analytics._ensure_client() # client is built lazily on first use
127
+ assert analytics._client is not None
128
+ anon = config.load_config().anonymous_id
129
+ assert anon # persisted to cli.json
130
+ assert analytics._distinct_id == anon # logged-out -> anonymous distinct id
131
+
132
+
133
+ def test_logged_in_uses_user_id_not_anonymous(home, force_enabled):
134
+ config.save_config(
135
+ config.CliConfig(base_url="http://d", session_token="tok", user_id="u-42")
136
+ )
137
+ analytics.init_analytics(config.load_config())
138
+ analytics._ensure_client() # client is built lazily on first use
139
+ assert analytics._distinct_id == "u-42"
140
+ # No anonymous id is minted while logged in.
141
+ assert config.load_config().anonymous_id is None
142
+
143
+
144
+ def test_super_properties_tag_product(home, force_enabled):
145
+ analytics.init_analytics(config.load_config())
146
+ analytics._ensure_client() # client is built lazily on first use
147
+ sup = analytics._client.kwargs["super_properties"]
148
+ # Must match analytics_config.PRODUCT exactly — it is the discriminator that keeps
149
+ # this CLI's events apart from the Enforcement CLI's in the shared PostHog project.
150
+ assert sup["product"] == "fp-cloud-cli"
151
+ assert sup["cli_version"] == __version__
152
+ assert sup["os"] and sup["python_version"]
153
+ # The client is constructed against the direct ingest host, geoip disabled.
154
+ assert analytics._client.kwargs["disable_geoip"] is True
155
+
156
+
157
+ def test_identify_links_anonymous_to_user(home, force_enabled):
158
+ config.save_config(config.CliConfig(anonymous_id="anon-1"))
159
+ analytics.init_analytics(config.load_config())
160
+ analytics.identify("user-xyz")
161
+ assert analytics._client.aliases == [("anon-1", "user-xyz")]
162
+
163
+
164
+ def test_identify_noop_when_disabled():
165
+ # No force_enabled -> client is None; must not raise.
166
+ analytics.init_analytics(config.CliConfig())
167
+ analytics.identify("user-xyz") # no-op, no exception
168
+
169
+
170
+ def test_reset_rotates_anonymous_id(home, force_enabled):
171
+ config.save_config(config.CliConfig(anonymous_id="old-anon"))
172
+ analytics.init_analytics(config.load_config())
173
+ analytics.reset()
174
+ rotated = config.load_config().anonymous_id
175
+ assert rotated and rotated != "old-anon"
176
+
177
+
178
+ def test_reset_rotates_even_when_disabled(home):
179
+ # The anonymous id is persistent state and the opt-out flag can flip between runs,
180
+ # so logout must rotate it regardless of whether telemetry is active this run.
181
+ config.save_config(config.CliConfig(anonymous_id="keep"))
182
+ analytics.init_analytics(config.CliConfig()) # disabled -> client None
183
+ analytics.reset()
184
+ after = config.load_config().anonymous_id
185
+ assert after and after != "keep"
186
+
187
+
188
+ # --- command_executed payload / privacy ------------------------------------------
189
+
190
+
191
+ def _only_event(client):
192
+ assert len(client.events) == 1
193
+ distinct_id, event, props = client.events[0]
194
+ return distinct_id, event, props
195
+
196
+
197
+ def test_command_executed_payload_allowlist(home, force_enabled):
198
+ analytics.init_analytics(config.load_config())
199
+ analytics.note_command("sessions", True)
200
+ argv = [
201
+ "--json", "sessions", "--since", "24h",
202
+ "--base-url", "https://secret.internal",
203
+ "--score", "helpfulness:..0.5",
204
+ ]
205
+ analytics.capture_command(exit_code=0, duration_ms=12, argv=argv)
206
+
207
+ _, event, props = _only_event(analytics._client)
208
+ assert event == "command_executed"
209
+ assert props == {
210
+ "command": "sessions",
211
+ "subcommand": None,
212
+ "success": True,
213
+ "exit_code": 0,
214
+ "error_category": None,
215
+ "duration_ms": 12,
216
+ "flags": ["--json", "--since", "--base-url", "--score"],
217
+ "json_output": True,
218
+ "auth_mode": "none",
219
+ }
220
+ # No argument VALUES leak — serialise the whole payload and scan.
221
+ blob = json.dumps(props)
222
+ for secret in ("secret.internal", "24h", "helpfulness", "0.5"):
223
+ assert secret not in blob
224
+
225
+
226
+ def test_command_executed_group_subcommand(home, force_enabled):
227
+ analytics.init_analytics(config.load_config())
228
+ analytics.note_command("agent", False)
229
+ analytics.capture_command(
230
+ exit_code=0, duration_ms=3, argv=["agent", "rename", "chat-001", "--title", "secret title"]
231
+ )
232
+ _, _, props = _only_event(analytics._client)
233
+ assert props["command"] == "agent"
234
+ assert props["subcommand"] == "rename"
235
+ assert props["flags"] == ["--title"]
236
+ blob = json.dumps(props)
237
+ assert "chat-001" not in blob and "secret title" not in blob
238
+
239
+
240
+ def test_auth_mode_rides_on_command_executed(home, force_enabled):
241
+ from fp_cli.client import AuthMode
242
+
243
+ analytics.init_analytics(config.load_config())
244
+ analytics.note_command("sessions", False, auth_mode=AuthMode.API_KEY)
245
+ analytics.capture_command(
246
+ exit_code=0, duration_ms=1, argv=["--api-key", "ak_live_SECRET", "sessions"]
247
+ )
248
+ _, _, props = _only_event(analytics._client)
249
+ assert props["auth_mode"] == "api_key"
250
+ # The mode is the whole signal: never the key, its length, or a prefix.
251
+ blob = json.dumps(props)
252
+ assert "ak_live_SECRET" not in blob and "SECRET" not in blob
253
+ assert props["flags"] == ["--api-key"] # the NAME only
254
+
255
+
256
+ @pytest.mark.parametrize("given,expected", [
257
+ ("session", "session"),
258
+ ("api_key", "api_key"),
259
+ (None, "none"),
260
+ ("something-else", "none"), # closed enum: an unexpected value degrades, never rides along
261
+ ])
262
+ def test_auth_mode_is_a_closed_enum(home, force_enabled, given, expected):
263
+ analytics.init_analytics(config.load_config())
264
+ analytics.note_command("whoami", False, auth_mode=given)
265
+ analytics.capture_command(exit_code=0, duration_ms=1, argv=["whoami"])
266
+ _, _, props = _only_event(analytics._client)
267
+ assert props["auth_mode"] == expected
268
+
269
+
270
+ def test_key_mode_forces_the_anonymous_distinct_id(home, force_enabled):
271
+ # A CI box with a logged-in human on it: the key's commands must NOT be attributed
272
+ # to that person.
273
+ config.save_config(
274
+ config.CliConfig(base_url="http://d", session_token="tok", user_id="u-42")
275
+ )
276
+ analytics.init_analytics(config.load_config(), force_anonymous=True)
277
+ analytics._ensure_client()
278
+ assert analytics._distinct_id != "u-42"
279
+ assert analytics._distinct_id == config.load_config().anonymous_id
280
+
281
+
282
+ def test_unknown_command_is_dropped(home, force_enabled):
283
+ analytics.init_analytics(config.load_config())
284
+ analytics.note_command("rm-rf-everything", False) # not a real command
285
+ analytics.capture_command(exit_code=0, duration_ms=1, argv=["rm-rf-everything"])
286
+ _, _, props = _only_event(analytics._client)
287
+ assert props["command"] is None
288
+
289
+
290
+ @pytest.mark.parametrize(
291
+ "code,category,success",
292
+ [
293
+ (0, None, True),
294
+ (2, "usage", False),
295
+ (3, "network", False),
296
+ (4, "auth", False),
297
+ (5, "forbidden", False),
298
+ (6, "not_found", False),
299
+ (1, "error", False),
300
+ ],
301
+ )
302
+ def test_error_category_from_exit_code(home, force_enabled, code, category, success):
303
+ analytics.init_analytics(config.load_config())
304
+ analytics.note_command("whoami", False)
305
+ analytics.capture_command(exit_code=code, duration_ms=1, argv=["whoami"])
306
+ _, _, props = _only_event(analytics._client)
307
+ assert props["error_category"] == category
308
+ assert props["success"] is success
309
+
310
+
311
+ # --- helpers ---------------------------------------------------------------------
312
+
313
+
314
+ def test_sanitize_flags_keeps_names_drops_values():
315
+ argv = [
316
+ "--base-url", "https://secret", "--token", "abc", "--email", "a@b.com",
317
+ "--score", "k:..0.5", "sessions", "-q", "--unknown-xyz", "-5",
318
+ "--fields=session_id,scores",
319
+ ]
320
+ flags = analytics._sanitize_flags(argv)
321
+ assert flags == ["--base-url", "--token", "--email", "--score", "--quiet", "--fields"]
322
+ blob = " ".join(flags)
323
+ for leak in ("secret", "abc", "a@b.com", "k:..0.5", "session_id"):
324
+ assert leak not in blob
325
+
326
+
327
+ @pytest.mark.parametrize(
328
+ "argv,expected",
329
+ [
330
+ (["agent", "show", "x"], "show"),
331
+ (["agent", "rename"], "rename"),
332
+ (["--json", "agent", "show"], "show"),
333
+ (["agent", "rename", "show"], "rename"), # first after 'agent' wins
334
+ (["sessions", "--all"], None), # 'sessions' is a leaf, not a group
335
+ (["whoami"], None),
336
+ ],
337
+ )
338
+ def test_detect_subcommand(argv, expected):
339
+ assert analytics._detect_subcommand(argv) == expected
340
+
341
+
342
+ def test_shutdown_is_idempotent(home, force_enabled):
343
+ analytics.init_analytics(config.load_config())
344
+ analytics._ensure_client() # client is built lazily on first use
345
+ client = analytics._client
346
+ analytics.shutdown()
347
+ assert analytics._client is None
348
+ assert client.flushed == 1 and client.shutdowns == 1
349
+ analytics.shutdown() # second call: no-op, no error
350
+ assert client.flushed == 1
351
+
352
+
353
+ # --- entry-point wrapper preserves exit codes ------------------------------------
354
+
355
+
356
+ def test_main_entry_preserves_exit_code(monkeypatch):
357
+ recorded = {}
358
+
359
+ def boom():
360
+ raise SystemExit(4)
361
+
362
+ monkeypatch.setattr(appmod, "app", boom)
363
+ monkeypatch.setattr(
364
+ appmod.analytics, "capture_command", lambda code, ms, argv: recorded.update(code=code)
365
+ )
366
+ monkeypatch.setattr(appmod.analytics, "shutdown", lambda: None)
367
+
368
+ with pytest.raises(SystemExit) as exc:
369
+ appmod.main_entry()
370
+ assert exc.value.code == 4
371
+ assert recorded["code"] == 4
372
+
373
+
374
+ def test_main_entry_success_path(monkeypatch):
375
+ recorded = {}
376
+ monkeypatch.setattr(appmod, "app", lambda: None) # returns without exiting
377
+ monkeypatch.setattr(
378
+ appmod.analytics, "capture_command", lambda code, ms, argv: recorded.update(code=code)
379
+ )
380
+ monkeypatch.setattr(appmod.analytics, "shutdown", lambda: None)
381
+
382
+ with pytest.raises(SystemExit) as exc:
383
+ appmod.main_entry()
384
+ assert exc.value.code == 0
385
+ assert recorded["code"] == 0