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
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Whether a given `failproofai` invocation may be interrupted by the
3
+ * first-run wizard.
4
+ *
5
+ * Deliberately has NO imports. It is consulted from `bin/failproofai.mjs`,
6
+ * which keeps its module graph small so the `--hook` fast path (one process
7
+ * per tool call) does not pay for loading the world; and it lives outside that
8
+ * file so it can be unit-tested without executing the CLI's top-level body.
9
+ *
10
+ * This decides only the SHAPE of the command. Whether the machine is actually
11
+ * unconfigured, whether there is a TTY, and whether we are running under sudo
12
+ * are decided later, in `maybeFirstRunConfigure`.
13
+ */
14
+
15
+ /**
16
+ * Subcommands that must never be interrupted by onboarding.
17
+ *
18
+ * All three are configuration actions in their own right: `config` IS the
19
+ * wizard, and `policies` / `policy` are the non-interactive way to do setup.
20
+ * Putting a wizard in front of them would override an intent the user just
21
+ * stated, and would hang any script that calls them.
22
+ */
23
+ export const FIRST_RUN_EXEMPT_SUBCOMMANDS: readonly string[] = [
24
+ "config",
25
+ "policies",
26
+ "policy",
27
+ ];
28
+
29
+ export function shouldOfferFirstRun(args: readonly string[]): boolean {
30
+ // "What is this / how do I use it" must never require setup first.
31
+ if (args.includes("--help") || args.includes("-h")) return false;
32
+ if (args.includes("--version") || args.includes("-v")) return false;
33
+ // `--hook` never reaches this check (the entry point handles and exits
34
+ // above), but a stray one here would mean an agent's tool call sitting at an
35
+ // interactive prompt it cannot answer. Cheap to be explicit.
36
+ if (args.includes("--hook")) return false;
37
+ if (args.length > 0 && FIRST_RUN_EXEMPT_SUBCOMMANDS.includes(args[0])) return false;
38
+ return true;
39
+ }
@@ -0,0 +1,497 @@
1
+ /**
2
+ * `VERSION`, `config.toml` and `credentials.toml` — the three files a human
3
+ * might actually open in `~/.failproofai`.
4
+ *
5
+ * ## Why the split
6
+ *
7
+ * `config.toml` inherits the umask and lands at 0664 on a normal machine.
8
+ * `credentials.toml` is written 0600 and holds every token. That boundary is
9
+ * not tidiness: it is why `ingest.json` and `cloud.json` were separate files
10
+ * before this, and it means CI, tooling and a screen-shared `cat config.toml`
11
+ * can read a machine's configuration without ever seeing a live credential.
12
+ *
13
+ * ## Why TOML here and JSON elsewhere
14
+ *
15
+ * These three files are the ones a person edits by hand, so they get comments
16
+ * and a forgiving syntax. Everything the machine writes on a hot path — the
17
+ * builtin policy set, cursors, collector health, the spool — stays JSON,
18
+ * parsed by the runtime's own native parser with no dependency and no import
19
+ * cost. A TOML parser on the per-tool-call path would be paying a
20
+ * human-ergonomics tax on a latency budget that exists precisely because
21
+ * per-call work was too expensive.
22
+ */
23
+ import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
24
+ import { dirname } from "node:path";
25
+ import { parse as parseToml } from "smol-toml";
26
+ import { version as cliVersion } from "../../package.json";
27
+ import {
28
+ LAYOUT_VERSION,
29
+ configFile,
30
+ credentialsFile,
31
+ failproofaiHome,
32
+ legacy,
33
+ versionFile,
34
+ } from "./fp-home";
35
+
36
+ // ── VERSION ──────────────────────────────────────────────────────────────────
37
+
38
+ export interface VersionFile {
39
+ /** The on-disk layout this home was written by. */
40
+ layout: number;
41
+ /** The CLI version that last wrote it — replaces the old `last-version`. */
42
+ cli: string;
43
+ /** The daemon binary version installed, if any. */
44
+ daemon?: string;
45
+ }
46
+
47
+ export type LayoutState =
48
+ /** No home yet, or an empty one. A fresh install. */
49
+ | { kind: "absent" }
50
+ /** A home written by this layout. */
51
+ | { kind: "current"; version: VersionFile }
52
+ /** A home from an older layout — must be reset, never half-read. */
53
+ | { kind: "stale"; found: number }
54
+ /** A home from a NEWER layout: a downgrade. Refuse rather than corrupt it. */
55
+ | { kind: "future"; found: number };
56
+
57
+ /**
58
+ * What layout the home on disk speaks.
59
+ *
60
+ * The `VERSION` file is the primary signal; its absence is only treated as
61
+ * "layout 1" when the home actually contains something, because an empty or
62
+ * missing directory is a fresh install and must not be reported as stale.
63
+ *
64
+ * A NEWER layout is called out separately from an older one. Both are
65
+ * unreadable, but only one of them is fixable by re-running setup — telling a
66
+ * user to reset a home written by a newer CLI would delete data that a simple
67
+ * upgrade would have read fine.
68
+ */
69
+ export function detectLayout(): LayoutState {
70
+ const raw = readVersionFile();
71
+ if (raw) {
72
+ if (raw.layout === LAYOUT_VERSION) return { kind: "current", version: raw };
73
+ return raw.layout > LAYOUT_VERSION
74
+ ? { kind: "future", found: raw.layout }
75
+ : { kind: "stale", found: raw.layout };
76
+ }
77
+
78
+ // No VERSION file. Layout 1 if any of its landmarks are present, otherwise
79
+ // this is simply a home that has not been set up yet.
80
+ const layoutOneMarkers = [
81
+ legacy.policyConfig(),
82
+ legacy.cacheDir(),
83
+ legacy.launcherMarker(),
84
+ legacy.lastVersion(),
85
+ legacy.cloudCredentials(),
86
+ legacy.ingestCredentials(),
87
+ legacy.auditDashboard(),
88
+ ];
89
+ return layoutOneMarkers.some((p) => existsSync(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
90
+ }
91
+
92
+ export function readVersionFile(): VersionFile | null {
93
+ try {
94
+ const parsed = parseToml(readFileSync(versionFile(), "utf8")) as Record<string, unknown>;
95
+ const layout = Number(parsed.layout);
96
+ if (!Number.isFinite(layout)) return null;
97
+ return {
98
+ layout,
99
+ cli: typeof parsed.cli === "string" ? parsed.cli : "",
100
+ daemon: typeof parsed.daemon === "string" ? parsed.daemon : undefined,
101
+ };
102
+ } catch {
103
+ // Absent, unreadable, or malformed. All three mean "cannot prove a
104
+ // layout", and the landmark check above decides what that implies.
105
+ return null;
106
+ }
107
+ }
108
+
109
+ export function writeVersionFile(
110
+ v: Partial<VersionFile> & { /** Erase the daemon version rather than keeping it. */ clearDaemon?: boolean } = {},
111
+ ): void {
112
+ const existing = readVersionFile();
113
+ const next: VersionFile = {
114
+ layout: LAYOUT_VERSION,
115
+ cli: v.cli ?? cliVersion,
116
+ // `undefined` means "leave whatever is there" — a CLI-only rewrite must not
117
+ // drop a daemon version it never touched. Erasing it is therefore an
118
+ // explicit act, used on uninstall.
119
+ daemon: v.clearDaemon ? undefined : (v.daemon ?? existing?.daemon),
120
+ };
121
+ const lines = [
122
+ "# Written by failproofai. `layout` is what tells a newer CLI whether this",
123
+ "# directory can be read as-is; do not edit it by hand.",
124
+ `layout = ${next.layout}`,
125
+ `cli = ${JSON.stringify(next.cli)}`,
126
+ ];
127
+ if (next.daemon) lines.push(`daemon = ${JSON.stringify(next.daemon)}`);
128
+ writeFileAt(versionFile(), lines.join("\n") + "\n");
129
+ }
130
+
131
+ // ── config.toml ──────────────────────────────────────────────────────────────
132
+
133
+ /**
134
+ * How this machine relates to Failproof Cloud.
135
+ *
136
+ * `oss` is not merely "no credentials configured" — it is a hard gate. In OSS
137
+ * mode nothing constructs a cloud URL, polls for policy, or spools an event
138
+ * for upload. A machine that never opted in must be provably silent, not
139
+ * silent-by-happenstance because a token lookup returned undefined.
140
+ */
141
+ export type Mode = "oss" | "cloud";
142
+
143
+ export interface FpConfig {
144
+ mode: Mode;
145
+ daemon: {
146
+ /**
147
+ * Route hooks through the daemon — and FAIL CLOSED when it is unreachable.
148
+ *
149
+ * The installed binary VERSION deliberately does not live here; it is in
150
+ * `VERSION`, which is the file about versions. Two copies could disagree,
151
+ * and a stale one would misreport which daemon a machine is running.
152
+ */
153
+ configured: boolean;
154
+ };
155
+ collector: {
156
+ sessions: boolean;
157
+ hooks: boolean;
158
+ hooksVerbosity: "all" | "decisions" | "off";
159
+ redact: "minimal" | "off";
160
+ environment: string;
161
+ machineId?: string;
162
+ };
163
+ telemetry: {
164
+ /**
165
+ * Anonymous product telemetry. On unless switched off.
166
+ *
167
+ * This is the ONLY off-switch that can reach the daemon.
168
+ * `FAILPROOFAI_TELEMETRY_DISABLED=1` is read from `process.env`, and
169
+ * failproofaid runs as a system-scope service whose environment carries
170
+ * essentially nothing — so a shell export is structurally incapable of
171
+ * turning it off there. A file both sides read is the only thing that works.
172
+ */
173
+ enabled: boolean;
174
+ };
175
+ audit: {
176
+ /**
177
+ * Scan this machine's agent history on a schedule.
178
+ *
179
+ * OFF by default, and the asymmetry with `telemetry.enabled` above is
180
+ * deliberate: the audit reads the CONTENTS of every session transcript on
181
+ * disk — prompts, file contents, command output — so nothing scans on a
182
+ * timer until somebody asks for it.
183
+ */
184
+ auto: boolean;
185
+ /** Days between scheduled runs. Wall clock, so it survives suspend. */
186
+ intervalDays: number;
187
+ };
188
+ }
189
+
190
+ /**
191
+ * Days between scheduled audits when nothing says otherwise, and the bounds a
192
+ * hand-written value is held to.
193
+ *
194
+ * Both ends fail quietly, which is why they are clamped at all. A measured full
195
+ * scan is ~104 seconds over 3,277 transcripts and grows with history, so an
196
+ * interval under a day spends minutes of disk every day re-reading transcripts
197
+ * that have barely changed. At the other end a typo'd `interval_days = 3650`
198
+ * reads as "never", which on a status line is indistinguishable from the
199
+ * feature being broken — and "never" already has its own switch, `auto = false`.
200
+ */
201
+ export const DEFAULT_AUDIT_INTERVAL_DAYS = 7;
202
+ const MIN_AUDIT_INTERVAL_DAYS = 1;
203
+ const MAX_AUDIT_INTERVAL_DAYS = 90;
204
+
205
+ /**
206
+ * Resolve `interval_days`, refusing anything that is not a whole number of days.
207
+ *
208
+ * 0, a negative, a fraction below a day, and any non-number all resolve to the
209
+ * DEFAULT rather than clamping up to the 1-day floor. A `0` almost certainly
210
+ * means "off", and reading it as a DAILY 104-second scan of every transcript on
211
+ * the machine is the loudest possible way to get that wrong. A too-large value
212
+ * is clamped DOWN to 90 instead, because that is the conservative direction of
213
+ * the two available: falling back to 7 there would scan an order of magnitude
214
+ * more often than was asked for.
215
+ */
216
+ function readIntervalDays(raw: unknown): number {
217
+ if (typeof raw !== "number" || !Number.isFinite(raw)) return DEFAULT_AUDIT_INTERVAL_DAYS;
218
+ const days = Math.floor(raw);
219
+ if (days < MIN_AUDIT_INTERVAL_DAYS) return DEFAULT_AUDIT_INTERVAL_DAYS;
220
+ return Math.min(days, MAX_AUDIT_INTERVAL_DAYS);
221
+ }
222
+
223
+ export const DEFAULT_CONFIG: FpConfig = {
224
+ mode: "oss",
225
+ daemon: { configured: false },
226
+ collector: {
227
+ sessions: false,
228
+ hooks: true,
229
+ hooksVerbosity: "decisions",
230
+ redact: "minimal",
231
+ environment: "local",
232
+ },
233
+ telemetry: { enabled: true },
234
+ audit: { auto: false, intervalDays: DEFAULT_AUDIT_INTERVAL_DAYS },
235
+ };
236
+
237
+ export function readConfig(): FpConfig {
238
+ try {
239
+ const parsed = parseToml(readFileSync(configFile(), "utf8")) as Record<string, unknown>;
240
+ const mode = (parsed.mode as Record<string, unknown> | undefined)?.kind;
241
+ const daemon = (parsed.daemon ?? {}) as Record<string, unknown>;
242
+ const collector = (parsed.collector ?? {}) as Record<string, unknown>;
243
+ const telemetry = (parsed.telemetry ?? {}) as Record<string, unknown>;
244
+ const audit = (parsed.audit ?? {}) as Record<string, unknown>;
245
+ return {
246
+ // Anything unrecognised reads as `oss`. The failure direction matters:
247
+ // a corrupt config must not be able to turn cloud reporting ON.
248
+ mode: mode === "cloud" ? "cloud" : "oss",
249
+ daemon: { configured: daemon.configured === true },
250
+ collector: {
251
+ sessions: collector.sessions === true,
252
+ hooks: collector.hooks !== false,
253
+ hooksVerbosity:
254
+ collector.hooks_verbosity === "all" || collector.hooks_verbosity === "off"
255
+ ? collector.hooks_verbosity
256
+ : "decisions",
257
+ redact: collector.redact === "off" ? "off" : "minimal",
258
+ environment:
259
+ typeof collector.environment === "string" ? collector.environment : "local",
260
+ machineId: typeof collector.machine_id === "string" ? collector.machine_id : undefined,
261
+ },
262
+ // Only an explicit `false` switches it off. Absent, or any other value,
263
+ // reads as on — the shipped default, and what a config with no
264
+ // [telemetry] block at all means.
265
+ telemetry: { enabled: telemetry.enabled !== false },
266
+ // Mirror image of telemetry above: only an explicit `true` switches the
267
+ // scheduled scan on. Absent, misspelled, or `"yes"` all read as off,
268
+ // because the failure direction here is a machine that starts reading
269
+ // every transcript it can find on a timer nobody set.
270
+ audit: { auto: audit.auto === true, intervalDays: readIntervalDays(audit.interval_days) },
271
+ // Same shape as `audit.auto` above and for the same reason: only an
272
+ // explicit `true` opts in. Anything else — absent, misspelled, `"yes"` —
273
+ // reads as off, because the failure direction is a machine that starts
274
+ // mailing a report nobody asked for to an org address it never sees.
275
+ };
276
+ } catch {
277
+ return structuredClone(DEFAULT_CONFIG);
278
+ }
279
+ }
280
+
281
+ export function writeConfig(config: FpConfig): void {
282
+ const c = config.collector;
283
+ const lines = [
284
+ "# failproofai configuration. Safe to edit by hand.",
285
+ "# Credentials are NOT here — see credentials.toml (owner-only).",
286
+ "",
287
+ "[mode]",
288
+ "# \"oss\" — fully local. No transcripts, hook activity or policy leave",
289
+ "# this machine.",
290
+ "# \"cloud\" — reports to, and receives policy from, Failproof Cloud.",
291
+ `kind = ${JSON.stringify(config.mode)}`,
292
+ "",
293
+ "[daemon]",
294
+ "# When true, hooks route through failproofaid AND FAIL CLOSED if it is",
295
+ "# unreachable. Never set this by hand: `failproofai config` sets it only",
296
+ "# after verifying the service is genuinely running.",
297
+ `configured = ${config.daemon.configured}`,
298
+ ];
299
+ lines.push(
300
+ "",
301
+ "[collector]",
302
+ "# Session transcripts carry prompts, file contents and command output.",
303
+ `sessions = ${c.sessions}`,
304
+ "# Hook decisions: which policy fired and what it decided. No file contents.",
305
+ `hooks = ${c.hooks}`,
306
+ `hooks_verbosity = ${JSON.stringify(c.hooksVerbosity)}`,
307
+ `redact = ${JSON.stringify(c.redact)}`,
308
+ `environment = ${JSON.stringify(c.environment)}`,
309
+ );
310
+ if (c.machineId) lines.push(`machine_id = ${JSON.stringify(c.machineId)}`);
311
+ // Written ONLY when switched off. A default install therefore carries no
312
+ // [telemetry] block at all, but an operator who added one by hand keeps it:
313
+ // writeConfig regenerates this file wholesale, so emitting the key only when
314
+ // it is set is what stops a later rewrite from silently switching telemetry
315
+ // back on underneath them.
316
+ if (!config.telemetry.enabled) {
317
+ lines.push("", "[telemetry]", "enabled = false");
318
+ }
319
+ // Written ALWAYS, unlike [telemetry] directly above — the two are opposites
320
+ // on purpose. Telemetry ships on and is deliberately not advertised in the
321
+ // file; the scheduled audit ships off and is meant to be FOUND, and a switch
322
+ // nobody can see is the same as a switch that does not exist. Emitting both
323
+ // keys unconditionally also makes "a user's setting survives a rewrite" total
324
+ // rather than conditional: every field the struct carries is on disk, so
325
+ // there is no value of this block that a later regeneration can drop.
326
+ lines.push(
327
+ "",
328
+ "[audit]",
329
+ "# Scan this machine's agent history on a schedule and refresh the audit",
330
+ "# dashboard. OFF by default: the scan reads the CONTENTS of every session",
331
+ "# transcript on disk, so it waits to be asked. It runs as a separate",
332
+ "# short-lived process — never on the hook path.",
333
+ `auto = ${config.audit.auto}`,
334
+ `interval_days = ${config.audit.intervalDays}`,
335
+ );
336
+ writeFileAt(configFile(), lines.join("\n") + "\n");
337
+ }
338
+
339
+ /** Merge a partial update into the config on disk. */
340
+ export function updateConfig(patch: {
341
+ mode?: Mode;
342
+ daemon?: Partial<FpConfig["daemon"]>;
343
+ collector?: Partial<FpConfig["collector"]>;
344
+ telemetry?: Partial<FpConfig["telemetry"]>;
345
+ audit?: Partial<FpConfig["audit"]>;
346
+ }): FpConfig {
347
+ const current = readConfig();
348
+ const next: FpConfig = {
349
+ mode: patch.mode ?? current.mode,
350
+ daemon: { ...current.daemon, ...patch.daemon },
351
+ collector: { ...current.collector, ...patch.collector },
352
+ telemetry: { ...current.telemetry, ...patch.telemetry },
353
+ audit: { ...current.audit, ...patch.audit },
354
+ };
355
+ writeConfig(next);
356
+ return next;
357
+ }
358
+
359
+ // ── credentials.toml ─────────────────────────────────────────────────────────
360
+
361
+ export interface FpCredentials {
362
+ cloud?: { url: string; machineId: string; token: string; machineLabel?: string };
363
+ /**
364
+ * Which organisation this machine's credentials report into, as the server
365
+ * named it at connect time (`/v1/auth/introspect`).
366
+ *
367
+ * ONE table, not a field on `[cloud]` and `[ingest]` both: it describes the
368
+ * TOKEN, and connect uses the same token for both capabilities, so two copies
369
+ * could only ever drift or disagree. It also has to survive the case that
370
+ * motivated recording it at all — an `events:add`-only key, which configures
371
+ * ingest and never writes `[cloud]`.
372
+ *
373
+ * Recorded so `--status` can answer "where does this machine's data go?"
374
+ * with no network call: the question asked when a dashboard looks empty, and
375
+ * the one a key pasted from the wrong org gets silently wrong. Absent against
376
+ * a server predating introspect, and in files written by an older CLI.
377
+ */
378
+ org?: { id?: string; slug?: string; name?: string };
379
+ ingest?: { url: string; key: string };
380
+ auth?: { baseUrl?: string; sessionToken?: string; expiresAt?: number; email?: string };
381
+ }
382
+
383
+ export function readCredentials(): FpCredentials {
384
+ try {
385
+ const parsed = parseToml(readFileSync(credentialsFile(), "utf8")) as Record<string, unknown>;
386
+ const cloud = parsed.cloud as Record<string, unknown> | undefined;
387
+ const ingest = parsed.ingest as Record<string, unknown> | undefined;
388
+ const auth = parsed.auth as Record<string, unknown> | undefined;
389
+ const out: FpCredentials = {};
390
+ if (
391
+ cloud &&
392
+ typeof cloud.url === "string" &&
393
+ typeof cloud.machine_id === "string" &&
394
+ typeof cloud.token === "string" &&
395
+ cloud.url &&
396
+ cloud.token
397
+ ) {
398
+ const machineLabel =
399
+ typeof cloud.machine_label === "string" && cloud.machine_label ? cloud.machine_label : undefined;
400
+ out.cloud = { url: cloud.url, machineId: cloud.machine_id, token: cloud.token, machineLabel };
401
+ }
402
+ const org = parsed.org as Record<string, unknown> | undefined;
403
+ if (org) {
404
+ // Every field independently optional: a server may send fewer than three,
405
+ // and a partial org record is still better than none for `--status`.
406
+ const pick = (v: unknown) => (typeof v === "string" && v ? v : undefined);
407
+ const built = { id: pick(org.id), slug: pick(org.slug), name: pick(org.name) };
408
+ if (built.id || built.slug || built.name) out.org = built;
409
+ }
410
+ if (ingest && typeof ingest.url === "string" && typeof ingest.key === "string" && ingest.url && ingest.key) {
411
+ out.ingest = { url: ingest.url, key: ingest.key };
412
+ }
413
+ if (auth) {
414
+ out.auth = {
415
+ baseUrl: typeof auth.base_url === "string" ? auth.base_url : undefined,
416
+ sessionToken: typeof auth.session_token === "string" ? auth.session_token : undefined,
417
+ expiresAt: typeof auth.expires_at === "number" ? auth.expires_at : undefined,
418
+ email: typeof auth.email === "string" ? auth.email : undefined,
419
+ };
420
+ }
421
+ return out;
422
+ } catch {
423
+ return {};
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Write credentials at 0600, tightening the home that holds them.
429
+ *
430
+ * A 0600 file inside a 0775 directory is still reachable by every local user,
431
+ * so the directory is tightened too. `mode` on `writeFileSync` applies only
432
+ * when the file is CREATED, so an existing over-permissive file keeps its mode
433
+ * without the explicit chmod.
434
+ */
435
+ export function writeCredentials(creds: FpCredentials): void {
436
+ const lines: string[] = [
437
+ "# failproofai credentials — owner-only (0600). Do not commit.",
438
+ ];
439
+ if (creds.cloud) {
440
+ lines.push(
441
+ "",
442
+ "[cloud]",
443
+ `url = ${JSON.stringify(creds.cloud.url)}`,
444
+ `machine_id = ${JSON.stringify(creds.cloud.machineId)}`,
445
+ ...(creds.cloud.machineLabel
446
+ ? [`machine_label = ${JSON.stringify(creds.cloud.machineLabel)}`]
447
+ : []),
448
+ `token = ${JSON.stringify(creds.cloud.token)}`,
449
+ );
450
+ }
451
+ if (creds.ingest) {
452
+ lines.push(
453
+ "",
454
+ "[ingest]",
455
+ `url = ${JSON.stringify(creds.ingest.url)}`,
456
+ `key = ${JSON.stringify(creds.ingest.key)}`,
457
+ );
458
+ }
459
+ if (creds.org && (creds.org.id || creds.org.slug || creds.org.name)) {
460
+ lines.push("", "[org]");
461
+ if (creds.org.id) lines.push(`id = ${JSON.stringify(creds.org.id)}`);
462
+ if (creds.org.slug) lines.push(`slug = ${JSON.stringify(creds.org.slug)}`);
463
+ if (creds.org.name) lines.push(`name = ${JSON.stringify(creds.org.name)}`);
464
+ }
465
+ if (creds.auth && (creds.auth.sessionToken || creds.auth.email)) {
466
+ lines.push("", "[auth]");
467
+ if (creds.auth.baseUrl) lines.push(`base_url = ${JSON.stringify(creds.auth.baseUrl)}`);
468
+ if (creds.auth.sessionToken) lines.push(`session_token = ${JSON.stringify(creds.auth.sessionToken)}`);
469
+ if (creds.auth.expiresAt) lines.push(`expires_at = ${creds.auth.expiresAt}`);
470
+ if (creds.auth.email) lines.push(`email = ${JSON.stringify(creds.auth.email)}`);
471
+ }
472
+
473
+ const home = failproofaiHome();
474
+ mkdirSync(home, { recursive: true });
475
+ try {
476
+ if (statSync(home).mode & 0o077) chmodSync(home, 0o700);
477
+ } catch {
478
+ // Not fatal: the file's own 0600 is the primary protection.
479
+ }
480
+ writeFileSync(credentialsFile(), lines.join("\n") + "\n", { mode: 0o600 });
481
+ try {
482
+ chmodSync(credentialsFile(), 0o600);
483
+ } catch {
484
+ /* best effort on filesystems without POSIX modes */
485
+ }
486
+ }
487
+
488
+ /** True when this machine has any cloud credential at all. */
489
+ export function hasCloudCredentials(): boolean {
490
+ const c = readCredentials();
491
+ return Boolean(c.cloud || c.ingest);
492
+ }
493
+
494
+ function writeFileAt(path: string, contents: string): void {
495
+ mkdirSync(dirname(path), { recursive: true });
496
+ writeFileSync(path, contents, "utf8");
497
+ }