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,416 @@
1
+ """The fleet: fleet list / show / deploy / diff / history / rollback / rename.
2
+
3
+ What each machine is TOLD to enforce. Authoring the policies is `fp policies`;
4
+ what they actually did is `fp guardrails`.
5
+
6
+ ## The one thing to understand before reading `deploy`
7
+
8
+ `PUT /enforcement/deployments/{id}` REPLACES a machine's whole policy set. There
9
+ is no merge and no server-side lock. The dashboard deliberately has no deploy
10
+ form for this reason — it edits the machine's own current set instead, because a
11
+ form that asks you to re-tick policies silently drops whatever you forget.
12
+
13
+ So `deploy` here defaults to a read-modify-write: it reads what the machine runs,
14
+ applies `--add`/`--remove`, shows the resulting FULL set, and writes that.
15
+ `--set` is the escape hatch for the declarative case and is the only way to say
16
+ "exactly these, drop the rest".
17
+ """
18
+ from __future__ import annotations
19
+
20
+ from typing import List, Optional
21
+
22
+ import typer
23
+
24
+ from .. import client as api
25
+ from .. import output
26
+ from .._context import GLOBALS_EPILOG, AppState, deny_in_key_mode, require_auth
27
+ from .. import _click_compat as click # the Click Typer is running; see _click_compat
28
+ from ..enforcement import (
29
+ RefError,
30
+ RefUsageError,
31
+ check_race,
32
+ disabled_ids,
33
+ latest_versions,
34
+ plan_deploy,
35
+ )
36
+ from ..errors import ApiError, NotFoundError
37
+ from . import _write
38
+
39
+ _KEY_MODE_REASON = (
40
+ "the fleet is an operator surface and is not exposed on the versioned API that "
41
+ "an API key authenticates against"
42
+ )
43
+
44
+
45
+ def _require_machine(cctx, machine_id: str) -> None:
46
+ """Refuse an id no machine has ever reported under.
47
+
48
+ Without this, a typo is indistinguishable from a real machine that simply
49
+ has nothing deployed: both render an empty set and exit 0. The id is also
50
+ interpolated into a URL path further down, so an id containing `/` would
51
+ address a different route entirely — the server rejects those, but a clear
52
+ "no machine" beats someone else's 404.
53
+ """
54
+ if machine_id not in {m.machine_id for m in api.list_machines(cctx)}:
55
+ raise NotFoundError(f"no machine {machine_id!r} has checked in")
56
+
57
+
58
+ def fleet_list(ctx: typer.Context) -> None:
59
+ """List machines and how many policies each is told to run.
60
+
61
+ Shows `machine · label · pol · intended · applied · seen · events · state`.
62
+ `intended` is the generation deployed, `applied` is the one the machine last
63
+ collected, and `seen` is when it last reported anything — a machine can be
64
+ in sync and dead, or alive and behind, and those are different problems.
65
+
66
+ A machine appears from its very first check-in, including the poll that
67
+ finds nothing deployed — that is exactly the machine you are usually looking
68
+ for. Needs `policies:read`. With `--json`: `{machines, deployments}`, where
69
+ each machine carries raw timestamps plus the computed `drifted`.
70
+
71
+ Example:
72
+
73
+ * `fp fleet list`
74
+ """
75
+ state: AppState = ctx.obj
76
+ deny_in_key_mode(state, "fleet", _KEY_MODE_REASON)
77
+ cctx = require_auth(state)
78
+ machines = api.list_machines(cctx)
79
+ if output.is_json():
80
+ # Only `--json` emits the deployments, and only `--json` pays for them.
81
+ # The table is built entirely from the machine records; fetching them
82
+ # for a human render was a second request whose result was discarded.
83
+ output.emit_json({
84
+ "machines": [m.to_dict() for m in machines],
85
+ "deployments": [d.to_dict() for d in api.list_deployments(cctx)],
86
+ })
87
+ return
88
+ output.render_fleet(machines)
89
+
90
+
91
+ def fleet_show(
92
+ ctx: typer.Context,
93
+ machine_id: str = typer.Argument(..., help="Machine id."),
94
+ ) -> None:
95
+ """Show exactly what one machine is told to enforce.
96
+
97
+ The set shown is the set that exists — read this before a `--set`, because
98
+ that flag replaces all of it.
99
+
100
+ Also reports whether the machine has actually COLLECTED that deployment. A
101
+ machine can be told to run a policy and not yet have it; the policy list
102
+ alone cannot tell you which, and that is usually the question.
103
+
104
+ Needs `policies:read`. With `--json`: `{machine, deployment}` — the machine
105
+ record (including `appliedDeployment`, `drifted`, `lastSeen` and both label
106
+ fields, with raw timestamps) and the deployment, or `deployment: null` when
107
+ nothing is deployed.
108
+
109
+ Example:
110
+
111
+ * `fp fleet show ci-runner-01`
112
+ """
113
+ state: AppState = ctx.obj
114
+ deny_in_key_mode(state, "fleet show", _KEY_MODE_REASON)
115
+ cctx = require_auth(state)
116
+ # Two reads on purpose. The deployment says what the machine was TOLD to
117
+ # run; only the machine record says whether it has collected it. Showing the
118
+ # first without the second is how this view came to imply a policy was in
119
+ # force when the host had never picked it up.
120
+ machines = api.list_machines(cctx)
121
+ machine = next((m for m in machines if m.machine_id == machine_id), None)
122
+ if machine is None:
123
+ raise NotFoundError(f"no machine {machine_id!r} has checked in")
124
+ dep = api.get_deployment(cctx, machine_id)
125
+
126
+ if output.is_json():
127
+ output.emit_json({
128
+ "machine": machine.to_dict(),
129
+ "deployment": dep.to_dict() if dep else None,
130
+ })
131
+ return
132
+ output.render_machine_policies(machine_id, dep, machine)
133
+
134
+
135
+ def fleet_deploy(
136
+ ctx: typer.Context,
137
+ machine_id: str = typer.Argument(..., help="Machine id."),
138
+ add: Optional[List[str]] = typer.Option(
139
+ None, "--add",
140
+ help="Add or update a policy: `id`, `id@version`, `id:effect` or `id@version:effect`.",
141
+ ),
142
+ remove: Optional[List[str]] = typer.Option(None, "--remove", help="Remove a policy by id."),
143
+ replace: Optional[List[str]] = typer.Option(
144
+ None, "--set",
145
+ help="REPLACE the whole set with exactly these. Cannot be combined with --add/--remove.",
146
+ ),
147
+ create: bool = typer.Option(
148
+ False, "--create",
149
+ help="Allow deploying to a machine id that has not checked in yet (pre-staging).",
150
+ ),
151
+ 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."),
152
+ ) -> None:
153
+ """Change what a machine enforces, showing the full resulting set first.
154
+
155
+ `--add`/`--remove` read the machine's current set and apply a delta, so
156
+ nothing you did not mention is disturbed. A bare `--add` on a policy the
157
+ machine already runs keeps its pinned version rather than silently
158
+ upgrading; pass `id@version` to move it.
159
+
160
+ `--set` replaces everything — the only way to drop policies you do not name.
161
+
162
+ **Concurrency.** The write is a full replace with no server-side lock, so the
163
+ CLI records the generation it read and refuses if the result is not exactly
164
+ one higher: that means somebody else deployed in between and a replace does
165
+ not merge. Needs `policies:write`. With `--json`: the plan plus the resulting
166
+ deployment.
167
+
168
+ Examples:
169
+
170
+ * `fp fleet deploy ci-runner-01 --add no-force-push`
171
+ * `fp fleet deploy ci-runner-01 --add prod-guard@1:observe --remove old-rule`
172
+ * `fp fleet deploy ci-runner-01 --set no-force-push --set no-secret-echo`
173
+ """
174
+ state: AppState = ctx.obj
175
+ deny_in_key_mode(state, "fleet deploy", _KEY_MODE_REASON)
176
+ cctx = require_auth(state)
177
+
178
+ if not add and not remove and replace is None:
179
+ # Exit 2 for the same reason `--set` with `--add` is: no flag
180
+ # combination was given that this command can act on. Both are the
181
+ # caller's command line, not the server's answer.
182
+ raise click.UsageError(
183
+ "nothing to do — pass --add, --remove, or --set. "
184
+ "`fp fleet show <machine>` prints the current set."
185
+ )
186
+
187
+ # The server accepts a deploy to ANY id — that is how a machine can be
188
+ # pre-staged before it ever polls. It also means a typo does not fail: it
189
+ # mints a machine nobody owns, carrying policies nobody will collect, and
190
+ # the only sign is an extra row in `fleet list`. The dashboard cannot hit
191
+ # this because it deploys to a machine picked from a list; a CLI takes free
192
+ # text, so the check has to be here.
193
+ if not create:
194
+ try:
195
+ _require_machine(cctx, machine_id)
196
+ except NotFoundError:
197
+ raise NotFoundError(
198
+ f"no machine {machine_id!r} has checked in — deploying would create "
199
+ "it as a new machine id. Pass --create if that is deliberate."
200
+ )
201
+
202
+ current = api.get_deployment(cctx, machine_id)
203
+ published = api.list_policies(cctx)
204
+ latest = latest_versions(published)
205
+ try:
206
+ plan = plan_deploy(
207
+ machine_id,
208
+ current=current.policies if current else None,
209
+ base=current.deployment if current else None,
210
+ add=add or (),
211
+ remove=remove or (),
212
+ replace=replace,
213
+ latest=latest,
214
+ disabled=disabled_ids(published),
215
+ )
216
+ except RefUsageError as exc:
217
+ # Exit 2, like every other bad flag value in this CLI (`--since`,
218
+ # `--expect`, `--file`). These are retype-the-command mistakes; exit 1
219
+ # says "the server refused", which is a different thing to script on.
220
+ raise click.UsageError(str(exc))
221
+ except RefError as exc:
222
+ raise ApiError(str(exc))
223
+
224
+ # A no-op exits 0 WITHOUT writing, which is desired-state semantics: a
225
+ # retrying harness re-running the same deploy should succeed, not error.
226
+ # Two consequences worth knowing rather than discovering:
227
+ # * `applied: false` in --json is the only way to tell "I changed it" from
228
+ # "it already matched" — the exit code is 0 either way, on purpose.
229
+ # * the short-circuit happens BEFORE the write, so a reader without
230
+ # `policies:write` also gets 0 here. They have not gained anything (the
231
+ # state already held and nothing was written), but the exit code alone
232
+ # is not proof of write access.
233
+ if plan.is_noop:
234
+ if output.is_json():
235
+ output.emit_json({"plan": plan.to_dict(), "deployment": None, "applied": False})
236
+ return
237
+ output.deployment_unchanged(machine_id)
238
+ return
239
+
240
+ if not output.is_json():
241
+ output.render_deploy_plan(plan)
242
+ dropped = len(plan.removed)
243
+ if not _write.confirm_destructive(
244
+ state, "replace the policy set on", machine_id,
245
+ consequence=(f"this REPLACES the whole set with the {len(plan.result)} shown above"
246
+ + (f"; {dropped} would be removed" if dropped else "")),
247
+ assume_yes=yes,
248
+ ):
249
+ if output.is_json():
250
+ output.emit_json({"plan": plan.to_dict(), "cancelled": True, "applied": False})
251
+ else:
252
+ output.print_cancelled()
253
+ return
254
+
255
+ result = api.deploy_policies(cctx, machine_id, plan.result)
256
+ check_race(plan.base, result.deployment)
257
+
258
+ if output.is_json():
259
+ output.emit_json({
260
+ "plan": plan.to_dict(),
261
+ "deployment": result.to_dict(),
262
+ "applied": True,
263
+ })
264
+ return
265
+ output.deployment_applied(machine_id, result.deployment, len(result.policies))
266
+
267
+
268
+ def fleet_diff(
269
+ ctx: typer.Context,
270
+ machine_id: Optional[str] = typer.Argument(None, help="Machine id. Omit for the whole fleet."),
271
+ ) -> None:
272
+ """Show intent vs delivery — what a machine is told to run vs what it last pulled.
273
+
274
+ The gap is the interesting part: a machine that has not collected its latest
275
+ deployment is not enforcing what the dashboard says it is, and nothing else
276
+ surfaces that as a single number. Needs `policies:read`. With `--json`:
277
+ `{machines:[{machineId, intended, delivered, drifted}]}` — `drifted` is the
278
+ field the CLI computes, so a harness need not derive it.
279
+
280
+ Example:
281
+
282
+ * `fp fleet diff`
283
+ """
284
+ state: AppState = ctx.obj
285
+ deny_in_key_mode(state, "fleet diff", _KEY_MODE_REASON)
286
+ cctx = require_auth(state)
287
+ machines = api.list_machines(cctx)
288
+ # Every other machine-scoped command refuses an id nobody has reported
289
+ # under; this one filtered to nothing and exited 0 saying "no machines have
290
+ # checked in yet" — false, and indistinguishable from a healthy fleet. The
291
+ # list is already in hand, so the check costs no extra request.
292
+ if machine_id and machine_id not in {m.machine_id for m in machines}:
293
+ raise NotFoundError(f"no machine {machine_id!r} has checked in")
294
+ rows = []
295
+ for m in sorted(machines, key=lambda x: x.machine_id):
296
+ if machine_id and m.machine_id != machine_id:
297
+ continue
298
+ rows.append({
299
+ "machineId": m.machine_id,
300
+ "intended": m.deployment,
301
+ "delivered": m.applied_deployment,
302
+ "drifted": m.drifted,
303
+ })
304
+ if output.is_json():
305
+ output.emit_json({"machines": rows})
306
+ return
307
+ output.render_fleet_diff(rows)
308
+
309
+
310
+ def fleet_history(
311
+ ctx: typer.Context,
312
+ machine_id: str = typer.Argument(..., help="Machine id."),
313
+ ) -> None:
314
+ """List a machine's deployment generations, newest first.
315
+
316
+ A reissue — the server rewriting a deployment because a policy was disabled
317
+ — appears as an ordinary entry. Needs `policies:read`. With `--json`:
318
+ `{machineId, history:[{deployment, policies, updatedAt}]}`.
319
+
320
+ Example:
321
+
322
+ * `fp fleet history ci-runner-01`
323
+ """
324
+ state: AppState = ctx.obj
325
+ deny_in_key_mode(state, "fleet history", _KEY_MODE_REASON)
326
+ cctx = require_auth(state)
327
+ _require_machine(cctx, machine_id)
328
+ entries = api.deployment_history(cctx, machine_id)
329
+ if output.is_json():
330
+ output.emit_json({"machineId": machine_id, "history": entries})
331
+ return
332
+ output.render_deployment_history(machine_id, entries)
333
+
334
+
335
+ def fleet_rollback(
336
+ ctx: typer.Context,
337
+ machine_id: str = typer.Argument(..., help="Machine id."),
338
+ deployment: int = typer.Argument(..., help="The generation to reinstate."),
339
+ 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."),
340
+ ) -> None:
341
+ """Reinstate a past generation's policy set.
342
+
343
+ This mints a NEW generation carrying the old set rather than rewinding the
344
+ counter, so history stays append-only. A generation containing a policy that
345
+ has since been disabled or deleted cannot be reinstated; the server says so.
346
+
347
+ Needs `policies:write`. With `--json`: the resulting deployment, or
348
+ `{"cancelled": true}` if you decline.
349
+
350
+ Example:
351
+
352
+ * `fp fleet rollback ci-runner-01 3`
353
+ """
354
+ state: AppState = ctx.obj
355
+ deny_in_key_mode(state, "fleet rollback", _KEY_MODE_REASON)
356
+ cctx = require_auth(state)
357
+ _require_machine(cctx, machine_id)
358
+ current = api.get_deployment(cctx, machine_id)
359
+ if not _write.confirm_destructive(
360
+ state, f"reinstate deployment #{deployment} on", machine_id,
361
+ consequence="this REPLACES the machine's current set with the one from that generation",
362
+ assume_yes=yes,
363
+ ):
364
+ if output.is_json():
365
+ output.emit_json({"cancelled": True})
366
+ else:
367
+ output.print_cancelled()
368
+ return
369
+ result = api.rollback_deployment(cctx, machine_id, deployment)
370
+ check_race(current.deployment if current else None, result.deployment)
371
+ if output.is_json():
372
+ output.emit_json(result.to_dict())
373
+ return
374
+ output.deployment_rolled_back(machine_id, deployment, result.deployment)
375
+
376
+
377
+ def fleet_rename(
378
+ ctx: typer.Context,
379
+ machine_id: str = typer.Argument(..., help="Machine id."),
380
+ label: str = typer.Argument(..., help="Human-readable label."),
381
+ ) -> None:
382
+ """Give a machine a human label. The id itself never changes.
383
+
384
+ Needs `policies:write`. With `--json`: `{machineId, labelOverride}` — the
385
+ server stores the label as an override beside the machine's self-asserted
386
+ one rather than replacing it.
387
+
388
+ Example:
389
+
390
+ * `fp fleet rename ci-runner-01 "CI runner (eu-west)"`
391
+ """
392
+ state: AppState = ctx.obj
393
+ deny_in_key_mode(state, "fleet rename", _KEY_MODE_REASON)
394
+ cctx = require_auth(state)
395
+ res = api.rename_machine(cctx, machine_id, label)
396
+ if output.is_json():
397
+ output.emit_json(res)
398
+ return
399
+ output.machine_renamed(machine_id, label)
400
+
401
+
402
+ def register(app: typer.Typer) -> None:
403
+ fleet_app = typer.Typer(
404
+ no_args_is_help=True,
405
+ rich_markup_mode="markdown",
406
+ context_settings={"help_option_names": ["-h", "--help"]},
407
+ help="The fleet and what each machine enforces (list / show / deploy / diff / history / rollback / rename).",
408
+ )
409
+ fleet_app.command("list", epilog=GLOBALS_EPILOG)(fleet_list)
410
+ fleet_app.command("show", epilog=GLOBALS_EPILOG)(fleet_show)
411
+ fleet_app.command("deploy", epilog=GLOBALS_EPILOG)(fleet_deploy)
412
+ fleet_app.command("diff", epilog=GLOBALS_EPILOG)(fleet_diff)
413
+ fleet_app.command("history", epilog=GLOBALS_EPILOG)(fleet_history)
414
+ fleet_app.command("rollback", epilog=GLOBALS_EPILOG)(fleet_rollback)
415
+ fleet_app.command("rename", epilog=GLOBALS_EPILOG)(fleet_rename)
416
+ app.add_typer(fleet_app, name="fleet")
@@ -0,0 +1,148 @@
1
+ """Guardrails: what enforcement actually did — `summary` and `timeline`.
2
+
3
+ The counterpart to `fp fleet`. That command says what the control plane
4
+ INTENDED; this says what happened — whether the fleet is really covered, what
5
+ got blocked, and which policies earn their place.
6
+
7
+ The two halves come from different stores and that is worth knowing when a
8
+ number looks wrong: coverage is Postgres (the deployments), while the decision
9
+ counts are ClickHouse (hook telemetry the machines reported). A machine can be
10
+ deployed-to and silent, or reporting and undeployed, and only the first half
11
+ moves when you run `fp fleet deploy`.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from typing import Optional
16
+
17
+ import typer
18
+
19
+ from .. import client as api
20
+ from .. import output
21
+ from .._context import GLOBALS_EPILOG, AppState, deny_in_key_mode, require_auth
22
+ from ..errors import NotFoundError
23
+
24
+ _KEY_MODE_REASON = (
25
+ "guardrails reads an operator surface that is not exposed on the versioned "
26
+ "API that an API key authenticates against"
27
+ )
28
+
29
+
30
+ def _require_machine(cctx, machine_id: Optional[str]) -> None:
31
+ """Refuse a `--machine` id nobody has ever reported under.
32
+
33
+ Both views answer "what happened here", and both answer an unknown machine
34
+ with an empty window — which reads as "this machine was quiet", not as "you
35
+ typed the id wrong". `fp fleet` refuses the same mistake everywhere else;
36
+ the check costs one request, and only when the flag is actually used.
37
+ """
38
+ if machine_id is None:
39
+ return
40
+ if machine_id not in {m.machine_id for m in api.list_machines(cctx)}:
41
+ raise NotFoundError(f"no machine {machine_id!r} has checked in")
42
+
43
+
44
+ def _hours(since: str) -> int:
45
+ """`1h`/`6h`/`24h`/`7d` → hours. The CLI's `--since` vocabulary, one window only.
46
+
47
+ `15m` is deliberately NOT here. These two endpoints take a whole-hour window
48
+ and nothing finer, so `15m` mapped to 1 and produced a request byte-identical
49
+ to `--since 1h`: the window was silently widened 4x with no tell anywhere —
50
+ `timeline` renders no window label at all, and `summary`'s title comes from
51
+ the server's echoed hours, so it read `guardrails · 1h` while contradicting
52
+ the flag the operator typed. An operator watching a rollout would read
53
+ denials from before their change as caused by it. Refusing it is honest;
54
+ accepting it and rounding is not.
55
+ """
56
+ table = {"1h": 1, "6h": 6, "24h": 24, "7d": 168}
57
+ if since in table:
58
+ return table[since]
59
+ # A usage error, not a runtime one: exit 2 like every other bad flag value,
60
+ # rather than the exit 1 an uncaught ValueError would produce.
61
+ raise typer.BadParameter(
62
+ f"invalid --since value {since!r}; choose one of {', '.join(table)}"
63
+ )
64
+
65
+
66
+ def guardrails_summary(
67
+ ctx: typer.Context,
68
+ since: str = typer.Option("24h", "--since", help="Window: 1h, 6h, 24h, 7d."),
69
+ machine: Optional[str] = typer.Option(None, "--machine", help="Scope to one machine id."),
70
+ ) -> None:
71
+ """Coverage, blocks, and the per-policy table for a window.
72
+
73
+ Shows evaluated/blocked totals, how many machines are enforcing versus
74
+ merely reporting, a 24-bin sparkline of denies, and each policy's
75
+ fired/blocked/instructed/p95.
76
+
77
+ A `(no policy)` row is normal, not a gap: most evaluations are allows that
78
+ no policy objected to, and the row keeps the denominator on screen — "14
79
+ blocked" means little without the 933 it came from.
80
+
81
+ Needs `policies:read`. With `--json`: the server summary plus the timeline.
82
+
83
+ Examples:
84
+
85
+ * `fp guardrails`
86
+ * `fp guardrails --since 7d --machine ci-runner-01`
87
+ """
88
+ state: AppState = ctx.obj
89
+ deny_in_key_mode(state, "guardrails", _KEY_MODE_REASON)
90
+ cctx = require_auth(state)
91
+ hours = _hours(since)
92
+ _require_machine(cctx, machine)
93
+ summary = api.enforcement_summary(cctx, hours=hours, machine_id=machine)
94
+ timeline = api.decision_timeline(cctx, hours=hours, machine_id=machine)
95
+ if output.is_json():
96
+ output.emit_json({"summary": summary, "timeline": timeline})
97
+ return
98
+ output.render_guardrails(summary, timeline)
99
+
100
+
101
+ def guardrails_timeline(
102
+ ctx: typer.Context,
103
+ since: str = typer.Option("24h", "--since", help="Window: 1h, 6h, 24h, 7d."),
104
+ machine: Optional[str] = typer.Option(None, "--machine", help="Scope to one machine id."),
105
+ ) -> None:
106
+ """When enforcement bit, and how hard — one row per time bucket.
107
+
108
+ Shows `time · activity · total · denied · instructed`. The bar is scaled to
109
+ the busiest bucket in the window, with the blocked share drawn in red inside
110
+ it, so a quiet hour and a heavily-blocked hour are distinguishable at a
111
+ glance rather than by reading numbers.
112
+
113
+ Times are UTC, and the label follows the bucket size the server chose — a
114
+ clock for hourly buckets, a date for daily ones.
115
+
116
+ Needs `policies:read`. With `--json`: the server's timeline verbatim.
117
+
118
+ Example:
119
+
120
+ * `fp guardrails timeline --since 24h`
121
+ """
122
+ state: AppState = ctx.obj
123
+ deny_in_key_mode(state, "guardrails timeline", _KEY_MODE_REASON)
124
+ cctx = require_auth(state)
125
+ hours = _hours(since)
126
+ _require_machine(cctx, machine)
127
+ data = api.decision_timeline(cctx, hours=hours, machine_id=machine)
128
+ if output.is_json():
129
+ output.emit_json(data)
130
+ return
131
+ output.render_decision_timeline(data)
132
+
133
+
134
+ def register(app: typer.Typer) -> None:
135
+ # A pure container, like every other group in this CLI: bare `fp guardrails`
136
+ # prints its help rather than running something. It used to run the summary
137
+ # from a callback, which made it the only group that did — and put `--since`
138
+ # in two places, where the group-level copy silently shadowed nothing and
139
+ # taught the wrong shape.
140
+ guardrails_app = typer.Typer(
141
+ no_args_is_help=True,
142
+ rich_markup_mode="markdown",
143
+ context_settings={"help_option_names": ["-h", "--help"]},
144
+ help="What enforcement actually did (summary / timeline).",
145
+ )
146
+ guardrails_app.command("summary", epilog=GLOBALS_EPILOG)(guardrails_summary)
147
+ guardrails_app.command("timeline", epilog=GLOBALS_EPILOG)(guardrails_timeline)
148
+ app.add_typer(guardrails_app, name="guardrails")