failproofai 1.0.0-beta.5 → 1.0.0-beta.7

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 (368) 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]__0c_qcgp._.js +3 -0
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kbc9bs._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +10 -10
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1a2hcb_._.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/_0r-dsre._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
  129. package/.next/standalone/.next/server/chunks/ssr/_0se484m._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/_0uz7o9y._.js +3 -0
  131. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/ssr/_194yh9u._.js +3 -0
  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/0945ybz3fsvba.js +1 -0
  150. package/.next/standalone/.next/static/chunks/{0cr85wuc9gc9k.js → 0gr76ps8_zm26.js} +1 -1
  151. package/.next/standalone/.next/static/chunks/{2cqk4a9s893oy.js → 0m-m7wgtxdoav.js} +1 -1
  152. package/.next/standalone/.next/static/chunks/{1tp9dex23zs8y.js → 11l6oh9cyb35a.js} +1 -1
  153. package/.next/standalone/.next/static/chunks/166f8qnh03jqc.js +6 -0
  154. package/.next/standalone/.next/static/chunks/1bm0q12hgs22l.css +2 -0
  155. package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
  156. package/.next/standalone/.next/static/chunks/{2s9g1dsde_ts1.js → 2aej0vi1fm87m.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/{1bkynffp3q1f7.js → 2bxld6-rym3ah.js} +22 -22
  158. package/.next/standalone/.next/static/chunks/{2v1rpzrh2i1wv.js → 2p2kz4aj1tooj.js} +1 -1
  159. package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
  160. package/.next/standalone/.next/static/chunks/36pc3_8fpb6ic.js +1 -0
  161. package/.next/standalone/.next/static/chunks/{2k285z07qce47.js → 3t1wr_kx6brwc.js} +1 -1
  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 +202 -86
  197. package/dist/cli.mjs +17803 -15346
  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 +160 -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 +25 -43
  229. package/src/hooks/configure-wizard.ts +750 -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 +605 -29
  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 +355 -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-attempt.ts +179 -0
  246. package/src/hooks/onboarding-lock.ts +119 -0
  247. package/src/hooks/policy-types.ts +0 -7
  248. package/src/hooks/session-pause-cli.ts +15 -13
  249. package/src/hooks/session-pause.ts +36 -4
  250. package/src/hooks/setup-state.ts +256 -0
  251. package/src/hooks/tui.ts +4 -4
  252. package/src/hooks/worker-server.ts +53 -1
  253. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0f8nu8d._.js +0 -3
  254. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0szqzl8._.js +0 -3
  255. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
  256. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
  257. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
  258. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
  259. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
  260. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
  261. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__094nop3._.js +0 -3
  262. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
  263. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
  264. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ls0gsr._.js +0 -4
  265. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n_uvv0._.js +0 -3
  266. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
  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]__1gak4pd._.js +0 -3
  270. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1j4u3yd._.js +0 -4
  271. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
  272. package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
  273. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_12c_2cz.js +0 -3
  274. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
  275. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
  276. package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
  277. package/.next/standalone/.next/static/chunks/1bw4dfzflelcv.js +0 -1
  278. package/.next/standalone/.next/static/chunks/34sjdw8lv46e3.js +0 -6
  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/Cargo.lock +0 -2222
  282. package/.next/standalone/Cargo.toml +0 -9
  283. package/.next/standalone/crates/CLOUD_POLICIES.md +0 -136
  284. package/.next/standalone/crates/PROTOCOL.md +0 -122
  285. package/.next/standalone/crates/failproofaid/Cargo.toml +0 -25
  286. package/.next/standalone/crates/failproofaid/src/cloud_client.rs +0 -484
  287. package/.next/standalone/crates/failproofaid/src/cloud_policies.rs +0 -921
  288. package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
  289. package/.next/standalone/crates/failproofaid/src/main.rs +0 -639
  290. package/.next/standalone/crates/failproofaid/src/paths.rs +0 -201
  291. package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
  292. package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
  293. package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
  294. package/.next/standalone/crates/fpai-collect/Cargo.toml +0 -43
  295. package/.next/standalone/crates/fpai-collect/src/config.rs +0 -420
  296. package/.next/standalone/crates/fpai-collect/src/cursor.rs +0 -520
  297. package/.next/standalone/crates/fpai-collect/src/delivery.rs +0 -472
  298. package/.next/standalone/crates/fpai-collect/src/filetail.rs +0 -487
  299. package/.next/standalone/crates/fpai-collect/src/health.rs +0 -369
  300. package/.next/standalone/crates/fpai-collect/src/lib.rs +0 -34
  301. package/.next/standalone/crates/fpai-collect/src/redact.rs +0 -516
  302. package/.next/standalone/crates/fpai-collect/src/sources/claude/mod.rs +0 -279
  303. package/.next/standalone/crates/fpai-collect/src/sources/claude/transform.rs +0 -619
  304. package/.next/standalone/crates/fpai-collect/src/sources/codex/mod.rs +0 -156
  305. package/.next/standalone/crates/fpai-collect/src/sources/codex/transform.rs +0 -715
  306. package/.next/standalone/crates/fpai-collect/src/sources/copilot/mod.rs +0 -158
  307. package/.next/standalone/crates/fpai-collect/src/sources/copilot/transform.rs +0 -512
  308. package/.next/standalone/crates/fpai-collect/src/sources/goose/mod.rs +0 -511
  309. package/.next/standalone/crates/fpai-collect/src/sources/goose/transform.rs +0 -586
  310. package/.next/standalone/crates/fpai-collect/src/sources/hermes/mod.rs +0 -318
  311. package/.next/standalone/crates/fpai-collect/src/sources/hermes/transform.rs +0 -579
  312. package/.next/standalone/crates/fpai-collect/src/sources/hooks/mod.rs +0 -314
  313. package/.next/standalone/crates/fpai-collect/src/sources/hooks/transform.rs +0 -448
  314. package/.next/standalone/crates/fpai-collect/src/sources/mod.rs +0 -23
  315. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/mod.rs +0 -176
  316. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/transform.rs +0 -500
  317. package/.next/standalone/crates/fpai-collect/src/sources/opencode/mod.rs +0 -315
  318. package/.next/standalone/crates/fpai-collect/src/sources/opencode/transform.rs +0 -493
  319. package/.next/standalone/crates/fpai-collect/src/sources/pi/mod.rs +0 -201
  320. package/.next/standalone/crates/fpai-collect/src/sources/pi/transform.rs +0 -533
  321. package/.next/standalone/crates/fpai-collect/src/spool.rs +0 -535
  322. package/.next/standalone/crates/fpai-collect/src/sqlitepoll.rs +0 -390
  323. package/.next/standalone/crates/fpai-collect/src/supervisor.rs +0 -423
  324. package/.next/standalone/crates/fpai-collect/src/uploader.rs +0 -600
  325. package/.next/standalone/crates/fpai-collect/tests/claude_source.rs +0 -739
  326. package/.next/standalone/crates/fpai-collect/tests/claude_subagents.rs +0 -394
  327. package/.next/standalone/crates/fpai-collect/tests/codex_source.rs +0 -976
  328. package/.next/standalone/crates/fpai-collect/tests/config.rs +0 -286
  329. package/.next/standalone/crates/fpai-collect/tests/copilot_source.rs +0 -1020
  330. package/.next/standalone/crates/fpai-collect/tests/delivery.rs +0 -281
  331. package/.next/standalone/crates/fpai-collect/tests/goose_source.rs +0 -1009
  332. package/.next/standalone/crates/fpai-collect/tests/hermes_source.rs +0 -1130
  333. package/.next/standalone/crates/fpai-collect/tests/hooks_source.rs +0 -622
  334. package/.next/standalone/crates/fpai-collect/tests/openclaw_source.rs +0 -855
  335. package/.next/standalone/crates/fpai-collect/tests/opencode_source.rs +0 -1478
  336. package/.next/standalone/crates/fpai-collect/tests/pi_source.rs +0 -1192
  337. package/.next/standalone/crates/fpai-collect/tests/supervisor.rs +0 -290
  338. package/.next/standalone/crates/fpai-collect/tests/uploader.rs +0 -506
  339. package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
  340. package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
  341. package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
  342. package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
  343. package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
  344. package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
  345. package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
  346. package/.next/standalone/integration-suite/Dockerfile +0 -47
  347. package/.next/standalone/integration-suite/README.md +0 -129
  348. package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
  349. package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
  350. package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
  351. package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
  352. package/.next/standalone/integration-suite/install-clis.sh +0 -136
  353. package/.next/standalone/integration-suite/probe-cli.sh +0 -248
  354. package/.next/standalone/integration-suite/report.js +0 -120
  355. package/.next/standalone/integration-suite/run.sh +0 -159
  356. package/.next/standalone/openclaw-plugin/index.js +0 -223
  357. package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
  358. package/.next/standalone/openclaw-plugin/package.json +0 -12
  359. package/.next/standalone/osv-scanner.toml +0 -17
  360. package/.next/standalone/pi-extension/index.ts +0 -524
  361. package/.next/standalone/pi-extension/package.json +0 -12
  362. package/.next/standalone/postcss.config.mjs +0 -8
  363. package/.next/standalone/readme-arch-hq.gif +0 -0
  364. package/.next/standalone/rust-toolchain.toml +0 -4
  365. package/src/auth/cli.ts +0 -371
  366. /package/.next/standalone/.next/static/{LOuwMmfomO6hQYfMQYs4K → LUaLkKg40nSkHrIajexIk}/_buildManifest.js +0 -0
  367. /package/.next/standalone/.next/static/{LOuwMmfomO6hQYfMQYs4K → LUaLkKg40nSkHrIajexIk}/_clientMiddlewareManifest.js +0 -0
  368. /package/.next/standalone/.next/static/{LOuwMmfomO6hQYfMQYs4K → LUaLkKg40nSkHrIajexIk}/_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
+ }