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
@@ -11,6 +11,7 @@
11
11
  * and results in an empty hook list for that file. Builtins continue normally.
12
12
  */
13
13
  import { resolve, isAbsolute, basename } from "node:path";
14
+ import { randomUUID } from "crypto";
14
15
  import { existsSync, readdirSync } from "node:fs";
15
16
  import { readFile } from "node:fs/promises";
16
17
  import { pathToFileURL } from "node:url";
@@ -18,12 +19,20 @@ import { homedir } from "node:os";
18
19
  import { createHash } from "node:crypto";
19
20
  import { hookLogWarn, hookLogError, hookLogInfo } from "./hook-logger";
20
21
  import { customPolicies, getCustomHooks, clearCustomHooks } from "./custom-hooks-registry";
21
- import { findDistIndex, rewriteFileTree, TMP_SUFFIX, cleanupTmpFiles } from "./loader-utils";
22
+ import {
23
+ findDistIndex,
24
+ rewriteFileTree,
25
+ TMP_SUFFIX,
26
+ cleanupTmpFiles,
27
+ isTmpArtifact,
28
+ sweepStaleTmpArtifacts,
29
+ } from "./loader-utils";
22
30
  import { findProjectConfigDir } from "./hooks-config";
23
31
  import { trackHookEvent } from "./hook-telemetry";
24
32
  import { getInstanceId } from "../../lib/telemetry-id";
25
33
  import type { CustomHook } from "./policy-types";
26
34
  import type { CloudManagedPolicyArtifact } from "./cloud-managed-policies";
35
+ import { customPoliciesDir, shimsDir } from "./fp-home";
27
36
 
28
37
  const LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__";
29
38
 
@@ -45,6 +54,15 @@ interface CachedPolicyModule {
45
54
  * functions prevents an unbounded ESM module-cache leak in the resident worker.
46
55
  * A source-tree change replaces this entry and imports one new module. */
47
56
  const policyModuleCache = new Map<string, CachedPolicyModule>();
57
+ /**
58
+ * Cap, for the reason `gitBranchCache` states and this cache did not carry
59
+ * over: a warm worker touching many projects over its lifetime must not grow
60
+ * this unboundedly. Keyed by absolute policy-file path and holding cloned hook
61
+ * closures, so the entries are not small. Cleared wholesale rather than evicted
62
+ * one at a time — the next load simply re-imports, which is the same cost the
63
+ * cache exists to avoid paying twice, not a correctness change.
64
+ */
65
+ const POLICY_MODULE_CACHE_MAX_ENTRIES = 500;
48
66
 
49
67
  /** Script extensions we could load, used to spot near-miss filenames. */
50
68
  const LOADABLE_EXT_RE = /\.(js|mjs|ts)$/;
@@ -87,7 +105,11 @@ export function findSkippedPolicyFiles(dir: string): string[] {
87
105
  e.isFile() &&
88
106
  LOADABLE_EXT_RE.test(e.name) &&
89
107
  !CONVENTION_FILE_RE.test(e.name) &&
90
- !e.name.endsWith(".d.ts"),
108
+ !e.name.endsWith(".d.ts") &&
109
+ // Never our own generated files. They are `.mjs` and never match the
110
+ // convention, so each one a killed load left behind was reported to
111
+ // the user as a policy file that would not load.
112
+ !isTmpArtifact(e.name),
91
113
  )
92
114
  .map((e) => e.name)
93
115
  .sort((a, b) => a.localeCompare(b));
@@ -96,13 +118,69 @@ export function findSkippedPolicyFiles(dir: string): string[] {
96
118
  }
97
119
  }
98
120
 
121
+ /**
122
+ * How long a policy module gets to finish evaluating its top level.
123
+ *
124
+ * Matches the 10s `handler.ts` gives a policy's `fn` to run, for the same
125
+ * reason: a policy file is user code, and user code that hangs must cost one
126
+ * skipped policy rather than the process.
127
+ *
128
+ * `FAILPROOFAI_POLICY_LOAD_TIMEOUT_MS` shortens it for tests — a suite proving
129
+ * the deadline fires should not have to sit out the real one.
130
+ */
131
+ function moduleLoadTimeoutMs(): number {
132
+ const override = Number(process.env.FAILPROOFAI_POLICY_LOAD_TIMEOUT_MS);
133
+ return Number.isFinite(override) && override > 0 ? override : 10_000;
134
+ }
135
+
136
+ /**
137
+ * `import()`, but it cannot hang forever.
138
+ *
139
+ * A bare `await import(...)` on a module whose top level contains an `await`
140
+ * that never resolves never settles — it does not reject, so no `catch` and no
141
+ * `finally` anywhere above it ever runs. In the one-shot hook path that costs
142
+ * one hook process, which the agent CLI's own timeout reaps. In the warm daemon
143
+ * worker it holds `worker-server.ts`'s serialization chain forever, and every
144
+ * subsequent hook on the machine queues behind it and fail-closed denies.
145
+ *
146
+ * Losing the race does NOT cancel the import — nothing can. It stops us
147
+ * *waiting* on it, which is the part that matters, and the caller's `catch`
148
+ * below reports the file as failed to load. The abandoned module keeps its
149
+ * half-initialized entry in the ESM cache, but each load writes a uniquely
150
+ * named temporary tree, so a retry imports a different specifier and is never
151
+ * poisoned by it.
152
+ */
153
+ async function importWithDeadline(fileUrl: string): Promise<void> {
154
+ const budget = moduleLoadTimeoutMs();
155
+ let timer: ReturnType<typeof setTimeout> | undefined;
156
+ try {
157
+ await Promise.race([
158
+ import(/* webpackIgnore: true */ fileUrl),
159
+ new Promise<never>((_, reject) => {
160
+ timer = setTimeout(
161
+ () => reject(new Error(`timed out loading module after ${budget}ms`)),
162
+ budget,
163
+ );
164
+ timer.unref?.();
165
+ }),
166
+ ]);
167
+ } finally {
168
+ if (timer) clearTimeout(timer);
169
+ }
170
+ }
171
+
99
172
  /**
100
173
  * Load a single policy file into the globalThis custom hooks registry.
101
174
  * Does NOT clear the registry — caller is responsible for that.
102
175
  */
103
176
  async function loadSingleFile(
104
177
  absPath: string,
105
- opts?: { strict?: boolean; conventionScope?: "project" | "user" },
178
+ opts?: {
179
+ strict?: boolean;
180
+ conventionScope?: "project" | "user";
181
+ /** Cloud-managed policies pass their pinned digest for load-time re-verification. */
182
+ verifyEntrySha?: string;
183
+ },
106
184
  ): Promise<void> {
107
185
  const g = globalThis as Record<string, unknown>;
108
186
  g[LOADING_KEY] = true;
@@ -113,8 +191,19 @@ async function loadSingleFile(
113
191
  const distUrl = distIndex ? pathToFileURL(distIndex).href : null;
114
192
 
115
193
  const sequence = ++loadSequence;
116
- const tmpSuffix = `${TMP_SUFFIX}.${process.pid}.${sequence}.mjs`;
117
- tmpFiles = await rewriteFileTree(absPath, distUrl, distIndex, tmpSuffix);
194
+ // pid + sequence is not unique enough on its own: two containers sharing a
195
+ // mounted HOME, or two pid namespaces, can collide — and on the shim's
196
+ // fallback path a collision means EEXIST, which fails the load OPEN. A
197
+ // random id removes the class. It costs nothing: `fingerprintTemporaryTree`
198
+ // normalises the whole suffix away, so the module cache still hits.
199
+ const tmpSuffix = `${TMP_SUFFIX}.${process.pid}.${sequence}.${randomUUID()}.mjs`;
200
+ tmpFiles = await rewriteFileTree(
201
+ absPath,
202
+ distUrl,
203
+ distIndex,
204
+ tmpSuffix,
205
+ opts?.verifyEntrySha,
206
+ );
118
207
 
119
208
  const fingerprint = await fingerprintTemporaryTree(tmpFiles, tmpSuffix);
120
209
  const cached = policyModuleCache.get(absPath);
@@ -126,7 +215,8 @@ async function loadSingleFile(
126
215
  const entryTmp = absPath + tmpSuffix;
127
216
  const fileUrl = pathToFileURL(entryTmp).href;
128
217
  const hooksBefore = getCustomHooks().length;
129
- await import(/* webpackIgnore: true */ fileUrl);
218
+ await importWithDeadline(fileUrl);
219
+ if (policyModuleCache.size >= POLICY_MODULE_CACHE_MAX_ENTRIES) policyModuleCache.clear();
130
220
  policyModuleCache.set(absPath, {
131
221
  fingerprint,
132
222
  hooks: getCustomHooks()
@@ -139,7 +229,9 @@ async function loadSingleFile(
139
229
  ? "module_not_found"
140
230
  : /SyntaxError|Unexpected token/i.test(msg)
141
231
  ? "syntax_error"
142
- : "runtime_error";
232
+ : /timed out loading module/i.test(msg)
233
+ ? "load_timeout"
234
+ : "runtime_error";
143
235
  void trackHookEvent(getInstanceId(), "custom_hooks_load_error", {
144
236
  error_type: errorType,
145
237
  is_convention: !!opts?.conventionScope,
@@ -282,7 +374,11 @@ export async function loadAllCustomHooks(
282
374
  if (!loadedPaths.has(absPath)) {
283
375
  loadedPaths.add(absPath);
284
376
  const hooksBefore = getCustomHooks().length;
285
- await loadSingleFile(absPath);
377
+ // A cloud-managed policy re-verifies its pinned digest at load, binding
378
+ // the imported bytes to what desired-state promised.
379
+ await loadSingleFile(absPath, {
380
+ verifyEntrySha: cloudManagedByPath.get(absPath)?.sha256,
381
+ });
286
382
  for (const hook of getCustomHooks().slice(hooksBefore)) {
287
383
  const cloudManaged = cloudManagedByPath.get(absPath);
288
384
  const tagged = hook as CustomHook & {
@@ -312,7 +408,22 @@ export async function loadAllCustomHooks(
312
408
  // exactly that — and it is discovered here as well. Skip what step 1 loaded,
313
409
  // or the file is imported twice and every hook in it fires twice per event.
314
410
  const projectDir = resolve(projectRoot, ".failproofai", "policies");
315
- if (conventionEnabled) warnSkippedPolicyFiles(projectDir, "project");
411
+ // Clear out generated files a killed load left behind, before anything scans
412
+ // this directory. The temporary tree has to be written beside the sources for
413
+ // a rewritten relative import to resolve, and its name now carries a pid and
414
+ // sequence number — so unlike the old fixed name, each abnormal termination
415
+ // leaks one file permanently rather than leaving one that the next load
416
+ // overwrites. Best-effort and age-gated, so it can never remove a tree
417
+ // another process is still importing.
418
+ if (conventionEnabled) {
419
+ void sweepStaleTmpArtifacts(projectDir);
420
+ warnSkippedPolicyFiles(projectDir, "project");
421
+ }
422
+ // The shim directory leaks the same way and is swept on the same terms. It is
423
+ // ours alone and outside the policy tree, so nothing else would ever reap it;
424
+ // the age gate is what keeps this from removing a shim another process is
425
+ // still importing.
426
+ void sweepStaleTmpArtifacts(shimsDir());
316
427
  const projectFiles = conventionEnabled
317
428
  ? discoverPolicyFiles(projectDir).filter((f) => !loadedPaths.has(f))
318
429
  : [];
@@ -347,8 +458,11 @@ export async function loadAllCustomHooks(
347
458
  // so the second import is a no-op — but Node honours the query and would
348
459
  // double-register. The binary runs under Bun and the tests under Node, so the
349
460
  // bug is invisible from both sides. Do not rely on that; dedupe the paths.
350
- const userDir = resolve(homedir(), ".failproofai", "policies");
351
- if (conventionEnabled && userDir !== projectDir) warnSkippedPolicyFiles(userDir, "user");
461
+ const userDir = customPoliciesDir();
462
+ if (conventionEnabled && userDir !== projectDir) {
463
+ void sweepStaleTmpArtifacts(userDir);
464
+ warnSkippedPolicyFiles(userDir, "user");
465
+ }
352
466
  const userFiles =
353
467
  conventionEnabled && userDir !== projectDir
354
468
  ? discoverPolicyFiles(userDir).filter((f) => !loadedPaths.has(f))
@@ -18,6 +18,8 @@ import { homedir } from "node:os";
18
18
  import type { IntegrationType } from "./types";
19
19
  import { readHooksConfig } from "./hooks-config";
20
20
  import { existsSync } from "node:fs";
21
+ import { daemonSocket as daemonSocketPath } from "./fp-home";
22
+ import { readConfig } from "./fp-config";
21
23
 
22
24
  const PROTOCOL_VERSION = 1;
23
25
 
@@ -67,6 +69,27 @@ export interface DaemonHookResponse {
67
69
  stderr: string;
68
70
  }
69
71
 
72
+ /**
73
+ * Why an attempt failed — and it matters, because the two cases deserve
74
+ * opposite answers.
75
+ *
76
+ * `protocol-mismatch` means a daemon answered and is demonstrably HEALTHY; we
77
+ * simply cannot speak its wire format. The cause is known and benign: the CLI
78
+ * upgraded via npm and the daemon has not been reinstalled yet. Falling back to
79
+ * in-process evaluation there loses no enforcement at all — it runs the
80
+ * identical policy engine, just slower — so denying every tool call over it
81
+ * would take a working machine offline to protect nothing.
82
+ *
83
+ * `unreachable` means nothing answered. That could be a stopped service, a
84
+ * deleted socket, or tampering, and we cannot tell which — so it keeps failing
85
+ * closed, which is the whole point of `daemonConfigured`.
86
+ */
87
+ export type DaemonFailure = "unreachable" | "protocol-mismatch";
88
+
89
+ export type DaemonAttempt =
90
+ | { ok: true; response: DaemonHookResponse }
91
+ | { ok: false; failure: DaemonFailure };
92
+
70
93
  /**
71
94
  * Whether a daemon is listening, regardless of how it was started.
72
95
  *
@@ -86,7 +109,7 @@ export function daemonSocketPresent(): boolean {
86
109
 
87
110
  function socketPath(): string {
88
111
  if (process.env.FAILPROOFAI_DAEMON_SOCKET) return process.env.FAILPROOFAI_DAEMON_SOCKET;
89
- return resolve(homedir(), ".failproofai", "run", "failproofaid.sock");
112
+ return daemonSocketPath();
90
113
  }
91
114
 
92
115
  /**
@@ -99,8 +122,11 @@ function socketPath(): string {
99
122
  */
100
123
  export function isDaemonConfigured(): boolean {
101
124
  try {
102
- return readHooksConfig().daemonConfigured === true;
125
+ return readConfig().daemon.configured === true;
103
126
  } catch {
127
+ // Unreadable config reads as NOT daemon-configured. The failure direction
128
+ // is deliberate: the alternative is a machine that fails closed on every
129
+ // tool call because a config file got truncated.
104
130
  return false;
105
131
  }
106
132
  }
@@ -120,24 +146,52 @@ function encodeFrame(value: unknown): Buffer {
120
146
  * falls through to whatever its own fallback policy is.
121
147
  */
122
148
  export async function tryDaemonHook(req: DaemonHookRequest): Promise<DaemonHookResponse | null> {
149
+ const attempt = await attemptDaemonHook(req);
150
+ return attempt.ok ? attempt.response : null;
151
+ }
152
+
153
+ /**
154
+ * The same attempt, but reporting WHY it failed.
155
+ *
156
+ * `tryDaemonHook` above collapses every failure into `null`, which was fine
157
+ * while the caller had one answer for all of them. It no longer does: a
158
+ * protocol mismatch must fall back rather than deny (see `DaemonFailure`), and
159
+ * a caller cannot make that distinction from `null`.
160
+ */
161
+ export async function attemptDaemonHook(
162
+ req: DaemonHookRequest,
163
+ opts?: {
164
+ /**
165
+ * Override the RESPONSE budget only — the connect budget is never
166
+ * relaxed. Used by the health probe (`probeDaemonEndToEnd`), which runs
167
+ * from an interactive command rather than a hook and must not make a
168
+ * person wait out the full 30s hook budget to be told their daemon is
169
+ * broken. Never set this on the hook path: 30s is matched to the
170
+ * daemon's own read timeout so this side never gives up on a request the
171
+ * daemon is still honestly working on.
172
+ */
173
+ responseTimeoutMs?: number;
174
+ },
175
+ ): Promise<DaemonAttempt> {
123
176
  // Windows never has a daemon in this phase (see the plan's platform
124
177
  // scope) — skip the attempt outright rather than depending on however
125
178
  // Node happens to behave when handed a POSIX socket path on Windows.
126
- if (process.platform === "win32") return null;
179
+ if (process.platform === "win32") return { ok: false, failure: "unreachable" };
180
+ const responseBudget = opts?.responseTimeoutMs ?? DAEMON_RESPONSE_TIMEOUT_MS;
127
181
 
128
- return new Promise<DaemonHookResponse | null>((resolvePromise) => {
182
+ return new Promise<DaemonAttempt>((resolvePromise) => {
129
183
  let settled = false;
130
184
  let timer: ReturnType<typeof setTimeout>;
131
185
  const arm = (ms: number) => {
132
186
  clearTimeout(timer);
133
- timer = setTimeout(() => finish(null), ms);
187
+ timer = setTimeout(() => fail("unreachable"), ms);
134
188
  // Don't let this timer alone keep the process alive if everything else
135
189
  // has already finished — it's always cleared on the success/failure
136
190
  // paths below, this just avoids it being the sole reason a --hook
137
191
  // process lingers if something upstream forgets to await us.
138
192
  timer.unref?.();
139
193
  };
140
- const finish = (result: DaemonHookResponse | null) => {
194
+ const finish = (result: DaemonAttempt) => {
141
195
  if (settled) return;
142
196
  settled = true;
143
197
  clearTimeout(timer);
@@ -145,6 +199,7 @@ export async function tryDaemonHook(req: DaemonHookRequest): Promise<DaemonHookR
145
199
  socket.destroy();
146
200
  resolvePromise(result);
147
201
  };
202
+ const fail = (failure: DaemonFailure) => finish({ ok: false, failure });
148
203
 
149
204
  const socket = createConnection({ path: socketPath() });
150
205
  arm(DAEMON_CONNECT_TIMEOUT_MS);
@@ -155,7 +210,7 @@ export async function tryDaemonHook(req: DaemonHookRequest): Promise<DaemonHookR
155
210
  socket.on("connect", () => {
156
211
  // Connected: the daemon is demonstrably reachable, so the question is
157
212
  // no longer "is it there" but "how long does this evaluation take".
158
- arm(DAEMON_RESPONSE_TIMEOUT_MS);
213
+ arm(responseBudget);
159
214
  socket.write(
160
215
  encodeFrame({
161
216
  type: "hook",
@@ -175,7 +230,7 @@ export async function tryDaemonHook(req: DaemonHookRequest): Promise<DaemonHookR
175
230
  if (recvBuf.length < 4) return;
176
231
  declaredLen = recvBuf.readUInt32BE(0);
177
232
  if (declaredLen > MAX_FRAME_LEN) {
178
- finish(null);
233
+ fail("unreachable");
179
234
  return;
180
235
  }
181
236
  recvBuf = recvBuf.subarray(4);
@@ -188,12 +243,16 @@ export async function tryDaemonHook(req: DaemonHookRequest): Promise<DaemonHookR
188
243
  try {
189
244
  message = JSON.parse(body.toString("utf8")) as Record<string, unknown>;
190
245
  } catch {
191
- finish(null);
246
+ fail("unreachable");
192
247
  return;
193
248
  }
194
249
 
195
250
  if (message.protocolVersion !== PROTOCOL_VERSION) {
196
- finish(null);
251
+ // Catches BOTH directions. A newer CLI sends v2 and the daemon replies
252
+ // with an error stamped v1; an older CLI sends v1 and a newer daemon
253
+ // replies stamped v2. Either way the versions disagree here, and either
254
+ // way a daemon answered — so this is skew, not absence.
255
+ fail("protocol-mismatch");
197
256
  return;
198
257
  }
199
258
  if (
@@ -202,16 +261,23 @@ export async function tryDaemonHook(req: DaemonHookRequest): Promise<DaemonHookR
202
261
  typeof message.stdout === "string" &&
203
262
  typeof message.stderr === "string"
204
263
  ) {
205
- finish({ exitCode: message.exitCode, stdout: message.stdout, stderr: message.stderr });
264
+ finish({
265
+ ok: true,
266
+ response: {
267
+ exitCode: message.exitCode,
268
+ stdout: message.stdout,
269
+ stderr: message.stderr,
270
+ },
271
+ });
206
272
  return;
207
273
  }
208
- // Anything else — an explicit `error` message, a `pong` (protocol
209
- // confusion), or a well-formed-but-wrong-shape body — is treated
210
- // identically to a connection failure: no partial trust.
211
- finish(null);
274
+ // Anything else — an explicit `error` message at a MATCHING protocol
275
+ // version, a `pong` (protocol confusion), or a well-formed-but-wrong-shape
276
+ // body — is treated identically to a connection failure: no partial trust.
277
+ fail("unreachable");
212
278
  });
213
279
 
214
- socket.on("error", () => finish(null));
215
- socket.on("close", () => finish(null));
280
+ socket.on("error", () => fail("unreachable"));
281
+ socket.on("close", () => fail("unreachable"));
216
282
  });
217
283
  }
@@ -33,13 +33,24 @@
33
33
  * this writes an executable that a service manager will then run at login.
34
34
  */
35
35
  import { createHash } from "node:crypto";
36
- import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
36
+ import {
37
+ chmodSync,
38
+ existsSync,
39
+ mkdirSync,
40
+ readdirSync,
41
+ readFileSync,
42
+ renameSync,
43
+ rmSync,
44
+ statSync,
45
+ writeFileSync,
46
+ } from "node:fs";
37
47
  import { createRequire } from "node:module";
38
48
  import { homedir } from "node:os";
39
49
  import { dirname, resolve } from "node:path";
40
50
  import { gunzipSync } from "node:zlib";
41
51
  import { version } from "../../package.json";
42
52
  import type { PlatformKey } from "./daemon-service";
53
+ import { binDir as layoutBinDir } from "./fp-home";
43
54
 
44
55
  /**
45
56
  * Where release assets live. Overridable so an air-gapped or proxied site can
@@ -62,7 +73,7 @@ function baseUrl(): string {
62
73
 
63
74
  /** Where downloaded binaries live. One directory, one file per version. */
64
75
  export function daemonBinaryDir(): string {
65
- return resolve(homedir(), ".failproofai", "bin");
76
+ return layoutBinDir();
66
77
  }
67
78
 
68
79
  /**
@@ -165,6 +176,47 @@ function platformPackageBinary(dir: string): string | null {
165
176
  * manager could pick up), mode 0755, pid-suffixed so two concurrent wizards
166
177
  * stay off each other's file.
167
178
  */
179
+ /**
180
+ * Delete daemon binaries older than the current and previous versions.
181
+ *
182
+ * Called AFTER the new binary is in place and the unit repointed, never
183
+ * before: a binary a running service points at must not be removed out from
184
+ * under it. One previous version is kept deliberately — a rollback is then a
185
+ * local file rather than a re-download, which is the difference between
186
+ * working and not on a machine that is offline or behind a proxy.
187
+ *
188
+ * Sorted by mtime rather than by parsing versions out of filenames: a
189
+ * prerelease ordering (`1.0.0-beta.10` vs `1.0.0-beta.9`) is easy to get
190
+ * subtly wrong, and "which did we install most recently" is the question that
191
+ * actually matters here.
192
+ */
193
+ export function pruneOldDaemonBinaries(keep = 2): string[] {
194
+ const removed: string[] = [];
195
+ try {
196
+ const dir = daemonBinaryDir();
197
+ const entries = readdirSync(dir)
198
+ .filter((f) => f.startsWith("failproofaid-") && !f.endsWith(".tmp"))
199
+ .map((f) => {
200
+ const full = resolve(dir, f);
201
+ return { full, mtime: statSync(full).mtimeMs };
202
+ })
203
+ .sort((a, b) => b.mtime - a.mtime);
204
+
205
+ for (const stale of entries.slice(keep)) {
206
+ try {
207
+ rmSync(stale.full, { force: true });
208
+ removed.push(stale.full);
209
+ } catch {
210
+ // In use, or not ours to delete. Leaving it costs disk; removing it by
211
+ // force could break a running service.
212
+ }
213
+ }
214
+ } catch {
215
+ // No directory yet, or unreadable — nothing to prune.
216
+ }
217
+ return removed;
218
+ }
219
+
168
220
  function installBinaryBytes(binary: Buffer): string {
169
221
  const target = installedBinaryPath();
170
222
  const tempPath = `${target}.${process.pid}.tmp`;