failproofai 1.0.0-beta.4 → 1.0.0-beta.6

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 (369) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +2 -1
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +3 -3
  5. package/.next/standalone/.next/required-server-files.json +1 -1
  6. package/.next/standalone/.next/routes-manifest.json +6 -0
  7. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page.js +3 -1
  9. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  10. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  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 +2 -2
  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/_head.segment.rsc +3 -3
  16. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  17. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page.js +3 -1
  20. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  22. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  23. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  24. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  25. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  26. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  27. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  28. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  29. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  30. package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
  31. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -3
  33. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/audit/status/route.js +1 -1
  35. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -1
  37. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +3 -2
  39. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -2
  41. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +3 -2
  43. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -2
  45. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  47. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  49. package/.next/standalone/.next/server/app/audit/page.js +5 -3
  50. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/app/index.html +1 -1
  53. package/.next/standalone/.next/server/app/index.rsc +15 -15
  54. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  55. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  56. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  57. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  58. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  59. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  60. package/.next/standalone/.next/server/app/page.js +3 -1
  61. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +10 -10
  64. package/.next/standalone/.next/server/app/policies/page.js +4 -2
  65. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  68. package/.next/standalone/.next/server/app/project/[name]/page.js +4 -2
  69. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  72. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  73. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
  74. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  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 +4 -2
  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/app-paths-manifest.json +3 -0
  81. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +18 -0
  82. package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +6 -0
  83. package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
  84. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +53 -0
  85. package/.next/standalone/.next/server/app/settings/page.js +18 -0
  86. package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
  87. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
  88. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
  89. package/.next/standalone/.next/server/app-paths-manifest.json +2 -1
  90. package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +3 -0
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +3 -0
  95. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
  96. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ky_n0r._.js} +2 -2
  97. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0lk_935._.js} +2 -2
  98. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p7s8-d._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
  99. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
  100. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0v2fpxe._.js} +2 -2
  101. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +3 -3
  102. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-dh5jy._.js → [root-of-the-server]__1_ps0jw._.js} +2 -2
  103. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__1a7i2q4._.js} +1 -1
  104. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1iny24d._.js +3 -0
  106. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tyedjt._.js +3 -0
  108. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +5 -0
  109. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +5 -0
  110. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +5 -0
  111. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
  112. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +10 -10
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_a_gwn._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1a2hcb_._.js +3 -0
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1e7-pa7._.js +3 -0
  119. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rtdfof._.js → [root-of-the-server]__1rdruw4._.js} +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tyf0tc._.js +5 -0
  122. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +4 -0
  123. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rag5dg._.js → [root-of-the-server]__1yw368c._.js} +2 -2
  125. package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +3 -0
  126. package/.next/standalone/.next/server/chunks/ssr/_04mekdy._.js +3 -0
  127. package/.next/standalone/.next/server/chunks/ssr/_0igtyj5._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/_0r-dsre._.js +3 -0
  129. package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/_0se484m._.js +3 -0
  131. package/.next/standalone/.next/server/chunks/ssr/_0uz7o9y._.js +3 -0
  132. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  133. package/.next/standalone/.next/server/chunks/ssr/_1fahg9e._.js +3 -0
  134. package/.next/standalone/.next/server/chunks/ssr/_1gmzkwj._.js +5 -0
  135. package/.next/standalone/.next/server/chunks/ssr/_1w8sola._.js +3 -0
  136. package/.next/standalone/.next/server/chunks/ssr/_1w_6p9g._.js +3 -0
  137. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
  138. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  140. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
  141. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
  142. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +5 -0
  143. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  144. package/.next/standalone/.next/server/middleware.js +2 -2
  145. package/.next/standalone/.next/server/pages/404.html +1 -1
  146. package/.next/standalone/.next/server/pages/500.html +1 -1
  147. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  148. package/.next/standalone/.next/server/server-reference-manifest.json +54 -12
  149. package/.next/standalone/.next/static/chunks/{2c90h1g6x8z8y.js → 0_2n13i94rnw5.js} +1 -1
  150. package/.next/standalone/.next/static/chunks/1bm0q12hgs22l.css +2 -0
  151. package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
  152. package/.next/standalone/.next/static/chunks/23557z4e6dtsr.js +1 -0
  153. package/.next/standalone/.next/static/chunks/28zkwzu50puki.js +1 -0
  154. package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
  155. package/.next/standalone/.next/static/chunks/3081gykdubv8b.js +1 -0
  156. package/.next/standalone/.next/static/chunks/{06jakt_xhoolk.js → 34fa65k0cldkj.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/35v07j9gc1pvr.js +6 -0
  158. package/.next/standalone/.next/static/chunks/{2zb3m1tzq6h98.js → 3f_939wi3kfnx.js} +22 -22
  159. package/.next/standalone/.next/static/chunks/{31cqiv_cpgj9m.js → 3p1vpm2_-tfwm.js} +1 -1
  160. package/.next/standalone/.next/static/chunks/{0ue9ak_xexiqh.js → 3rbpw_waieafm.js} +1 -1
  161. package/.next/standalone/.next/static/chunks/40ffnfpkeyvb0.js +1 -0
  162. package/.next/standalone/app/actions/get-hooks-config.ts +2 -1
  163. package/.next/standalone/app/actions/get-scheduled-audit.ts +70 -0
  164. package/.next/standalone/app/actions/update-scheduled-audit.ts +44 -0
  165. package/.next/standalone/app/api/audit/run/route.ts +28 -0
  166. package/.next/standalone/app/api/audit/status/route.ts +13 -1
  167. package/.next/standalone/app/api/auth/status/route.ts +1 -1
  168. package/.next/standalone/app/audit/_components/auth-dialog.css +159 -0
  169. package/.next/standalone/app/audit/_components/auth-dialog.tsx +3 -0
  170. package/.next/standalone/app/audit/audit-styles.css +6 -149
  171. package/.next/standalone/app/settings/page.tsx +31 -0
  172. package/.next/standalone/app/settings/settings-client.tsx +488 -0
  173. package/.next/standalone/components/navbar.tsx +2 -0
  174. package/.next/standalone/lib/auth/api-server-client.ts +2 -3
  175. package/.next/standalone/lib/auth/auth-store.ts +4 -4
  176. package/.next/standalone/lib/codex-sessions.ts +2 -1
  177. package/.next/standalone/lib/dashboard-host.ts +101 -0
  178. package/.next/standalone/lib/install-check.ts +22 -7
  179. package/.next/standalone/lib/pi-sessions.ts +99 -14
  180. package/.next/standalone/lib/telemetry-enabled.ts +54 -0
  181. package/.next/standalone/lib/telemetry-id.ts +52 -1
  182. package/.next/standalone/lib/telemetry.ts +8 -4
  183. package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
  184. package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
  185. package/.next/standalone/node_modules/react/package.json +3 -3
  186. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
  187. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
  188. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
  189. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
  190. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
  191. package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
  192. package/.next/standalone/node_modules/react-dom/package.json +4 -4
  193. package/.next/standalone/package.json +7 -6
  194. package/.next/standalone/proxy.ts +89 -1
  195. package/.next/standalone/server.js +1 -1
  196. package/bin/failproofai.mjs +197 -80
  197. package/dist/cli.mjs +17699 -15345
  198. package/dist/worker.mjs +1620 -257
  199. package/lib/auth/api-server-client.ts +2 -3
  200. package/lib/auth/auth-store.ts +4 -4
  201. package/lib/codex-sessions.ts +2 -1
  202. package/lib/dashboard-host.ts +101 -0
  203. package/lib/install-check.ts +22 -7
  204. package/lib/pi-sessions.ts +99 -14
  205. package/lib/telemetry-enabled.ts +54 -0
  206. package/lib/telemetry-id.ts +52 -1
  207. package/lib/telemetry.ts +8 -4
  208. package/package.json +7 -6
  209. package/scripts/install-telemetry.mjs +8 -1
  210. package/scripts/launch.ts +25 -3
  211. package/scripts/parse-script-args.ts +17 -1
  212. package/scripts/prune-standalone.mjs +20 -0
  213. package/scripts/translate-docs/readme-translator.ts +163 -4
  214. package/scripts/validate-mdx.ts +153 -13
  215. package/src/audit/audit-lock.ts +285 -0
  216. package/src/audit/audit-schedule.ts +111 -0
  217. package/src/audit/cache.ts +18 -4
  218. package/src/audit/cli-adapters/goose.ts +2 -1
  219. package/src/audit/cli-adapters/hermes.ts +28 -10
  220. package/src/audit/cli.ts +191 -35
  221. package/src/audit/dashboard-cache.ts +2 -1
  222. package/src/hooks/builtin-policies.ts +100 -3
  223. package/src/hooks/cloud-connection.ts +163 -14
  224. package/src/hooks/cloud-enrollment-cli.ts +119 -21
  225. package/src/hooks/cloud-enrollment.ts +87 -17
  226. package/src/hooks/cloud-introspect.ts +147 -0
  227. package/src/hooks/cloud-managed-policies.ts +33 -2
  228. package/src/hooks/collector-config.ts +32 -40
  229. package/src/hooks/configure-wizard.ts +696 -318
  230. package/src/hooks/custom-hooks-loader.ts +125 -11
  231. package/src/hooks/daemon-client.ts +83 -17
  232. package/src/hooks/daemon-download.ts +54 -2
  233. package/src/hooks/daemon-service.ts +595 -27
  234. package/src/hooks/first-run-gate.ts +39 -0
  235. package/src/hooks/fp-config.ts +497 -0
  236. package/src/hooks/fp-home.ts +342 -0
  237. package/src/hooks/fp-reset.ts +332 -0
  238. package/src/hooks/handler.ts +42 -1
  239. package/src/hooks/hook-activity-store.ts +35 -20
  240. package/src/hooks/hook-logger.ts +2 -1
  241. package/src/hooks/hook-telemetry.ts +5 -1
  242. package/src/hooks/hooks-config.ts +4 -3
  243. package/src/hooks/loader-utils.ts +166 -5
  244. package/src/hooks/manager.ts +5 -4
  245. package/src/hooks/onboarding-lock.ts +119 -0
  246. package/src/hooks/policy-types.ts +0 -7
  247. package/src/hooks/session-pause-cli.ts +15 -13
  248. package/src/hooks/session-pause.ts +36 -4
  249. package/src/hooks/setup-state.ts +256 -0
  250. package/src/hooks/tui.ts +4 -4
  251. package/src/hooks/worker-server.ts +53 -1
  252. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0f8nu8d._.js +0 -3
  253. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0szqzl8._.js +0 -3
  254. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
  255. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
  256. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
  257. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
  258. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
  259. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
  260. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cmdyn_._.js +0 -3
  261. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
  262. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
  263. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ls0gsr._.js +0 -4
  264. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n_uvv0._.js +0 -3
  265. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
  266. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0sdrz-j._.js +0 -3
  267. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +0 -4
  268. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
  269. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1j4u3yd._.js +0 -4
  270. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
  271. package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
  272. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1q9xxca.js +0 -3
  273. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
  274. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
  275. package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
  276. package/.next/standalone/.next/static/chunks/0oe2-hp0_2tzs.js +0 -6
  277. package/.next/standalone/.next/static/chunks/1fw3pxobk_otl.js +0 -1
  278. package/.next/standalone/.next/static/chunks/3h5_3u46gu9jx.js +0 -1
  279. package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
  280. package/.next/standalone/.next/static/chunks/3zdwnnthucs_1.css +0 -2
  281. package/.next/standalone/.next/static/chunks/43a1ywqsg9bq9.js +0 -1
  282. package/.next/standalone/Cargo.lock +0 -2222
  283. package/.next/standalone/Cargo.toml +0 -9
  284. package/.next/standalone/crates/CLOUD_POLICIES.md +0 -136
  285. package/.next/standalone/crates/PROTOCOL.md +0 -122
  286. package/.next/standalone/crates/failproofaid/Cargo.toml +0 -25
  287. package/.next/standalone/crates/failproofaid/src/cloud_client.rs +0 -484
  288. package/.next/standalone/crates/failproofaid/src/cloud_policies.rs +0 -921
  289. package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
  290. package/.next/standalone/crates/failproofaid/src/main.rs +0 -532
  291. package/.next/standalone/crates/failproofaid/src/paths.rs +0 -201
  292. package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
  293. package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
  294. package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
  295. package/.next/standalone/crates/fpai-collect/Cargo.toml +0 -43
  296. package/.next/standalone/crates/fpai-collect/src/config.rs +0 -412
  297. package/.next/standalone/crates/fpai-collect/src/cursor.rs +0 -520
  298. package/.next/standalone/crates/fpai-collect/src/delivery.rs +0 -472
  299. package/.next/standalone/crates/fpai-collect/src/filetail.rs +0 -484
  300. package/.next/standalone/crates/fpai-collect/src/health.rs +0 -369
  301. package/.next/standalone/crates/fpai-collect/src/lib.rs +0 -34
  302. package/.next/standalone/crates/fpai-collect/src/redact.rs +0 -516
  303. package/.next/standalone/crates/fpai-collect/src/sources/claude/mod.rs +0 -279
  304. package/.next/standalone/crates/fpai-collect/src/sources/claude/transform.rs +0 -619
  305. package/.next/standalone/crates/fpai-collect/src/sources/codex/mod.rs +0 -156
  306. package/.next/standalone/crates/fpai-collect/src/sources/codex/transform.rs +0 -715
  307. package/.next/standalone/crates/fpai-collect/src/sources/copilot/mod.rs +0 -158
  308. package/.next/standalone/crates/fpai-collect/src/sources/copilot/transform.rs +0 -512
  309. package/.next/standalone/crates/fpai-collect/src/sources/goose/mod.rs +0 -511
  310. package/.next/standalone/crates/fpai-collect/src/sources/goose/transform.rs +0 -586
  311. package/.next/standalone/crates/fpai-collect/src/sources/hermes/mod.rs +0 -318
  312. package/.next/standalone/crates/fpai-collect/src/sources/hermes/transform.rs +0 -579
  313. package/.next/standalone/crates/fpai-collect/src/sources/hooks/mod.rs +0 -310
  314. package/.next/standalone/crates/fpai-collect/src/sources/hooks/transform.rs +0 -448
  315. package/.next/standalone/crates/fpai-collect/src/sources/mod.rs +0 -23
  316. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/mod.rs +0 -176
  317. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/transform.rs +0 -500
  318. package/.next/standalone/crates/fpai-collect/src/sources/opencode/mod.rs +0 -315
  319. package/.next/standalone/crates/fpai-collect/src/sources/opencode/transform.rs +0 -493
  320. package/.next/standalone/crates/fpai-collect/src/sources/pi/mod.rs +0 -201
  321. package/.next/standalone/crates/fpai-collect/src/sources/pi/transform.rs +0 -533
  322. package/.next/standalone/crates/fpai-collect/src/spool.rs +0 -458
  323. package/.next/standalone/crates/fpai-collect/src/sqlitepoll.rs +0 -387
  324. package/.next/standalone/crates/fpai-collect/src/supervisor.rs +0 -423
  325. package/.next/standalone/crates/fpai-collect/src/uploader.rs +0 -600
  326. package/.next/standalone/crates/fpai-collect/tests/claude_source.rs +0 -738
  327. package/.next/standalone/crates/fpai-collect/tests/claude_subagents.rs +0 -393
  328. package/.next/standalone/crates/fpai-collect/tests/codex_source.rs +0 -975
  329. package/.next/standalone/crates/fpai-collect/tests/config.rs +0 -286
  330. package/.next/standalone/crates/fpai-collect/tests/copilot_source.rs +0 -1019
  331. package/.next/standalone/crates/fpai-collect/tests/delivery.rs +0 -281
  332. package/.next/standalone/crates/fpai-collect/tests/goose_source.rs +0 -1008
  333. package/.next/standalone/crates/fpai-collect/tests/hermes_source.rs +0 -1129
  334. package/.next/standalone/crates/fpai-collect/tests/hooks_source.rs +0 -621
  335. package/.next/standalone/crates/fpai-collect/tests/openclaw_source.rs +0 -854
  336. package/.next/standalone/crates/fpai-collect/tests/opencode_source.rs +0 -1478
  337. package/.next/standalone/crates/fpai-collect/tests/pi_source.rs +0 -1191
  338. package/.next/standalone/crates/fpai-collect/tests/supervisor.rs +0 -290
  339. package/.next/standalone/crates/fpai-collect/tests/uploader.rs +0 -506
  340. package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
  341. package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
  342. package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
  343. package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
  344. package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
  345. package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
  346. package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
  347. package/.next/standalone/integration-suite/Dockerfile +0 -47
  348. package/.next/standalone/integration-suite/README.md +0 -129
  349. package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
  350. package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
  351. package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
  352. package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
  353. package/.next/standalone/integration-suite/install-clis.sh +0 -136
  354. package/.next/standalone/integration-suite/probe-cli.sh +0 -248
  355. package/.next/standalone/integration-suite/report.js +0 -120
  356. package/.next/standalone/integration-suite/run.sh +0 -159
  357. package/.next/standalone/openclaw-plugin/index.js +0 -223
  358. package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
  359. package/.next/standalone/openclaw-plugin/package.json +0 -12
  360. package/.next/standalone/osv-scanner.toml +0 -17
  361. package/.next/standalone/pi-extension/index.ts +0 -524
  362. package/.next/standalone/pi-extension/package.json +0 -12
  363. package/.next/standalone/postcss.config.mjs +0 -8
  364. package/.next/standalone/readme-arch-hq.gif +0 -0
  365. package/.next/standalone/rust-toolchain.toml +0 -4
  366. package/src/auth/cli.ts +0 -371
  367. /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_buildManifest.js +0 -0
  368. /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_clientMiddlewareManifest.js +0 -0
  369. /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_ssgManifest.js +0 -0
@@ -255,155 +255,12 @@
255
255
  margin-right: 6px;
256
256
  }
257
257
 
258
- /* ───────────────────────── auth dialog (set-a-reminder gate) ───────────────────────── */
259
-
260
- .auth-dialog-backdrop {
261
- position: fixed; inset: 0; z-index: 10000;
262
- display: grid; place-items: center;
263
- padding: 32px 16px;
264
- background: rgba(8,8,10,0.7);
265
- backdrop-filter: blur(6px);
266
- -webkit-backdrop-filter: blur(6px);
267
- }
268
-
269
- .auth-dialog {
270
- position: relative;
271
- width: 100%;
272
- max-width: 420px;
273
- padding: 28px 28px 24px;
274
- border: 1px solid var(--line-2);
275
- background: var(--bg-2);
276
- font-family: var(--font-mono);
277
- color: var(--ink);
278
- }
279
-
280
- .auth-close {
281
- position: absolute; top: 12px; right: 14px;
282
- font-family: var(--font-mono); font-size: 20px;
283
- line-height: 1;
284
- color: var(--dim);
285
- background: transparent; border: none; padding: 4px 8px;
286
- cursor: pointer;
287
- transition: color 120ms;
288
- }
289
- .auth-close:hover { color: var(--accent-pink); }
290
- .auth-close:disabled { color: var(--line-2); cursor: not-allowed; }
291
-
292
- .auth-headline {
293
- font-family: var(--font-mono);
294
- font-size: 18px;
295
- font-weight: 600;
296
- letter-spacing: 0;
297
- line-height: 1.3;
298
- text-transform: none;
299
- color: var(--ink);
300
- margin: 0 0 8px;
301
- }
302
-
303
- .auth-sub {
304
- font-family: var(--font-mono); font-size: 12px;
305
- line-height: 1.55; color: var(--ink-2);
306
- margin: 0 0 18px;
307
- }
308
- .auth-sub .auth-email {
309
- color: var(--accent-pink);
310
- }
311
- .auth-sub .auth-ok {
312
- color: var(--accent-green);
313
- margin-right: 6px;
314
- }
315
-
316
- .auth-form { display: flex; flex-direction: column; gap: 10px; }
317
-
318
- .auth-field-label {
319
- font-family: var(--font-mono); font-size: 10px;
320
- letter-spacing: 0.22em; text-transform: uppercase;
321
- color: var(--accent-green);
322
- }
323
-
324
- .auth-input {
325
- width: 100%;
326
- padding: 11px 14px;
327
- background: var(--bg);
328
- border: 1px solid var(--line-2);
329
- color: var(--ink);
330
- font-family: var(--font-mono); font-size: 14px;
331
- letter-spacing: 0.03em;
332
- outline: none;
333
- transition: border-color 120ms, box-shadow 120ms;
334
- }
335
- .auth-input:focus {
336
- border-color: var(--accent-pink);
337
- box-shadow: 0 0 0 1px var(--accent-pink-soft);
338
- }
339
- .auth-input:disabled {
340
- opacity: 0.55; cursor: not-allowed;
341
- }
342
- .auth-input-code {
343
- letter-spacing: 0.5em;
344
- text-align: center;
345
- font-size: 18px;
346
- font-variant-numeric: tabular-nums;
347
- }
348
- .auth-input::placeholder { color: var(--dim); }
349
-
350
- .auth-error {
351
- font-family: var(--font-mono); font-size: 12px;
352
- color: var(--accent-pink);
353
- padding: 8px 12px;
354
- background: var(--accent-pink-bg);
355
- border: 1px solid var(--accent-pink);
356
- border-left-width: 3px;
357
- letter-spacing: 0.02em;
358
- margin-top: 4px;
359
- }
360
-
361
- .auth-actions {
362
- display: flex; gap: 10px; flex-wrap: wrap;
363
- margin-top: 14px;
364
- }
365
-
366
- .auth-btn {
367
- display: inline-flex; align-items: center; gap: 8px;
368
- padding: 10px 16px;
369
- font-family: var(--font-mono); font-size: 12px;
370
- letter-spacing: 0.06em;
371
- background: transparent;
372
- border: 1px solid var(--line-2);
373
- color: var(--ink);
374
- cursor: pointer;
375
- transition: all 120ms ease;
376
- }
377
- .auth-btn:hover:not(:disabled) {
378
- border-color: var(--ink); background: rgba(255,255,255,0.04);
379
- }
380
- .auth-btn:disabled { opacity: 0.45; cursor: not-allowed; }
381
- .auth-btn.primary {
382
- border-color: var(--accent-pink);
383
- color: var(--accent-pink);
384
- background: var(--accent-pink-bg);
385
- }
386
- .auth-btn.primary:hover:not(:disabled) {
387
- background: var(--accent-pink); color: var(--bg);
388
- }
389
-
390
- .auth-back {
391
- align-self: flex-start;
392
- margin-top: 4px;
393
- background: transparent; border: none; padding: 6px 0;
394
- font-family: var(--font-mono); font-size: 11px;
395
- letter-spacing: 0.1em; color: var(--dim);
396
- cursor: pointer;
397
- transition: color 120ms;
398
- }
399
- .auth-back:hover:not(:disabled) { color: var(--ink-2); }
400
- .auth-back:disabled { opacity: 0.45; cursor: not-allowed; }
401
-
402
- @media (max-width: 520px) {
403
- .auth-dialog { padding: 26px 22px 22px; }
404
- .auth-actions { flex-direction: column; align-items: stretch; }
405
- .auth-btn { justify-content: center; }
406
- }
258
+ /* ───────────────────────── auth dialog (set-a-reminder gate) ─────────────────────────
259
+ * The dialog's own styles moved to app/audit/_components/auth-dialog.css, which
260
+ * <AuthDialog> imports directly, so the modal is styled on every route that
261
+ * renders it (/audit and now /settings) rather than only this route stylesheet.
262
+ * The `.auth-status-pill` below stays here — it belongs to the /audit return
263
+ * CTA (come-back-better-section), not the dialog. */
407
264
 
408
265
  /* status pill in the return CTA: shows current logged-in email */
409
266
  .auth-status-pill {
@@ -0,0 +1,31 @@
1
+ /**
2
+ * /settings — one page, sections. The single home for the machine-level
3
+ * controls the design plan collected here: the scheduled local audit and
4
+ * emailed audit reports.
5
+ *
6
+ * Deliberately NOT a home for telemetry: the product decision is that telemetry
7
+ * is documented but not advertised in-product, so there is no telemetry control
8
+ * or status on this page. `config.toml` plus the docs are the whole story.
9
+ *
10
+ * Thin server wrapper (Suspense boundary + the disabled-pages gate every route
11
+ * uses); all the reads/writes live in the client and its server actions.
12
+ */
13
+ import { Suspense } from "react";
14
+ import { notFound } from "next/navigation";
15
+ import SettingsClient from "./settings-client";
16
+
17
+ export const dynamic = "force-dynamic";
18
+
19
+ export default async function SettingsPage() {
20
+ const disabled = (process.env.FAILPROOFAI_DISABLE_PAGES ?? "")
21
+ .split(",")
22
+ .map((s) => s.trim())
23
+ .filter(Boolean);
24
+ if (disabled.includes("settings")) notFound();
25
+
26
+ return (
27
+ <Suspense>
28
+ <SettingsClient />
29
+ </Suspense>
30
+ );
31
+ }
@@ -0,0 +1,488 @@
1
+ "use client";
2
+
3
+ /**
4
+ * /settings client — two sections (scheduled audit, email reports) plus the
5
+ * degraded states that are most of the real screens: daemon not installed /
6
+ * stopped / unsupported, no scan ever run, a scan running now, a last run that
7
+ * failed, signed out, and not cloud-enrolled. Each is shown explicitly, because
8
+ * a missing control reads as a bug.
9
+ *
10
+ * Visual conventions are the site chrome's, matched to /policies: the brutalist
11
+ * `.report`/`.section`/`.panel`/`.btn` classes from globals.css, the same
12
+ * emerald switch /policies uses (PolicyToggle), inline `var(--…)` colours, and
13
+ * the shared `toast()`. No new design language, colour, or component library.
14
+ *
15
+ * All writes go through server actions that call `updateConfig` — never a raw
16
+ * file write — so the CLI and dashboard cannot diverge. The parity mapping is
17
+ * documented on each action module.
18
+ */
19
+
20
+ import React, { useCallback, useEffect, useRef, useState } from "react";
21
+ import { getScheduledAuditAction, type ScheduledAuditView } from "@/app/actions/get-scheduled-audit";
22
+ import { setAutoAuditAction, setAuditIntervalAction } from "@/app/actions/update-scheduled-audit";
23
+ import { triggerRun, RerunError } from "@/app/audit/_components/rerun-button";
24
+ import { AuthDialog } from "@/app/audit/_components/auth-dialog";
25
+ import { toast } from "@/app/components/toast";
26
+ import { fetchWithTimeout } from "@/lib/fetch-with-timeout";
27
+ import { formatRelativeTime } from "@/lib/format-duration";
28
+
29
+ // ── formatting helpers ───────────────────────────────────────────────────────
30
+
31
+ function fmtAbsolute(ms: number): string {
32
+ return new Date(ms).toLocaleString(undefined, {
33
+ month: "short",
34
+ day: "numeric",
35
+ hour: "2-digit",
36
+ minute: "2-digit",
37
+ });
38
+ }
39
+
40
+ /** "in 6d" / "in 3h" / "in 12m" / "now". formatRelativeTime only speaks past. */
41
+ function fmtFuture(ms: number): string {
42
+ const diff = ms - Date.now();
43
+ if (diff <= 0) return "now";
44
+ if (diff < 3_600_000) return `in ${Math.max(1, Math.floor(diff / 60_000))}m`;
45
+ if (diff < 86_400_000) return `in ${Math.floor(diff / 3_600_000)}h`;
46
+ return `in ${Math.floor(diff / 86_400_000)}d`;
47
+ }
48
+
49
+ // ── shared primitives (match /policies) ──────────────────────────────────────
50
+
51
+ /** The exact switch /policies uses — copied shape, not a new control. */
52
+ function Toggle({
53
+ enabled,
54
+ onChange,
55
+ disabled,
56
+ label,
57
+ }: {
58
+ enabled: boolean;
59
+ onChange: () => void;
60
+ disabled?: boolean;
61
+ label: string;
62
+ }) {
63
+ return (
64
+ <button
65
+ type="button"
66
+ onClick={onChange}
67
+ disabled={disabled}
68
+ role="switch"
69
+ aria-checked={enabled}
70
+ aria-label={label}
71
+ className={`relative inline-flex h-4 w-7 shrink-0 items-center rounded-full transition-colors disabled:opacity-40 ${
72
+ enabled ? "bg-emerald-500" : "bg-muted-foreground/30"
73
+ }`}
74
+ >
75
+ <span
76
+ className={`inline-block h-3 w-3 rounded-full bg-white shadow transition-transform ${
77
+ enabled ? "translate-x-3.5" : "translate-x-0.5"
78
+ }`}
79
+ />
80
+ </button>
81
+ );
82
+ }
83
+
84
+ type PillTone = "ok" | "warn" | "bad" | "muted";
85
+ const PILL_TONE: Record<PillTone, { fg: string; bg: string; bd: string }> = {
86
+ ok: { fg: "var(--accent-green)", bg: "rgba(102,209,181,0.10)", bd: "rgba(102,209,181,0.30)" },
87
+ warn: { fg: "var(--amber)", bg: "rgba(232,196,106,0.10)", bd: "rgba(232,196,106,0.30)" },
88
+ bad: { fg: "var(--accent-pink)", bg: "rgba(228,88,124,0.10)", bd: "rgba(228,88,124,0.30)" },
89
+ muted: { fg: "var(--ink-2)", bg: "transparent", bd: "var(--line-2)" },
90
+ };
91
+
92
+ function Pill({ tone, children }: { tone: PillTone; children: React.ReactNode }) {
93
+ const t = PILL_TONE[tone];
94
+ return (
95
+ <span
96
+ style={{
97
+ display: "inline-flex",
98
+ alignItems: "center",
99
+ gap: 6,
100
+ padding: "3px 9px",
101
+ fontFamily: "var(--font-mono)",
102
+ fontSize: 11,
103
+ letterSpacing: "0.04em",
104
+ color: t.fg,
105
+ background: t.bg,
106
+ border: `1px solid ${t.bd}`,
107
+ textTransform: "uppercase",
108
+ }}
109
+ >
110
+ {children}
111
+ </span>
112
+ );
113
+ }
114
+
115
+ const SECTION_TITLE: React.CSSProperties = {
116
+ fontFamily: "var(--font-mono)",
117
+ fontSize: 16,
118
+ fontWeight: 600,
119
+ letterSpacing: "-0.01em",
120
+ color: "var(--ink)",
121
+ margin: "0 0 4px",
122
+ };
123
+ const BODY: React.CSSProperties = {
124
+ fontFamily: "var(--font-mono)",
125
+ fontSize: 13,
126
+ color: "var(--ink-2)",
127
+ lineHeight: 1.65,
128
+ margin: 0,
129
+ };
130
+ const MUTED: React.CSSProperties = { ...BODY, color: "var(--dim)", fontSize: 12 };
131
+ const CODE: React.CSSProperties = { color: "var(--ink)", fontVariantLigatures: "none" };
132
+
133
+ /** A monospace inline command the user can copy by eye. */
134
+ function Cmd({ children }: { children: React.ReactNode }) {
135
+ return (
136
+ <code
137
+ style={{
138
+ fontFamily: "var(--font-mono)",
139
+ fontSize: 12,
140
+ color: "var(--accent-green)",
141
+ background: "var(--bg-2)",
142
+ border: "1px solid var(--line-2)",
143
+ padding: "1px 6px",
144
+ whiteSpace: "nowrap",
145
+ }}
146
+ >
147
+ {children}
148
+ </code>
149
+ );
150
+ }
151
+
152
+ // ── scheduled audit section ──────────────────────────────────────────────────
153
+
154
+ function ScheduledAuditSection({
155
+ view,
156
+ onReload,
157
+ }: {
158
+ view: ScheduledAuditView;
159
+ onReload: () => Promise<void>;
160
+ }) {
161
+ const [auto, setAuto] = useState(view.auto);
162
+ const [interval, setIntervalDays] = useState(view.intervalDays);
163
+ const [savingAuto, setSavingAuto] = useState(false);
164
+ const [savingInterval, setSavingInterval] = useState(false);
165
+ const [running, setRunning] = useState(false);
166
+ const [runningNow, setRunningNow] = useState(false);
167
+
168
+ // Keep local state honest if a background reload brought new server truth
169
+ // (e.g. someone toggled via CLI, or the interval clamp changed the value).
170
+ useEffect(() => setAuto(view.auto), [view.auto]);
171
+ useEffect(() => setIntervalDays(view.intervalDays), [view.intervalDays]);
172
+
173
+ // Reflect a scan already in flight (started here or from /audit) so the button
174
+ // and status line don't claim the machine is idle when it isn't.
175
+ useEffect(() => {
176
+ let cancelled = false;
177
+ (async () => {
178
+ try {
179
+ const res = await fetchWithTimeout("/api/audit/status", { cache: "no-store" });
180
+ if (res.ok && !cancelled) {
181
+ const s = (await res.json()) as { running?: boolean };
182
+ setRunning(Boolean(s.running));
183
+ }
184
+ } catch {
185
+ /* status is best-effort; a missing poll just means we assume idle */
186
+ }
187
+ })();
188
+ return () => {
189
+ cancelled = true;
190
+ };
191
+ }, []);
192
+
193
+ const daemonInactive = view.daemon !== "running";
194
+ const daemonUnsupported = view.daemon === "unsupported-platform";
195
+
196
+ const onToggleAuto = useCallback(async () => {
197
+ const next = !auto;
198
+ setAuto(next); // optimistic
199
+ setSavingAuto(true);
200
+ try {
201
+ const res = await setAutoAuditAction(next);
202
+ setAuto(res.auto);
203
+ toast(res.auto ? "Scheduled scanning on." : "Scheduled scanning off.");
204
+ await onReload();
205
+ } catch {
206
+ setAuto(!next); // revert
207
+ toast("Could not save that.");
208
+ } finally {
209
+ setSavingAuto(false);
210
+ }
211
+ }, [auto, onReload]);
212
+
213
+ const commitInterval = useCallback(
214
+ async (raw: number) => {
215
+ setSavingInterval(true);
216
+ try {
217
+ // The config owns the 1..90 clamp; we reflect whatever it stored.
218
+ const res = await setAuditIntervalAction(raw);
219
+ setIntervalDays(res.intervalDays);
220
+ toast(`Scanning every ${res.intervalDays} day${res.intervalDays === 1 ? "" : "s"}.`);
221
+ } catch {
222
+ setIntervalDays(view.intervalDays);
223
+ toast("Could not save that.");
224
+ } finally {
225
+ setSavingInterval(false);
226
+ }
227
+ },
228
+ [view.intervalDays],
229
+ );
230
+
231
+ const onRunNow = useCallback(async () => {
232
+ if (runningNow || running) return;
233
+ setRunningNow(true);
234
+ setRunning(true);
235
+ try {
236
+ await triggerRun({ cli: [], since: "all", noCache: false });
237
+ toast("Audit complete.");
238
+ await onReload();
239
+ } catch (err) {
240
+ const msg =
241
+ err instanceof RerunError && err.kind === "timeout"
242
+ ? "The scan is taking a while — it will finish in the background."
243
+ : "The scan could not be completed.";
244
+ toast(msg);
245
+ } finally {
246
+ setRunningNow(false);
247
+ setRunning(false);
248
+ }
249
+ }, [runningNow, running, onReload]);
250
+
251
+ const sched = view.schedule;
252
+ const lastExitBad =
253
+ sched?.lastExitCode != null && sched.lastExitCode !== 0 && sched.lastExitCode !== 75;
254
+
255
+ return (
256
+ <div className="panel" style={{ padding: 20, marginBottom: 20 }}>
257
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, flexWrap: "wrap" }}>
258
+ <div>
259
+ <h3 style={SECTION_TITLE}>Scheduled audit</h3>
260
+ <p style={MUTED}>Scan this machine on a timer, in the background.</p>
261
+ </div>
262
+ <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
263
+ {view.daemon === "running" && <Pill tone="ok">daemon running</Pill>}
264
+ {view.daemon === "stopped" && <Pill tone="warn">daemon stopped</Pill>}
265
+ {view.daemon === "not-installed" && <Pill tone="warn">daemon not installed</Pill>}
266
+ {view.daemon === "unsupported-platform" && <Pill tone="muted">daemon unavailable</Pill>}
267
+ </div>
268
+ </div>
269
+
270
+ {/* Enable toggle + the plain statement about what the scan reads. */}
271
+ <div style={{ display: "flex", alignItems: "flex-start", gap: 12, marginTop: 18 }}>
272
+ <div style={{ marginTop: 2 }}>
273
+ <Toggle
274
+ enabled={auto}
275
+ onChange={onToggleAuto}
276
+ disabled={savingAuto || daemonUnsupported}
277
+ label={auto ? "Turn off scheduled scanning" : "Turn on scheduled scanning"}
278
+ />
279
+ </div>
280
+ <div style={{ minWidth: 0 }}>
281
+ <p style={{ ...BODY, color: "var(--ink)" }}>
282
+ {auto ? "Scanning this machine on a schedule." : "Scan this machine on a schedule."}
283
+ </p>
284
+ <p style={{ ...BODY, marginTop: 6 }}>
285
+ The scan reads the <span style={CODE}>contents</span> of every session transcript on
286
+ disk across all installed agent CLIs — your prompts, the files they read and wrote,
287
+ and command output. It runs entirely on this machine. Nothing is sent anywhere unless
288
+ you also turn on emailed reports below.
289
+ </p>
290
+ </div>
291
+ </div>
292
+
293
+ {/* Interval. The number bounds mirror the config's own 1..90 clamp as a UX
294
+ hint; the config remains the authority and we reflect what it stored. */}
295
+ <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 16, flexWrap: "wrap" }}>
296
+ <label htmlFor="scan-interval" style={{ ...BODY }}>
297
+ Scan every
298
+ </label>
299
+ <input
300
+ id="scan-interval"
301
+ type="number"
302
+ min={1}
303
+ max={90}
304
+ step={1}
305
+ value={interval}
306
+ disabled={savingInterval || daemonUnsupported}
307
+ onChange={(e) => setIntervalDays(Number(e.target.value))}
308
+ onBlur={(e) => {
309
+ const v = Number(e.target.value);
310
+ // A cleared/garbage field must not persist NaN — snap back to the
311
+ // stored value and let the config keep owning the real bounds.
312
+ if (!Number.isFinite(v)) {
313
+ setIntervalDays(view.intervalDays);
314
+ return;
315
+ }
316
+ if (v !== view.intervalDays) void commitInterval(v);
317
+ }}
318
+ style={{
319
+ width: 64,
320
+ padding: "6px 8px",
321
+ background: "var(--bg)",
322
+ border: "1px solid var(--line-2)",
323
+ color: "var(--ink)",
324
+ fontFamily: "var(--font-mono)",
325
+ fontSize: 13,
326
+ textAlign: "center",
327
+ }}
328
+ />
329
+ <span style={BODY}>day{interval === 1 ? "" : "s"}.</span>
330
+ <span style={MUTED}>1–90; the config keeps it in range.</span>
331
+ </div>
332
+
333
+ {/* Last run / next due — read from the daemon-written schedule file. */}
334
+ <div
335
+ style={{
336
+ marginTop: 18,
337
+ paddingTop: 16,
338
+ borderTop: "1px dashed var(--line)",
339
+ display: "flex",
340
+ flexDirection: "column",
341
+ gap: 6,
342
+ }}
343
+ >
344
+ {running && (
345
+ <p style={{ ...BODY, color: "var(--accent-green)" }}>A scan is running now…</p>
346
+ )}
347
+
348
+ {/* Last run */}
349
+ {sched?.lastRunAtMs != null ? (
350
+ <p style={BODY}>
351
+ Last scheduled scan:{" "}
352
+ <span style={CODE}>{fmtAbsolute(sched.lastRunAtMs)}</span>{" "}
353
+ <span style={MUTED}>({formatRelativeTime(sched.lastRunAtMs)})</span>
354
+ </p>
355
+ ) : view.lastResultAt ? (
356
+ <p style={BODY}>
357
+ Last audit result:{" "}
358
+ <span style={CODE}>{fmtAbsolute(new Date(view.lastResultAt).getTime())}</span>{" "}
359
+ <span style={MUTED}>(no scheduled scan has run yet)</span>
360
+ </p>
361
+ ) : (
362
+ <p style={BODY}>No scan has run yet.</p>
363
+ )}
364
+
365
+ {/* Next due */}
366
+ {auto ? (
367
+ sched?.nextDueAtMs != null ? (
368
+ <p style={BODY}>
369
+ Next scan due:{" "}
370
+ <span style={CODE}>{fmtAbsolute(sched.nextDueAtMs)}</span>{" "}
371
+ <span style={MUTED}>({fmtFuture(sched.nextDueAtMs)})</span>
372
+ </p>
373
+ ) : (
374
+ <p style={BODY}>
375
+ Next scan:{" "}
376
+ <span style={MUTED}>the daemon will schedule it shortly.</span>
377
+ </p>
378
+ )
379
+ ) : (
380
+ <p style={MUTED}>Scheduled scanning is off — no scan is scheduled.</p>
381
+ )}
382
+
383
+ {lastExitBad && (
384
+ <p style={{ ...BODY, color: "var(--amber)" }}>
385
+ The last scheduled scan exited with code {sched?.lastExitCode}. It will retry on the
386
+ next tick.
387
+ </p>
388
+ )}
389
+ {sched?.schemaAhead && (
390
+ <p style={MUTED}>
391
+ A newer daemon wrote this schedule; some fields may not be shown.
392
+ </p>
393
+ )}
394
+ </div>
395
+
396
+ {/* Degraded daemon guidance — say plainly why "on" may still not run. */}
397
+ {auto && daemonInactive && (
398
+ <p style={{ ...BODY, color: "var(--amber)", marginTop: 14 }}>
399
+ {daemonUnsupported ? (
400
+ <>The background daemon isn&apos;t available on this platform, so scheduled scans
401
+ can&apos;t run here. You can still run one now, and use the audit page.</>
402
+ ) : view.daemon === "not-installed" ? (
403
+ <>Scheduled scanning is on, but the background service isn&apos;t installed, so nothing
404
+ will run on the timer yet. Install it with <Cmd>failproofai config</Cmd>.</>
405
+ ) : (
406
+ <>Scheduled scanning is on, but the background service is stopped, so nothing will run
407
+ until it starts. Reinstall or repair it with <Cmd>failproofai config</Cmd>.</>
408
+ )}
409
+ </p>
410
+ )}
411
+
412
+ {/* Run now — reuses the existing /api/audit/run route via triggerRun. */}
413
+ <div style={{ marginTop: 18 }}>
414
+ <button
415
+ type="button"
416
+ className="btn"
417
+ onClick={onRunNow}
418
+ disabled={runningNow || running}
419
+ >
420
+ {runningNow || running ? "[ scanning… ]" : "[ run a scan now ]"}
421
+ </button>
422
+ </div>
423
+ </div>
424
+ );
425
+ }
426
+
427
+ // ── page ─────────────────────────────────────────────────────────────────────
428
+
429
+ export default function SettingsClient() {
430
+ const [scheduled, setScheduled] = useState<ScheduledAuditView | null>(null);
431
+ const [loading, setLoading] = useState(true);
432
+ const [error, setError] = useState(false);
433
+ const mounted = useRef(true);
434
+
435
+ const reload = useCallback(async () => {
436
+ const s = await getScheduledAuditAction();
437
+ if (!mounted.current) return;
438
+ setScheduled(s);
439
+ }, []);
440
+
441
+ useEffect(() => {
442
+ mounted.current = true;
443
+ (async () => {
444
+ try {
445
+ await reload();
446
+ } catch {
447
+ if (mounted.current) setError(true);
448
+ } finally {
449
+ if (mounted.current) setLoading(false);
450
+ }
451
+ })();
452
+ return () => {
453
+ mounted.current = false;
454
+ };
455
+ }, [reload]);
456
+
457
+ return (
458
+ <main className="report">
459
+ <section className="section" data-screen-label="settings">
460
+ <h2
461
+ style={{
462
+ fontFamily: "var(--font-mono)",
463
+ fontSize: 24,
464
+ fontWeight: 600,
465
+ letterSpacing: "-0.01em",
466
+ color: "var(--ink)",
467
+ margin: "0 0 8px",
468
+ }}
469
+ >
470
+ Settings
471
+ </h2>
472
+ <p style={{ ...BODY, maxWidth: 720, margin: "0 0 24px" }}>
473
+ Machine-level controls for scheduled scanning and emailed reports.
474
+ </p>
475
+
476
+ {loading ? (
477
+ <p style={MUTED}>Loading…</p>
478
+ ) : error || !scheduled ? (
479
+ <p style={{ ...BODY, color: "var(--accent-pink)" }}>
480
+ Could not load settings. Refresh to try again.
481
+ </p>
482
+ ) : (
483
+ <ScheduledAuditSection view={scheduled} onReload={reload} />
484
+ )}
485
+ </section>
486
+ </main>
487
+ );
488
+ }
@@ -19,6 +19,7 @@ const NAV_LINKS = [
19
19
  { href: "/projects", label: "projects" },
20
20
  { href: "/policies", label: "policies" },
21
21
  { href: "/audit", label: "audit" },
22
+ { href: "/settings", label: "settings" },
22
23
  ];
23
24
 
24
25
  const REMOTE_LOGO_URL =
@@ -59,6 +60,7 @@ export const Navbar: React.FC<{
59
60
  const sectionLabel = (() => {
60
61
  if (pathname.startsWith("/policies")) return "policies";
61
62
  if (pathname.startsWith("/audit")) return "audit";
63
+ if (pathname.startsWith("/settings")) return "settings";
62
64
  if (pathname.startsWith("/projects") || pathname.startsWith("/project/")) return "projects";
63
65
  return "";
64
66
  })();