failproofai 1.0.10 → 1.0.11-beta.1

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 (297) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +6 -6
  3. package/.next/standalone/.next/prerender-manifest.json +4 -4
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
  6. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page.js +4 -4
  8. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  9. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.meta +8 -0
  12. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  14. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  15. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
  17. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  18. package/.next/standalone/.next/server/app/_not-found/page.js +4 -4
  19. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  22. package/.next/standalone/.next/server/app/_not-found.meta +8 -0
  23. package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
  24. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  25. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
  26. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  27. package/.next/standalone/.next/server/app/api/audit/invite/route.js +4 -4
  28. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -4
  30. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/audit/status/route.js +1 -1
  32. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -2
  34. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +4 -4
  36. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -3
  38. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -3
  40. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  42. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
  44. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  45. package/.next/standalone/.next/server/app/audit/page.js +4 -4
  46. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  48. package/.next/standalone/.next/server/app/index.html +1 -1
  49. package/.next/standalone/.next/server/app/index.meta +8 -0
  50. package/.next/standalone/.next/server/app/index.rsc +14 -14
  51. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
  52. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  53. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  54. package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
  55. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  56. package/.next/standalone/.next/server/app/page.js +4 -4
  57. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  58. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  59. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
  60. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  61. package/.next/standalone/.next/server/app/policies/page.js +8 -8
  62. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  63. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  64. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
  65. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  66. package/.next/standalone/.next/server/app/project/[name]/page.js +5 -5
  67. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  68. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  69. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
  70. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  71. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  72. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +5 -5
  73. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  74. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  75. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
  76. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  77. package/.next/standalone/.next/server/app/projects/page.js +5 -5
  78. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +3 -3
  81. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +8 -8
  82. package/.next/standalone/.next/server/app/settings/page.js +9 -9
  83. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  84. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  85. package/.next/standalone/.next/server/chunks/{[externals]__0qxhecv._.js → [externals]__0d7i9ea._.js} +2 -2
  86. package/.next/standalone/.next/server/chunks/{[externals]__0sg9-3p._.js → [externals]__0hjaw70._.js} +1 -1
  87. package/.next/standalone/.next/server/chunks/[externals]__1_9ze9b._.js +3 -0
  88. package/.next/standalone/.next/server/chunks/[externals]__1oenfmn._.js +3 -0
  89. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08dchfu._.js +1 -1
  90. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ga9hs7._.js +3 -0
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l3yhx4._.js +2 -2
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0moxohc._.js +22 -0
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0z2ll-a._.js +3 -0
  95. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1adacul._.js +11 -11
  96. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1autwno._.js +3 -0
  97. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1v25n-6._.js +3 -0
  98. package/.next/standalone/.next/server/chunks/_09dz7xv._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  100. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  101. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +2 -2
  102. package/.next/standalone/.next/server/chunks/_1fz8m31._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/_1jrv3hg._.js +3 -0
  104. package/.next/standalone/.next/server/chunks/_1nx8fim._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/_1ogwigf._.js +1 -1
  106. package/.next/standalone/.next/server/chunks/{_1q5i8mb._.js → _1q27azx._.js} +2 -2
  107. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
  108. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_0el6rg8._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0ol5pa3._.js} +2 -2
  109. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_04op_ou._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_1rqmk27._.js} +2 -2
  110. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_0dr20uz._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_1w7tiq4._.js} +2 -2
  111. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  112. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0oqrl71._.js → [root-of-the-server]__042u-jn._.js} +2 -2
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04usis8._.js +2 -2
  114. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1-bm5zf._.js → [root-of-the-server]__0502lat._.js} +1 -1
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_gn5iz._.js +4 -0
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0f765xr._.js +4 -0
  117. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__01bmjsj._.js → [root-of-the-server]__0h38gzz._.js} +1 -1
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hoaz1g._.js +4 -0
  119. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1ouobn4._.js → [root-of-the-server]__0mxmwtx._.js} +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__19evfi8._.js → [root-of-the-server]__0uescqv._.js} +1 -1
  121. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__13d-wb6._.js +1 -1
  122. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1484zv2._.js +3 -0
  123. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1mf3zp6._.js → [root-of-the-server]__19dutr7._.js} +4 -4
  124. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1u6sn69._.js → [root-of-the-server]__1ab4ifb._.js} +1 -1
  125. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1fbrk56._.js +4 -0
  126. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pprgri._.js +2 -2
  127. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1um4yxn._.js +4 -0
  128. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1wb4yt2._.js +4 -0
  129. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1y3n3cn._.js +4 -0
  130. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  131. package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/ssr/{_0yq5_3a._.js → _1ffxwxz._.js} +1 -1
  133. package/.next/standalone/.next/server/chunks/ssr/_1gb0ifp._.js +2 -2
  134. package/.next/standalone/.next/server/chunks/ssr/{_042cgl1._.js → _1o95rf0._.js} +2 -2
  135. package/.next/standalone/.next/server/chunks/ssr/{_1zopuov._.js → _1thr5rf._.js} +2 -2
  136. package/.next/standalone/.next/server/chunks/ssr/{_16pa9ht._.js → _1v1o2ya._.js} +2 -2
  137. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +12 -14
  138. package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9sni._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  140. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  141. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  142. package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h4._.js +1 -1
  143. package/.next/standalone/.next/server/chunks/ssr/node_modules_1hl81y4._.js +1 -1
  144. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_0nkkbfv._.js +6 -0
  145. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
  146. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  147. package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
  148. package/.next/standalone/.next/server/middleware.js +1 -1
  149. package/.next/standalone/.next/server/pages/404.html +1 -1
  150. package/.next/standalone/.next/server/pages/500.html +1 -1
  151. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  152. package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
  153. package/.next/standalone/.next/static/chunks/08tmc_7n-rba1.js +1 -0
  154. package/.next/standalone/.next/static/chunks/{3mjfvng11xnoo.js → 09tlrmucn6oq3.js} +1 -1
  155. package/.next/standalone/.next/static/chunks/{078dofaxvjgpd.js → 0i69k917myh7b.js} +1 -1
  156. package/.next/standalone/.next/static/chunks/{03h3b-uomi7_d.js → 0u_nhsujkactp.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/{2j92xul6y24_v.js → 0zp51hzx62i-u.js} +1 -1
  158. package/.next/standalone/.next/static/chunks/{3u1s8dsqiean5.js → 1fz7q0gtnmeod.js} +1 -1
  159. package/.next/standalone/.next/static/chunks/{3ob2161ap37ul.js → 1j75le8wln-26.js} +1 -1
  160. package/.next/standalone/.next/static/chunks/{2kj826h1w9r47.js → 1z4km7tw0_50h.js} +1 -1
  161. package/.next/standalone/.next/static/chunks/{1fqukzzowjflt.js → 239ay_80-xr8z.js} +1 -1
  162. package/.next/standalone/.next/static/chunks/{3nkc2jlgyygda.js → 2c9pc5xco6rnu.js} +1 -1
  163. package/.next/standalone/.next/static/chunks/{3i8rg2fv9nd6e.js → 3n46e45atic-m.js} +1 -1
  164. package/.next/standalone/.next/static/chunks/{0542cuk_va8g1.js → 3sg07wnn969xq.js} +1 -1
  165. package/.next/standalone/.next/static/chunks/{turbopack-11dn2pegmg9_9.js → turbopack-1opxka4dtjbc5.js} +1 -1
  166. package/.next/standalone/app/actions/install-hooks-web.ts +10 -2
  167. package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
  168. package/.next/standalone/node_modules/@next/env/package.json +1 -1
  169. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  170. package/.next/standalone/node_modules/next/dist/compiled/hash.js/sha256/256.js +1 -0
  171. package/.next/standalone/node_modules/next/dist/compiled/hash.js/sha256/package.json +1 -0
  172. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +17 -17
  173. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +18 -18
  174. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js +3 -3
  175. package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +13 -13
  176. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  177. package/.next/standalone/node_modules/next/dist/server/base-server.js +1 -2
  178. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  179. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  180. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  181. package/.next/standalone/node_modules/next/dist/server/image-optimizer.js +265 -53
  182. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  183. package/.next/standalone/node_modules/next/dist/server/lib/incremental-cache/file-system-cache.js +177 -19
  184. package/.next/standalone/node_modules/next/dist/server/lib/incremental-cache/index.js +36 -17
  185. package/.next/standalone/node_modules/next/dist/server/lib/incremental-cache/shared-cache-controls.external.js +14 -6
  186. package/.next/standalone/node_modules/next/dist/server/lib/match-next-data-pathname.js +3 -1
  187. package/.next/standalone/node_modules/next/dist/server/lib/node-fs-methods.js +14 -0
  188. package/.next/standalone/node_modules/next/dist/server/lib/route-cache-key.js +77 -0
  189. package/.next/standalone/node_modules/next/dist/server/lib/router-utils/block-cross-site-dev.js +9 -11
  190. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  191. package/.next/standalone/node_modules/next/dist/server/mcp/get-mcp-middleware.js +4 -2
  192. package/.next/standalone/node_modules/next/dist/server/next-server.js +7 -3
  193. package/.next/standalone/node_modules/next/dist/server/response-cache/index.js +32 -16
  194. package/.next/standalone/node_modules/next/dist/server/route-matcher-providers/app-page-route-matcher-provider.js +2 -2
  195. package/.next/standalone/node_modules/next/dist/server/route-modules/pages/pages-handler.js +6 -1
  196. package/.next/standalone/node_modules/next/dist/server/route-modules/route-module.js +9 -2
  197. package/.next/standalone/node_modules/next/dist/server/use-cache/use-cache-wrapper.js +30 -29
  198. package/.next/standalone/node_modules/next/dist/shared/lib/router/utils/app-paths.js +18 -1
  199. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  200. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  201. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  202. package/.next/standalone/node_modules/next/package.json +10 -10
  203. package/.next/standalone/package.json +14 -11
  204. package/.next/standalone/sdk/python/README.md +41 -0
  205. package/.next/standalone/sdk/python/docs/README.md +1 -0
  206. package/.next/standalone/sdk/python/docs/otel/README.md +36 -0
  207. package/.next/standalone/sdk/python/docs/otel/examples/agent_trace.py +30 -0
  208. package/.next/standalone/sdk/python/docs/otel/examples/quickstart.py +22 -0
  209. package/.next/standalone/sdk/python/failproofai_sdk/__init__.py +8 -0
  210. package/.next/standalone/sdk/python/failproofai_sdk/integrations/__init__.py +3 -0
  211. package/.next/standalone/sdk/python/failproofai_sdk/integrations/otel.py +189 -0
  212. package/.next/standalone/sdk/python/pyproject.toml +2 -1
  213. package/.next/standalone/sdk/python/tests/test_docs.py +16 -3
  214. package/.next/standalone/sdk/python/tests/test_integrations.py +1 -1
  215. package/.next/standalone/sdk/python/tests/test_otel.py +147 -0
  216. package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +4 -1
  217. package/.next/standalone/sdk/python/uv.lock +258 -147
  218. package/.next/standalone/sdk/typescript/README.md +43 -0
  219. package/.next/standalone/sdk/typescript/integration/ai.test.ts +9 -2
  220. package/.next/standalone/sdk/typescript/package-lock.json +57 -5
  221. package/.next/standalone/sdk/typescript/package.json +2 -0
  222. package/.next/standalone/sdk/typescript/src/edge/index.ts +9 -0
  223. package/.next/standalone/sdk/typescript/src/index.ts +2 -0
  224. package/.next/standalone/sdk/typescript/src/integrations/index.ts +4 -1
  225. package/.next/standalone/sdk/typescript/src/integrations/otel.ts +204 -0
  226. package/.next/standalone/sdk/typescript/test/integrations.test.ts +1 -1
  227. package/.next/standalone/sdk/typescript/test/otel.test.ts +125 -0
  228. package/.next/standalone/server.js +1 -1
  229. package/bin/failproofai.mjs +653 -1077
  230. package/dist/cli.mjs +8174 -3930
  231. package/dist/worker.mjs +170 -62
  232. package/hermes-plugin/plugin.yaml +1 -1
  233. package/package.json +14 -11
  234. package/scripts/launch-facts.ts +157 -0
  235. package/scripts/launch-ready.ts +228 -0
  236. package/scripts/launch-screen.ts +202 -0
  237. package/scripts/launch.ts +106 -49
  238. package/scripts/skew-log-filter.ts +42 -0
  239. package/src/audit/cli-login.ts +39 -59
  240. package/src/audit/cli.ts +308 -228
  241. package/src/audit/index.ts +45 -2
  242. package/src/audit/schedule-cli.ts +83 -120
  243. package/src/audit/types.ts +31 -0
  244. package/src/hooks/agent-selection.ts +155 -0
  245. package/src/hooks/backfill-cli.ts +151 -137
  246. package/src/hooks/backfill-request.ts +149 -0
  247. package/src/hooks/cloud-connection.ts +1 -1
  248. package/src/hooks/cloud-enrollment-cli.ts +78 -28
  249. package/src/hooks/cloud-introspect.ts +3 -0
  250. package/src/hooks/collector-config.ts +10 -1
  251. package/src/hooks/config-status.ts +606 -0
  252. package/src/hooks/configure-wizard.ts +915 -989
  253. package/src/hooks/daemon-service.ts +16 -6
  254. package/src/hooks/did-you-mean.ts +185 -0
  255. package/src/hooks/flush-cli.ts +33 -7
  256. package/src/hooks/fp-config.ts +96 -0
  257. package/src/hooks/fp-home.ts +11 -0
  258. package/src/hooks/fp-reset.ts +102 -95
  259. package/src/hooks/handler.ts +4 -0
  260. package/src/hooks/harness-cli.ts +87 -138
  261. package/src/hooks/install-prompt.ts +38 -477
  262. package/src/hooks/integrations.ts +1 -1
  263. package/src/hooks/jev-cli.ts +183 -196
  264. package/src/hooks/manager.ts +554 -358
  265. package/src/hooks/onboarding-attempt.ts +11 -9
  266. package/src/hooks/otel-cli.ts +344 -0
  267. package/src/hooks/otel-output.ts +24 -0
  268. package/src/hooks/otel-writers.ts +225 -0
  269. package/src/hooks/pack-cli.ts +219 -204
  270. package/src/hooks/report-body.ts +50 -0
  271. package/src/hooks/safe-config-write.ts +4 -2
  272. package/src/hooks/session-pause-cli.ts +93 -73
  273. package/src/hooks/tui.ts +849 -389
  274. package/src/hooks/uninstall-cli.ts +136 -149
  275. package/src/hooks/untraced-note.ts +52 -0
  276. package/.next/standalone/.next/server/chunks/[externals]__19_pzeq._.js +0 -3
  277. package/.next/standalone/.next/server/chunks/[externals]__20kzpkf._.js +0 -3
  278. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +0 -3
  279. package/.next/standalone/.next/server/chunks/[root-of-the-server]__05ja_05._.js +0 -3
  280. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0igxt32._.js +0 -3
  281. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zyc_nm._.js +0 -3
  282. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ttrwnd._.js +0 -22
  283. package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +0 -3
  284. package/.next/standalone/.next/server/chunks/_185gyk9._.js +0 -3
  285. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__056wjo4._.js +0 -4
  286. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n0xg95._.js +0 -4
  287. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rwtwpm._.js +0 -4
  288. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0yrsbd_._.js +0 -3
  289. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11mayhe._.js +0 -4
  290. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15578wp._.js +0 -4
  291. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1m_svbe._.js +0 -4
  292. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1q4p5b8._.js +0 -4
  293. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js +0 -6
  294. package/.next/standalone/.next/static/chunks/1lyg_puttohbc.js +0 -1
  295. /package/.next/standalone/.next/static/{ECs8__hcNabYV58WfDh-3 → UZKCiP4Q2TeU8zOB-h9Xj}/_buildManifest.js +0 -0
  296. /package/.next/standalone/.next/static/{ECs8__hcNabYV58WfDh-3 → UZKCiP4Q2TeU8zOB-h9Xj}/_clientMiddlewareManifest.js +0 -0
  297. /package/.next/standalone/.next/static/{ECs8__hcNabYV58WfDh-3 → UZKCiP4Q2TeU8zOB-h9Xj}/_ssgManifest.js +0 -0
@@ -0,0 +1,157 @@
1
+ /**
2
+ * What the launch screen states about this machine, read from disk.
3
+ *
4
+ * Files only: no daemon probe, no `systemctl`, no `which` (`detectInstalled()`
5
+ * spawns one per agent), and no policy file imported. This runs on every bare
6
+ * `failproofai`, while someone waits for a URL. Each fact is read on its own
7
+ * and degrades on its own, so an unreadable file costs one row its detail and
8
+ * never the screen.
9
+ */
10
+ // Aliased for the same reason as the long names below: the CLI is one
11
+ // scope-hoisted bundle, where another plain `resolve` import renumbers every
12
+ // later module's `resolveN` binding.
13
+ import { resolve as resolveLaunchPath } from "node:path";
14
+ import { INTEGRATION_TYPES } from "../src/hooks/types";
15
+ import { getIntegration } from "../src/hooks/integrations";
16
+ import { integrationsInstalledAt, notEnforcingReason } from "../src/hooks/manager";
17
+ import { readConfig, readCredentials, type FpConfig } from "../src/hooks/fp-config";
18
+ import { readCloudCredentials } from "../src/hooks/cloud-enrollment";
19
+ import { readIngestCredential } from "../src/hooks/collector-config";
20
+ import { cloudBaseFor } from "../src/hooks/cloud-connection";
21
+ import { deliveryHealth } from "../src/hooks/delivery-health";
22
+ import { hasInstalledRegexPacks, readInstalledPacks } from "../src/hooks/pack-manifest";
23
+ import { readActiveCloudManagedPolicies } from "../src/hooks/cloud-managed-policies";
24
+ import { configuredCustomPolicyPaths, findProjectConfigDir, readMergedHooksConfig } from "../src/hooks/hooks-config";
25
+ import { discoverPolicyFiles } from "../src/hooks/custom-hooks-loader";
26
+ import { customPoliciesDir } from "../src/hooks/fp-home";
27
+ import {
28
+ describeCloudConnection,
29
+ describePoliciesOn,
30
+ describeTracedAgents,
31
+ type LaunchCloudSummary,
32
+ type LaunchPoliciesSummary,
33
+ } from "./launch-screen";
34
+
35
+ export interface LaunchMachineFacts {
36
+ policies: string;
37
+ agents: string;
38
+ cloud: string;
39
+ notEnforcing: boolean;
40
+ }
41
+
42
+ export function gatherLaunchFacts(cwd: string): LaunchMachineFacts {
43
+ const row = (read: () => string): string => {
44
+ try {
45
+ return read();
46
+ } catch {
47
+ return "could not be read";
48
+ }
49
+ };
50
+ let notEnforcing = false;
51
+ try {
52
+ notEnforcing = notEnforcingReason(cwd) !== null;
53
+ } catch {
54
+ // Unknown is not a warning: `failproofai policies` is the full answer.
55
+ }
56
+ return {
57
+ policies: row(() => describePoliciesOn(readLaunchPolicies(cwd))),
58
+ agents: row(() => describeTracedAgents(readTracedAgentNames(cwd))),
59
+ cloud: row(() => describeCloudConnection(readLaunchCloud())),
60
+ notEnforcing,
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Policies switched on, from the same inputs `notEnforcingReason` reads, so the
66
+ * row and the warning under it cannot disagree: installed packs (minus the
67
+ * per-version switches the dashboard used to write), the cloud deployment, the
68
+ * legacy built-ins on a machine still on the pre-pack shim, and whether custom
69
+ * policy files exist. Their contents are never imported to count them.
70
+ */
71
+ export function readLaunchPolicies(cwd: string): LaunchPoliciesSummary {
72
+ const config = readMergedHooksConfig(cwd);
73
+ const disabled = new Set(config.disabledCustomPolicies ?? []);
74
+ const sources: LaunchPoliciesSummary["sources"] = [];
75
+ try {
76
+ for (const pack of readInstalledPacks().packs) {
77
+ const taken = pack.enabled ?? pack.policies.map((policy) => policy.name);
78
+ const on = taken.filter((name) => !disabled.has(`pack:${pack.id}@${pack.version}:${name}`)).length;
79
+ if (on > 0) sources.push({ label: `${pack.id}@${pack.version}`, count: on, pack: true });
80
+ }
81
+ } catch {
82
+ // An unreadable manifest is reported by `failproofai policies`.
83
+ }
84
+ try {
85
+ const managed = readActiveCloudManagedPolicies();
86
+ if (managed.length > 0) sources.push({ label: "FailproofAI Cloud", count: managed.length, pack: false });
87
+ } catch {
88
+ // No deployment, or an unreadable one: nothing is on from there.
89
+ }
90
+ if (config.enabledPolicies.length > 0 && !hasInstalledRegexPacks()) {
91
+ sources.push({ label: "built-in policies", count: config.enabledPolicies.length, pack: false });
92
+ }
93
+ const projectDir = resolveLaunchPath(findProjectConfigDir(cwd), ".failproofai", "policies");
94
+ const customFiles =
95
+ configuredCustomPolicyPaths(config).length > 0 ||
96
+ discoverPolicyFiles(projectDir).length > 0 ||
97
+ discoverPolicyFiles(customPoliciesDir()).length > 0;
98
+ return { sources, customFiles };
99
+ }
100
+
101
+ /**
102
+ * The agents failproofai traces, by display name in the canonical order: the
103
+ * saved selection when there is one, else the agents hooked at user scope.
104
+ */
105
+ export function readTracedAgentNames(cwd: string, config: FpConfig = readConfig()): string[] {
106
+ const ids: readonly string[] = config.agents ? config.agents.selected : integrationsInstalledAt("user", cwd);
107
+ return INTEGRATION_TYPES.filter((id) => ids.includes(id)).map((id) => {
108
+ try {
109
+ return getIntegration(id).displayName;
110
+ } catch {
111
+ return id;
112
+ }
113
+ });
114
+ }
115
+
116
+ /**
117
+ * The connection, in the precedence `config --status` uses
118
+ * (`connectionStatusReport`): the environment first, then either credential,
119
+ * and the collector's record of refused uploads over the files' claim, because
120
+ * a revoked key leaves both files byte-for-byte correct while nothing arrives.
121
+ * The daemon's state is left out: reading it runs `systemctl`.
122
+ */
123
+ export function readLaunchCloud(): LaunchCloudSummary {
124
+ const envUrl = process.env.FAILPROOFAI_CLOUD_URL;
125
+ if (envUrl) return { kind: "environment", url: envUrl };
126
+ const creds = readCloudCredentials();
127
+ const ingest = readIngestCredential();
128
+ if (!creds && !ingest) return { kind: "none" };
129
+ const org = readCredentials().org;
130
+ const where = creds ? creds.url : cloudBaseFor(ingest!.url);
131
+ let host = where;
132
+ try {
133
+ host = new URL(where).host || where;
134
+ } catch {
135
+ // Not a URL: show it as written.
136
+ }
137
+ let refused: { codes: number[]; credential: boolean } | null = null;
138
+ if (ingest) {
139
+ const health = deliveryHealth();
140
+ if (health.rejected > 0) {
141
+ refused = {
142
+ codes: Object.keys(health.byStatus)
143
+ .map(Number)
144
+ .sort((a, b) => a - b),
145
+ credential: health.credentialRejected > 0,
146
+ };
147
+ }
148
+ }
149
+ return {
150
+ kind: "connected",
151
+ org: org?.name || org?.slug || org?.id || null,
152
+ host,
153
+ pulling: Boolean(creds),
154
+ sending: Boolean(ingest),
155
+ refused,
156
+ };
157
+ }
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Watches the dashboard server start, so the launch screen can say it is live.
3
+ *
4
+ * Only `start` mode can: it pipes the standalone server's output, which is
5
+ * where Next says it is listening. Three signals decide the start, in order of
6
+ * preference:
7
+ *
8
+ * - Next's `Ready in` line on stdout (`makeNextStartupFilter`), logged from the
9
+ * server's own `listening` handler.
10
+ * - A TCP connect to the bind address, tried every 250ms once 5s have passed
11
+ * with no ready line: a Next upgrade that rewords the line must not leave
12
+ * the screen waiting for the deadline.
13
+ * - The server exiting first. That is a failure to start, and the cause is in
14
+ * what it wrote to stderr.
15
+ *
16
+ * Until then the server's output is held back, so the screen opens the output
17
+ * rather than appearing halfway down it. After 30s with no answer the screen
18
+ * prints anyway, marked as still starting, and Next's own banner is let through
19
+ * from then on, so a late start still says when it happened.
20
+ */
21
+ // Aliased: in the scope-hoisted CLI bundle a bare `connect` binding would make
22
+ // the bundler rename every local `connect` elsewhere (the setup wizard has one).
23
+ import { connect as connectDashboardProbe } from "node:net";
24
+ import { createInterface } from "node:readline";
25
+ import type { EventEmitter } from "node:events";
26
+ import { makeNextStartupFilter, makeSkewLogFilter } from "./skew-log-filter";
27
+
28
+ export interface DashboardChild extends EventEmitter {
29
+ stdout: NodeJS.ReadableStream | null;
30
+ stderr: NodeJS.ReadableStream | null;
31
+ }
32
+
33
+ export interface DashboardSink {
34
+ write(chunk: string): unknown;
35
+ }
36
+
37
+ export type DashboardStartOutcome =
38
+ | { kind: "ready"; via: "ready-line" | "probe" }
39
+ | { kind: "timeout" }
40
+ | { kind: "failed"; code: number | null; portInUse: boolean; cause: string | null };
41
+
42
+ export interface DashboardStartWatch {
43
+ child: DashboardChild;
44
+ stdout: DashboardSink;
45
+ stderr: DashboardSink;
46
+ /** True once something accepts a connection on the bind address and port. */
47
+ probe: () => Promise<boolean>;
48
+ /**
49
+ * Called once, when the start is decided. The held-back output is released
50
+ * right after it for a ready or a timeout, so the screen comes first, and
51
+ * right before it for a failure, so the ✕ line comes last.
52
+ */
53
+ onSettled: (outcome: DashboardStartOutcome) => void;
54
+ /** The server exited, or errored, after the start was decided. */
55
+ onExit: (code: number | null, error?: Error) => void;
56
+ probeAfterMs?: number;
57
+ probeEveryMs?: number;
58
+ timeoutMs?: number;
59
+ /** How long to wait after `exit` for the streams to close, in case a grandchild holds them open. */
60
+ exitGraceMs?: number;
61
+ }
62
+
63
+ export function watchDashboardStart(watch: DashboardStartWatch): void {
64
+ const probeAfterMs = watch.probeAfterMs ?? 5_000;
65
+ const probeEveryMs = watch.probeEveryMs ?? 250;
66
+ const timeoutMs = watch.timeoutMs ?? 30_000;
67
+ const exitGraceMs = watch.exitGraceMs ?? 500;
68
+ // A server that floods its output before it listens stops being held back
69
+ // past this many lines, and so does what is kept to find a failure's cause.
70
+ const holdLimit = 2_000;
71
+
72
+ let starting = true;
73
+ let holding = true;
74
+ let bannerActive = true;
75
+ let finished = false;
76
+ const held: Array<{ sink: DashboardSink; line: string; fromStderr: boolean }> = [];
77
+ const startupText: string[] = [];
78
+ const timers = new Set<ReturnType<typeof setTimeout>>();
79
+ const later = (fn: () => void, ms: number): void => {
80
+ const timer = setTimeout(() => {
81
+ timers.delete(timer);
82
+ fn();
83
+ }, ms);
84
+ timers.add(timer);
85
+ };
86
+
87
+ const release = (dropStderr: boolean): void => {
88
+ holding = false;
89
+ for (const entry of held.splice(0)) {
90
+ if (!(dropStderr && entry.fromStderr)) entry.sink.write(`${entry.line}\n`);
91
+ }
92
+ };
93
+
94
+ const decide = (outcome: DashboardStartOutcome): void => {
95
+ if (!starting) return;
96
+ starting = false;
97
+ for (const timer of timers) clearTimeout(timer);
98
+ timers.clear();
99
+ if (outcome.kind === "failed") {
100
+ // Next's own dump for a port in use is a stack trace that says what the
101
+ // ✕ line already says; anything else is the evidence, and stays.
102
+ release(outcome.portInUse);
103
+ watch.onSettled(outcome);
104
+ return;
105
+ }
106
+ if (outcome.kind === "timeout") bannerActive = false;
107
+ try {
108
+ watch.onSettled(outcome);
109
+ } finally {
110
+ release(false);
111
+ }
112
+ };
113
+
114
+ const pipe = (src: NodeJS.ReadableStream | null, sink: DashboardSink, fromStderr: boolean): void => {
115
+ if (!src) return;
116
+ // One of each per stream: both filters are stateful.
117
+ const skew = makeSkewLogFilter();
118
+ const banner = fromStderr ? null : makeNextStartupFilter(() => decide({ kind: "ready", via: "ready-line" }));
119
+ createInterface({ input: src, crlfDelay: Infinity }).on("line", (raw) => {
120
+ let line = skew(raw);
121
+ if (line !== null && banner && bannerActive) line = banner(line);
122
+ if (line === null) return;
123
+ if (starting && startupText.length < holdLimit) startupText.push(line);
124
+ if (holding && held.length >= holdLimit) release(false);
125
+ if (holding) held.push({ sink, line, fromStderr });
126
+ else sink.write(`${line}\n`);
127
+ });
128
+ };
129
+ pipe(watch.child.stdout, watch.stdout, false);
130
+ pipe(watch.child.stderr, watch.stderr, true);
131
+
132
+ const probeOnce = (): void => {
133
+ if (!starting) return;
134
+ const next = (): void => {
135
+ if (starting) later(probeOnce, probeEveryMs);
136
+ };
137
+ let attempt: Promise<boolean>;
138
+ try {
139
+ attempt = watch.probe();
140
+ } catch {
141
+ next();
142
+ return;
143
+ }
144
+ attempt.then((up) => {
145
+ if (!starting) return;
146
+ if (up) decide({ kind: "ready", via: "probe" });
147
+ else next();
148
+ }, next);
149
+ };
150
+ later(probeOnce, probeAfterMs);
151
+ later(() => decide({ kind: "timeout" }), timeoutMs);
152
+
153
+ // `close` comes after the streams have ended, so the last lines (where the
154
+ // cause is) have been read. `exit` alone can arrive first; the grace timer
155
+ // covers a grandchild that keeps a stream open and so delays `close`.
156
+ let grace: ReturnType<typeof setTimeout> | undefined;
157
+ const finish = (code: number | null, error?: Error): void => {
158
+ if (finished) return;
159
+ finished = true;
160
+ if (grace) clearTimeout(grace);
161
+ if (!starting) {
162
+ watch.onExit(code, error);
163
+ return;
164
+ }
165
+ const portInUse = !error && startupText.some((line) => line.includes("EADDRINUSE"));
166
+ decide({
167
+ kind: "failed",
168
+ code,
169
+ portInUse,
170
+ cause: error ? error.message : portInUse ? null : dashboardStartupCause(startupText),
171
+ });
172
+ };
173
+ watch.child.on("exit", (code: number | null) => {
174
+ if (grace === undefined) grace = setTimeout(() => finish(code), exitGraceMs);
175
+ });
176
+ watch.child.on("close", (code: number | null) => finish(code));
177
+ watch.child.on("error", (error: Error) => finish(null, error));
178
+ }
179
+
180
+ /**
181
+ * The line that says why the server stopped: the first `Error:` line (Node
182
+ * prints the message first), else Next's first `⨯` line. A plain `Error: `
183
+ * prefix is dropped as noise; a named one (`TypeError: …`) is kept.
184
+ */
185
+ export function dashboardStartupCause(lines: readonly string[]): string | null {
186
+ const plain = lines.map((line) => line.replace(/\x1B\[[0-9;]*m/g, "").trim());
187
+ for (const line of plain) {
188
+ const match = /(?:^|[\s[])((?:[A-Z][A-Za-z]*)?Error): (.+)$/.exec(line);
189
+ if (match) return match[1] === "Error" ? match[2].trim() : `${match[1]}: ${match[2].trim()}`;
190
+ }
191
+ for (const line of plain) {
192
+ if (line.startsWith("⨯ ")) return line.slice(2).trim();
193
+ }
194
+ return null;
195
+ }
196
+
197
+ /** Where to probe for a bind address: a wildcard bind answers on loopback. */
198
+ export function dashboardProbeHost(bindHost: string): string {
199
+ const raw = bindHost.trim();
200
+ if (raw === "" || raw === "0.0.0.0") return "127.0.0.1";
201
+ if (raw === "::" || raw === "[::]" || raw === "0:0:0:0:0:0:0:0") return "::1";
202
+ return raw.startsWith("[") && raw.endsWith("]") ? raw.slice(1, -1) : raw;
203
+ }
204
+
205
+ /** One TCP connect: true when something accepts it within `timeoutMs`. */
206
+ export function probeDashboardPort(host: string, port: number, timeoutMs = 1_000): Promise<boolean> {
207
+ return new Promise((settle) => {
208
+ let socket: ReturnType<typeof connectDashboardProbe>;
209
+ try {
210
+ socket = connectDashboardProbe({ host, port });
211
+ } catch {
212
+ // A port that is not a port (`--port abc`) throws here; the server has
213
+ // already failed on it, and its exit says why.
214
+ settle(false);
215
+ return;
216
+ }
217
+ let answered = false;
218
+ const answer = (up: boolean): void => {
219
+ if (answered) return;
220
+ answered = true;
221
+ socket.destroy();
222
+ settle(up);
223
+ };
224
+ socket.setTimeout(timeoutMs, () => answer(false));
225
+ socket.once("connect", () => answer(true));
226
+ socket.once("error", () => answer(false));
227
+ });
228
+ }
@@ -0,0 +1,202 @@
1
+ /**
2
+ * The screen bare `failproofai` prints when the local dashboard is up: the
3
+ * logomark, the wordmark header, a DASHBOARD block, at most one attention line,
4
+ * LINKS and the key hint. The design target is `reference/screens.js` → `launch`
5
+ * in the TUI redesign handoff.
6
+ *
7
+ * Pure on purpose: facts in, lines out. Reading the machine is
8
+ * `launch-facts.ts`, and noticing that the server is listening is
9
+ * `launch-ready.ts`, so every state of this screen is asserted without starting
10
+ * a server.
11
+ *
12
+ * Every top-level name here is long and specific. The CLI ships as one
13
+ * scope-hoisted bundle, and a short name that matches a local inside a builtin
14
+ * policy or an audit detector makes the bundler rename that local, which
15
+ * changes the audit cache key and rescans every user's history (see
16
+ * `screenKit` in `src/hooks/tui.ts`).
17
+ */
18
+ import { screenKit } from "../src/hooks/tui";
19
+
20
+ /** The links the screen ends on. `https://` stays: GNOME/VTE and others only
21
+ * make a link clickable when it carries a scheme. The docs root is the live
22
+ * docs' home page (`docs/index.mdx`); the `/introduction` page #461 pointed at
23
+ * was removed when the docs site was rebuilt (#699) and has no redirect. */
24
+ export const LAUNCH_SCREEN_LINKS: ReadonlyArray<readonly [string, string]> = [
25
+ ["docs", "https://docs.befailproof.ai"],
26
+ ["discord", "https://discord.befailproof.ai/"],
27
+ ["github", "https://github.com/failproofai/failproofai"],
28
+ ["reddit", "https://www.reddit.com/r/failproofai/"],
29
+ ];
30
+
31
+ export interface LaunchScreenFacts {
32
+ /** The address to open, from the bind address and port (`dashboardBrowseUrl`). */
33
+ url: string;
34
+ /** The server is listening. False when it was still starting at the deadline, or in dev mode. */
35
+ live: boolean;
36
+ /** The `policies` value (`describePoliciesOn`). */
37
+ policies: string;
38
+ /** The `agents` value (`describeTracedAgents`). */
39
+ agents: string;
40
+ /** The `cloud` value (`describeCloudConnection`). */
41
+ cloud: string;
42
+ /** Set when the bind address is reachable from other machines. Outranks every other warning. */
43
+ exposed?: { host: string; fix: string };
44
+ /** `notEnforcingReason()` answered with a cause. */
45
+ notEnforcing: boolean;
46
+ }
47
+
48
+ export interface LaunchScreenOpts {
49
+ color: boolean;
50
+ cols?: number;
51
+ /** Draw the logomark. The caller decides: a terminal only, and not after the setup wizard drew one. */
52
+ logo: boolean;
53
+ version?: string;
54
+ }
55
+
56
+ export function launchScreenLines(facts: LaunchScreenFacts, opts: LaunchScreenOpts): string[] {
57
+ const k = screenKit({ color: opts.color, cols: opts.cols, version: opts.version });
58
+ const art = opts.logo ? k.logo() : [];
59
+ // One attention line at most. The exposed bind is a security warning and
60
+ // outranks the state one; the fix sits on its own line because the sentence
61
+ // carries all three consequences and is too long to share one.
62
+ const attention = facts.exposed
63
+ ? [k.caution(exposedBindWarning(facts.exposed.host)), ` ${k.cmd(facts.exposed.fix)}`]
64
+ : facts.notEnforcing
65
+ ? [k.caution("Policies are not enforcing yet.", "failproofai config")]
66
+ : [];
67
+ return [
68
+ "",
69
+ ...(art.length > 0 ? [...art, ""] : []),
70
+ k.header("End-to-end failure layer for AI agents"),
71
+ "",
72
+ k.head("Dashboard"),
73
+ ...k.kv([
74
+ ["url", `${k.cmd(facts.url)} ${facts.live ? `${k.on} live` : `${k.off} starting`}`],
75
+ ["policies", facts.policies],
76
+ ["agents", facts.agents],
77
+ ["cloud", facts.cloud],
78
+ ]),
79
+ ...(attention.length > 0 ? ["", ...attention] : []),
80
+ "",
81
+ k.head("Links"),
82
+ ...k.kv(LAUNCH_SCREEN_LINKS.map(([label, href]): [string, string] => [label, href])),
83
+ "",
84
+ k.keys(["ctrl+c stop the dashboard"]),
85
+ "",
86
+ ];
87
+ }
88
+
89
+ /**
90
+ * The address a browser can open for a bind address. Brackets an IPv6 literal,
91
+ * and turns a wildcard bind into this machine's loopback, which it also serves:
92
+ * `0.0.0.0` is not an address anyone can browse to.
93
+ */
94
+ export function dashboardBrowseUrl(bindHost: string, port: string | number): string {
95
+ const raw = bindHost.trim();
96
+ const host =
97
+ raw === "" || raw === "0.0.0.0"
98
+ ? "127.0.0.1"
99
+ : raw === "::" || raw === "[::]" || raw === "0:0:0:0:0:0:0:0"
100
+ ? "[::1]"
101
+ : raw.includes(":") && !raw.startsWith("[")
102
+ ? `[${raw}]`
103
+ : raw;
104
+ return `http://${host}:${port}`;
105
+ }
106
+
107
+ /** The security warning for a bind other machines can reach. All three consequences stay. */
108
+ export function exposedBindWarning(bindHost: string): string {
109
+ return (
110
+ `The dashboard is bound to ${bindHost}, which is reachable from outside this machine, and has no ` +
111
+ "authentication: anyone who can reach it can read your session transcripts, disable your policies " +
112
+ "and uninstall failproofai's hooks."
113
+ );
114
+ }
115
+
116
+ /** Policies switched on, per source that has any, and whether custom policy files exist. */
117
+ export interface LaunchPoliciesSummary {
118
+ /** A pack (`id@version`), the cloud deployment, or the legacy built-ins. */
119
+ sources: Array<{ label: string; count: number; pack: boolean }>;
120
+ /** Configured or convention policy files exist. Never imported to count what they hold. */
121
+ customFiles: boolean;
122
+ }
123
+
124
+ export function describePoliciesOn(summary: LaunchPoliciesSummary): string {
125
+ const total = summary.sources.reduce((n, source) => n + source.count, 0);
126
+ if (total === 0) return summary.customFiles ? "custom policy files only" : "none on";
127
+ const from =
128
+ summary.sources.length === 1
129
+ ? summary.sources[0].label
130
+ : `${summary.sources.length} ${summary.sources.every((source) => source.pack) ? "packs" : "sources"}`;
131
+ return `${total} on from ${from}${summary.customFiles ? ", plus custom policy files" : ""}`;
132
+ }
133
+
134
+ /**
135
+ * `9 traced: Claude Code, OpenAI Codex, GitHub Copilot, Cursor Agent and 5 more`.
136
+ * Four names and a count past five; five or fewer are all named, because
137
+ * "and 1 more" hides a name it had room for.
138
+ */
139
+ export function describeTracedAgents(names: readonly string[]): string {
140
+ if (names.length === 0) return "none traced";
141
+ const shown = names.length <= 5 ? names : names.slice(0, 4);
142
+ const rest = names.length - shown.length;
143
+ const list =
144
+ rest > 0
145
+ ? `${shown.join(", ")} and ${rest} more`
146
+ : shown.length === 1
147
+ ? shown[0]
148
+ : `${shown.slice(0, -1).join(", ")} and ${shown[shown.length - 1]}`;
149
+ return `${names.length} traced: ${list}`;
150
+ }
151
+
152
+ /** The connection, in the precedence `config --status` uses. */
153
+ export type LaunchCloudSummary =
154
+ | { kind: "environment"; url: string }
155
+ | { kind: "none" }
156
+ | {
157
+ kind: "connected";
158
+ /** The org recorded at connect time, or null when the server did not name one. */
159
+ org: string | null;
160
+ host: string;
161
+ /** A policy credential: managed policies are pulled. */
162
+ pulling: boolean;
163
+ /** An ingest credential: hook activity is sent. */
164
+ sending: boolean;
165
+ /** The server definitively refused uploads, by status code. Overrides everything else. */
166
+ refused: { codes: number[]; credential: boolean } | null;
167
+ };
168
+
169
+ export function describeCloudConnection(summary: LaunchCloudSummary): string {
170
+ if (summary.kind === "environment") return `Configured by environment (${summary.url})`;
171
+ if (summary.kind === "none") return "Not connected";
172
+ const lead = summary.org ? `Connected as ${summary.org}` : `Connected to ${summary.host}`;
173
+ if (summary.refused) {
174
+ const what = summary.refused.credential ? "its key is" : "its uploads are";
175
+ return `${lead}, but ${what} refused (${summary.refused.codes.join("/")})`;
176
+ }
177
+ if (!summary.sending) return `${lead}, not sending activity`;
178
+ if (!summary.pulling) return `${lead}, reporting only`;
179
+ return lead;
180
+ }
181
+
182
+ /** Why the dashboard did not start, as the ✕ block launch prints before exiting. */
183
+ export function launchFailureLines(
184
+ failure: { portInUse: boolean; cause: string | null; code: number | null },
185
+ opts: { port: string; portFromFlag: boolean; color: boolean },
186
+ ): string[] {
187
+ const k = screenKit({ color: opts.color });
188
+ if (failure.portInUse) {
189
+ // The shipped CLI always serves on 8020 (the bin refuses --port), so the
190
+ // way out is to free the port. Only the contributor scripts take --port.
191
+ const fix = opts.portFromFlag
192
+ ? `Pass a different ${k.cmd("--port")}, or stop whatever is using ${opts.port}.`
193
+ : `Stop whatever is using it, often another failproofai dashboard, then run ${k.cmd("failproofai")} again.`;
194
+ return ["", k.fail(`Port ${opts.port} is already in use, so the dashboard did not start.`), ` ${fix}`, ""];
195
+ }
196
+ const text = failure.cause
197
+ ? `The dashboard did not start: ${failure.cause}`
198
+ : failure.code === null
199
+ ? "The dashboard stopped before it was ready."
200
+ : `The dashboard stopped before it was ready (exit code ${failure.code}).`;
201
+ return ["", k.fail(text), ""];
202
+ }