failproofai 1.0.0-beta.2 → 1.0.0-beta.21

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 (381) 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 +6 -6
  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/build-manifest.json +3 -3
  8. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  9. package/.next/standalone/.next/server/app/_global-error/page.js +3 -1
  10. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  11. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  13. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  14. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  15. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  16. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  17. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  18. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
  20. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  21. package/.next/standalone/.next/server/app/_not-found/page.js +3 -1
  22. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  24. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  25. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  26. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  27. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  28. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  29. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  30. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  31. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  32. package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
  33. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -3
  35. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/audit/status/route.js +1 -1
  37. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +3 -1
  39. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +3 -2
  41. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -2
  43. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +3 -2
  45. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -2
  47. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  49. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
  51. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  52. package/.next/standalone/.next/server/app/audit/page.js +5 -3
  53. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  55. package/.next/standalone/.next/server/app/index.html +1 -1
  56. package/.next/standalone/.next/server/app/index.rsc +15 -15
  57. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  58. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  59. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  60. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  61. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  62. package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
  63. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  64. package/.next/standalone/.next/server/app/page.js +3 -1
  65. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
  68. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +30 -18
  69. package/.next/standalone/.next/server/app/policies/page.js +5 -3
  70. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  72. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
  73. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  74. package/.next/standalone/.next/server/app/project/[name]/page.js +4 -2
  75. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  76. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  77. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
  78. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  79. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  80. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
  81. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  82. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  83. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
  84. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  85. package/.next/standalone/.next/server/app/projects/page.js +4 -2
  86. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  88. package/.next/standalone/.next/server/app/settings/page/app-paths-manifest.json +3 -0
  89. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +18 -0
  90. package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +6 -0
  91. package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
  92. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +53 -0
  93. package/.next/standalone/.next/server/app/settings/page.js +18 -0
  94. package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
  95. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
  96. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
  97. package/.next/standalone/.next/server/app-paths-manifest.json +2 -1
  98. package/.next/standalone/.next/server/chunks/[externals]__0h9_83k._.js +3 -0
  99. package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
  100. package/.next/standalone/.next/server/chunks/[externals]__1ifpkx-._.js +3 -0
  101. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__00ni9za._.js} +2 -2
  102. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +3 -0
  103. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08y94xv._.js +3 -0
  104. package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  106. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +3 -0
  107. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-dh5jy._.js → [root-of-the-server]__0effm9m._.js} +2 -2
  108. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
  109. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0ix8gm7._.js} +2 -2
  110. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ji11a-._.js} +2 -2
  111. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0ojwjxl._.js} +2 -2
  112. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__16le-kd._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
  113. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
  114. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vb5fl2._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +4 -4
  116. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
  118. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20d06rh._.js +3 -0
  119. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +3 -0
  120. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +3 -0
  122. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
  123. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -1
  124. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -1
  125. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/[externals]__1aq139v._.js +3 -0
  127. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-wfhrp._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
  129. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1apb7k_._.js → [root-of-the-server]__0_1tiya._.js} +2 -2
  131. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__170799-._.js → [root-of-the-server]__0spkm68._.js} +11 -11
  132. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0t281d4._.js +3 -0
  133. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17itmx-._.js +5 -0
  134. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__01od0m6._.js → [root-of-the-server]__1n2vcv4._.js} +2 -2
  135. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +1 -1
  136. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
  137. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tlvv9a._.js +3 -0
  138. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +4 -0
  139. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +3 -0
  140. package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +3 -0
  141. package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
  142. package/.next/standalone/.next/server/chunks/ssr/_0vo2qyt._.js +3 -0
  143. package/.next/standalone/.next/server/chunks/ssr/_0xej-0e._.js +3 -0
  144. package/.next/standalone/.next/server/chunks/ssr/_10399ge._.js +3 -0
  145. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  146. package/.next/standalone/.next/server/chunks/ssr/_149k9oy._.js +3 -0
  147. package/.next/standalone/.next/server/chunks/ssr/_1_hwp06._.js +3 -0
  148. package/.next/standalone/.next/server/chunks/ssr/_1ctse6u._.js +3 -0
  149. package/.next/standalone/.next/server/chunks/ssr/_1e19atl._.js +1 -1
  150. package/.next/standalone/.next/server/chunks/ssr/_1mnhi_r._.js +3 -0
  151. package/.next/standalone/.next/server/chunks/ssr/_1ror7k9._.js +3 -0
  152. package/.next/standalone/.next/server/chunks/ssr/_1sdgprl._.js +5 -0
  153. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
  154. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  155. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  156. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
  157. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -1
  158. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
  159. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +4 -0
  160. package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
  161. package/.next/standalone/.next/server/middleware.js +2 -2
  162. package/.next/standalone/.next/server/pages/404.html +1 -1
  163. package/.next/standalone/.next/server/pages/500.html +1 -1
  164. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  165. package/.next/standalone/.next/server/server-reference-manifest.json +74 -20
  166. package/.next/standalone/.next/static/chunks/{03fmihek9n986.js → 06zgzyh3wv1ig.js} +1 -1
  167. package/.next/standalone/.next/static/chunks/0km-jn9o5xyqs.css +2 -0
  168. package/.next/standalone/.next/static/chunks/{1hdhteui932bq.js → 0qmo2t50kpb30.js} +1 -1
  169. package/.next/standalone/.next/static/chunks/{2sbelanzvxvv0.js → 113g8wtal6_c4.js} +22 -22
  170. package/.next/standalone/.next/static/chunks/16p68cybetr31.js +1 -0
  171. package/.next/standalone/.next/static/chunks/18wqn0_3mzylf.js +1 -0
  172. package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
  173. package/.next/standalone/.next/static/chunks/{0fi645si-uiiq.js → 1xq1m8h14tnlj.js} +1 -1
  174. package/.next/standalone/.next/static/chunks/{3hp8ulqknybil.js → 1y3vwogqb88dx.js} +1 -1
  175. package/.next/standalone/.next/static/chunks/{3vs4ay36tjds_.js → 1zmh-b0ulrqu7.js} +1 -1
  176. package/.next/standalone/.next/static/chunks/23kev26by1e4f.js +1 -0
  177. package/.next/standalone/.next/static/chunks/299lhw60e8gr6.js +1 -0
  178. package/.next/standalone/.next/static/chunks/{18rvdw5v72364.js → 2adbpdaxp5ygq.js} +1 -1
  179. package/.next/standalone/.next/static/chunks/{2j94uwkb4s3qr.js → 2kbqcw-6swmav.js} +1 -1
  180. package/.next/standalone/.next/static/chunks/2n9n5m0963pdg.js +6 -0
  181. package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
  182. package/.next/standalone/.next/static/chunks/{31feojxhjyjpl.js → 30nrrpwymht5m.js} +1 -1
  183. package/.next/standalone/.next/static/chunks/31coz11ia5vc4.js +1 -0
  184. package/.next/standalone/.next/static/chunks/{turbopack-1lsv92x7klwrn.js → turbopack-3diub4ctvn1n0.js} +1 -1
  185. package/.next/standalone/SECURITY.md +8 -2
  186. package/.next/standalone/app/actions/get-active-pauses.ts +16 -0
  187. package/.next/standalone/app/actions/get-hooks-config.ts +2 -2
  188. package/.next/standalone/app/actions/get-scheduled-audit.ts +70 -0
  189. package/.next/standalone/app/actions/update-scheduled-audit.ts +44 -0
  190. package/.next/standalone/app/api/audit/run/route.ts +28 -0
  191. package/.next/standalone/app/api/audit/status/route.ts +13 -1
  192. package/.next/standalone/app/api/auth/status/route.ts +1 -1
  193. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +1 -3
  194. package/.next/standalone/app/audit/_components/audit-progress-strip.tsx +1 -1
  195. package/.next/standalone/app/audit/_components/auth-dialog.css +159 -0
  196. package/.next/standalone/app/audit/_components/auth-dialog.tsx +3 -0
  197. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +1 -1
  198. package/.next/standalone/app/audit/_components/empty-state.tsx +0 -1
  199. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +1 -1
  200. package/.next/standalone/app/audit/_components/quirks-section.tsx +0 -1
  201. package/.next/standalone/app/audit/_components/report-footer.tsx +0 -2
  202. package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
  203. package/.next/standalone/app/audit/_components/strengths-section.tsx +0 -1
  204. package/.next/standalone/app/audit/audit-styles.css +6 -149
  205. package/.next/standalone/app/components/pause-notices.tsx +111 -0
  206. package/.next/standalone/app/components/raw-log-viewer.tsx +1 -1
  207. package/.next/standalone/app/components/toast.tsx +1 -1
  208. package/.next/standalone/app/policies/hooks-client.tsx +84 -8
  209. package/.next/standalone/app/settings/page.tsx +31 -0
  210. package/.next/standalone/app/settings/settings-client.tsx +487 -0
  211. package/.next/standalone/components/navbar.tsx +2 -0
  212. package/.next/standalone/lib/auth/api-server-client.ts +2 -3
  213. package/.next/standalone/lib/auth/auth-store.ts +4 -5
  214. package/.next/standalone/lib/codex-sessions.ts +2 -1
  215. package/.next/standalone/lib/dashboard-host.ts +101 -0
  216. package/.next/standalone/lib/install-check.ts +22 -7
  217. package/.next/standalone/lib/pi-sessions.ts +99 -14
  218. package/.next/standalone/lib/telemetry-enabled.ts +54 -0
  219. package/.next/standalone/lib/telemetry-id.ts +52 -1
  220. package/.next/standalone/lib/telemetry.ts +8 -4
  221. package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
  222. package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
  223. package/.next/standalone/node_modules/react/package.json +3 -3
  224. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
  225. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
  226. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
  227. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
  228. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
  229. package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
  230. package/.next/standalone/node_modules/react-dom/package.json +4 -4
  231. package/.next/standalone/package.json +23 -12
  232. package/.next/standalone/proxy.ts +111 -1
  233. package/.next/standalone/server.js +1 -1
  234. package/bin/failproofai-worker.mjs +1 -1
  235. package/bin/failproofai.mjs +1041 -81
  236. package/bin/failproofaid-shim.mjs +4 -4
  237. package/dist/cli.mjs +18513 -10474
  238. package/dist/worker.mjs +1223 -299
  239. package/lib/auth/api-server-client.ts +2 -3
  240. package/lib/auth/auth-store.ts +4 -5
  241. package/lib/codex-sessions.ts +2 -1
  242. package/lib/dashboard-host.ts +101 -0
  243. package/lib/install-check.ts +22 -7
  244. package/lib/pi-sessions.ts +99 -14
  245. package/lib/telemetry-enabled.ts +54 -0
  246. package/lib/telemetry-id.ts +52 -1
  247. package/lib/telemetry.ts +8 -4
  248. package/package.json +23 -12
  249. package/scripts/build-daemon-packages.mjs +247 -0
  250. package/scripts/daemon-platforms.mjs +42 -0
  251. package/scripts/dev-hook.mjs +46 -5
  252. package/scripts/install-telemetry.mjs +8 -1
  253. package/scripts/launch.ts +25 -3
  254. package/scripts/parse-script-args.ts +27 -1
  255. package/scripts/prune-standalone.mjs +26 -0
  256. package/scripts/publish-aliases.mjs +62 -41
  257. package/scripts/repro-npm-install.sh +155 -0
  258. package/scripts/translate-docs/cache.ts +1 -1
  259. package/scripts/translate-docs/cli.ts +2 -2
  260. package/scripts/translate-docs/mintlify-nav.ts +1 -1
  261. package/scripts/translate-docs/readme-translator.ts +163 -4
  262. package/scripts/validate-mdx.ts +153 -13
  263. package/src/audit/audit-lock.ts +285 -0
  264. package/src/audit/audit-schedule.ts +111 -0
  265. package/src/audit/cache.ts +18 -5
  266. package/src/audit/cli-adapters/goose.ts +2 -1
  267. package/src/audit/cli-adapters/hermes.ts +28 -10
  268. package/src/audit/cli.ts +191 -35
  269. package/src/audit/dashboard-cache.ts +2 -3
  270. package/src/audit/report.ts +0 -1
  271. package/src/hooks/backfill-cli.ts +247 -0
  272. package/src/hooks/builtin-policies.ts +391 -3
  273. package/src/hooks/cloud-connection.ts +354 -0
  274. package/src/hooks/cloud-enrollment-cli.ts +439 -0
  275. package/src/hooks/cloud-enrollment.ts +284 -0
  276. package/src/hooks/cloud-introspect.ts +147 -0
  277. package/src/hooks/cloud-managed-policies.ts +206 -0
  278. package/src/hooks/collector-config.ts +218 -0
  279. package/src/hooks/configure-wizard.ts +1211 -233
  280. package/src/hooks/custom-hooks-loader.ts +243 -37
  281. package/src/hooks/daemon-client.ts +136 -33
  282. package/src/hooks/daemon-download.ts +264 -31
  283. package/src/hooks/daemon-service.ts +1124 -76
  284. package/src/hooks/first-run-gate.ts +50 -0
  285. package/src/hooks/flush-cli.ts +180 -0
  286. package/src/hooks/fp-config.ts +835 -0
  287. package/src/hooks/fp-home.ts +714 -0
  288. package/src/hooks/fp-reset.ts +1363 -0
  289. package/src/hooks/handler.ts +185 -6
  290. package/src/hooks/harness-cli.ts +386 -0
  291. package/src/hooks/hook-activity-store.ts +171 -24
  292. package/src/hooks/hook-logger.ts +2 -2
  293. package/src/hooks/hook-telemetry.ts +5 -1
  294. package/src/hooks/hooks-config.ts +4 -3
  295. package/src/hooks/integrations.ts +0 -1
  296. package/src/hooks/loader-utils.ts +172 -9
  297. package/src/hooks/manager.ts +41 -5
  298. package/src/hooks/migrations.ts +450 -0
  299. package/src/hooks/onboarding-attempt.ts +195 -0
  300. package/src/hooks/onboarding-lock.ts +118 -0
  301. package/src/hooks/policy-types.ts +28 -0
  302. package/src/hooks/session-pause-cli.ts +159 -0
  303. package/src/hooks/session-pause.ts +267 -0
  304. package/src/hooks/setup-state.ts +256 -0
  305. package/src/hooks/tui.ts +209 -9
  306. package/src/hooks/uninstall-cli.ts +418 -0
  307. package/src/hooks/worker-server.ts +134 -15
  308. package/.next/standalone/.next/server/chunks/[externals]__14odj07._.js +0 -3
  309. package/.next/standalone/.next/server/chunks/[externals]__1s61mel._.js +0 -3
  310. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0id_xf7._.js +0 -3
  311. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0longh-._.js +0 -3
  312. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
  313. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
  314. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
  315. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
  316. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
  317. package/.next/standalone/.next/server/chunks/ssr/[externals]__1_g_b3t._.js +0 -3
  318. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0114ewg._.js +0 -4
  319. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
  320. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
  321. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
  322. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0nb0syg._.js +0 -3
  323. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
  324. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +0 -4
  325. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0uno39i._.js +0 -3
  326. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
  327. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ath6v_._.js +0 -4
  328. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1psjybo._.js +0 -3
  329. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
  330. package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
  331. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_0oy_-36.js +0 -3
  332. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
  333. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
  334. package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
  335. package/.next/standalone/.next/static/chunks/0zbxssxh53n-3.js +0 -1
  336. package/.next/standalone/.next/static/chunks/2mazwb1qmur1p.js +0 -1
  337. package/.next/standalone/.next/static/chunks/2pujpnfx4pws6.css +0 -2
  338. package/.next/standalone/.next/static/chunks/33u59vf_8xpd-.js +0 -1
  339. package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
  340. package/.next/standalone/.next/static/chunks/3s7myk_co0gbh.js +0 -1
  341. package/.next/standalone/.next/static/chunks/40k1_v4os4cw6.js +0 -6
  342. package/.next/standalone/Cargo.lock +0 -428
  343. package/.next/standalone/Cargo.toml +0 -9
  344. package/.next/standalone/crates/PROTOCOL.md +0 -122
  345. package/.next/standalone/crates/failproofaid/Cargo.toml +0 -18
  346. package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
  347. package/.next/standalone/crates/failproofaid/src/main.rs +0 -83
  348. package/.next/standalone/crates/failproofaid/src/paths.rs +0 -172
  349. package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
  350. package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
  351. package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
  352. package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
  353. package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
  354. package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
  355. package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
  356. package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
  357. package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
  358. package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
  359. package/.next/standalone/integration-suite/Dockerfile +0 -47
  360. package/.next/standalone/integration-suite/README.md +0 -129
  361. package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
  362. package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
  363. package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
  364. package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
  365. package/.next/standalone/integration-suite/install-clis.sh +0 -136
  366. package/.next/standalone/integration-suite/probe-cli.sh +0 -248
  367. package/.next/standalone/integration-suite/report.js +0 -120
  368. package/.next/standalone/integration-suite/run.sh +0 -159
  369. package/.next/standalone/openclaw-plugin/index.js +0 -223
  370. package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
  371. package/.next/standalone/openclaw-plugin/package.json +0 -12
  372. package/.next/standalone/osv-scanner.toml +0 -17
  373. package/.next/standalone/pi-extension/index.ts +0 -524
  374. package/.next/standalone/pi-extension/package.json +0 -12
  375. package/.next/standalone/postcss.config.mjs +0 -8
  376. package/.next/standalone/readme-arch-hq.gif +0 -0
  377. package/.next/standalone/rust-toolchain.toml +0 -4
  378. package/src/auth/cli.ts +0 -371
  379. /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_buildManifest.js +0 -0
  380. /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_clientMiddlewareManifest.js +0 -0
  381. /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_ssgManifest.js +0 -0
@@ -0,0 +1,487 @@
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 { 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 { toast } from "@/app/components/toast";
25
+ import { fetchWithTimeout } from "@/lib/fetch-with-timeout";
26
+ import { formatRelativeTime } from "@/lib/format-duration";
27
+
28
+ // ── formatting helpers ───────────────────────────────────────────────────────
29
+
30
+ function fmtAbsolute(ms: number): string {
31
+ return new Date(ms).toLocaleString(undefined, {
32
+ month: "short",
33
+ day: "numeric",
34
+ hour: "2-digit",
35
+ minute: "2-digit",
36
+ });
37
+ }
38
+
39
+ /** "in 6d" / "in 3h" / "in 12m" / "now". formatRelativeTime only speaks past. */
40
+ function fmtFuture(ms: number): string {
41
+ const diff = ms - Date.now();
42
+ if (diff <= 0) return "now";
43
+ if (diff < 3_600_000) return `in ${Math.max(1, Math.floor(diff / 60_000))}m`;
44
+ if (diff < 86_400_000) return `in ${Math.floor(diff / 3_600_000)}h`;
45
+ return `in ${Math.floor(diff / 86_400_000)}d`;
46
+ }
47
+
48
+ // ── shared primitives (match /policies) ──────────────────────────────────────
49
+
50
+ /** The exact switch /policies uses — copied shape, not a new control. */
51
+ function Toggle({
52
+ enabled,
53
+ onChange,
54
+ disabled,
55
+ label,
56
+ }: {
57
+ enabled: boolean;
58
+ onChange: () => void;
59
+ disabled?: boolean;
60
+ label: string;
61
+ }) {
62
+ return (
63
+ <button
64
+ type="button"
65
+ onClick={onChange}
66
+ disabled={disabled}
67
+ role="switch"
68
+ aria-checked={enabled}
69
+ aria-label={label}
70
+ className={`relative inline-flex h-4 w-7 shrink-0 items-center rounded-full transition-colors disabled:opacity-40 ${
71
+ enabled ? "bg-emerald-500" : "bg-muted-foreground/30"
72
+ }`}
73
+ >
74
+ <span
75
+ className={`inline-block h-3 w-3 rounded-full bg-white shadow transition-transform ${
76
+ enabled ? "translate-x-3.5" : "translate-x-0.5"
77
+ }`}
78
+ />
79
+ </button>
80
+ );
81
+ }
82
+
83
+ type PillTone = "ok" | "warn" | "bad" | "muted";
84
+ const PILL_TONE: Record<PillTone, { fg: string; bg: string; bd: string }> = {
85
+ ok: { fg: "var(--accent-green)", bg: "rgba(102,209,181,0.10)", bd: "rgba(102,209,181,0.30)" },
86
+ warn: { fg: "var(--amber)", bg: "rgba(232,196,106,0.10)", bd: "rgba(232,196,106,0.30)" },
87
+ bad: { fg: "var(--accent-pink)", bg: "rgba(228,88,124,0.10)", bd: "rgba(228,88,124,0.30)" },
88
+ muted: { fg: "var(--ink-2)", bg: "transparent", bd: "var(--line-2)" },
89
+ };
90
+
91
+ function Pill({ tone, children }: { tone: PillTone; children: React.ReactNode }) {
92
+ const t = PILL_TONE[tone];
93
+ return (
94
+ <span
95
+ style={{
96
+ display: "inline-flex",
97
+ alignItems: "center",
98
+ gap: 6,
99
+ padding: "3px 9px",
100
+ fontFamily: "var(--font-mono)",
101
+ fontSize: 11,
102
+ letterSpacing: "0.04em",
103
+ color: t.fg,
104
+ background: t.bg,
105
+ border: `1px solid ${t.bd}`,
106
+ textTransform: "uppercase",
107
+ }}
108
+ >
109
+ {children}
110
+ </span>
111
+ );
112
+ }
113
+
114
+ const SECTION_TITLE: React.CSSProperties = {
115
+ fontFamily: "var(--font-mono)",
116
+ fontSize: 16,
117
+ fontWeight: 600,
118
+ letterSpacing: "-0.01em",
119
+ color: "var(--ink)",
120
+ margin: "0 0 4px",
121
+ };
122
+ const BODY: React.CSSProperties = {
123
+ fontFamily: "var(--font-mono)",
124
+ fontSize: 13,
125
+ color: "var(--ink-2)",
126
+ lineHeight: 1.65,
127
+ margin: 0,
128
+ };
129
+ const MUTED: React.CSSProperties = { ...BODY, color: "var(--dim)", fontSize: 12 };
130
+ const CODE: React.CSSProperties = { color: "var(--ink)", fontVariantLigatures: "none" };
131
+
132
+ /** A monospace inline command the user can copy by eye. */
133
+ function Cmd({ children }: { children: React.ReactNode }) {
134
+ return (
135
+ <code
136
+ style={{
137
+ fontFamily: "var(--font-mono)",
138
+ fontSize: 12,
139
+ color: "var(--accent-green)",
140
+ background: "var(--bg-2)",
141
+ border: "1px solid var(--line-2)",
142
+ padding: "1px 6px",
143
+ whiteSpace: "nowrap",
144
+ }}
145
+ >
146
+ {children}
147
+ </code>
148
+ );
149
+ }
150
+
151
+ // ── scheduled audit section ──────────────────────────────────────────────────
152
+
153
+ function ScheduledAuditSection({
154
+ view,
155
+ onReload,
156
+ }: {
157
+ view: ScheduledAuditView;
158
+ onReload: () => Promise<void>;
159
+ }) {
160
+ const [auto, setAuto] = useState(view.auto);
161
+ const [interval, setIntervalDays] = useState(view.intervalDays);
162
+ const [savingAuto, setSavingAuto] = useState(false);
163
+ const [savingInterval, setSavingInterval] = useState(false);
164
+ const [running, setRunning] = useState(false);
165
+ const [runningNow, setRunningNow] = useState(false);
166
+
167
+ // Keep local state honest if a background reload brought new server truth
168
+ // (e.g. someone toggled via CLI, or the interval clamp changed the value).
169
+ useEffect(() => setAuto(view.auto), [view.auto]);
170
+ useEffect(() => setIntervalDays(view.intervalDays), [view.intervalDays]);
171
+
172
+ // Reflect a scan already in flight (started here or from /audit) so the button
173
+ // and status line don't claim the machine is idle when it isn't.
174
+ useEffect(() => {
175
+ let cancelled = false;
176
+ (async () => {
177
+ try {
178
+ const res = await fetchWithTimeout("/api/audit/status", { cache: "no-store" });
179
+ if (res.ok && !cancelled) {
180
+ const s = (await res.json()) as { running?: boolean };
181
+ setRunning(Boolean(s.running));
182
+ }
183
+ } catch {
184
+ /* status is best-effort; a missing poll just means we assume idle */
185
+ }
186
+ })();
187
+ return () => {
188
+ cancelled = true;
189
+ };
190
+ }, []);
191
+
192
+ const daemonInactive = view.daemon !== "running";
193
+ const daemonUnsupported = view.daemon === "unsupported-platform";
194
+
195
+ const onToggleAuto = useCallback(async () => {
196
+ const next = !auto;
197
+ setAuto(next); // optimistic
198
+ setSavingAuto(true);
199
+ try {
200
+ const res = await setAutoAuditAction(next);
201
+ setAuto(res.auto);
202
+ toast(res.auto ? "Scheduled scanning on." : "Scheduled scanning off.");
203
+ await onReload();
204
+ } catch {
205
+ setAuto(!next); // revert
206
+ toast("Could not save that.");
207
+ } finally {
208
+ setSavingAuto(false);
209
+ }
210
+ }, [auto, onReload]);
211
+
212
+ const commitInterval = useCallback(
213
+ async (raw: number) => {
214
+ setSavingInterval(true);
215
+ try {
216
+ // The config owns the 1..90 clamp; we reflect whatever it stored.
217
+ const res = await setAuditIntervalAction(raw);
218
+ setIntervalDays(res.intervalDays);
219
+ toast(`Scanning every ${res.intervalDays} day${res.intervalDays === 1 ? "" : "s"}.`);
220
+ } catch {
221
+ setIntervalDays(view.intervalDays);
222
+ toast("Could not save that.");
223
+ } finally {
224
+ setSavingInterval(false);
225
+ }
226
+ },
227
+ [view.intervalDays],
228
+ );
229
+
230
+ const onRunNow = useCallback(async () => {
231
+ if (runningNow || running) return;
232
+ setRunningNow(true);
233
+ setRunning(true);
234
+ try {
235
+ await triggerRun({ cli: [], since: "all", noCache: false });
236
+ toast("Audit complete.");
237
+ await onReload();
238
+ } catch (err) {
239
+ const msg =
240
+ err instanceof RerunError && err.kind === "timeout"
241
+ ? "The scan is taking a while — it will finish in the background."
242
+ : "The scan could not be completed.";
243
+ toast(msg);
244
+ } finally {
245
+ setRunningNow(false);
246
+ setRunning(false);
247
+ }
248
+ }, [runningNow, running, onReload]);
249
+
250
+ const sched = view.schedule;
251
+ const lastExitBad =
252
+ sched?.lastExitCode != null && sched.lastExitCode !== 0 && sched.lastExitCode !== 75;
253
+
254
+ return (
255
+ <div className="panel" style={{ padding: 20, marginBottom: 20 }}>
256
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, flexWrap: "wrap" }}>
257
+ <div>
258
+ <h3 style={SECTION_TITLE}>Scheduled audit</h3>
259
+ <p style={MUTED}>Scan this machine on a timer, in the background.</p>
260
+ </div>
261
+ <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
262
+ {view.daemon === "running" && <Pill tone="ok">daemon running</Pill>}
263
+ {view.daemon === "stopped" && <Pill tone="warn">daemon stopped</Pill>}
264
+ {view.daemon === "not-installed" && <Pill tone="warn">daemon not installed</Pill>}
265
+ {view.daemon === "unsupported-platform" && <Pill tone="muted">daemon unavailable</Pill>}
266
+ </div>
267
+ </div>
268
+
269
+ {/* Enable toggle + the plain statement about what the scan reads. */}
270
+ <div style={{ display: "flex", alignItems: "flex-start", gap: 12, marginTop: 18 }}>
271
+ <div style={{ marginTop: 2 }}>
272
+ <Toggle
273
+ enabled={auto}
274
+ onChange={onToggleAuto}
275
+ disabled={savingAuto || daemonUnsupported}
276
+ label={auto ? "Turn off scheduled scanning" : "Turn on scheduled scanning"}
277
+ />
278
+ </div>
279
+ <div style={{ minWidth: 0 }}>
280
+ <p style={{ ...BODY, color: "var(--ink)" }}>
281
+ {auto ? "Scanning this machine on a schedule." : "Scan this machine on a schedule."}
282
+ </p>
283
+ <p style={{ ...BODY, marginTop: 6 }}>
284
+ The scan reads the <span style={CODE}>contents</span> of every session transcript on
285
+ disk across all installed agent CLIs — your prompts, the files they read and wrote,
286
+ and command output. It runs entirely on this machine. Nothing is sent anywhere unless
287
+ you also turn on emailed reports below.
288
+ </p>
289
+ </div>
290
+ </div>
291
+
292
+ {/* Interval. The number bounds mirror the config's own 1..90 clamp as a UX
293
+ hint; the config remains the authority and we reflect what it stored. */}
294
+ <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 16, flexWrap: "wrap" }}>
295
+ <label htmlFor="scan-interval" style={{ ...BODY }}>
296
+ Scan every
297
+ </label>
298
+ <input
299
+ id="scan-interval"
300
+ type="number"
301
+ min={1}
302
+ max={90}
303
+ step={1}
304
+ value={interval}
305
+ disabled={savingInterval || daemonUnsupported}
306
+ onChange={(e) => setIntervalDays(Number(e.target.value))}
307
+ onBlur={(e) => {
308
+ const v = Number(e.target.value);
309
+ // A cleared/garbage field must not persist NaN — snap back to the
310
+ // stored value and let the config keep owning the real bounds.
311
+ if (!Number.isFinite(v)) {
312
+ setIntervalDays(view.intervalDays);
313
+ return;
314
+ }
315
+ if (v !== view.intervalDays) void commitInterval(v);
316
+ }}
317
+ style={{
318
+ width: 64,
319
+ padding: "6px 8px",
320
+ background: "var(--bg)",
321
+ border: "1px solid var(--line-2)",
322
+ color: "var(--ink)",
323
+ fontFamily: "var(--font-mono)",
324
+ fontSize: 13,
325
+ textAlign: "center",
326
+ }}
327
+ />
328
+ <span style={BODY}>day{interval === 1 ? "" : "s"}.</span>
329
+ <span style={MUTED}>1–90; the config keeps it in range.</span>
330
+ </div>
331
+
332
+ {/* Last run / next due — read from the daemon-written schedule file. */}
333
+ <div
334
+ style={{
335
+ marginTop: 18,
336
+ paddingTop: 16,
337
+ borderTop: "1px dashed var(--line)",
338
+ display: "flex",
339
+ flexDirection: "column",
340
+ gap: 6,
341
+ }}
342
+ >
343
+ {running && (
344
+ <p style={{ ...BODY, color: "var(--accent-green)" }}>A scan is running now…</p>
345
+ )}
346
+
347
+ {/* Last run */}
348
+ {sched?.lastRunAtMs != null ? (
349
+ <p style={BODY}>
350
+ Last scheduled scan:{" "}
351
+ <span style={CODE}>{fmtAbsolute(sched.lastRunAtMs)}</span>{" "}
352
+ <span style={MUTED}>({formatRelativeTime(sched.lastRunAtMs)})</span>
353
+ </p>
354
+ ) : view.lastResultAt ? (
355
+ <p style={BODY}>
356
+ Last audit result:{" "}
357
+ <span style={CODE}>{fmtAbsolute(new Date(view.lastResultAt).getTime())}</span>{" "}
358
+ <span style={MUTED}>(no scheduled scan has run yet)</span>
359
+ </p>
360
+ ) : (
361
+ <p style={BODY}>No scan has run yet.</p>
362
+ )}
363
+
364
+ {/* Next due */}
365
+ {auto ? (
366
+ sched?.nextDueAtMs != null ? (
367
+ <p style={BODY}>
368
+ Next scan due:{" "}
369
+ <span style={CODE}>{fmtAbsolute(sched.nextDueAtMs)}</span>{" "}
370
+ <span style={MUTED}>({fmtFuture(sched.nextDueAtMs)})</span>
371
+ </p>
372
+ ) : (
373
+ <p style={BODY}>
374
+ Next scan:{" "}
375
+ <span style={MUTED}>the daemon will schedule it shortly.</span>
376
+ </p>
377
+ )
378
+ ) : (
379
+ <p style={MUTED}>Scheduled scanning is off — no scan is scheduled.</p>
380
+ )}
381
+
382
+ {lastExitBad && (
383
+ <p style={{ ...BODY, color: "var(--amber)" }}>
384
+ The last scheduled scan exited with code {sched?.lastExitCode}. It will retry on the
385
+ next tick.
386
+ </p>
387
+ )}
388
+ {sched?.schemaAhead && (
389
+ <p style={MUTED}>
390
+ A newer daemon wrote this schedule; some fields may not be shown.
391
+ </p>
392
+ )}
393
+ </div>
394
+
395
+ {/* Degraded daemon guidance — say plainly why "on" may still not run. */}
396
+ {auto && daemonInactive && (
397
+ <p style={{ ...BODY, color: "var(--amber)", marginTop: 14 }}>
398
+ {daemonUnsupported ? (
399
+ <>The background daemon isn&apos;t available on this platform, so scheduled scans
400
+ can&apos;t run here. You can still run one now, and use the audit page.</>
401
+ ) : view.daemon === "not-installed" ? (
402
+ <>Scheduled scanning is on, but the background service isn&apos;t installed, so nothing
403
+ will run on the timer yet. Install it with <Cmd>failproofai config</Cmd>.</>
404
+ ) : (
405
+ <>Scheduled scanning is on, but the background service is stopped, so nothing will run
406
+ until it starts. Reinstall or repair it with <Cmd>failproofai config</Cmd>.</>
407
+ )}
408
+ </p>
409
+ )}
410
+
411
+ {/* Run now — reuses the existing /api/audit/run route via triggerRun. */}
412
+ <div style={{ marginTop: 18 }}>
413
+ <button
414
+ type="button"
415
+ className="btn"
416
+ onClick={onRunNow}
417
+ disabled={runningNow || running}
418
+ >
419
+ {runningNow || running ? "[ scanning… ]" : "[ run a scan now ]"}
420
+ </button>
421
+ </div>
422
+ </div>
423
+ );
424
+ }
425
+
426
+ // ── page ─────────────────────────────────────────────────────────────────────
427
+
428
+ export default function SettingsClient() {
429
+ const [scheduled, setScheduled] = useState<ScheduledAuditView | null>(null);
430
+ const [loading, setLoading] = useState(true);
431
+ const [error, setError] = useState(false);
432
+ const mounted = useRef(true);
433
+
434
+ const reload = useCallback(async () => {
435
+ const s = await getScheduledAuditAction();
436
+ if (!mounted.current) return;
437
+ setScheduled(s);
438
+ }, []);
439
+
440
+ useEffect(() => {
441
+ mounted.current = true;
442
+ (async () => {
443
+ try {
444
+ await reload();
445
+ } catch {
446
+ if (mounted.current) setError(true);
447
+ } finally {
448
+ if (mounted.current) setLoading(false);
449
+ }
450
+ })();
451
+ return () => {
452
+ mounted.current = false;
453
+ };
454
+ }, [reload]);
455
+
456
+ return (
457
+ <main className="report">
458
+ <section className="section" data-screen-label="settings">
459
+ <h2
460
+ style={{
461
+ fontFamily: "var(--font-mono)",
462
+ fontSize: 24,
463
+ fontWeight: 600,
464
+ letterSpacing: "-0.01em",
465
+ color: "var(--ink)",
466
+ margin: "0 0 8px",
467
+ }}
468
+ >
469
+ Settings
470
+ </h2>
471
+ <p style={{ ...BODY, maxWidth: 720, margin: "0 0 24px" }}>
472
+ Machine-level controls for scheduled scanning and emailed reports.
473
+ </p>
474
+
475
+ {loading ? (
476
+ <p style={MUTED}>Loading…</p>
477
+ ) : error || !scheduled ? (
478
+ <p style={{ ...BODY, color: "var(--accent-pink)" }}>
479
+ Could not load settings. Refresh to try again.
480
+ </p>
481
+ ) : (
482
+ <ScheduledAuditSection view={scheduled} onReload={reload} />
483
+ )}
484
+ </section>
485
+ </main>
486
+ );
487
+ }
@@ -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
  })();
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Low-level HTTP client for the FailproofAI api-server's /v0/auth/* endpoints.
3
3
  *
4
- * Shared by both the CLI (failproofai auth ...) and the dashboard's Next.js
5
- * API route proxies. Has no filesystem access — token persistence lives in
6
- * `./auth-store.ts`.
4
+ * Used by the dashboard's Next.js API route proxies. Has no filesystem access
5
+ * — token persistence lives in `./auth-store.ts`.
7
6
  *
8
7
  * The base URL is resolved from FAILPROOF_API_URL (preferred) or the legacy
9
8
  * FAILPROOFAI_API_URL, falling back to the hosted api-server. Local-dev
@@ -2,16 +2,15 @@
2
2
  * Persistence layer for the FailproofAI auth.json file.
3
3
  *
4
4
  * Tokens live at ~/.failproofai/auth.json with mode 0600. The dashboard's
5
- * Next.js API routes and the CLI both read/write through here so the user's
6
- * session survives across `failproofai` (dashboard) and `failproofai auth`
7
- * (CLI) invocations.
5
+ * Next.js API routes read and write through here, so a session survives across
6
+ * dashboard runs.
8
7
  */
9
8
 
10
9
  import { existsSync, readFileSync, rmSync } from "node:fs";
11
- import { homedir } from "node:os";
12
10
  import { join } from "node:path";
13
11
 
14
12
  import { writeJsonAtomically } from "../atomic-write";
13
+ import { failproofaiHome } from "../../src/hooks/fp-home";
15
14
  import {
16
15
  AuthApiError,
17
16
  decodeJwt,
@@ -31,7 +30,7 @@ export interface StoredAuth {
31
30
  export function getAuthDir(): string {
32
31
  const override = process.env.FAILPROOFAI_AUTH_DIR;
33
32
  if (override) return override;
34
- return join(homedir(), ".failproofai");
33
+ return failproofaiHome();
35
34
  }
36
35
 
37
36
  export function getAuthFilePath(): string {
@@ -33,10 +33,11 @@ import {
33
33
  type LogSource,
34
34
  } from "./log-entries";
35
35
  import { formatDuration } from "./format-duration";
36
+ import { codexSessionPathsFile } from "../src/hooks/fp-home";
36
37
 
37
38
  // ── Transcript discovery ──
38
39
 
39
- const CACHE_PATH = join(homedir(), ".failproofai", "cache", "codex-session-paths.json");
40
+ const CACHE_PATH = codexSessionPathsFile();
40
41
 
41
42
  function readCache(): Record<string, string> {
42
43
  try {
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Where the local dashboard binds, and which `Host` headers it will answer to.
3
+ *
4
+ * Shared by `scripts/launch.ts` (which chooses the bind address) and `proxy.ts`
5
+ * (which enforces it per request), because the two drifting apart is exactly how
6
+ * a lockdown quietly stops working.
7
+ *
8
+ * ## Why this exists
9
+ *
10
+ * The dashboard is a WRITE surface for this machine's security configuration —
11
+ * it can toggle policies and uninstall failproofai's hooks from every agent CLI
12
+ * — and it has no authentication of any kind. It used to bind `0.0.0.0`
13
+ * unconditionally, so every peer on the network could do all of that.
14
+ *
15
+ * Binding loopback is necessary but NOT sufficient. A malicious page can still
16
+ * reach `http://localhost:8020` from the victim's own browser, and DNS
17
+ * rebinding (a domain that resolves to 127.0.0.1 after first load) defeats an
18
+ * `Origin === Host` comparison, because both then read as the attacker's
19
+ * domain. So the defence is three layers, and all three are load-bearing:
20
+ *
21
+ * 1. bind loopback by default (kills every off-machine caller);
22
+ * 2. answer only to a loopback `Host` (kills DNS rebinding, whose whole trick
23
+ * is arriving with an attacker-controlled Host);
24
+ * 3. reject cross-origin mutating requests by `Origin` (kills the ordinary
25
+ * drive-by, which arrives with a perfectly valid `Host: localhost:8020`
26
+ * and an `Origin` of the attacker's site).
27
+ *
28
+ * Layer 2 is what makes layer 3 trustworthy: without it, a rebound page is
29
+ * same-origin by the browser's own reckoning and layer 3 waves it through.
30
+ */
31
+
32
+ /** The default bind address. Loopback — never the wildcard. */
33
+ export const DEFAULT_DASHBOARD_HOST = "127.0.0.1";
34
+
35
+ /**
36
+ * Hostnames that mean "this machine". `[::1]` is the bracketed form as it
37
+ * appears in a `Host` header; `::1` is the bare form as it appears in a bind
38
+ * address.
39
+ */
40
+ const LOOPBACK_HOSTNAMES = new Set(["localhost", "127.0.0.1", "::1", "[::1]", "0:0:0:0:0:0:0:1"]);
41
+
42
+ /**
43
+ * True for any address that only this machine can reach.
44
+ *
45
+ * The whole 127.0.0.0/8 block is loopback, not just 127.0.0.1 — some setups
46
+ * bind 127.0.0.2 to separate services, and treating that as external would
47
+ * refuse a request the user deliberately arranged.
48
+ */
49
+ export function isLoopbackHostname(hostname: string): boolean {
50
+ const h = hostname.trim().toLowerCase();
51
+ if (!h) return false;
52
+ if (LOOPBACK_HOSTNAMES.has(h)) return true;
53
+ return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(h);
54
+ }
55
+
56
+ /**
57
+ * The hostname part of a `Host` header, with the port removed.
58
+ *
59
+ * IPv6 literals are bracketed (`[::1]:8020`), so the port cannot be found by
60
+ * looking for the last colon without handling the brackets first — `[::1]`
61
+ * is all colons.
62
+ */
63
+ export function hostnameFromHostHeader(hostHeader: string): string {
64
+ const raw = hostHeader.trim();
65
+ if (raw.startsWith("[")) {
66
+ const end = raw.indexOf("]");
67
+ return end === -1 ? raw : raw.slice(0, end + 1);
68
+ }
69
+ const colon = raw.indexOf(":");
70
+ return colon === -1 ? raw : raw.slice(0, colon);
71
+ }
72
+
73
+ /** The hostname part of an `Origin` header (`http://localhost:8020` -> `localhost`). */
74
+ export function hostnameFromOrigin(origin: string): string | null {
75
+ const raw = origin.trim();
76
+ // "null" is a real Origin value (sandboxed iframes, some redirects). It is
77
+ // never ours, so it must not parse into something permissive.
78
+ if (!raw || raw === "null") return null;
79
+ try {
80
+ const url = new URL(raw);
81
+ // `URL.hostname` KEEPS the brackets on an IPv6 literal ("[::1]"), which is
82
+ // already the form a Host header carries — so it passes through untouched.
83
+ // Bracketing it again would produce "[[::1]]" and never match anything.
84
+ return url.hostname || null;
85
+ } catch {
86
+ return null;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Resolve the bind address from an explicit flag, then the environment, then
92
+ * the safe default.
93
+ *
94
+ * An explicit non-loopback value is honoured — someone running the dashboard in
95
+ * a container or on a remote dev box has a real need — but it is a deliberate
96
+ * act, and `launch.ts` says so out loud when it happens.
97
+ */
98
+ export function resolveDashboardHost(flagHost?: string, envHost?: string): string {
99
+ const chosen = (flagHost ?? envHost ?? "").trim();
100
+ return chosen || DEFAULT_DASHBOARD_HOST;
101
+ }