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
@@ -16,11 +16,14 @@
16
16
  * Environment variables still win over this file, so CI, containers and the
17
17
  * existing tests are unaffected.
18
18
  */
19
+ import { randomUUID } from "node:crypto";
19
20
  import { existsSync, readFileSync, rmSync } from "node:fs";
20
- import { homedir } from "node:os";
21
+ import { homedir, hostname } from "node:os";
21
22
  import { join } from "node:path";
22
23
  import { writeJsonAtomically } from "../../lib/atomic-write";
23
24
  import { fetchWithTimeout, isAbortError } from "../../lib/fetch-with-timeout";
25
+ import { credentialsFile } from "./fp-home";
26
+ import { readCredentials, writeCredentials } from "./fp-config";
24
27
 
25
28
  const SCHEMA_VERSION = 1;
26
29
  const VERIFY_TIMEOUT_MS = 10_000;
@@ -29,17 +32,34 @@ export interface CloudCredentials {
29
32
  url: string;
30
33
  machineId: string;
31
34
  token: string;
35
+ /**
36
+ * A human-facing name for this machine (e.g. the hostname, or "Chetan's
37
+ * laptop"). Display only — the identity is `machineId`. Optional: absent on
38
+ * credentials written before labels existed, and never required to connect.
39
+ */
40
+ machineLabel?: string;
32
41
  }
33
42
 
34
43
  interface StoredCredentials extends CloudCredentials {
35
44
  schemaVersion: number;
36
45
  }
37
46
 
38
- /** `~/.failproofai/cloud.json`. The override is honoured by the daemon too. */
47
+ /**
48
+ * Where the cloud credential lives.
49
+ *
50
+ * Layout 2 moved it out of `cloud.json` and into the `[cloud]` table of
51
+ * `credentials.toml`, alongside every other token, so there is exactly one
52
+ * owner-only file to protect rather than three. The env override still names a
53
+ * standalone JSON file, because the daemon and CI both use it that way and it
54
+ * predates the consolidation.
55
+ */
39
56
  export function cloudCredentialPath(): string {
40
- const override = process.env.FAILPROOFAI_CLOUD_CREDENTIALS;
41
- if (override) return override;
42
- return join(homedir(), ".failproofai", "cloud.json");
57
+ return process.env.FAILPROOFAI_CLOUD_CREDENTIALS ?? credentialsFile();
58
+ }
59
+
60
+ /** True when the credential is coming from the JSON override, not the TOML. */
61
+ function usingJsonOverride(): boolean {
62
+ return Boolean(process.env.FAILPROOFAI_CLOUD_CREDENTIALS);
43
63
  }
44
64
 
45
65
  /**
@@ -81,6 +101,7 @@ export function maskToken(token: string): string {
81
101
  }
82
102
 
83
103
  export function readCloudCredentials(): CloudCredentials | null {
104
+ if (!usingJsonOverride()) return readCredentials().cloud ?? null;
84
105
  const path = cloudCredentialPath();
85
106
  if (!existsSync(path)) return null;
86
107
  try {
@@ -90,7 +111,8 @@ export function readCloudCredentials(): CloudCredentials | null {
90
111
  return null;
91
112
  }
92
113
  if (!raw.url || !raw.machineId || !raw.token) return null;
93
- return { url: raw.url, machineId: raw.machineId, token: raw.token };
114
+ const label = typeof raw.machineLabel === "string" && raw.machineLabel ? raw.machineLabel : undefined;
115
+ return { url: raw.url, machineId: raw.machineId, token: raw.token, machineLabel: label };
94
116
  } catch {
95
117
  // Unreadable or malformed reads as "not connected" rather than throwing:
96
118
  // this is consulted by `--status`, and a corrupt file should be reported
@@ -100,21 +122,63 @@ export function readCloudCredentials(): CloudCredentials | null {
100
122
  }
101
123
 
102
124
  export function writeCloudCredentials(creds: CloudCredentials): void {
103
- // 0600 inside a 0700 dir — the default for writeJsonAtomically, and the
104
- // whole reason this is a file of ours rather than a line in the unit.
105
- writeJsonAtomically(cloudCredentialPath(), { schemaVersion: SCHEMA_VERSION, ...creds });
125
+ if (usingJsonOverride()) {
126
+ // 0600 inside a 0700 dir — the default for writeJsonAtomically.
127
+ writeJsonAtomically(cloudCredentialPath(), { schemaVersion: SCHEMA_VERSION, ...creds });
128
+ return;
129
+ }
130
+ // Merge, never replace: credentials.toml also carries the ingest key and the
131
+ // auth session, and rewriting the file from one caller must not drop the
132
+ // others.
133
+ writeCredentials({ ...readCredentials(), cloud: creds });
134
+ }
135
+
136
+ /**
137
+ * The stable key that identifies this machine to the cloud.
138
+ *
139
+ * An explicit `--machine-id` always wins. Otherwise the id already enrolled on
140
+ * this machine is reused, so re-running `--connect` is idempotent and never
141
+ * "moves" the machine. Only a machine that has none mints a fresh one — and it
142
+ * mints a random id, NOT the hostname, because two hosts sharing a hostname
143
+ * (fresh cloud VMs, cloned images) would otherwise silently merge into one
144
+ * machine on the server. The hostname becomes the human label instead.
145
+ */
146
+ export function resolveMachineId(explicit?: string): string {
147
+ const trimmed = explicit?.trim();
148
+ if (trimmed) return trimmed;
149
+ const existing = readCloudCredentials();
150
+ if (existing?.machineId) return existing.machineId;
151
+ return randomUUID();
152
+ }
153
+
154
+ /**
155
+ * The human-facing name for this machine. Defaults to the hostname as a
156
+ * *suggestion* — mutable, not the identity, and free to collide with another
157
+ * machine's label because `machineId` keeps them apart.
158
+ */
159
+ export function resolveMachineLabel(explicit?: string): string {
160
+ return explicit?.trim() || hostname();
106
161
  }
107
162
 
108
163
  /** True if there was something to remove. */
109
164
  export function clearCloudCredentials(): boolean {
110
- const path = cloudCredentialPath();
111
- if (!existsSync(path)) return false;
112
- try {
113
- rmSync(path);
114
- return true;
115
- } catch {
116
- return false;
165
+ if (usingJsonOverride()) {
166
+ const path = cloudCredentialPath();
167
+ if (!existsSync(path)) return false;
168
+ try {
169
+ rmSync(path);
170
+ return true;
171
+ } catch {
172
+ return false;
173
+ }
117
174
  }
175
+ const current = readCredentials();
176
+ if (!current.cloud) return false;
177
+ // Drop only the cloud table — disconnecting policy must not also revoke the
178
+ // ingest key or the dashboard session.
179
+ const { cloud: _dropped, ...rest } = current;
180
+ writeCredentials(rest);
181
+ return true;
118
182
  }
119
183
 
120
184
  export type VerifyResult =
@@ -131,7 +195,13 @@ export type VerifyResult =
131
195
  * came back.
132
196
  */
133
197
  export async function verifyCloudCredentials(creds: CloudCredentials): Promise<VerifyResult> {
134
- const url = `${creds.url}/enforcement/v1/desired-state?machineId=${encodeURIComponent(creds.machineId)}`;
198
+ // The label rides the enrolment request so the server can record a
199
+ // human-facing name alongside the id. An older server simply ignores the
200
+ // extra query param, so sending it is always safe.
201
+ const labelParam = creds.machineLabel
202
+ ? `&label=${encodeURIComponent(creds.machineLabel)}`
203
+ : "";
204
+ const url = `${creds.url}/enforcement/v1/desired-state?machineId=${encodeURIComponent(creds.machineId)}${labelParam}`;
135
205
  let response: Response;
136
206
  try {
137
207
  response = await fetchWithTimeout(
@@ -0,0 +1,147 @@
1
+ /**
2
+ * `GET /v1/auth/introspect` — asking the server what a key actually is.
3
+ *
4
+ * Before this endpoint existed a machine could not describe its own
5
+ * credential: every other `/keys*` route needs a `keys:*` permission that a
6
+ * machine credential must never hold. So a revoked key, a valid key missing
7
+ * one permission, and a valid key pointed at the WRONG ORG all failed
8
+ * identically — at the point of use, later, as an empty dashboard.
9
+ *
10
+ * Introspect answers all three at connect time, in one request:
11
+ *
12
+ * is the key accepted at all? 401 vs 200
13
+ * which org will this machine report into? org_slug / org_name
14
+ * can it do the job it was provisioned for? permissions
15
+ *
16
+ * ## Effective, not granted — the subtle part
17
+ *
18
+ * Permissions imply other permissions, and the server widens a grant at
19
+ * authentication time (`expand_implied`): `alerts:read` also carries
20
+ * `issues:read`, and so on. Authorization is checked against the WIDENED set.
21
+ *
22
+ * The dashboard's key page and the CLI both display the *stored* grant, so both
23
+ * understate what a key can do. Introspect returns what the server actually
24
+ * enforces — which is precisely why a local permission check is only correct
25
+ * when it is built against this response and not against the displayed list.
26
+ *
27
+ * ## Do not poll this
28
+ *
29
+ * `/v1` has no rate limiting and failed auth is not negatively cached: one bad
30
+ * token is one database lookup per request. This runs ONCE, when a machine is
31
+ * provisioned. Revocation and expiry are cached server-side for up to a minute,
32
+ * so treat a positive answer as current to within a minute — never to the
33
+ * second.
34
+ */
35
+
36
+ /** The path introspect is served on, appended to the cloud origin. */
37
+ export const INTROSPECT_PATH = "/v1/auth/introspect";
38
+
39
+ export interface KeyIdentity {
40
+ keyId?: string;
41
+ orgId?: string;
42
+ orgSlug?: string;
43
+ orgName?: string;
44
+ /** The EFFECTIVE permission set — what the server enforces. */
45
+ permissions: string[];
46
+ expiresAt?: string | null;
47
+ }
48
+
49
+ export type IntrospectResult =
50
+ | { kind: "ok"; identity: KeyIdentity }
51
+ /** The key was refused. Nothing more is knowable — the body carries no detail. */
52
+ | { kind: "rejected" }
53
+ /**
54
+ * This server has no introspect endpoint, i.e. it predates the endpoint.
55
+ * The caller must fall back to probing rather than treating it as a bad key:
56
+ * the CLI ships independently of whatever AgentEye a customer runs, so a
57
+ * perfectly good key on an older server must still connect.
58
+ */
59
+ | { kind: "unsupported" }
60
+ /** Network/DNS/TLS — indistinguishable from a typo'd host, so reported as-is. */
61
+ | { kind: "unreachable"; reason: string };
62
+
63
+ export function introspectUrlFor(origin: string): string {
64
+ return `${origin.replace(/\/$/, "")}${INTROSPECT_PATH}`;
65
+ }
66
+
67
+ export async function introspectKey(
68
+ origin: string,
69
+ token: string,
70
+ fetchImpl: typeof fetch = fetch,
71
+ timeoutMs = 10_000,
72
+ ): Promise<IntrospectResult> {
73
+ // AbortController rather than the shared fetchWithTimeout helper: this needs
74
+ // an injectable `fetch` so tests never reach the network, and that helper
75
+ // closes over the global one.
76
+ const controller = new AbortController();
77
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
78
+ let res: Response;
79
+ try {
80
+ res = await fetchImpl(introspectUrlFor(origin), {
81
+ headers: { Authorization: `Bearer ${token}` },
82
+ // Never follow a redirect. The dashboard app answers unrouted paths and
83
+ // will happily 307 to a login page that then returns 200 — which would
84
+ // read as a valid key against an endpoint that authenticated nothing.
85
+ redirect: "manual",
86
+ signal: controller.signal,
87
+ });
88
+ } catch (err) {
89
+ const message = err instanceof Error ? err.message : String(err);
90
+ // Deliberately does not echo the URL: setup is routinely run while
91
+ // screen-sharing, and the host may be one the user would rather not show.
92
+ return { kind: "unreachable", reason: message };
93
+ } finally {
94
+ clearTimeout(timer);
95
+ }
96
+
97
+ if (res.status === 401 || res.status === 403) return { kind: "rejected" };
98
+ // 404 is "this server has no such route"; a redirect means we reached the web
99
+ // app rather than the API, which on an older deployment means the same thing.
100
+ if (res.status === 404 || (res.status >= 300 && res.status < 400)) {
101
+ return { kind: "unsupported" };
102
+ }
103
+ if (!res.ok) return { kind: "unreachable", reason: `the server answered ${res.status}` };
104
+
105
+ let body: unknown;
106
+ try {
107
+ body = await res.json();
108
+ } catch {
109
+ // A 200 that is not JSON is a proxy or a static host, not introspect.
110
+ return { kind: "unsupported" };
111
+ }
112
+
113
+ const b = body as Record<string, unknown>;
114
+ // `valid: false` is not documented as a 200 shape, but treating a negative
115
+ // assertion as success would be the worst possible reading of it.
116
+ if (b.valid === false) return { kind: "rejected" };
117
+ if (!Array.isArray(b.permissions)) {
118
+ // Shaped like something else entirely — an unrelated 200 from a proxy.
119
+ return { kind: "unsupported" };
120
+ }
121
+
122
+ return {
123
+ kind: "ok",
124
+ identity: {
125
+ keyId: typeof b.key_id === "string" ? b.key_id : undefined,
126
+ orgId: typeof b.org_id === "string" ? b.org_id : undefined,
127
+ orgSlug: typeof b.org_slug === "string" ? b.org_slug : undefined,
128
+ orgName: typeof b.org_name === "string" ? b.org_name : undefined,
129
+ permissions: b.permissions.filter((p): p is string => typeof p === "string"),
130
+ expiresAt: typeof b.expires_at === "string" ? b.expires_at : null,
131
+ },
132
+ };
133
+ }
134
+
135
+ /** The permission each capability needs. Named so the messages can quote them. */
136
+ export const PERMISSION_EVENTS = "events:add";
137
+ export const PERMISSION_POLICIES = "policies:pull";
138
+
139
+ export function hasPermission(identity: KeyIdentity, permission: string): boolean {
140
+ return identity.permissions.includes(permission);
141
+ }
142
+
143
+ /** "Acme Inc (acme)", or the slug/id alone when the server sent less. */
144
+ export function describeOrg(identity: KeyIdentity): string {
145
+ if (identity.orgName && identity.orgSlug) return `${identity.orgName} (${identity.orgSlug})`;
146
+ return identity.orgName ?? identity.orgSlug ?? identity.orgId ?? "an unknown organisation";
147
+ }
@@ -5,9 +5,10 @@
5
5
  * before import so the worker never knowingly executes modified bytes.
6
6
  */
7
7
  import { createHash } from "node:crypto";
8
- import { existsSync, readFileSync, realpathSync } from "node:fs";
8
+ import { existsSync, readFileSync, realpathSync, rmSync } from "node:fs";
9
9
  import { homedir } from "node:os";
10
10
  import { isAbsolute, relative, resolve } from "node:path";
11
+ import { cloudPoliciesDir } from "./fp-home";
11
12
 
12
13
  const ACTIVE_SCHEMA_VERSION = 1;
13
14
  const SHA256_RE = /^[a-f0-9]{64}$/;
@@ -47,7 +48,7 @@ interface ActiveManifest {
47
48
  export function cloudManagedPolicyRoot(): string {
48
49
  return (
49
50
  process.env.FAILPROOFAI_CLOUD_POLICY_DIR ??
50
- resolve(homedir(), ".failproofai", "policies", "cloud-managed")
51
+ cloudPoliciesDir()
51
52
  );
52
53
  }
53
54
 
@@ -85,6 +86,36 @@ function resolveManagedPath(root: string, candidate: string): string {
85
86
  return realFile;
86
87
  }
87
88
 
89
+ /**
90
+ * Stop enforcing cloud-managed policies on this machine.
91
+ *
92
+ * Removes `active.json` only — the manifest that says which generation is
93
+ * live. The generation directories and content-addressed artifacts are left
94
+ * alone: they are large, hash-verified on use, and inert once nothing points
95
+ * at them, so keeping them makes a reconnect cheap and offline-safe.
96
+ *
97
+ * Called by `--disconnect`, which without it did not disconnect. Clearing the
98
+ * credential stops the daemon REFRESHING policy; every artifact already on disk
99
+ * kept being loaded and enforced on every tool call, so a user who had
100
+ * deliberately left their organisation's cloud went on being governed by
101
+ * whatever generation happened to be current when they left — indefinitely,
102
+ * with `--status` reporting the machine as unconnected.
103
+ *
104
+ * Returns true when a manifest was actually removed.
105
+ */
106
+ export function clearActiveCloudManagedPolicies(): boolean {
107
+ const activePath = resolve(cloudManagedPolicyRoot(), "active.json");
108
+ if (!existsSync(activePath)) return false;
109
+ try {
110
+ rmSync(activePath, { force: true });
111
+ return true;
112
+ } catch {
113
+ // Best-effort: `--disconnect` still clears the credentials, and reporting
114
+ // a failure to remove one file would obscure that the rest succeeded.
115
+ return false;
116
+ }
117
+ }
118
+
88
119
  export function readActiveCloudManagedPolicies(): CloudManagedPolicyArtifact[] {
89
120
  const root = cloudManagedPolicyRoot();
90
121
  const activePath = resolve(root, "active.json");
@@ -28,9 +28,11 @@ import { join } from "node:path";
28
28
  import { homedir } from "node:os";
29
29
 
30
30
  import { readHooksConfig, writeHooksConfig } from "./hooks-config";
31
+ import { credentialsFile, failproofaiHome as layoutHome } from "./fp-home";
32
+ import { readCredentials, writeCredentials, updateConfig } from "./fp-config";
31
33
 
32
34
  /** The hosted ingest endpoint — a COMPLETE endpoint, not a base to join onto. */
33
- export const DEFAULT_INGEST_URL = "https://server.befailproof.ai/events";
35
+ export const DEFAULT_INGEST_URL = "https://server.befailproof.ai/v1/events";
34
36
 
35
37
  export interface IngestCredential {
36
38
  url: string;
@@ -45,19 +47,26 @@ export interface CollectorSettings {
45
47
  hooksVerbosity?: "all" | "decisions" | "off";
46
48
  redact?: "minimal" | "off";
47
49
  environment?: string;
50
+ /**
51
+ * The machine's id, so the daemon can stamp it on every collected event and
52
+ * the dashboard groups by machine rather than by `agent_id` (a per-project
53
+ * identity). Written by `--connect --machine-id`; the Rust collector reads it
54
+ * as `collector.machineId`.
55
+ */
56
+ machineId?: string;
48
57
  }
49
58
 
50
59
  export function failproofaiHome(): string {
51
- return process.env.FAILPROOFAI_HOME || join(homedir(), ".failproofai");
60
+ return layoutHome();
52
61
  }
53
62
 
54
63
  export function ingestPath(): string {
55
- return join(failproofaiHome(), "ingest.json");
64
+ return credentialsFile();
56
65
  }
57
66
 
58
67
  /** Whether an ingest credential is already configured on this machine. */
59
68
  export function hasIngestCredential(): boolean {
60
- return existsSync(ingestPath());
69
+ return Boolean(readCredentials().ingest);
61
70
  }
62
71
 
63
72
  /**
@@ -69,24 +78,19 @@ export function hasIngestCredential(): boolean {
69
78
  * cannot read it either, is the truth.
70
79
  */
71
80
  export function readIngestCredential(): IngestCredential | null {
72
- try {
73
- const raw = JSON.parse(readFileSync(ingestPath(), "utf8")) as Partial<IngestCredential>;
74
- if (typeof raw.url !== "string" || typeof raw.key !== "string") return null;
75
- if (!raw.url || !raw.key) return null;
76
- return { url: raw.url, key: raw.key };
77
- } catch {
78
- return null;
79
- }
81
+ const ingest = readCredentials().ingest;
82
+ return ingest ? { url: ingest.url, key: ingest.key } : null;
80
83
  }
81
84
 
82
85
  /** Remove the ingest credential. Returns whether a file was actually removed. */
83
86
  export function clearIngestCredential(): boolean {
84
- try {
85
- rmSync(ingestPath());
86
- return true;
87
- } catch {
88
- return false;
89
- }
87
+ const current = readCredentials();
88
+ if (!current.ingest) return false;
89
+ // Drop only the ingest table: stopping event reporting must not also revoke
90
+ // the policy-pull credential or the dashboard session.
91
+ const { ingest: _dropped, ...rest } = current;
92
+ writeCredentials(rest);
93
+ return true;
90
94
  }
91
95
 
92
96
  /**
@@ -98,38 +102,26 @@ export function clearIngestCredential(): boolean {
98
102
  * without POSIX modes; the write itself is not.
99
103
  */
100
104
  export function writeIngestCredential(cred: IngestCredential): string {
101
- const home = failproofaiHome();
102
- mkdirSync(home, { recursive: true });
103
- try {
104
- const mode = statSync(home).mode & 0o777;
105
- if (mode & 0o077) chmodSync(home, 0o700);
106
- } catch {
107
- // Not fatal: the file's own 0600 is the primary protection.
108
- }
109
-
110
- const path = ingestPath();
111
- // `mode` on writeFileSync applies only when the file is CREATED, so an
112
- // existing over-permissive file would keep its mode without the chmod.
113
- writeFileSync(path, JSON.stringify(cred, null, 2) + "\n", { mode: 0o600 });
114
- try {
115
- chmodSync(path, 0o600);
116
- } catch {
117
- /* best effort */
118
- }
119
- return path;
105
+ // Merge, never replace — credentials.toml also holds the cloud token and the
106
+ // auth session, and one caller rewriting the file must not drop the others.
107
+ // writeCredentials owns the 0600 mode and tightens the home around it.
108
+ writeCredentials({ ...readCredentials(), ingest: cred });
109
+ return credentialsFile();
120
110
  }
121
111
 
122
112
  /** Merge the collector block into the shared (non-secret) config. */
123
113
  export function writeCollectorSettings(settings: CollectorSettings): void {
124
- const config = readHooksConfig();
125
- writeHooksConfig({
126
- ...config,
114
+ // Collector preferences are NOT secret, so they live in config.toml. The key
115
+ // that makes them meaningful lives in credentials.toml; that split is the
116
+ // whole point of layout 2.
117
+ updateConfig({
127
118
  collector: {
128
119
  sessions: settings.sessions,
129
120
  hooks: settings.hooks,
130
121
  hooksVerbosity: settings.hooksVerbosity ?? "decisions",
131
122
  redact: settings.redact ?? "minimal",
132
123
  environment: settings.environment ?? "local",
124
+ machineId: settings.machineId,
133
125
  },
134
126
  });
135
127
  }