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
@@ -1,400 +0,0 @@
1
- //! Spawns and supervises the warm Node/Bun worker process, and relays
2
- //! `Hook` requests to it.
3
- //!
4
- //! The worker speaks a SEPARATE, simpler internal protocol on its own
5
- //! socket (no `protocolVersion` — this process always spawns a
6
- //! version-matched worker, so there's nothing to negotiate) — this module
7
- //! is the only thing that talks to it, and it's the one place that
8
- //! translates between that internal protocol and the client-facing
9
- //! [`fpai_ipc::ServerMessage`] envelope (which DOES carry `protocolVersion`,
10
- //! since that one crosses a real compatibility boundary against whatever
11
- //! `failproofai` CLI version happens to be installed).
12
-
13
- use fpai_ipc::framing::{read_message, write_message};
14
- use serde_json::json;
15
- use std::io;
16
- use std::os::unix::net::UnixStream;
17
- use std::os::unix::process::CommandExt;
18
- use std::path::{Path, PathBuf};
19
- use std::process::{Child, Command, Stdio};
20
- use std::sync::Mutex;
21
- use std::time::{Duration, Instant};
22
-
23
- #[derive(Debug)]
24
- pub enum WorkerError {
25
- Io(io::Error),
26
- /// The worker never created its socket within the startup deadline.
27
- StartupTimedOut,
28
- /// A response arrived but wasn't a well-formed hookResult/error.
29
- BadResponse(String),
30
- }
31
-
32
- impl std::fmt::Display for WorkerError {
33
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34
- match self {
35
- WorkerError::Io(e) => write!(f, "worker io error: {e}"),
36
- WorkerError::StartupTimedOut => write!(f, "worker did not start in time"),
37
- WorkerError::BadResponse(s) => write!(f, "unexpected worker response: {s}"),
38
- }
39
- }
40
- }
41
-
42
- impl std::error::Error for WorkerError {}
43
-
44
- #[derive(Debug)]
45
- pub struct HookOutcome {
46
- pub exit_code: i32,
47
- pub stdout: String,
48
- pub stderr: String,
49
- }
50
-
51
- /// How to launch the worker process. `FAILPROOFAI_WORKER_CMD` (dev/test
52
- /// override — a full shell command string, run via `sh -c`) always takes
53
- /// precedence; otherwise falls back to `node <script>` for a production
54
- /// install where `dist/worker.mjs` sits next to this binary.
55
- ///
56
- /// Resolved explicitly by the caller (`from_env`, called once from
57
- /// `main.rs`) rather than read from the environment inside `Worker` itself
58
- /// — `std::env::var` is process-global, and Rust's test harness runs tests
59
- /// in parallel by default, so multiple tests setting
60
- /// `FAILPROOFAI_WORKER_CMD` to different values would race. Tests instead
61
- /// construct a `WorkerCommand` directly via `shell`/`node`.
62
- pub enum WorkerCommand {
63
- Shell(String),
64
- Node { script: PathBuf },
65
- }
66
-
67
- impl WorkerCommand {
68
- pub fn from_env() -> Self {
69
- if let Ok(cmd) = std::env::var("FAILPROOFAI_WORKER_CMD") {
70
- return WorkerCommand::shell(cmd);
71
- }
72
- // Packaging (Stage 5) lands dist/worker.mjs; until then this is only
73
- // reachable via the explicit override above in dev/test.
74
- WorkerCommand::Node {
75
- script: PathBuf::from("dist/worker.mjs"),
76
- }
77
- }
78
-
79
- pub fn shell(cmd: impl Into<String>) -> Self {
80
- WorkerCommand::Shell(cmd.into())
81
- }
82
-
83
- fn spawn(&self, worker_socket: &Path) -> io::Result<Child> {
84
- let mut command = match self {
85
- WorkerCommand::Shell(shell_cmd) => {
86
- let mut c = Command::new("sh");
87
- c.arg("-c").arg(shell_cmd);
88
- c
89
- }
90
- WorkerCommand::Node { script } => {
91
- let mut c = Command::new("node");
92
- c.arg(script);
93
- c
94
- }
95
- };
96
- command
97
- .env("FAILPROOFAI_WORKER_SOCKET", worker_socket)
98
- .stdin(Stdio::null())
99
- // Piped, not inherited: an inherited stdout/stderr hands the
100
- // worker its own copy of this process's fds, and if this
101
- // process's own stdout is itself part of a pipeline (a test
102
- // harness, `cargo test | tail`, a shell capturing output), that
103
- // pipe never sees EOF as long as the worker — however it exits
104
- // — still holds a copy open. Piped fds belong to this process
105
- // alone and close cleanly when the worker does. Both pipes are
106
- // drained below: the worker outlives the daemon's whole
107
- // lifetime running real policy code, so any `console.log` from
108
- // a user's custom policy accumulates, and an undrained pipe
109
- // would eventually fill its OS buffer and block the worker
110
- // mid-write — every later hook call would then fail closed
111
- // until the daemon restarted.
112
- .stdout(Stdio::piped())
113
- .stderr(Stdio::piped())
114
- // New session + process group leader: `sh -c "<cmd>"` is not
115
- // guaranteed to exec(2) into `<cmd>` in place (some shells fork
116
- // instead), so killing only the tracked PID can leave a real
117
- // grandchild worker process running, reparented and orphaned.
118
- // Killing the whole group (see `kill_process_group`) reaches it
119
- // either way.
120
- .process_group(0);
121
- let mut child = command.spawn()?;
122
- if let Some(out) = child.stdout.take() {
123
- std::thread::spawn(move || forward_worker_output("stdout", out));
124
- }
125
- if let Some(err) = child.stderr.take() {
126
- std::thread::spawn(move || forward_worker_output("stderr", err));
127
- }
128
- Ok(child)
129
- }
130
- }
131
-
132
- /// Drains one of the worker's output pipes for its whole life, relaying it
133
- /// to the daemon's own stderr (which systemd/launchd already capture into
134
- /// the service log). Ends on EOF when the worker exits.
135
- fn forward_worker_output(label: &'static str, pipe: impl io::Read) {
136
- use std::io::BufRead;
137
- for line in io::BufReader::new(pipe).lines().map_while(Result::ok) {
138
- eprintln!("[failproofaid] worker {label}: {line}");
139
- }
140
- }
141
-
142
- /// Kills the entire process group `child` leads (see `.process_group(0)` in
143
- /// `WorkerCommand::spawn`), not just the single tracked PID — reaches a
144
- /// `sh -c "<cmd>"` grandchild even when `sh` forked rather than exec'd.
145
- fn kill_process_group(child: &mut Child) {
146
- let pgid = child.id() as libc::pid_t;
147
- unsafe {
148
- libc::kill(-pgid, libc::SIGKILL);
149
- }
150
- let _ = child.wait();
151
- }
152
-
153
- pub struct Worker {
154
- socket_path: PathBuf,
155
- cmd: WorkerCommand,
156
- child: Mutex<Option<Child>>,
157
- }
158
-
159
- impl Worker {
160
- pub fn new(socket_path: PathBuf, cmd: WorkerCommand) -> Self {
161
- Worker {
162
- socket_path,
163
- cmd,
164
- child: Mutex::new(None),
165
- }
166
- }
167
-
168
- /// Spawns the worker if it isn't already running (or has died since the
169
- /// last check), then waits for it to actually accept connections —
170
- /// failing fast (rather than waiting out the full startup deadline) if
171
- /// the process exits before ever getting there, e.g. a broken
172
- /// FAILPROOFAI_WORKER_CMD.
173
- ///
174
- /// Readiness is a real `connect()`, not `socket_path.exists()`: a Unix
175
- /// socket file outlives the process that bound it, so a dead worker
176
- /// always leaves one behind. `exists()` would see the *previous*
177
- /// worker's leftover file the instant this one is spawned, break out of
178
- /// the wait immediately, and hand `call()` a socket nothing is
179
- /// listening on — `ECONNREFUSED` on every request until the daemon
180
- /// restarted, which is exactly the crash-recovery path this loop is
181
- /// here to provide. The stale file is removed first for the same
182
- /// reason.
183
- fn ensure_started(&self) -> Result<(), WorkerError> {
184
- let mut guard = self.child.lock().unwrap();
185
- let needs_spawn = match guard.as_mut() {
186
- Some(child) => !matches!(child.try_wait(), Ok(None)), // Ok(None) == still running
187
- None => true,
188
- };
189
- if needs_spawn {
190
- let _ = std::fs::remove_file(&self.socket_path);
191
- let mut child = self.cmd.spawn(&self.socket_path).map_err(WorkerError::Io)?;
192
- let deadline = Instant::now() + Duration::from_secs(5);
193
- loop {
194
- if UnixStream::connect(&self.socket_path).is_ok() {
195
- break;
196
- }
197
- if let Ok(Some(status)) = child.try_wait() {
198
- return Err(WorkerError::Io(io::Error::other(format!(
199
- "worker process exited before creating its socket (status: {status})"
200
- ))));
201
- }
202
- if Instant::now() >= deadline {
203
- kill_process_group(&mut child);
204
- let _ = std::fs::remove_file(&self.socket_path);
205
- return Err(WorkerError::StartupTimedOut);
206
- }
207
- std::thread::sleep(Duration::from_millis(20));
208
- }
209
- *guard = Some(child);
210
- }
211
- Ok(())
212
- }
213
-
214
- /// Spawns the worker eagerly and blocks until it's ready, without
215
- /// running a hook through it. Called once from `main.rs` right after
216
- /// startup so the worker's ~hundreds-of-ms cold start (process spawn +
217
- /// module load) happens before any real request arrives, not on the
218
- /// critical path of the first one. The client's fail-closed budget
219
- /// (`DAEMON_ATTEMPT_TIMEOUT_MS` in `daemon-client.ts`) is deliberately
220
- /// tight for a *healthy warm* daemon and is not meant to also cover a
221
- /// cold worker spawn — without this, the very first hook call after
222
- /// every daemon (re)start would fail closed even though the daemon
223
- /// itself is up, which defeats the point of keeping evaluation warm.
224
- /// Errors are logged, not propagated: a failed warm-up isn't fatal to
225
- /// the daemon itself, since `call()` retries `ensure_started` on the
226
- /// next real request anyway.
227
- pub fn warm(&self) {
228
- if let Err(err) = self.ensure_started() {
229
- eprintln!("[failproofaid] worker warm-up failed (will retry on first request): {err}");
230
- }
231
- }
232
-
233
- /// Relays one hook evaluation to the worker and returns its verdict.
234
- /// `ensure_started` is called first so a worker that crashed since the
235
- /// last request gets one restart attempt before this request fails —
236
- /// but a request already in flight is never retried mid-call; a
237
- /// failure here surfaces as `WorkerError` and the caller (server.rs)
238
- /// turns that into a client-facing `Error` response.
239
- pub fn call(
240
- &self,
241
- hook_event: &str,
242
- cli: &str,
243
- stdin: &str,
244
- cwd: Option<&str>,
245
- ) -> Result<HookOutcome, WorkerError> {
246
- self.ensure_started()?;
247
-
248
- let mut stream = UnixStream::connect(&self.socket_path).map_err(WorkerError::Io)?;
249
- stream
250
- .set_read_timeout(Some(Duration::from_secs(30)))
251
- .map_err(WorkerError::Io)?;
252
-
253
- let request = json!({
254
- "type": "hook",
255
- "hookEvent": hook_event,
256
- "cli": cli,
257
- "stdin": stdin,
258
- "cwd": cwd,
259
- });
260
- write_message(&mut stream, &request).map_err(|e| WorkerError::Io(io::Error::other(e)))?;
261
-
262
- let response: serde_json::Value =
263
- read_message(&mut stream).map_err(|e| WorkerError::Io(io::Error::other(e)))?;
264
-
265
- match response.get("type").and_then(|t| t.as_str()) {
266
- Some("hookResult") => {
267
- let exit_code = response
268
- .get("exitCode")
269
- .and_then(|v| v.as_i64())
270
- .ok_or_else(|| WorkerError::BadResponse("missing exitCode".to_string()))?
271
- as i32;
272
- let stdout = response
273
- .get("stdout")
274
- .and_then(|v| v.as_str())
275
- .unwrap_or("")
276
- .to_string();
277
- let stderr = response
278
- .get("stderr")
279
- .and_then(|v| v.as_str())
280
- .unwrap_or("")
281
- .to_string();
282
- Ok(HookOutcome {
283
- exit_code,
284
- stdout,
285
- stderr,
286
- })
287
- }
288
- Some("error") => {
289
- let message = response
290
- .get("message")
291
- .and_then(|v| v.as_str())
292
- .unwrap_or("worker reported an error")
293
- .to_string();
294
- Err(WorkerError::BadResponse(message))
295
- }
296
- _ => Err(WorkerError::BadResponse(response.to_string())),
297
- }
298
- }
299
- }
300
-
301
- impl Drop for Worker {
302
- fn drop(&mut self) {
303
- if let Ok(mut guard) = self.child.lock()
304
- && let Some(mut child) = guard.take()
305
- {
306
- kill_process_group(&mut child);
307
- // The worker's socket file survives the process that bound it,
308
- // so a clean shutdown has to clear it too — otherwise the next
309
- // daemon inherits debris that looks, to anything checking the
310
- // filesystem, like a live worker.
311
- let _ = std::fs::remove_file(&self.socket_path);
312
- }
313
- }
314
- }
315
-
316
- #[cfg(test)]
317
- mod tests {
318
- use super::*;
319
- use std::os::unix::net::UnixListener;
320
-
321
- fn temp_socket_path(name: &str) -> PathBuf {
322
- let nanos = std::time::SystemTime::now()
323
- .duration_since(std::time::UNIX_EPOCH)
324
- .unwrap()
325
- .as_nanos();
326
- std::env::temp_dir().join(format!(
327
- "failproofaid-worker-test-{}-{}-{}",
328
- std::process::id(),
329
- name,
330
- nanos
331
- ))
332
- }
333
-
334
- /// The restart-on-crash path: a worker that died left its socket file
335
- /// behind, and the supervisor must not read that file's mere existence
336
- /// as "a worker is listening". Before the readiness probe became a real
337
- /// `connect()`, this returned `Ok(())` immediately and every later
338
- /// request hit `ECONNREFUSED` on a dead socket.
339
- #[test]
340
- fn a_stale_socket_file_is_not_mistaken_for_a_live_worker() {
341
- let socket_path = temp_socket_path("stale");
342
- // Exactly the debris a dead worker leaves: std does not unlink the
343
- // path when the listener is dropped.
344
- let listener = UnixListener::bind(&socket_path).unwrap();
345
- drop(listener);
346
- assert!(
347
- socket_path.exists(),
348
- "premise: a bound-then-dropped socket file outlives its listener"
349
- );
350
-
351
- // A "worker" that exits without ever binding. The stale file is the
352
- // only thing on disk that could be mistaken for readiness.
353
- let worker = Worker::new(socket_path.clone(), WorkerCommand::shell("exit 0"));
354
- let err = worker
355
- .call("PreToolUse", "claude", "{}", None)
356
- .expect_err("a worker that never bound must not report ready");
357
- assert!(
358
- err.to_string()
359
- .contains("exited before creating its socket"),
360
- "got: {err}"
361
- );
362
-
363
- std::fs::remove_file(&socket_path).ok();
364
- }
365
-
366
- /// Readiness against the real worker, over a socket path that already
367
- /// holds a stale file — the exact shape of a restart after an unclean
368
- /// exit. `ensure_started` must clear the debris, wait for the new
369
- /// worker to genuinely accept connections, and `Drop` must leave the
370
- /// path clean again so the next daemon starts from a blank slate.
371
- #[test]
372
- fn restarts_over_a_stale_socket_file_and_cleans_up_on_drop() {
373
- let repo_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
374
- .parent()
375
- .and_then(|p| p.parent())
376
- .expect("crates/failproofaid should be two levels under the repo root")
377
- .to_path_buf();
378
- let worker_script = repo_root.join("bin").join("failproofai-worker.mjs");
379
-
380
- let socket_path = temp_socket_path("restart-over-stale");
381
- let listener = UnixListener::bind(&socket_path).unwrap();
382
- drop(listener);
383
-
384
- let worker = Worker::new(
385
- socket_path.clone(),
386
- WorkerCommand::shell(format!("bun {}", worker_script.display())),
387
- );
388
- worker
389
- .ensure_started()
390
- .expect("the worker should start over the stale file");
391
- // Ready means *connectable*, not merely "a file is there".
392
- UnixStream::connect(&socket_path).expect("a ready worker accepts connections");
393
-
394
- drop(worker);
395
- assert!(
396
- !socket_path.exists(),
397
- "Drop should remove the worker socket file"
398
- );
399
- }
400
- }
@@ -1,241 +0,0 @@
1
- //! Black-box tests that spawn the real compiled `failproofaid` binary as a
2
- //! subprocess and talk to it over a real Unix socket — closer to how a
3
- //! systemd unit / launchd agent will actually invoke it (Stage 4) than the
4
- //! in-process unit tests in `src/server.rs` are.
5
-
6
- use fpai_ipc::{ClientMessage, PROTOCOL_VERSION, ServerMessage, read_message, write_message};
7
- use std::io::BufRead;
8
- use std::os::unix::net::UnixStream;
9
- use std::path::PathBuf;
10
- use std::process::{Child, Command, ExitStatus, Stdio};
11
- use std::sync::{Arc, Mutex};
12
- use std::time::{Duration, Instant};
13
-
14
- fn binary_path() -> &'static str {
15
- env!("CARGO_BIN_EXE_failproofaid")
16
- }
17
-
18
- /// A dedicated, never-before-existing run directory per test — mirroring a
19
- /// real deployment, where `~/.failproofai/run` is always failproofaid's own
20
- /// directory that it creates itself. `ensure_run_dir` (see `src/paths.rs`)
21
- /// deliberately refuses to chmod a pre-existing directory it didn't create,
22
- /// so reusing a shared directory like the bare system temp dir here would
23
- /// make every test in this file fail with a permissions error instead of
24
- /// exercising the daemon at all.
25
- fn unique_socket_path(name: &str) -> PathBuf {
26
- let nanos = std::time::SystemTime::now()
27
- .duration_since(std::time::UNIX_EPOCH)
28
- .unwrap()
29
- .as_nanos();
30
- std::env::temp_dir()
31
- .join(format!(
32
- "failproofaid-e2e-{}-{}-{}",
33
- std::process::id(),
34
- name,
35
- nanos
36
- ))
37
- .join("failproofaid.sock")
38
- }
39
-
40
- /// Owns a spawned daemon subprocess and guarantees it is killed and reaped
41
- /// on scope exit — including during an unwind from a failed assertion.
42
- /// Without this, any panic between `spawn_daemon` and a test's explicit
43
- /// SIGTERM orphaned a live daemon (and the worker process group under it)
44
- /// for the rest of the machine's uptime.
45
- struct DaemonGuard {
46
- child: Option<Child>,
47
- /// Everything the daemon has written to stderr so far, drained
48
- /// continuously on a background thread. Drained rather than merely
49
- /// piped: an unread pipe fills at ~64 KiB and blocks the daemon
50
- /// mid-write, hanging these tests. Kept so a failure can report what
51
- /// the daemon itself said, which is the part actually worth reading.
52
- stderr: Arc<Mutex<String>>,
53
- }
54
-
55
- impl DaemonGuard {
56
- fn stderr(&self) -> String {
57
- self.stderr.lock().unwrap().clone()
58
- }
59
-
60
- /// SIGTERM, then wait — the clean-shutdown path a systemd `stop` /
61
- /// launchd unload takes. Consumes the child, so `Drop` becomes a no-op.
62
- fn terminate(&mut self) -> ExitStatus {
63
- let mut child = self.child.take().expect("daemon already reaped");
64
- unsafe {
65
- libc::kill(child.id() as libc::pid_t, libc::SIGTERM);
66
- }
67
- child.wait().expect("daemon should exit after SIGTERM")
68
- }
69
- }
70
-
71
- impl Drop for DaemonGuard {
72
- fn drop(&mut self) {
73
- if let Some(mut child) = self.child.take() {
74
- child.kill().ok();
75
- child.wait().ok();
76
- }
77
- }
78
- }
79
-
80
- fn spawn_daemon(socket_path: &PathBuf) -> DaemonGuard {
81
- let mut child = Command::new(binary_path())
82
- .env("FAILPROOFAI_DAEMON_SOCKET", socket_path)
83
- .stdout(Stdio::null())
84
- .stderr(Stdio::piped())
85
- .spawn()
86
- .expect("failed to spawn failproofaid binary");
87
-
88
- let stderr_log = Arc::new(Mutex::new(String::new()));
89
- if let Some(pipe) = child.stderr.take() {
90
- let sink = stderr_log.clone();
91
- std::thread::spawn(move || {
92
- for line in std::io::BufReader::new(pipe).lines().map_while(Result::ok) {
93
- sink.lock().unwrap().push_str(&line);
94
- sink.lock().unwrap().push('\n');
95
- }
96
- });
97
- }
98
- let mut guard = DaemonGuard {
99
- child: Some(child),
100
- stderr: stderr_log,
101
- };
102
-
103
- let deadline = Instant::now() + Duration::from_secs(5);
104
- while !socket_path.exists() {
105
- // A daemon that died during startup should report *why* rather than
106
- // burn the full deadline and then panic with no diagnosis.
107
- if let Some(status) = guard
108
- .child
109
- .as_mut()
110
- .expect("daemon still owned here")
111
- .try_wait()
112
- .expect("try_wait should not fail")
113
- {
114
- panic!(
115
- "daemon exited before creating its socket ({status}). stderr:\n{}",
116
- guard.stderr()
117
- );
118
- }
119
- if Instant::now() > deadline {
120
- panic!(
121
- "daemon never created its socket file within 5s. stderr:\n{}",
122
- guard.stderr()
123
- );
124
- }
125
- std::thread::sleep(Duration::from_millis(20));
126
- }
127
- guard
128
- }
129
-
130
- #[test]
131
- fn version_flag_prints_a_version_and_exits_without_binding_a_socket() {
132
- let output = Command::new(binary_path())
133
- .arg("--version")
134
- .output()
135
- .expect("failed to run --version");
136
- assert!(output.status.success());
137
- let stdout = String::from_utf8_lossy(&output.stdout);
138
- assert!(stdout.starts_with("failproofaid "), "got: {stdout:?}");
139
- }
140
-
141
- #[test]
142
- fn real_binary_answers_ping_over_a_real_socket() {
143
- let socket_path = unique_socket_path("ping");
144
- let mut daemon = spawn_daemon(&socket_path);
145
-
146
- let mut stream = UnixStream::connect(&socket_path).expect("connect should succeed");
147
- write_message(
148
- &mut stream,
149
- &ClientMessage::Ping {
150
- protocol_version: PROTOCOL_VERSION,
151
- },
152
- )
153
- .unwrap();
154
- let response: ServerMessage = read_message(&mut stream).unwrap();
155
- assert_eq!(
156
- response,
157
- ServerMessage::Pong {
158
- protocol_version: PROTOCOL_VERSION
159
- }
160
- );
161
-
162
- let status = daemon.terminate();
163
- assert!(
164
- status.success(),
165
- "daemon should exit 0 on SIGTERM, got {status:?}"
166
- );
167
- assert!(
168
- !socket_path.exists(),
169
- "daemon should remove its socket file on clean shutdown"
170
- );
171
- }
172
-
173
- #[test]
174
- fn a_second_daemon_on_the_same_socket_refuses_to_start() {
175
- let socket_path = unique_socket_path("singleton");
176
- let mut first = spawn_daemon(&socket_path);
177
-
178
- let second_output = Command::new(binary_path())
179
- .env("FAILPROOFAI_DAEMON_SOCKET", &socket_path)
180
- .output()
181
- .expect("failed to run second instance");
182
- assert!(
183
- !second_output.status.success(),
184
- "a second daemon on the same socket must not exit 0"
185
- );
186
- let stderr = String::from_utf8_lossy(&second_output.stderr);
187
- assert!(
188
- stderr.contains("already running"),
189
- "expected an 'already running' message, got: {stderr:?}"
190
- );
191
-
192
- // First instance is still healthy and answers requests after the
193
- // second one's failed startup attempt.
194
- let mut stream = UnixStream::connect(&socket_path).unwrap();
195
- write_message(
196
- &mut stream,
197
- &ClientMessage::Ping {
198
- protocol_version: PROTOCOL_VERSION,
199
- },
200
- )
201
- .unwrap();
202
- let response: ServerMessage = read_message(&mut stream).unwrap();
203
- assert_eq!(
204
- response,
205
- ServerMessage::Pong {
206
- protocol_version: PROTOCOL_VERSION
207
- }
208
- );
209
-
210
- first.terminate();
211
- }
212
-
213
- #[test]
214
- fn a_fresh_daemon_can_bind_again_after_the_previous_one_shut_down_cleanly() {
215
- let socket_path = unique_socket_path("restart");
216
-
217
- let mut first = spawn_daemon(&socket_path);
218
- let status = first.terminate();
219
- assert!(status.success());
220
-
221
- // The lock is released by the kernel when the process exits, so a
222
- // fresh daemon must be able to acquire it and bind immediately.
223
- let mut second = spawn_daemon(&socket_path);
224
- let mut stream = UnixStream::connect(&socket_path).unwrap();
225
- write_message(
226
- &mut stream,
227
- &ClientMessage::Ping {
228
- protocol_version: PROTOCOL_VERSION,
229
- },
230
- )
231
- .unwrap();
232
- let response: ServerMessage = read_message(&mut stream).unwrap();
233
- assert_eq!(
234
- response,
235
- ServerMessage::Pong {
236
- protocol_version: PROTOCOL_VERSION
237
- }
238
- );
239
-
240
- second.terminate();
241
- }
@@ -1,43 +0,0 @@
1
- [package]
2
- name = "fpai-collect"
3
- version.workspace = true
4
- edition.workspace = true
5
- license-file.workspace = true
6
- repository.workspace = true
7
- description = "Session-log and hook-activity collection for failproofaid: fault-isolated supervisor, spool, and uploader. Carries no policy logic and never shares fate with the enforcement socket."
8
- publish = false
9
-
10
- [dependencies]
11
- serde = { version = "1", features = ["derive"] }
12
- serde_json = "1"
13
- # The collector is the only async part of the daemon. `failproofaid`'s socket
14
- # server stays blocking std threads on purpose (see supervisor.rs), so this
15
- # runtime is created here and never leaks into the enforcement path.
16
- tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "fs", "sync", "io-util"] }
17
- tracing = "0.1"
18
- # `rustls-tls` rather than the default native-tls: the daemon cross-compiles to
19
- # four targets and pulling OpenSSL onto each of them is the kind of build
20
- # dependency that only breaks on the leg nobody tests. `failproofaid` already
21
- # depends on reqwest with the same TLS backend, so this adds no new C code.
22
- reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] }
23
- # inotify on Linux, FSEvents on macOS. Only ever a latency optimisation — the
24
- # sweeper is what guarantees delivery, so a watcher that fails to register
25
- # degrades to interval-only rather than losing anything.
26
- notify = "8"
27
- # Epoch-millis to RFC3339 with microseconds. Hand-rolling civil date maths is
28
- # a silent-failure risk — a leap-year slip would put every event on the wrong
29
- # day with nothing to notice it — and this pulls no C code onto any of the
30
- # four cross-compiled targets.
31
- # `bundled` vendors libsqlite3 rather than linking the host's, so the four
32
- # cross-compiled release binaries do not depend on whatever sqlite the target
33
- # happens to ship. It compiles C, which `ring` already does on every leg.
34
- #
35
- # A real libsqlite3 is not optional here: goose's sessions.db main file contains
36
- # ZERO tables, with all 1.25 MB of schema and rows in an uncheckpointed -wal.
37
- # Anything that parses the main file directly reads an empty database.
38
- rusqlite = { version = "0.40", features = ["bundled"] }
39
- time = { version = "0.3", default-features = false, features = ["std", "formatting", "macros"] }
40
-
41
- [dev-dependencies]
42
- tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "sync", "test-util"] }
43
- wiremock = "0.6"