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,35 @@
1
+ """usage — show the active organization's current billing-window usage."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ from .. import client as api
8
+ from .. import output
9
+ from .._context import GLOBALS_EPILOG, AppState, require_auth
10
+
11
+
12
+ def usage(ctx: typer.Context) -> None:
13
+ """Show usage for the active org's current fixed 30-day metering window.
14
+
15
+ Reports telemetry, evaluations, workspace objects, audits, API keys, and members. This is
16
+ read-only and does not apply or display limits. Needs `usage:read`.
17
+
18
+ With `--json`, returns the dashboard response unchanged: `org_id`, `billing_anchor`,
19
+ `window`, `usage`, `calculated_at`, and `stale_after`.
20
+
21
+ Examples:
22
+
23
+ * `fp usage`
24
+ * `fp --json usage | jq '.usage.events_ingested'`
25
+ """
26
+ state: AppState = ctx.obj
27
+ data = api.get_usage(require_auth(state))
28
+ if state.json:
29
+ output.emit_json(data)
30
+ return
31
+ output.render_usage(data)
32
+
33
+
34
+ def register(app: typer.Typer) -> None:
35
+ app.command("usage", epilog=GLOBALS_EPILOG)(usage)
@@ -0,0 +1,404 @@
1
+ """Org member management: users list / show / create / update / disable / enable.
2
+
3
+ Members are referenced by their **email** (unique within the org) — not a raw id — across
4
+ ``show`` / ``update`` / ``disable`` / ``enable`` (a UUID-shaped handle is also accepted). The
5
+ whole group converges on the shared boxed visual language: a boxed member list, identity cards,
6
+ the shared grouped permissions panel, and the shared confirm/cancel helpers. Effective grants are
7
+ ``(set ∪ added) − removed``; ``--add`` / ``--remove`` use the compact ``slug:act.act`` token
8
+ format (the same parser as ``keys``). Protected members can't be edited/disabled.
9
+
10
+ A member's role comes from a **permission set** that lives **per org on the server** (managed in
11
+ the dashboard): the built-in ``read-only`` / ``standard`` / ``admin`` plus any custom sets the org
12
+ defines. The CLI hardcodes only those three built-ins — and only to *preview* the resulting grants
13
+ locally; the ``--permission-set`` value is resolved + expanded server-side, so custom org sets work
14
+ too (but a custom set can't be previewed, so ``update`` confirms it generically).
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from typing import List, Optional
20
+
21
+ import typer
22
+
23
+ from .. import _click_compat as click # the Click Typer is running; see _click_compat
24
+ from .. import client as api
25
+ from .. import output, permissions, theme
26
+ from .._context import GLOBALS_EPILOG, AppState, require_auth
27
+ from ..errors import ForbiddenError
28
+ from . import _write
29
+
30
+
31
+ def _resolve_user_or_exit(state: AppState, users, handle: str):
32
+ """Resolve a member by **email** (primary), falling back to an exact id match, raising a
33
+ typed error the central chokepoint renders (JSON envelope under ``--json``, red box
34
+ otherwise): none → exit 6, several → exit 2."""
35
+ return _write.resolve_one(
36
+ users,
37
+ handle,
38
+ kind="user",
39
+ ref="with email",
40
+ key="email",
41
+ list_cmd="users list",
42
+ # The server lowercases on create, so the address the caller typed is not the address
43
+ # stored. Without this, `fp users create Alice.Chen@Example.com` succeeds and every
44
+ # subsequent show/update/disable/enable on that same string reports "no user with
45
+ # email" — the member is reachable only via a lowercased form nothing told them about.
46
+ casefold=True,
47
+ )
48
+
49
+
50
+ def _parse_user_tokens_or_exit(state: AppState, tokens) -> List[str]:
51
+ """Expand the compact ``slug:act1.act2`` ``--add`` / ``--remove`` tokens into a flat
52
+ assignable permission list (empty when no value was given). A malformed/unknown token → a clean
53
+ usage error (exit 2), before any read/write. Space-separated tokens (quoted) and repeated flags
54
+ both compose — e.g. ``--add "keys:create.regenerate users:create.update"`` or
55
+ ``--add keys:create.regenerate --add users:create.update``."""
56
+ if not tokens:
57
+ return []
58
+ try:
59
+ return permissions.parse_permission_tokens(tokens)
60
+ except permissions.PermissionTokenError as exc:
61
+ raise click.UsageError(str(exc))
62
+
63
+
64
+ def users_list(
65
+ ctx: typer.Context,
66
+ active_only: bool = typer.Option(False, "--active-only", help="Show only enabled members (hide disabled ones)."),
67
+ show_id: bool = typer.Option(False, "--show-id", help="Prepend a short user-id column (the full id is always in --json)."),
68
+ ) -> None:
69
+ """List org members in a boxed table — active members first, disabled ones dimmed at the bottom.
70
+
71
+ Shows `email · access · permissions · joined · status` with a leading 🔒 marker on protected
72
+ members; status is derived from the disable state (`● active` / `○ disabled`), and `joined`
73
+ comes from each member's join date. The raw id is hidden by default (use `--show-id` for a
74
+ short id, or `--json` for the full id). Needs `users:read`. With `--json`:
75
+ `{"users": [{id, email, permissions, permission_set, disabled_at, is_protected, created_at, …}]}`.
76
+
77
+ Examples:
78
+
79
+ * `fp users list`
80
+ * `fp users list --active-only`
81
+ """
82
+ state: AppState = ctx.obj
83
+ cctx = require_auth(state)
84
+ users = api.list_users(cctx)
85
+ if active_only:
86
+ users = [u for u in users if not u.disabled_at]
87
+ users = sorted(users, key=lambda u: 1 if u.disabled_at else 0) # active first, disabled last
88
+ if state.json:
89
+ output.emit_json({"users": users})
90
+ return
91
+ output.render_users(users, show_id=show_id)
92
+ output.users_footer(users)
93
+
94
+
95
+ def users_show(
96
+ ctx: typer.Context,
97
+ email: str = typer.Argument(..., metavar="EMAIL", help="User email (or id)."),
98
+ ) -> None:
99
+ """Show one member's identity and full effective permissions — like `whoami` for someone else.
100
+
101
+ Renders an identity card (email, role, status) then the shared grouped permissions panel with
102
+ **all** the member's grants (no truncation). Referenced by **email** (or a UUID-shaped id).
103
+ Needs `users:read`. With `--json`: the full member object.
104
+
105
+ Example:
106
+
107
+ * `fp users show dev@example.com`
108
+ """
109
+ state: AppState = ctx.obj
110
+ cctx = require_auth(state)
111
+ user = _resolve_user_or_exit(state, api.list_users(cctx), email)
112
+ if state.json:
113
+ output.emit_json(user)
114
+ return
115
+ output.render_user_show(user)
116
+
117
+
118
+ def _validate_permission_set_or_exit(state: AppState, cctx, set_name: Optional[str]) -> None:
119
+ """Reject an unknown ``--permission-set`` client-side (clean exit 2) instead of a raw server
120
+ 422 with a request-id. Valid = the org's sets ∪ the built-in presets (the server expands it).
121
+ Mirrors how ``keys create`` validates its set."""
122
+ presets = set(permissions.PRESETS) - {"clear"} # `clear` is an internal preset, not assignable
123
+ if not set_name or set_name in presets:
124
+ return # no set, or a built-in preset (always valid) → no extra round-trip
125
+ valid = set(api.list_permission_sets(cctx)) | presets
126
+ if set_name not in valid:
127
+ raise click.UsageError(
128
+ f'unknown permission set "{set_name}". available: {", ".join(sorted(valid))}'
129
+ )
130
+
131
+
132
+ def users_create(
133
+ ctx: typer.Context,
134
+ email: str = typer.Argument(..., metavar="EMAIL", help="Email of the member to invite (unique within the org)."),
135
+ permission_set: Optional[str] = typer.Option(None, "--permission-set", help="Role to start from — a permission set: `read-only`, `standard`, `admin`, or a custom set your org defines in the dashboard. Omit for no base role."),
136
+ add: Optional[List[str]] = typer.Option(None, "--add", help="Grant extra permissions on top of the set, as `slug:action.action` tokens (dotted actions expand: `keys:create.regenerate` → `keys:create`, `keys:regenerate`). Several via comma, repeated flag, or a quoted group: `--add keys:create,users:read` · `--add a --add b` · `--add \"a b\"`."),
137
+ remove: Optional[List[str]] = typer.Option(None, "--remove", help="Revoke permissions from the set, same `slug:action.action` token format as --add (comma / repeated / quoted)."),
138
+ ) -> None:
139
+ """Invite an org member and show their new identity + permissions.
140
+
141
+ Give the member's **email** (positional). Their role comes from `--permission-set` (a built-in
142
+ `read-only` / `standard` / `admin`, or a custom set your org defines in the dashboard), and you
143
+ can fine-tune it per member with `--add` / `--remove`. The effective grants are
144
+ `(set ∪ added) − removed`, expanded server-side; the permissions box shows the result.
145
+
146
+ `--add` / `--remove` take the compact `slug:action.action` token format — dotted actions expand
147
+ (`keys:create.regenerate` → two grants) and several compose by comma, repeated flag, or a quoted
148
+ group. Needs `users:create`. With `--json`: `{id, email, permission_set, permissions}`
149
+ (`permissions` = the expanded set).
150
+
151
+ Examples:
152
+
153
+ * `fp users create dev@example.com --permission-set standard` — invite with the standard role
154
+ * `fp users create ci@example.com --permission-set read-only --add keys:create.regenerate` — read-only plus key management
155
+ * `fp users create lead@example.com --permission-set admin --remove settings:write` — admin minus settings writes
156
+ """
157
+ state: AppState = ctx.obj
158
+ parsed_add = _parse_user_tokens_or_exit(state, add)
159
+ parsed_remove = _parse_user_tokens_or_exit(state, remove)
160
+ cctx = require_auth(state)
161
+ _validate_permission_set_or_exit(state, cctx, permission_set)
162
+ # Casefolded, matching `_write.resolve_one`. The server lowercases an email
163
+ # on create, so the address the caller typed is not the address stored —
164
+ # 6372ecf2 fixed the RESOLVER for that and left this guard an exact
165
+ # comparison, which made the group internally inconsistent about case: `fp
166
+ # users show Alice.Chen@Example.com` resolved the member, while `fp users
167
+ # create Alice.Chen@Example.com` failed to see them, sent the POST, and
168
+ # surfaced the server's 409 as exit 1. The skill's own guidance branches on
169
+ # the code — exit 2 means "report it and ask", exit 1 means "unexpected
170
+ # server error" — so an agent took the wrong branch for the same logical
171
+ # outcome, decided purely by the capitalisation a human typed.
172
+ if any((u.email or "").casefold() == email.casefold() for u in api.list_users(cctx)):
173
+ raise click.UsageError(f'a user with email "{email}" already exists')
174
+ user = api.create_user(
175
+ cctx, email=email, permission_set=permission_set,
176
+ permission_added=parsed_add or None, permission_removed=parsed_remove or None,
177
+ )
178
+ _write.record_action("user_created", resource="user", success=True, permission_count=len(user.permissions))
179
+ if state.json:
180
+ output.emit_json({"id": user.id, "email": user.email,
181
+ "permission_set": user.permission_set, "permissions": user.permissions})
182
+ return
183
+ output.render_user_created(user)
184
+
185
+
186
+ def users_update(
187
+ ctx: typer.Context,
188
+ email: str = typer.Argument(..., metavar="EMAIL", help="Email (or id) of the member to update."),
189
+ permission_set: Optional[str] = typer.Option(None, "--permission-set", help="Reassign the member's role — a permission set: `read-only`, `standard`, `admin`, or a custom set your org defines in the dashboard. Replaces their per-member overrides (apply fresh ones with --add/--remove)."),
190
+ add: Optional[List[str]] = typer.Option(None, "--add", help="Grant permissions, same `slug:action.action` token format as `users create` (dotted actions expand; comma / repeated flag / quoted group compose). Incremental — merged into the member's CURRENT grants, unless --permission-set is also given."),
191
+ remove: Optional[List[str]] = typer.Option(None, "--remove", help="Revoke permissions, same `slug:action.action` token format as --add. Incremental — applied to the member's CURRENT grants, unless --permission-set is also given."),
192
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirmation prompt. The prompt only appears on an interactive terminal: under --json, or with stdin redirected, this command proceeds without asking."),
193
+ ) -> None:
194
+ """Change a member's permissions and show the diff. Referenced by **email** (or a UUID id).
195
+
196
+ Two ways to use it (combine if you like):
197
+
198
+ * **Reassign a role** — `--permission-set <set>` swaps the member's base role (a built-in
199
+ `read-only` / `standard` / `admin`, or a custom org set); add `--add`/`--remove` to layer
200
+ fresh per-member overrides on top.
201
+ * **Tweak incrementally** — `--add` / `--remove` **alone** adjust the member's *current* grants
202
+ (their set is kept). `--add` grants, `--remove` revokes; both take the compact
203
+ `slug:action.action` token format (dotted actions expand; comma / repeated / quoted compose).
204
+
205
+ The CLI reads the current grants, computes the resulting set `(set ∪ added) − removed`, and
206
+ shows a git-style diff — added green, removed struck-through, unchanged dim. It confirms first
207
+ (default no; `--yes` skips) and a no-op exits without calling the server. Needs `users:update`
208
+ (+ `users:read`). With `--json`: `{id, email, permission_set, permissions, added, removed}`.
209
+
210
+ Examples:
211
+
212
+ * `fp users update dev@example.com --add keys:create.regenerate` — grant on top of current grants
213
+ * `fp users update dev@example.com --remove alerts:read,incidents:ack` — revoke a couple of grants
214
+ * `fp users update dev@example.com --add events:read --remove keys:delete` — add and remove at once
215
+ * `fp users update dev@example.com --permission-set admin --yes` — reassign the role (no prompt)
216
+ """
217
+ state: AppState = ctx.obj
218
+ if permission_set is None and not add and not remove:
219
+ raise typer.BadParameter("nothing to update — pass --permission-set, --add, and/or --remove.")
220
+ parsed_add = _parse_user_tokens_or_exit(state, add)
221
+ parsed_remove = _parse_user_tokens_or_exit(state, remove)
222
+ both = sorted(set(parsed_add) & set(parsed_remove))
223
+ if both:
224
+ raise typer.BadParameter(f"{', '.join(both)} given to both --add and --remove.")
225
+ cctx = require_auth(state)
226
+ _validate_permission_set_or_exit(state, cctx, permission_set)
227
+ user = _resolve_user_or_exit(state, api.list_users(cctx), email)
228
+ before = set(user.permissions)
229
+ add_set, remove_set = set(parsed_add), set(parsed_remove)
230
+
231
+ # Build the body to send + predict the resulting effective set (for the confirm preview).
232
+ if permission_set is None:
233
+ # Incremental: merge the override deltas into the member's CURRENT overrides (keep the set).
234
+ new_set = user.permission_set
235
+ merged_add = (set(user.permission_added or []) | add_set) - remove_set
236
+ merged_remove = (set(user.permission_removed or []) | remove_set) - add_set
237
+ new_added, new_removed = sorted(merged_add), sorted(merged_remove)
238
+ # Applying the deltas to the current effective set is exact (base is unchanged).
239
+ predicted_after = (before | add_set) - remove_set
240
+ else:
241
+ # Assign a role: the set is the base; the flags are its fresh overrides.
242
+ new_set, new_added, new_removed = permission_set, sorted(add_set), sorted(remove_set)
243
+ if permission_set in permissions.PRESETS:
244
+ predicted_after = (set(permissions.PRESETS[permission_set]) | add_set) - remove_set
245
+ else:
246
+ predicted_after = None # a custom set we can't expand locally → confirm generically
247
+
248
+ if predicted_after is not None:
249
+ p_added = predicted_after - before
250
+ p_removed = before - predicted_after
251
+ if not p_added and not p_removed: # no-op: don't call the server, don't prompt
252
+ if state.json:
253
+ output.emit_json({"id": user.id, "email": user.email, "permission_set": new_set,
254
+ "permissions": sorted(before), "added": [], "removed": []})
255
+ else:
256
+ output.user_no_change()
257
+ return
258
+ proceed = (not _write.should_prompt(state, yes)) or output.confirm_user_update(
259
+ user.email, len(p_added), len(p_removed))
260
+ else:
261
+ proceed = _write.confirm_action(
262
+ state, "change permissions for", user.email,
263
+ consequence="this reassigns their role; the user keeps access", assume_yes=yes)
264
+
265
+ if not proceed:
266
+ if state.json:
267
+ output.emit_json({"cancelled": True})
268
+ else:
269
+ output.print_cancelled("permissions unchanged")
270
+ return
271
+
272
+ result = api.update_user(
273
+ cctx, user.id, permission_set=new_set, permission_added=new_added, permission_removed=new_removed)
274
+ after = set(result.permissions)
275
+ added, removed = sorted(after - before), sorted(before - after)
276
+ _write.record_action("user_updated", resource="user", success=True, permission_count=len(after))
277
+ if state.json:
278
+ output.emit_json({"id": result.id, "email": result.email, "permission_set": result.permission_set,
279
+ "permissions": sorted(after), "added": added, "removed": removed})
280
+ return
281
+ if not added and not removed: # custom-set assign that turned out to be a no-op
282
+ output.user_no_change()
283
+ return
284
+ output.render_user_updated(result, added=added, removed=removed, union=sorted(before | after))
285
+
286
+
287
+ def users_disable(
288
+ ctx: typer.Context,
289
+ email: str = typer.Argument(..., metavar="EMAIL", help="User email (or id) to disable."),
290
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirmation prompt. The prompt only appears on an interactive terminal: under --json, or with stdin redirected, this command proceeds without asking."),
291
+ ) -> None:
292
+ """Disable a member by email — they can no longer sign in (reversible with `users enable`).
293
+
294
+ Confirms first (amber, default no; `--yes` skips). Refuses a protected member or your own
295
+ account. Already-disabled is a calm no-op. Needs `users:delete`. With `--json`:
296
+ `{id, email, status: "disabled"}` (or `{cancelled: true}` on a declined prompt).
297
+
298
+ Example:
299
+
300
+ * `fp users disable dev@example.com`
301
+ """
302
+ state: AppState = ctx.obj
303
+ cctx = require_auth(state)
304
+ user = _resolve_user_or_exit(state, api.list_users(cctx), email)
305
+ if user.is_protected:
306
+ raise ForbiddenError(f'"{user.email}" is protected and can\'t be disabled')
307
+ if state.config.email and user.email == state.config.email:
308
+ raise ForbiddenError("you can't disable your own account")
309
+ if user.disabled_at: # already disabled — a no-op, not an error
310
+ if state.json:
311
+ output.emit_json({"id": user.id, "email": user.email, "status": "disabled"})
312
+ else:
313
+ output.user_already_disabled(user.email)
314
+ return
315
+ if not _write.confirm_action(
316
+ state, "disable user", user.email,
317
+ consequence="they can no longer sign in — you can re-enable them later", assume_yes=yes,
318
+ ):
319
+ if state.json:
320
+ output.emit_json({"cancelled": True})
321
+ else:
322
+ output.print_cancelled("nothing changed")
323
+ return
324
+ api.disable_user(cctx, user.id)
325
+ _write.record_action("user_disabled", resource="user", success=True, destructive=True)
326
+ if state.json:
327
+ output.emit_json({"id": user.id, "email": user.email, "status": "disabled"})
328
+ else:
329
+ output.user_disabled(user.email)
330
+
331
+
332
+ def users_enable(
333
+ ctx: typer.Context,
334
+ email: str = typer.Argument(..., metavar="EMAIL", help="User email (or id) to re-enable."),
335
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirmation prompt. The prompt only appears on an interactive terminal: under --json, or with stdin redirected, this command proceeds without asking."),
336
+ ) -> None:
337
+ """Re-enable a disabled member by email — they can sign in again.
338
+
339
+ Confirms first (a calm re-activation prompt, default no; `--yes` skips). Already-active is a
340
+ calm no-op. Needs `users:delete`. With `--json`: `{id, email, status: "active"}` (or
341
+ `{cancelled: true}` on a declined prompt).
342
+
343
+ Example:
344
+
345
+ * `fp users enable dev@example.com`
346
+ """
347
+ state: AppState = ctx.obj
348
+ cctx = require_auth(state)
349
+ user = _resolve_user_or_exit(state, api.list_users(cctx), email)
350
+ if not user.disabled_at: # already active — a no-op, not an error
351
+ if state.json:
352
+ output.emit_json({"id": user.id, "email": user.email, "status": "active"})
353
+ else:
354
+ output.user_already_active(user.email)
355
+ return
356
+ if not _write.confirm_action(
357
+ state, "re-enable user", user.email,
358
+ consequence="they'll be able to sign in again", assume_yes=yes,
359
+ glyph="↑", color=theme.ACCENT,
360
+ ):
361
+ if state.json:
362
+ output.emit_json({"cancelled": True})
363
+ else:
364
+ output.print_cancelled("nothing changed")
365
+ return
366
+ result = api.enable_user(cctx, user.id)
367
+ _write.record_action("user_enabled", resource="user", success=True)
368
+ if state.json:
369
+ output.emit_json({"id": result.id, "email": result.email, "status": "active"})
370
+ else:
371
+ output.user_enabled(user.email)
372
+
373
+
374
+ _USERS_GROUP_HELP = """Manage org members — list, inspect, invite, and adjust their permissions.
375
+
376
+ Members are referenced by **email** (unique in the org). A member's grants are a permission
377
+ set (role) plus optional per-member `--add` / `--remove` overrides.
378
+
379
+ **Subcommands:** `list` · `show` · `create` · `update` · `disable` · `enable`
380
+
381
+ **Examples:**
382
+
383
+ * `fp users list --active-only` — current members, active first
384
+ * `fp users show dev@example.com` — one member's identity + full grants
385
+ * `fp users create dev@example.com --permission-set standard` — invite with a role
386
+ * `fp users update dev@example.com --add keys:create --remove alerts:read` — tweak grants
387
+ * `fp users disable dev@example.com` / `fp users enable dev@example.com` — revoke / restore sign-in
388
+ """
389
+
390
+
391
+ def register(app: typer.Typer) -> None:
392
+ users_app = typer.Typer(
393
+ no_args_is_help=True,
394
+ rich_markup_mode="markdown",
395
+ context_settings={"help_option_names": ["-h", "--help"]},
396
+ help=_USERS_GROUP_HELP,
397
+ )
398
+ users_app.command("list", epilog=GLOBALS_EPILOG)(users_list)
399
+ users_app.command("show", epilog=GLOBALS_EPILOG)(users_show)
400
+ users_app.command("create", epilog=GLOBALS_EPILOG)(users_create)
401
+ users_app.command("update", epilog=GLOBALS_EPILOG)(users_update)
402
+ users_app.command("disable", epilog=GLOBALS_EPILOG)(users_disable)
403
+ users_app.command("enable", epilog=GLOBALS_EPILOG)(users_enable)
404
+ app.add_typer(users_app, name="users")