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,345 @@
1
+ """The logic behind `fp policies` and `fp fleet`, with no HTTP in it.
2
+
3
+ Everything here is pure so it can be tested without a server, because the two
4
+ things most likely to lose someone's work are decided here rather than in a
5
+ handler: what a deploy's resulting policy set is, and whether somebody else
6
+ wrote while we were deciding.
7
+
8
+ ## Why a diff at all
9
+
10
+ `PUT /enforcement/deployments/{id}` is a FULL REPLACE. Send `{"policies": [a]}`
11
+ to a machine running `[a, b, c]` and it now runs `[a]` — permanently, with a
12
+ 200 and no warning. The dashboard never exposes that as a form for exactly this
13
+ reason (`app/(dashboard)/[org]/enforcement/page.tsx`: "a form that asks you to
14
+ re-pick a machine and re-tick its policies silently drops whatever you forget
15
+ to tick"). It edits the machine's own current set instead.
16
+
17
+ So `--add`/`--remove` are the CLI's equivalent: read the current set, apply the
18
+ delta, write the whole thing back. `--set` remains for the declarative case,
19
+ and is the only way to express "exactly these, drop the rest".
20
+
21
+ ## Why the race check
22
+
23
+ There is no optimistic locking on that endpoint. The dashboard detects a
24
+ collision AFTER the fact by checking the returned generation is exactly
25
+ `base + 1` (`lib/enforcementFleet.ts`, `staleness()`). The same check here is
26
+ what stops two operators silently overwriting each other — the CLI refuses and
27
+ re-reads rather than reporting a success that erased somebody.
28
+ """
29
+ from __future__ import annotations
30
+
31
+ import re
32
+ import sys
33
+ from dataclasses import dataclass
34
+ from typing import Dict, Iterable, List, Optional, Sequence, Tuple
35
+
36
+ from .errors import ApiError
37
+ from .models import PolicyRef, PolicyVersion
38
+
39
+ VALID_EFFECTS = ("enforce", "observe")
40
+
41
+ #: `id`, `id@3`, `id:observe`, `id@3:observe`. The id charset mirrors the
42
+ #: server's `safe_identifier`, so a ref this accepts is one the server will too
43
+ #: — a rejection should come from the policy not existing, not from parsing.
44
+ _REF = re.compile(r"^(?P<id>[A-Za-z0-9._-]{1,128})(?:@(?P<version>\d+))?(?:[:](?P<effect>[a-z]+))?$")
45
+
46
+
47
+ class RefError(ValueError):
48
+ """A malformed `--add` / `--remove` / `--set` token, with the reason."""
49
+
50
+
51
+ class RefUsageError(RefError):
52
+ """A RefError the caller can fix by retyping the command.
53
+
54
+ Split out so the command layer can exit 2 (usage) rather than 1 (API error)
55
+ for these, which is what the documented exit-code table promises and what
56
+ `--since` and `--expect` in these same commands already do. A malformed
57
+ token, two flags that contradict each other, or a path that is not readable
58
+ text are all "you typed it wrong" — not "the server said no".
59
+
60
+ Subclasses ``RefError`` so every existing caller and test that catches the
61
+ base class keeps working unchanged.
62
+ """
63
+
64
+
65
+ def parse_ref(token: str) -> Tuple[str, Optional[int], Optional[str]]:
66
+ """``"id@2:observe"`` → ``("id", 2, "observe")``; omitted parts are None.
67
+
68
+ Version and effect are resolved later — ``None`` means "whatever is current",
69
+ which is not the same as a default, because for an existing deployment the
70
+ current value is the deployed one rather than the newest one.
71
+ """
72
+ token = token.strip()
73
+ if not token:
74
+ raise RefUsageError("empty policy reference")
75
+ m = _REF.match(token)
76
+ if not m:
77
+ raise RefUsageError(
78
+ f"{token!r} is not a policy reference — expected id, id@version, "
79
+ "id:effect or id@version:effect"
80
+ )
81
+ effect = m.group("effect")
82
+ if effect is not None and effect not in VALID_EFFECTS:
83
+ raise RefUsageError(
84
+ f"{token!r} has effect {effect!r}; expected one of {', '.join(VALID_EFFECTS)}"
85
+ )
86
+ version = m.group("version")
87
+ return m.group("id"), (int(version) if version is not None else None), effect
88
+
89
+
90
+ @dataclass
91
+ class DeployPlan:
92
+ """The resulting set, and how it differs from what the machine runs now.
93
+
94
+ `result` is what will be PUT — the whole set, because that is what the
95
+ endpoint takes. The three lists exist to be shown to a human before it is.
96
+ """
97
+
98
+ machine_id: str
99
+ base: Optional[int]
100
+ result: List[PolicyRef]
101
+ added: List[PolicyRef]
102
+ removed: List[PolicyRef]
103
+ changed: List[Tuple[PolicyRef, PolicyRef]]
104
+ unchanged: List[PolicyRef]
105
+
106
+ @property
107
+ def is_noop(self) -> bool:
108
+ return not (self.added or self.removed or self.changed)
109
+
110
+ def to_dict(self) -> Dict[str, object]:
111
+ return {
112
+ "machineId": self.machine_id,
113
+ "base": self.base,
114
+ "result": [p.to_dict() for p in self.result],
115
+ "added": [p.to_dict() for p in self.added],
116
+ "removed": [p.to_dict() for p in self.removed],
117
+ "changed": [{"from": a.to_dict(), "to": b.to_dict()} for a, b in self.changed],
118
+ "unchanged": [p.to_dict() for p in self.unchanged],
119
+ "noop": self.is_noop,
120
+ }
121
+
122
+
123
+ def latest_versions(policies: Iterable[PolicyVersion]) -> Dict[str, int]:
124
+ """`{policy_id: newest published version}`, ignoring archived policies."""
125
+ out: Dict[str, int] = {}
126
+ for p in policies:
127
+ if p.archived:
128
+ continue
129
+ if p.version > out.get(p.id, 0):
130
+ out[p.id] = p.version
131
+ return out
132
+
133
+
134
+ def disabled_ids(policies: Iterable[PolicyVersion]) -> set:
135
+ """Policies the server will refuse to deploy.
136
+
137
+ The server rejects these anyway, but only after the CLI has drawn a plan and
138
+ asked the operator to confirm it — so the last thing on screen is a change
139
+ that cannot happen, under a prompt that implied it could. Everything else
140
+ the plan depends on (the machine exists, the policy exists) is already
141
+ checked before the plan is built; this was the one gap.
142
+ """
143
+ return {p.id for p in policies if p.disabled and not p.archived}
144
+
145
+
146
+ def resolve_ref(
147
+ token: str,
148
+ *,
149
+ latest: Dict[str, int],
150
+ current: Dict[str, PolicyRef],
151
+ disabled: Optional[set] = None,
152
+ ) -> PolicyRef:
153
+ """Turn one `--add`/`--set` token into a concrete `PolicyRef`.
154
+
155
+ Version: explicit wins; else the version already deployed (so `--add` on a
156
+ policy the machine already runs is a no-op rather than a silent upgrade);
157
+ else the newest published.
158
+
159
+ Effect: explicit wins; else the deployed effect; else `enforce`, matching
160
+ the server's own default for an omitted effect.
161
+ """
162
+ pid, version, effect = parse_ref(token)
163
+ if disabled and pid in disabled and pid not in current:
164
+ raise RefError(
165
+ f"{pid!r} is disabled — `fp policies enable {pid}` first, or the machine "
166
+ "would be sent a deployment the server refuses"
167
+ )
168
+ existing = current.get(pid)
169
+ if version is None:
170
+ version = existing.version if existing else latest.get(pid)
171
+ if version is None:
172
+ raise RefError(
173
+ f"no published policy named {pid!r} — run `fp policies list` to see what exists"
174
+ )
175
+ if effect is None:
176
+ effect = existing.effect if existing else "enforce"
177
+ return PolicyRef(id=pid, version=version, effect=effect)
178
+
179
+
180
+ def plan_deploy(
181
+ machine_id: str,
182
+ *,
183
+ current: Optional[Sequence[PolicyRef]],
184
+ base: Optional[int],
185
+ add: Sequence[str] = (),
186
+ remove: Sequence[str] = (),
187
+ replace: Optional[Sequence[str]] = None,
188
+ latest: Optional[Dict[str, int]] = None,
189
+ disabled: Optional[set] = None,
190
+ ) -> DeployPlan:
191
+ """Compute the full resulting set, plus the diff to show before writing.
192
+
193
+ `replace` (`--set`) is exclusive with `add`/`remove`: mixing "these exactly"
194
+ with "these as well" has no single obvious reading, and guessing one would
195
+ be guessing about somebody's fleet.
196
+ """
197
+ latest = latest or {}
198
+ current_list = list(current or [])
199
+ current_map = {p.id: p for p in current_list}
200
+
201
+ if replace is not None:
202
+ if add or remove:
203
+ raise RefUsageError(
204
+ "--set replaces the whole set; it cannot be combined with --add/--remove"
205
+ )
206
+ result_map = {}
207
+ for token in replace:
208
+ ref = resolve_ref(token, latest=latest, current=current_map, disabled=disabled)
209
+ result_map[ref.id] = ref
210
+ else:
211
+ result_map = dict(current_map)
212
+ for token in remove:
213
+ pid, _, _ = parse_ref(token)
214
+ if pid not in result_map:
215
+ raise RefError(
216
+ f"{pid!r} is not deployed to {machine_id} — nothing to remove"
217
+ )
218
+ del result_map[pid]
219
+ for token in add:
220
+ ref = resolve_ref(token, latest=latest, current=current_map, disabled=disabled)
221
+ result_map[ref.id] = ref
222
+
223
+ result = sorted(result_map.values(), key=lambda p: p.id)
224
+ added, removed, changed, unchanged = [], [], [], []
225
+ for pid, ref in sorted(result_map.items()):
226
+ was = current_map.get(pid)
227
+ if was is None:
228
+ added.append(ref)
229
+ elif (was.version, was.effect) != (ref.version, ref.effect):
230
+ changed.append((was, ref))
231
+ else:
232
+ unchanged.append(ref)
233
+ for pid, was in sorted(current_map.items()):
234
+ if pid not in result_map:
235
+ removed.append(was)
236
+
237
+ return DeployPlan(
238
+ machine_id=machine_id,
239
+ base=base,
240
+ result=result,
241
+ added=added,
242
+ removed=removed,
243
+ changed=changed,
244
+ unchanged=unchanged,
245
+ )
246
+
247
+
248
+ def check_race(base: Optional[int], returned: int) -> None:
249
+ """Raise when a deploy landed on top of somebody else's.
250
+
251
+ `base` is the generation read before the write. A clean write is exactly
252
+ `base + 1`; anything else means another writer got in between, and their
253
+ change is already gone — a full replace does not merge. Reporting success
254
+ here is how the CLI would become the easiest way to silently overwrite a
255
+ colleague.
256
+ """
257
+ if base is None:
258
+ return
259
+ if returned != base + 1:
260
+ raise ApiError(
261
+ f"deployment {returned} landed where {base + 1} was expected — someone "
262
+ "else deployed to this machine while this command was deciding, and a "
263
+ "deploy REPLACES the whole set rather than merging.",
264
+ hint="re-run `fp fleet show <machine>` to see the current set, then deploy again",
265
+ )
266
+
267
+
268
+ def read_source(
269
+ value: Optional[str],
270
+ *,
271
+ stdin=None,
272
+ isatty: Optional[bool] = None,
273
+ prompt=None,
274
+ ) -> str:
275
+ """Resolve policy source from a path, `@path`, `-`, a pipe, or a paste.
276
+
277
+ The five shapes exist because the thing being supplied is a file that people
278
+ have in five different places: on disk, in a pipeline, in a heredoc, or on
279
+ the clipboard. Refusing the clipboard would mean "save it to a file first"
280
+ for the most common one-off case.
281
+
282
+ A bare `-` and a piped stdin are the same read; the difference is only
283
+ whether the user said so. On a TTY with nothing given we prompt, because
284
+ silently blocking on stdin is indistinguishable from a hang.
285
+ """
286
+ stream = sys.stdin if stdin is None else stdin
287
+ tty = stream.isatty() if isatty is None else isatty
288
+
289
+ if value == "-":
290
+ return _checked(_read_stream(stream))
291
+ if value:
292
+ path = value[1:] if value.startswith("@") else value
293
+ try:
294
+ with open(path, "r", encoding="utf-8") as fh:
295
+ return _checked(fh.read())
296
+ except FileNotFoundError:
297
+ raise RefUsageError(f"no such file: {path}")
298
+ except UnicodeDecodeError:
299
+ # NOT an OSError, so the handler below never saw it and the
300
+ # decode error escaped as a raw traceback. `_checked` cannot
301
+ # catch this either: it inspects text, and there is no text yet.
302
+ raise RefUsageError(_NOT_TEXT.format(what=path))
303
+ except OSError as exc:
304
+ raise RefUsageError(f"cannot read {path}: {exc}")
305
+ if not tty:
306
+ return _checked(_read_stream(stream))
307
+ if prompt is not None:
308
+ prompt()
309
+ return _checked(_read_stream(stream))
310
+
311
+
312
+ #: Said the same way whether the bytes arrived by path or down a pipe.
313
+ _NOT_TEXT = (
314
+ "{what} is not UTF-8 text — this looks like a binary file rather than a policy"
315
+ )
316
+
317
+
318
+ def _read_stream(stream) -> str:
319
+ """Read stdin, turning undecodable bytes into a sentence.
320
+
321
+ ``sys.stdin`` decodes as it reads, so piping a binary file raises
322
+ ``UnicodeDecodeError`` here rather than returning bytes ``_checked`` could
323
+ inspect — which is how `cat rule.png | fp policies publish x` printed a
324
+ traceback instead of the NUL-byte message written for exactly that mistake.
325
+ """
326
+ try:
327
+ return stream.read()
328
+ except UnicodeDecodeError:
329
+ raise RefUsageError(_NOT_TEXT.format(what="the input"))
330
+
331
+
332
+ def _checked(text: str) -> str:
333
+ """Reject bytes the store cannot hold, with a message that says what happened.
334
+
335
+ A NUL byte in policy source reaches Postgres and comes back as a bare
336
+ "database error" — a raw internal failure shown to somebody who most likely
337
+ pointed the command at a binary file by mistake. The server ought to refuse
338
+ it; until it does, refusing here turns an unexplained 500 into a sentence.
339
+ """
340
+ if "\x00" in text:
341
+ raise RefUsageError(
342
+ "policy source contains a NUL byte — this looks like a binary file "
343
+ "rather than a policy"
344
+ )
345
+ return text
@@ -0,0 +1,98 @@
1
+ """CLI exception hierarchy.
2
+
3
+ Errors subclass ``ClickException``, so Typer/Click catch them automatically, print the
4
+ message to stderr, and exit with ``exit_code``. The base class comes from
5
+ :mod:`._click_compat`, **not** from a plain ``import click``: Typer 0.26+ vendors its
6
+ own Click and catches only that one, so subclassing pip Click's ``ClickException``
7
+ makes every typed error escape uncaught as exit 1 with an empty stderr. See
8
+ ``_click_compat`` for the full failure mode.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Optional
14
+
15
+ from . import _click_compat as click # the Click Typer is running; see _click_compat
16
+
17
+
18
+ class FpCliError(click.ClickException):
19
+ """Base class for all CLI errors.
20
+
21
+ Carries an optional ``hint`` (a short "what to do next" line). The single error
22
+ chokepoint in ``app.py`` renders it — under ``--json`` as a ``"hint"`` field, else
23
+ as the dim second line of the red error box — so commands just ``raise`` a typed
24
+ error with a hint instead of hand-rolling a JSON-vs-box branch per call site.
25
+ """
26
+
27
+ exit_code = 1
28
+
29
+ def __init__(self, message: str, *, hint: Optional[str] = None) -> None:
30
+ super().__init__(message)
31
+ self.hint = hint
32
+
33
+
34
+ class KeyModeUnsupportedError(FpCliError):
35
+ """This command cannot work with an API key (`--api-key` / ``FP_API_KEY``).
36
+
37
+ Reuses **exit 2 (usage error)**, deliberately: the exit-code table is a scripted
38
+ contract restated in ``app.py``'s help, ``cli/skill/SKILL.md`` and
39
+ ``enterprise-docs/cli.md``, so a seventh code would have to be added to all three
40
+ at once — and "you asked for something this credential can never do" IS a usage
41
+ error. Every raise site fires BEFORE any HTTP call, so an unsupported command
42
+ never half-runs.
43
+
44
+ Like every error here it subclasses through ``_click_compat``; bind it to pip
45
+ Click and Typer 0.26+ lets it escape as exit 1 with an empty stderr.
46
+ """
47
+
48
+ exit_code = 2
49
+
50
+
51
+ class NetworkError(FpCliError):
52
+ """The dashboard could not be reached."""
53
+
54
+ exit_code = 3
55
+
56
+
57
+ class AuthError(FpCliError):
58
+ """Not logged in, or the stored session has expired."""
59
+
60
+ exit_code = 4
61
+
62
+
63
+ class ForbiddenError(FpCliError):
64
+ """Authenticated, but the account lacks the required permission."""
65
+
66
+ exit_code = 5
67
+
68
+
69
+ class NotFoundError(FpCliError):
70
+ """The requested resource does not exist."""
71
+
72
+ exit_code = 6
73
+
74
+
75
+ class ApiError(FpCliError):
76
+ """The dashboard returned an unexpected error status."""
77
+
78
+ exit_code = 1
79
+
80
+ def __init__(
81
+ self,
82
+ message: str,
83
+ *,
84
+ status: Optional[int] = None,
85
+ request_id: Optional[str] = None,
86
+ hint: Optional[str] = None,
87
+ ) -> None:
88
+ super().__init__(message, hint=hint)
89
+ self.status = status
90
+ self.request_id = request_id
91
+
92
+ def format_message(self) -> str:
93
+ parts = [self.message]
94
+ if self.status is not None:
95
+ parts.append(f"(HTTP {self.status})")
96
+ if self.request_id:
97
+ parts.append(f"[request-id: {self.request_id}]")
98
+ return " ".join(parts)