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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +2 -1
  3. package/.next/standalone/.next/build-manifest.json +6 -6
  4. package/.next/standalone/.next/prerender-manifest.json +3 -3
  5. package/.next/standalone/.next/required-server-files.json +1 -1
  6. package/.next/standalone/.next/routes-manifest.json +6 -0
  7. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
  8. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  9. package/.next/standalone/.next/server/app/_global-error/page.js +3 -1
  10. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  11. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  13. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  14. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  15. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  16. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  17. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  18. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
  20. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  21. package/.next/standalone/.next/server/app/_not-found/page.js +3 -1
  22. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  24. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  25. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  26. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  27. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  28. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  29. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  30. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  31. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  32. package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
  33. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -3
  35. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/audit/status/route.js +1 -1
  37. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +3 -1
  39. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +3 -2
  41. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -2
  43. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +3 -2
  45. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -2
  47. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  49. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
  51. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  52. package/.next/standalone/.next/server/app/audit/page.js +5 -3
  53. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  55. package/.next/standalone/.next/server/app/index.html +1 -1
  56. package/.next/standalone/.next/server/app/index.rsc +15 -15
  57. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  58. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  59. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  60. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  61. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  62. package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
  63. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  64. package/.next/standalone/.next/server/app/page.js +3 -1
  65. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
  68. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +30 -18
  69. package/.next/standalone/.next/server/app/policies/page.js +5 -3
  70. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  72. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
  73. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  74. package/.next/standalone/.next/server/app/project/[name]/page.js +4 -2
  75. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  76. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  77. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
  78. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  79. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  80. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
  81. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  82. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  83. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
  84. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  85. package/.next/standalone/.next/server/app/projects/page.js +4 -2
  86. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  88. package/.next/standalone/.next/server/app/settings/page/app-paths-manifest.json +3 -0
  89. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +18 -0
  90. package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +6 -0
  91. package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
  92. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +53 -0
  93. package/.next/standalone/.next/server/app/settings/page.js +18 -0
  94. package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
  95. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
  96. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
  97. package/.next/standalone/.next/server/app-paths-manifest.json +2 -1
  98. package/.next/standalone/.next/server/chunks/[externals]__0h9_83k._.js +3 -0
  99. package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
  100. package/.next/standalone/.next/server/chunks/[externals]__1ifpkx-._.js +3 -0
  101. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__00ni9za._.js} +2 -2
  102. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +3 -0
  103. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08y94xv._.js +3 -0
  104. package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  106. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +3 -0
  107. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-dh5jy._.js → [root-of-the-server]__0effm9m._.js} +2 -2
  108. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
  109. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0ix8gm7._.js} +2 -2
  110. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ji11a-._.js} +2 -2
  111. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0ojwjxl._.js} +2 -2
  112. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__16le-kd._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
  113. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
  114. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vb5fl2._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +4 -4
  116. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
  118. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20d06rh._.js +3 -0
  119. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +3 -0
  120. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +3 -0
  122. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
  123. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -1
  124. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -1
  125. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/[externals]__1aq139v._.js +3 -0
  127. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-wfhrp._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
  129. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1apb7k_._.js → [root-of-the-server]__0_1tiya._.js} +2 -2
  131. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__170799-._.js → [root-of-the-server]__0spkm68._.js} +11 -11
  132. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0t281d4._.js +3 -0
  133. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17itmx-._.js +5 -0
  134. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__01od0m6._.js → [root-of-the-server]__1n2vcv4._.js} +2 -2
  135. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +1 -1
  136. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
  137. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tlvv9a._.js +3 -0
  138. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +4 -0
  139. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +3 -0
  140. package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +3 -0
  141. package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
  142. package/.next/standalone/.next/server/chunks/ssr/_0vo2qyt._.js +3 -0
  143. package/.next/standalone/.next/server/chunks/ssr/_0xej-0e._.js +3 -0
  144. package/.next/standalone/.next/server/chunks/ssr/_10399ge._.js +3 -0
  145. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  146. package/.next/standalone/.next/server/chunks/ssr/_149k9oy._.js +3 -0
  147. package/.next/standalone/.next/server/chunks/ssr/_1_hwp06._.js +3 -0
  148. package/.next/standalone/.next/server/chunks/ssr/_1ctse6u._.js +3 -0
  149. package/.next/standalone/.next/server/chunks/ssr/_1e19atl._.js +1 -1
  150. package/.next/standalone/.next/server/chunks/ssr/_1mnhi_r._.js +3 -0
  151. package/.next/standalone/.next/server/chunks/ssr/_1ror7k9._.js +3 -0
  152. package/.next/standalone/.next/server/chunks/ssr/_1sdgprl._.js +5 -0
  153. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
  154. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  155. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  156. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
  157. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -1
  158. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
  159. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +4 -0
  160. package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
  161. package/.next/standalone/.next/server/middleware.js +2 -2
  162. package/.next/standalone/.next/server/pages/404.html +1 -1
  163. package/.next/standalone/.next/server/pages/500.html +1 -1
  164. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  165. package/.next/standalone/.next/server/server-reference-manifest.json +74 -20
  166. package/.next/standalone/.next/static/chunks/{03fmihek9n986.js → 06zgzyh3wv1ig.js} +1 -1
  167. package/.next/standalone/.next/static/chunks/0km-jn9o5xyqs.css +2 -0
  168. package/.next/standalone/.next/static/chunks/{1hdhteui932bq.js → 0qmo2t50kpb30.js} +1 -1
  169. package/.next/standalone/.next/static/chunks/{2sbelanzvxvv0.js → 113g8wtal6_c4.js} +22 -22
  170. package/.next/standalone/.next/static/chunks/16p68cybetr31.js +1 -0
  171. package/.next/standalone/.next/static/chunks/18wqn0_3mzylf.js +1 -0
  172. package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
  173. package/.next/standalone/.next/static/chunks/{0fi645si-uiiq.js → 1xq1m8h14tnlj.js} +1 -1
  174. package/.next/standalone/.next/static/chunks/{3hp8ulqknybil.js → 1y3vwogqb88dx.js} +1 -1
  175. package/.next/standalone/.next/static/chunks/{3vs4ay36tjds_.js → 1zmh-b0ulrqu7.js} +1 -1
  176. package/.next/standalone/.next/static/chunks/23kev26by1e4f.js +1 -0
  177. package/.next/standalone/.next/static/chunks/299lhw60e8gr6.js +1 -0
  178. package/.next/standalone/.next/static/chunks/{18rvdw5v72364.js → 2adbpdaxp5ygq.js} +1 -1
  179. package/.next/standalone/.next/static/chunks/{2j94uwkb4s3qr.js → 2kbqcw-6swmav.js} +1 -1
  180. package/.next/standalone/.next/static/chunks/2n9n5m0963pdg.js +6 -0
  181. package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
  182. package/.next/standalone/.next/static/chunks/{31feojxhjyjpl.js → 30nrrpwymht5m.js} +1 -1
  183. package/.next/standalone/.next/static/chunks/31coz11ia5vc4.js +1 -0
  184. package/.next/standalone/.next/static/chunks/{turbopack-1lsv92x7klwrn.js → turbopack-3diub4ctvn1n0.js} +1 -1
  185. package/.next/standalone/SECURITY.md +8 -2
  186. package/.next/standalone/app/actions/get-active-pauses.ts +16 -0
  187. package/.next/standalone/app/actions/get-hooks-config.ts +2 -2
  188. package/.next/standalone/app/actions/get-scheduled-audit.ts +70 -0
  189. package/.next/standalone/app/actions/update-scheduled-audit.ts +44 -0
  190. package/.next/standalone/app/api/audit/run/route.ts +28 -0
  191. package/.next/standalone/app/api/audit/status/route.ts +13 -1
  192. package/.next/standalone/app/api/auth/status/route.ts +1 -1
  193. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +1 -3
  194. package/.next/standalone/app/audit/_components/audit-progress-strip.tsx +1 -1
  195. package/.next/standalone/app/audit/_components/auth-dialog.css +159 -0
  196. package/.next/standalone/app/audit/_components/auth-dialog.tsx +3 -0
  197. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +1 -1
  198. package/.next/standalone/app/audit/_components/empty-state.tsx +0 -1
  199. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +1 -1
  200. package/.next/standalone/app/audit/_components/quirks-section.tsx +0 -1
  201. package/.next/standalone/app/audit/_components/report-footer.tsx +0 -2
  202. package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
  203. package/.next/standalone/app/audit/_components/strengths-section.tsx +0 -1
  204. package/.next/standalone/app/audit/audit-styles.css +6 -149
  205. package/.next/standalone/app/components/pause-notices.tsx +111 -0
  206. package/.next/standalone/app/components/raw-log-viewer.tsx +1 -1
  207. package/.next/standalone/app/components/toast.tsx +1 -1
  208. package/.next/standalone/app/policies/hooks-client.tsx +84 -8
  209. package/.next/standalone/app/settings/page.tsx +31 -0
  210. package/.next/standalone/app/settings/settings-client.tsx +487 -0
  211. package/.next/standalone/components/navbar.tsx +2 -0
  212. package/.next/standalone/lib/auth/api-server-client.ts +2 -3
  213. package/.next/standalone/lib/auth/auth-store.ts +4 -5
  214. package/.next/standalone/lib/codex-sessions.ts +2 -1
  215. package/.next/standalone/lib/dashboard-host.ts +101 -0
  216. package/.next/standalone/lib/install-check.ts +22 -7
  217. package/.next/standalone/lib/pi-sessions.ts +99 -14
  218. package/.next/standalone/lib/telemetry-enabled.ts +54 -0
  219. package/.next/standalone/lib/telemetry-id.ts +52 -1
  220. package/.next/standalone/lib/telemetry.ts +8 -4
  221. package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
  222. package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
  223. package/.next/standalone/node_modules/react/package.json +3 -3
  224. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
  225. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
  226. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
  227. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
  228. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
  229. package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
  230. package/.next/standalone/node_modules/react-dom/package.json +4 -4
  231. package/.next/standalone/package.json +23 -12
  232. package/.next/standalone/proxy.ts +111 -1
  233. package/.next/standalone/server.js +1 -1
  234. package/bin/failproofai-worker.mjs +1 -1
  235. package/bin/failproofai.mjs +1041 -81
  236. package/bin/failproofaid-shim.mjs +4 -4
  237. package/dist/cli.mjs +18513 -10474
  238. package/dist/worker.mjs +1223 -299
  239. package/lib/auth/api-server-client.ts +2 -3
  240. package/lib/auth/auth-store.ts +4 -5
  241. package/lib/codex-sessions.ts +2 -1
  242. package/lib/dashboard-host.ts +101 -0
  243. package/lib/install-check.ts +22 -7
  244. package/lib/pi-sessions.ts +99 -14
  245. package/lib/telemetry-enabled.ts +54 -0
  246. package/lib/telemetry-id.ts +52 -1
  247. package/lib/telemetry.ts +8 -4
  248. package/package.json +23 -12
  249. package/scripts/build-daemon-packages.mjs +247 -0
  250. package/scripts/daemon-platforms.mjs +42 -0
  251. package/scripts/dev-hook.mjs +46 -5
  252. package/scripts/install-telemetry.mjs +8 -1
  253. package/scripts/launch.ts +25 -3
  254. package/scripts/parse-script-args.ts +27 -1
  255. package/scripts/prune-standalone.mjs +26 -0
  256. package/scripts/publish-aliases.mjs +62 -41
  257. package/scripts/repro-npm-install.sh +155 -0
  258. package/scripts/translate-docs/cache.ts +1 -1
  259. package/scripts/translate-docs/cli.ts +2 -2
  260. package/scripts/translate-docs/mintlify-nav.ts +1 -1
  261. package/scripts/translate-docs/readme-translator.ts +163 -4
  262. package/scripts/validate-mdx.ts +153 -13
  263. package/src/audit/audit-lock.ts +285 -0
  264. package/src/audit/audit-schedule.ts +111 -0
  265. package/src/audit/cache.ts +18 -5
  266. package/src/audit/cli-adapters/goose.ts +2 -1
  267. package/src/audit/cli-adapters/hermes.ts +28 -10
  268. package/src/audit/cli.ts +191 -35
  269. package/src/audit/dashboard-cache.ts +2 -3
  270. package/src/audit/report.ts +0 -1
  271. package/src/hooks/backfill-cli.ts +247 -0
  272. package/src/hooks/builtin-policies.ts +391 -3
  273. package/src/hooks/cloud-connection.ts +354 -0
  274. package/src/hooks/cloud-enrollment-cli.ts +439 -0
  275. package/src/hooks/cloud-enrollment.ts +284 -0
  276. package/src/hooks/cloud-introspect.ts +147 -0
  277. package/src/hooks/cloud-managed-policies.ts +206 -0
  278. package/src/hooks/collector-config.ts +218 -0
  279. package/src/hooks/configure-wizard.ts +1211 -233
  280. package/src/hooks/custom-hooks-loader.ts +243 -37
  281. package/src/hooks/daemon-client.ts +136 -33
  282. package/src/hooks/daemon-download.ts +264 -31
  283. package/src/hooks/daemon-service.ts +1124 -76
  284. package/src/hooks/first-run-gate.ts +50 -0
  285. package/src/hooks/flush-cli.ts +180 -0
  286. package/src/hooks/fp-config.ts +835 -0
  287. package/src/hooks/fp-home.ts +714 -0
  288. package/src/hooks/fp-reset.ts +1363 -0
  289. package/src/hooks/handler.ts +185 -6
  290. package/src/hooks/harness-cli.ts +386 -0
  291. package/src/hooks/hook-activity-store.ts +171 -24
  292. package/src/hooks/hook-logger.ts +2 -2
  293. package/src/hooks/hook-telemetry.ts +5 -1
  294. package/src/hooks/hooks-config.ts +4 -3
  295. package/src/hooks/integrations.ts +0 -1
  296. package/src/hooks/loader-utils.ts +172 -9
  297. package/src/hooks/manager.ts +41 -5
  298. package/src/hooks/migrations.ts +450 -0
  299. package/src/hooks/onboarding-attempt.ts +195 -0
  300. package/src/hooks/onboarding-lock.ts +118 -0
  301. package/src/hooks/policy-types.ts +28 -0
  302. package/src/hooks/session-pause-cli.ts +159 -0
  303. package/src/hooks/session-pause.ts +267 -0
  304. package/src/hooks/setup-state.ts +256 -0
  305. package/src/hooks/tui.ts +209 -9
  306. package/src/hooks/uninstall-cli.ts +418 -0
  307. package/src/hooks/worker-server.ts +134 -15
  308. package/.next/standalone/.next/server/chunks/[externals]__14odj07._.js +0 -3
  309. package/.next/standalone/.next/server/chunks/[externals]__1s61mel._.js +0 -3
  310. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0id_xf7._.js +0 -3
  311. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0longh-._.js +0 -3
  312. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
  313. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
  314. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
  315. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
  316. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
  317. package/.next/standalone/.next/server/chunks/ssr/[externals]__1_g_b3t._.js +0 -3
  318. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0114ewg._.js +0 -4
  319. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
  320. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
  321. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
  322. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0nb0syg._.js +0 -3
  323. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
  324. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +0 -4
  325. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0uno39i._.js +0 -3
  326. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
  327. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ath6v_._.js +0 -4
  328. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1psjybo._.js +0 -3
  329. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
  330. package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
  331. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_0oy_-36.js +0 -3
  332. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
  333. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
  334. package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
  335. package/.next/standalone/.next/static/chunks/0zbxssxh53n-3.js +0 -1
  336. package/.next/standalone/.next/static/chunks/2mazwb1qmur1p.js +0 -1
  337. package/.next/standalone/.next/static/chunks/2pujpnfx4pws6.css +0 -2
  338. package/.next/standalone/.next/static/chunks/33u59vf_8xpd-.js +0 -1
  339. package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
  340. package/.next/standalone/.next/static/chunks/3s7myk_co0gbh.js +0 -1
  341. package/.next/standalone/.next/static/chunks/40k1_v4os4cw6.js +0 -6
  342. package/.next/standalone/Cargo.lock +0 -428
  343. package/.next/standalone/Cargo.toml +0 -9
  344. package/.next/standalone/crates/PROTOCOL.md +0 -122
  345. package/.next/standalone/crates/failproofaid/Cargo.toml +0 -18
  346. package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
  347. package/.next/standalone/crates/failproofaid/src/main.rs +0 -83
  348. package/.next/standalone/crates/failproofaid/src/paths.rs +0 -172
  349. package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
  350. package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
  351. package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
  352. package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
  353. package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
  354. package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
  355. package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
  356. package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
  357. package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
  358. package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
  359. package/.next/standalone/integration-suite/Dockerfile +0 -47
  360. package/.next/standalone/integration-suite/README.md +0 -129
  361. package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
  362. package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
  363. package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
  364. package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
  365. package/.next/standalone/integration-suite/install-clis.sh +0 -136
  366. package/.next/standalone/integration-suite/probe-cli.sh +0 -248
  367. package/.next/standalone/integration-suite/report.js +0 -120
  368. package/.next/standalone/integration-suite/run.sh +0 -159
  369. package/.next/standalone/openclaw-plugin/index.js +0 -223
  370. package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
  371. package/.next/standalone/openclaw-plugin/package.json +0 -12
  372. package/.next/standalone/osv-scanner.toml +0 -17
  373. package/.next/standalone/pi-extension/index.ts +0 -524
  374. package/.next/standalone/pi-extension/package.json +0 -12
  375. package/.next/standalone/postcss.config.mjs +0 -8
  376. package/.next/standalone/readme-arch-hq.gif +0 -0
  377. package/.next/standalone/rust-toolchain.toml +0 -4
  378. package/src/auth/cli.ts +0 -371
  379. /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_buildManifest.js +0 -0
  380. /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_clientMiddlewareManifest.js +0 -0
  381. /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_ssgManifest.js +0 -0
@@ -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,16 +0,0 @@
1
- [package]
2
- name = "fpai-ipc"
3
- version.workspace = true
4
- edition.workspace = true
5
- license-file.workspace = true
6
- repository.workspace = true
7
- description = "Wire protocol (framing + envelope + peer verification) shared by failproofaid and its tests"
8
- publish = false
9
-
10
- [dependencies]
11
- serde = { version = "1", features = ["derive"] }
12
- serde_json = "1"
13
- libc = "0.2"
14
-
15
- [dev-dependencies]
16
- proptest = "1"