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,330 @@
1
+ """Persistent CLI configuration at ``~/.failproofai/fpcli/cli-auth.json`` (mode 0600).
2
+
3
+ This used to be ``~/.fp/cli.json`` — a third top-level dotfile beside
4
+ ``~/.failproofai`` (the Enforcement CLI) and ``~/.agenteye`` (the SDK and
5
+ collector's event spool). One product owning three home directories is one more
6
+ than anybody can keep track of, so the CLI moved under the failproofai home.
7
+ ``~/.agenteye`` stays where it is: it is a wire contract with the collector, not
8
+ a preference (see the SDK's ``test_server_contract.py``).
9
+
10
+ The failproofai home is a governed layout, NOT a free directory. Its shape is
11
+ declared in one place — ``src/hooks/fp-home.ts`` in this repo, mirrored for the
12
+ daemon in ``crates/failproofaid/src/paths.rs`` — and that file's rule is that
13
+ nothing outside it may join a path onto the home. So ``fpcli/cli-auth.json`` is
14
+ registered there too, classified ``user-typed`` in ``HOME_CLASSES``. That
15
+ classification is what keeps it: a layout migration deletes only ``derived`` and
16
+ ``refetchable`` paths, and ``resettablePaths()`` is a filter over that table
17
+ rather than a hand-written list. It follows ``audit/session.json``, which is the
18
+ same thing for the audit tool and is likewise TS-side only — the daemon never
19
+ opens a human credential.
20
+
21
+ We only ever create. ``mkdir(parents=True, exist_ok=True)`` will bring
22
+ ``~/.failproofai`` into existence on a machine that has never run the
23
+ Enforcement CLI, and leaves a populated one exactly as it found it. Nothing here
24
+ removes or rewrites a path it does not own.
25
+
26
+ The move is invisible to the user: a session at the old path is adopted on the
27
+ next command, so nobody is signed out by an upgrade. The old file is copied, not
28
+ moved, which keeps a downgrade working — an older `fp` still finds its session
29
+ where it left it.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import errno
35
+ import json
36
+ import os
37
+ import stat
38
+ import tempfile
39
+ from dataclasses import asdict, dataclass
40
+ from datetime import datetime, timedelta, timezone
41
+ from pathlib import Path
42
+ from typing import Optional
43
+
44
+ # The dashboard the CLI talks to when nothing else says otherwise. Resolution is
45
+ # always explicit flag/env (`--base-url` / `FP_DASHBOARD_URL`) > saved config
46
+ # (`~/.failproofai/fpcli/cli-auth.json`) > this default, so a fresh install points at the hosted
47
+ # product with zero configuration, while a self-hosted or dev user overrides it
48
+ # once (at login, or per command) and never thinks about it again.
49
+ # NOT stored in `CliConfig` — a saved config with no `base_url` still reads back
50
+ # as `None`; the default is applied only when resolving the effective URL.
51
+ DEFAULT_BASE_URL = "https://app.befailproof.ai"
52
+
53
+
54
+ #: The CLI's own directory inside the failproofai home. Mirrors ``fpcliDir`` in
55
+ #: ``src/hooks/fp-home.ts``. Something checks now:
56
+ #: ``tests/test_fp_home_contract.py`` reads that file and fails if the two names
57
+ #: drift. Before it existed, renaming one side left 53 TS tests and 59 Python
58
+ #: tests passing while the credential sat at a path the layout register had
59
+ #: never heard of.
60
+ FPCLI_SUBDIR = "fpcli"
61
+
62
+ #: The pre-move location. READ once, to adopt a session that would otherwise be
63
+ #: lost, and never written or deleted — see :func:`load_config`, which copies it
64
+ #: to the new path and leaves the original so a downgrade still finds it.
65
+ #:
66
+ #: This said "never read" until it was checked against the code. Adoption
67
+ #: arrived after the move and the comment did not follow it.
68
+ LEGACY_DIR_NAME = ".fp"
69
+ LEGACY_FILE_NAME = "cli.json"
70
+
71
+
72
+ def base_dir() -> Path:
73
+ """Where ``cli-auth.json`` lives.
74
+
75
+ ``$FP_HOME`` (the CLI's own variable, and the one the docs have always
76
+ named) wins, so an existing override keeps working untouched. Otherwise
77
+ ``$FAILPROOFAI_HOME`` — the variable the rest of the failproofai home
78
+ already honours, which containers and tests routinely set — and finally
79
+ ``~/.failproofai/fpcli``.
80
+
81
+ Both env vars name a DIRECTORY that the config file sits directly in.
82
+ ``FAILPROOFAI_HOME`` points at the home root, so the subdirectory is
83
+ appended; ``FP_HOME`` points at the CLI's own directory and is used as-is,
84
+ which is what it meant before the move.
85
+ """
86
+ override = os.environ.get("FP_HOME")
87
+ if override:
88
+ return Path(override)
89
+ fp_home = os.environ.get("FAILPROOFAI_HOME")
90
+ if fp_home:
91
+ return Path(fp_home) / FPCLI_SUBDIR
92
+ return Path.home() / ".failproofai" / FPCLI_SUBDIR
93
+
94
+
95
+ def config_path() -> Path:
96
+ return base_dir() / "cli-auth.json"
97
+
98
+
99
+ def legacy_config_paths() -> list[Path]:
100
+ """Every place a pre-move session could still be sitting.
101
+
102
+ Two, not one, and the second is the one that is easy to miss. The move
103
+ changed the FILENAME as well as the directory, so somebody who exported
104
+ ``FP_HOME`` — the documented way to relocate this config, and the shape CI
105
+ images use — has their old session at ``$FP_HOME/cli.json`` and will never
106
+ own a ``~/.fp`` at all. Checking only the default would hand exactly those
107
+ users an unexplained logout, which is the group least able to shrug at one.
108
+
109
+ A session found here is COPIED to the new location on the next command and
110
+ the original is left alone — see :func:`load_config`. Copying rather than
111
+ moving keeps a downgrade working: an older `fp` still finds its session.
112
+
113
+ The relocated path is checked FIRST. When both exist, the one in the
114
+ directory this invocation actually resolved is the one that explains this
115
+ user's logout; naming the default instead sends somebody with ``FP_HOME``
116
+ set to delete an unrelated file on a machine they may share.
117
+ """
118
+ candidates = [base_dir() / LEGACY_FILE_NAME]
119
+ # `~/.fp` is only a candidate when the user has NOT redirected the config.
120
+ # Someone who exported `FP_HOME` said where their config lives; reaching past
121
+ # that into the home directory would adopt a session from a different context
122
+ # — a different tenant, or another user's leftovers on a shared box — and is
123
+ # also how this fallback quietly picked up the developer's own login when the
124
+ # suite ran.
125
+ if not os.environ.get("FP_HOME"):
126
+ default = Path.home() / LEGACY_DIR_NAME / LEGACY_FILE_NAME
127
+ if default not in candidates:
128
+ candidates.append(default)
129
+ return candidates
130
+
131
+
132
+ def legacy_config_path() -> Optional[Path]:
133
+ """The first pre-move session file that actually exists, if any."""
134
+ for path in legacy_config_paths():
135
+ try:
136
+ if path.is_file():
137
+ return path
138
+ except OSError:
139
+ continue
140
+ return None
141
+
142
+
143
+ def legacy_install_detected() -> bool:
144
+ """True when a pre-move session is on disk and the new one is not.
145
+
146
+ False once the new config exists, so the notice stops after the first
147
+ successful `fp login` rather than nagging forever.
148
+ """
149
+ try:
150
+ if config_path().is_file():
151
+ return False
152
+ except OSError:
153
+ return False
154
+ return legacy_config_path() is not None
155
+
156
+
157
+ @dataclass
158
+ class CliConfig:
159
+ base_url: Optional[str] = None
160
+ session_token: Optional[str] = None
161
+ expires_at: Optional[str] = None # ISO 8601, e.g. 2026-05-26T12:00:00Z
162
+ email: Optional[str] = None
163
+ user_id: Optional[str] = None
164
+ insecure: bool = False
165
+ org: Optional[str] = None # active tenant slug, chosen at login (multi-tenant)
166
+ anonymous_id: Optional[str] = None # stable per-machine id for anonymous telemetry
167
+
168
+
169
+ def _parse(path: Path) -> Optional[CliConfig]:
170
+ """Read one config file, or ``None`` if it is missing/unreadable/not ours."""
171
+ try:
172
+ data = json.loads(path.read_text())
173
+ except (FileNotFoundError, json.JSONDecodeError, OSError, UnicodeDecodeError):
174
+ return None
175
+ if not isinstance(data, dict):
176
+ return None
177
+ return _from_dict(data)
178
+
179
+
180
+ def load_config() -> CliConfig:
181
+ """Load the session, adopting a pre-move one the first time we see it.
182
+
183
+ The move is silent for the user: nobody is signed out, no command changes
184
+ behaviour, and CI that authenticates by env var never enters this path at
185
+ all. The adoption is a copy — the old file is left exactly where it is, so
186
+ downgrading to a previous `fp` finds its session intact and this is
187
+ reversible on the machine as well as in the release.
188
+
189
+ Adoption is best-effort by design. If the new location cannot be written
190
+ (read-only home, a symlink we refuse, a full disk) the caller still gets the
191
+ session that was found, so a machine that cannot be migrated keeps working
192
+ rather than being logged out by our own housekeeping.
193
+ """
194
+ current = _parse(config_path())
195
+ if current is not None:
196
+ return current
197
+
198
+ for legacy in legacy_config_paths():
199
+ adopted = _parse(legacy)
200
+ if adopted is None:
201
+ continue
202
+ try:
203
+ save_config(adopted)
204
+ except OSError:
205
+ pass # unwritable target: still hand back the session we found
206
+ return adopted
207
+
208
+ return CliConfig()
209
+
210
+
211
+ def _from_dict(data: dict) -> CliConfig:
212
+ return CliConfig(
213
+ base_url=data.get("base_url"),
214
+ session_token=data.get("session_token"),
215
+ expires_at=data.get("expires_at"),
216
+ email=data.get("email"),
217
+ user_id=data.get("user_id"),
218
+ insecure=bool(data.get("insecure", False)),
219
+ org=data.get("org"),
220
+ anonymous_id=data.get("anonymous_id"),
221
+ )
222
+
223
+
224
+ def save_config(cfg: CliConfig) -> None:
225
+ """Write the session atomically, owner-only, without following any link.
226
+
227
+ Both halves of this matter only because the file now sits in a directory
228
+ another product's secrets live in. Writing in place was fine when the CLI
229
+ owned ``~/.fp`` outright; beside ``credentials.json`` it is a way to destroy
230
+ someone else's token.
231
+
232
+ * A **symlink** at ``cli-auth.json`` is refused outright. It is not removed:
233
+ a link is something a person put there, and replacing it silently is the
234
+ behaviour this function exists to avoid.
235
+ * A **hard link** cannot be detected the same way — it is not a link, it is a
236
+ second name for one inode, so ``O_NOFOLLOW`` says nothing about it. Writing
237
+ to a temporary file and ``os.replace``-ing it into position is what
238
+ actually answers this: rename swaps the DIRECTORY ENTRY, so the other name
239
+ keeps the old inode and the neighbour's file is untouched.
240
+
241
+ The same rename gives two things worth having on their own: a reader never
242
+ observes a half-written credential, and two ``fp`` processes racing end with
243
+ one of the two sessions rather than a splice of both.
244
+ """
245
+ path = config_path()
246
+ # `mode` applies to the LEAF only, which is exactly the split we want: our
247
+ # own directory is created `0700` because it holds a credential, while a
248
+ # `~/.failproofai` we happen to be the first to create is left to the user's
249
+ # umask — the same shape the Enforcement CLI would have made it. An existing
250
+ # directory keeps its mode either way: `exist_ok=True` does not chmod, and
251
+ # re-permissioning a home another product owns is not ours to do.
252
+ path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
253
+
254
+ # `lstat`, not `exists`: the question is what the NAME is, not what it leads
255
+ # to. A dangling link must be refused too, or the refusal depends on whether
256
+ # the attacker's target happens to exist yet.
257
+ try:
258
+ if stat.S_ISLNK(os.lstat(path).st_mode):
259
+ raise OSError(
260
+ errno.ELOOP,
261
+ f"{path} is a symbolic link. Refusing to write the session "
262
+ "through it — that would overwrite whatever it points at, and "
263
+ "this directory is shared with the Enforcement CLI. Remove the "
264
+ "link and run the command again.",
265
+ )
266
+ except FileNotFoundError:
267
+ pass # the ordinary first-login case
268
+
269
+ payload = json.dumps(asdict(cfg), indent=2) + "\n"
270
+ # Same directory, so the rename is on one filesystem and therefore atomic.
271
+ #
272
+ # `mkstemp` rather than a name built from the pid: two THREADS share a pid,
273
+ # so a pid-derived name collided under `O_EXCL` and turned a concurrent
274
+ # `save_config` into a crash. It also creates `0600` and `O_EXCL` itself,
275
+ # which is the same guarantee hand-rolled with fewer ways to get it wrong.
276
+ fd, tmp_name = tempfile.mkstemp(dir=str(path.parent), prefix=f".{path.name}.", suffix=".tmp")
277
+ tmp = Path(tmp_name)
278
+ try:
279
+ with os.fdopen(fd, "w") as fh:
280
+ fh.write(payload)
281
+ fh.flush()
282
+ os.fsync(fh.fileno()) # survive a crash between write and rename
283
+ os.chmod(tmp, 0o600)
284
+ os.replace(tmp, path)
285
+ except BaseException:
286
+ # Never leave a stray credential behind on the failure paths.
287
+ try:
288
+ os.unlink(tmp)
289
+ except OSError:
290
+ pass
291
+ raise
292
+
293
+
294
+ def clear_token(cfg: CliConfig) -> CliConfig:
295
+ """Clear the whole logged-in session and persist.
296
+
297
+ Drops everything tied to *who* was signed in — token, expiry, email, user id,
298
+ and the active org/tenant — so a logout leaves no stale identity (and the next
299
+ `login` starts the org picker fresh, with no remembered tenant). Kept on
300
+ purpose: `base_url` and the `insecure` TLS preference (so the next login
301
+ doesn't need them re-specified) and the machine-stable `anonymous_id`.
302
+ """
303
+ cfg.session_token = None
304
+ cfg.expires_at = None
305
+ cfg.email = None
306
+ cfg.user_id = None
307
+ cfg.org = None
308
+ save_config(cfg)
309
+ return cfg
310
+
311
+
312
+ def _parse_iso(value: str) -> datetime:
313
+ # Python 3.10's fromisoformat does not accept a trailing 'Z'.
314
+ normalized = value.strip().replace("Z", "+00:00")
315
+ dt = datetime.fromisoformat(normalized)
316
+ if dt.tzinfo is None:
317
+ dt = dt.replace(tzinfo=timezone.utc)
318
+ return dt
319
+
320
+
321
+ def is_expired(cfg: CliConfig, *, skew_secs: int = 60, now: Optional[datetime] = None) -> bool:
322
+ """True if there is no usable, unexpired token (with a safety skew)."""
323
+ if not cfg.session_token or not cfg.expires_at:
324
+ return True
325
+ try:
326
+ expires = _parse_iso(cfg.expires_at)
327
+ except ValueError:
328
+ return True
329
+ now = now or datetime.now(timezone.utc)
330
+ return now >= expires - timedelta(seconds=skew_secs)
@@ -0,0 +1,78 @@
1
+ """Relative date-range presets, mirroring the dashboard's ``resolveDateRange``.
2
+
3
+ ``--since`` accepts one of ``15m|1h|6h|24h|7d|all`` and is converted to a
4
+ ``ts_from`` lower bound (open-ended to "now"). ``--from``/``--to`` provide an
5
+ explicit custom range and take precedence over ``--since``.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from datetime import datetime, timedelta, timezone
11
+ from typing import Optional, Tuple
12
+
13
+ PRESETS = {
14
+ "15m": timedelta(minutes=15),
15
+ "1h": timedelta(hours=1),
16
+ "6h": timedelta(hours=6),
17
+ "24h": timedelta(hours=24),
18
+ "7d": timedelta(days=7),
19
+ }
20
+
21
+ # Accepted values for --since (including the no-op "all").
22
+ SINCE_CHOICES = ["all", "15m", "1h", "6h", "24h", "7d"]
23
+
24
+
25
+ def _iso(dt: datetime) -> str:
26
+ return dt.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
27
+
28
+
29
+ def _validate_iso(value: str, flag: str) -> str:
30
+ """Validate an explicit ``--from``/``--to`` is a full RFC3339 UTC datetime.
31
+
32
+ The server deserializes ``ts_from``/``ts_to`` as ``chrono::DateTime<Utc>``, which
33
+ requires a ``T`` separator AND an explicit timezone (``Z`` or ``±HH:MM``). A
34
+ date-only, timezone-less (``2026-05-01T00:00:00``), or space-separated value is
35
+ sent verbatim and rejected by the server with a 400; validating here turns that
36
+ into a clean usage error (exit 2) instead.
37
+ """
38
+ v = value.strip()
39
+ try:
40
+ parsed = datetime.fromisoformat(v.replace("Z", "+00:00"))
41
+ except ValueError:
42
+ parsed = None
43
+ if parsed is None or "T" not in v or parsed.tzinfo is None:
44
+ raise ValueError(
45
+ f"invalid {flag} value {value!r}; expected an ISO-8601 UTC timestamp, "
46
+ "e.g. 2026-05-01T00:00:00Z"
47
+ )
48
+ return v
49
+
50
+
51
+ def resolve_range(
52
+ since: Optional[str] = None,
53
+ ts_from: Optional[str] = None,
54
+ ts_to: Optional[str] = None,
55
+ *,
56
+ now: Optional[datetime] = None,
57
+ ) -> Tuple[Optional[str], Optional[str]]:
58
+ """Return ``(ts_from, ts_to)`` for the given inputs.
59
+
60
+ Explicit ``ts_from``/``ts_to`` win. Otherwise a ``--since`` preset sets the
61
+ lower bound. ``since="all"`` (or ``None``) means no bounds.
62
+ """
63
+ if ts_from is not None or ts_to is not None:
64
+ return (
65
+ _validate_iso(ts_from, "--from") if ts_from is not None else None,
66
+ _validate_iso(ts_to, "--to") if ts_to is not None else None,
67
+ )
68
+
69
+ if since is None or since == "all":
70
+ return None, None
71
+
72
+ if since not in PRESETS:
73
+ raise ValueError(
74
+ f"invalid --since value {since!r}; choose one of {', '.join(SINCE_CHOICES)}"
75
+ )
76
+
77
+ now = now or datetime.now(timezone.utc)
78
+ return _iso(now - PRESETS[since]), None