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
@@ -0,0 +1,418 @@
1
+ /**
2
+ * `failproofai uninstall` — the sanctioned way off this machine.
3
+ *
4
+ * It exists because npm cannot do it. npm runs NO uninstall script (this repo
5
+ * proved that empirically and deleted the dead `preuninstall` that assumed
6
+ * otherwise), so `npm rm -g failproofai` deletes the package and leaves behind
7
+ * every durable thing the package installed: hook entries in up to twelve agent
8
+ * CLIs' settings files, and a root-owned systemd unit.
9
+ *
10
+ * That leftovers set is not inert. The hook entries invoke `npx -y failproofai`,
11
+ * which happily re-downloads the package from the registry — so a "removed"
12
+ * failproofai keeps running on every tool call. And on a machine with
13
+ * `[daemon] configured = true`, the surviving unit points at a worker script
14
+ * that npm just deleted, which under fail-closed semantics denies EVERY tool
15
+ * call across every agent CLI, with nothing on screen naming the cause.
16
+ *
17
+ * ORDER IS THE SAFETY PROPERTY HERE, and it is not the obvious one.
18
+ *
19
+ * `daemonConfigured` is cleared FIRST — before hooks, before the service, before
20
+ * anything that can fail or need a password. From that instant the machine can
21
+ * only fail OPEN: policies evaluate in-process, and every later step is
22
+ * best-effort cleanup. Doing it in the intuitive order (tear the service down,
23
+ * then update config) leaves a window where the flag demands a daemon that is
24
+ * already gone, and that window is a total lockout of the user's agent — the
25
+ * exact failure `healDaemonFlag` was written to repair after it bricked a
26
+ * machine during development.
27
+ *
28
+ * Nothing here is silent about failure. A step that cannot complete (no sudo,
29
+ * an unwritable settings file) is reported with the command to finish it by
30
+ * hand, and the exit code says whether anything was left behind — because the
31
+ * one thing worse than a leftover unit is a leftover unit the operator was told
32
+ * did not exist.
33
+ */
34
+ import { existsSync, rmSync } from "node:fs";
35
+
36
+ import { failproofaiHome } from "./fp-home";
37
+ import { readConfig } from "./fp-config";
38
+ import {
39
+ daemonServiceFilePath,
40
+ daemonServiceStatus,
41
+ daemonStatusCommand,
42
+ isDaemonSupportedPlatform,
43
+ primeElevation,
44
+ setDaemonConfigured,
45
+ uninstallDaemonService,
46
+ } from "./daemon-service";
47
+ import { listInstallableIds, getIntegration } from "./integrations";
48
+ import type { IntegrationType } from "./types";
49
+ import { removeHooks } from "./manager";
50
+
51
+ export interface UninstallOptions {
52
+ /** Also delete ~/.failproofai (config, credentials, state, audit cache, daemon binary). */
53
+ purge?: boolean;
54
+ /** Report what would change and touch nothing. */
55
+ dryRun?: boolean;
56
+ /** Proceed without the interactive confirmation. */
57
+ yes?: boolean;
58
+ /** Project/local scopes are read relative to here. */
59
+ cwd?: string;
60
+ /**
61
+ * Injected so the decision is testable without a TTY. Absent means "no
62
+ * confirmation available", which is a REFUSAL rather than an assumed yes —
63
+ * see the non-interactive branch.
64
+ */
65
+ confirm?: (lines: string[]) => Promise<boolean>;
66
+ /**
67
+ * Asked, on a plain uninstall only, whether the daemon service should go too.
68
+ *
69
+ * A plain uninstall removes the hooks and leaves the machine otherwise intact,
70
+ * so the daemon is a genuine choice: someone clearing hooks before reinstalling
71
+ * has no reason to tear down a system service and re-enter their password for
72
+ * it. Absent means "could not ask" and the daemon is KEPT — declining to remove
73
+ * a service is the recoverable half of that decision, and removing one nobody
74
+ * asked about is not.
75
+ *
76
+ * NOT consulted under `--purge`. See `removeDaemon` in the implementation for
77
+ * why purge cannot leave it behind.
78
+ */
79
+ confirmDaemon?: () => Promise<boolean>;
80
+ /**
81
+ * Ask for the sudo password before removing the service. Injected for tests;
82
+ * defaults to `primeElevation`.
83
+ */
84
+ elevate?: () => boolean;
85
+ }
86
+
87
+ export interface UninstallResult {
88
+ exitCode: number;
89
+ lines: string[];
90
+ /**
91
+ * How many leading entries of `lines` are the plan — the block already handed
92
+ * to `confirm`.
93
+ *
94
+ * Returned rather than inferred so the caller can skip re-printing it without
95
+ * pattern-matching its own output. A caller that guesses (by searching for a
96
+ * blank line, say) silently prints the whole plan twice the day a message
97
+ * above it gains one.
98
+ */
99
+ planLines: number;
100
+ /**
101
+ * Whether `~/.failproofai` was actually deleted.
102
+ *
103
+ * The caller needs this to know that NOTHING may touch the home afterwards.
104
+ * Telemetry is the trap: `getInstanceId()` lazily writes
105
+ * `state/telemetry-id`, so a routine post-command event re-created the whole
106
+ * directory seconds after the purge reported deleting it — leaving a machine
107
+ * the user had just wiped holding a brand-new tracking identifier, and the
108
+ * command's own "✓ deleted" line a lie. Caught by the container test, which
109
+ * checked the filesystem rather than the output.
110
+ */
111
+ purged: boolean;
112
+ }
113
+
114
+ /** Everything this machine has that uninstalling would remove. */
115
+ interface Leftovers {
116
+ clis: IntegrationType[];
117
+ servicePath: string | null;
118
+ serviceInstalled: boolean;
119
+ daemonConfigured: boolean;
120
+ homeExists: boolean;
121
+ }
122
+
123
+ function survey(cwd?: string): Leftovers {
124
+ const clis: IntegrationType[] = [];
125
+ // Every INSTALLABLE cli, not every DETECTED one. A CLI can be uninstalled
126
+ // after failproofai wrote hooks into its settings file, and those entries are
127
+ // exactly the orphans this command exists to clear — surveying only what is
128
+ // currently on PATH would walk straight past them.
129
+ for (const id of listInstallableIds()) {
130
+ for (const scope of ["user", "project", "local"] as const) {
131
+ try {
132
+ if (getIntegration(id).hooksInstalledInSettings(scope, cwd)) {
133
+ clis.push(id);
134
+ break;
135
+ }
136
+ } catch {
137
+ // An unreadable or malformed settings file is not proof of absence, but
138
+ // it is not proof of presence either, and `removeHooks` will report its
139
+ // own failure against it later with a better message than a guess here.
140
+ }
141
+ }
142
+ }
143
+
144
+ let daemonConfigured = false;
145
+ try {
146
+ daemonConfigured = readConfig().daemon.configured;
147
+ } catch {
148
+ /* no config = nothing configured */
149
+ }
150
+
151
+ const servicePath = isDaemonSupportedPlatform() ? daemonServiceFilePath() : null;
152
+ return {
153
+ clis,
154
+ servicePath,
155
+ serviceInstalled: !!servicePath && existsSync(servicePath),
156
+ daemonConfigured,
157
+ homeExists: existsSync(failproofaiHome()),
158
+ };
159
+ }
160
+
161
+ export async function runUninstallCommand(opts: UninstallOptions = {}): Promise<UninstallResult> {
162
+ const found = survey(opts.cwd);
163
+ const lines: string[] = [];
164
+
165
+ const nothingToDo =
166
+ found.clis.length === 0 && !found.serviceInstalled && !found.daemonConfigured &&
167
+ !(opts.purge && found.homeExists);
168
+ if (nothingToDo) {
169
+ return {
170
+ exitCode: 0,
171
+ planLines: 0,
172
+ purged: false,
173
+ lines: [
174
+ "Nothing to uninstall — no hook entries, no daemon service, and nothing configured.",
175
+ ...(found.homeExists && !opts.purge
176
+ ? [``, `${failproofaiHome()} still holds settings and audit history.`,
177
+ `Remove it with \`failproofai uninstall --purge\`.`]
178
+ : []),
179
+ ],
180
+ };
181
+ }
182
+
183
+ // The plan is shown before anything happens and is the same text the
184
+ // confirmation asks about, so what is agreed to and what is done cannot drift.
185
+ const plan: string[] = [];
186
+ if (found.clis.length > 0) {
187
+ plan.push(
188
+ ` • remove failproofai hook entries from ${found.clis.length} agent CLI${found.clis.length === 1 ? "" : "s"}: ` +
189
+ found.clis.map((id) => getIntegration(id).displayName ?? id).join(", "),
190
+ );
191
+ }
192
+ if (found.daemonConfigured) {
193
+ plan.push(` • stop requiring the daemon (policies go back to evaluating in-process)`);
194
+ }
195
+ if (found.serviceInstalled) {
196
+ plan.push(
197
+ opts.purge || opts.yes
198
+ ? ` • stop, disable and delete the service at ${found.servicePath} — needs sudo`
199
+ : ` • ask whether to remove the service at ${found.servicePath} (kept unless you say so)`,
200
+ );
201
+ }
202
+ let purged = false;
203
+ if (opts.purge && found.homeExists) {
204
+ plan.push(` • delete ${failproofaiHome()} — settings, credentials, audit history and the daemon binary`);
205
+ }
206
+
207
+ lines.push("failproofai uninstall will:", ...plan);
208
+ if (!opts.purge && found.homeExists) {
209
+ lines.push(
210
+ ``,
211
+ `${failproofaiHome()} is KEPT (settings and audit history survive a reinstall).`,
212
+ `Add --purge to delete it too.`,
213
+ );
214
+ }
215
+ // Everything pushed so far IS the plan; `confirm` is shown exactly this.
216
+ const planLines = lines.length;
217
+
218
+ if (opts.dryRun) {
219
+ lines.push(``, `--dry-run: nothing was changed.`);
220
+ return { exitCode: 0, lines, planLines, purged: false };
221
+ }
222
+
223
+ if (!opts.yes) {
224
+ // No confirmation channel and no --yes is a REFUSAL, never an assumed yes.
225
+ // This runs in scripts and CI, where a prompt that cannot be answered would
226
+ // otherwise read as consent to delete a root-owned service.
227
+ if (!opts.confirm) {
228
+ lines.push(``, `Refusing to uninstall without confirmation. Re-run with --yes.`);
229
+ return { exitCode: 1, lines, planLines, purged: false };
230
+ }
231
+ if (!(await opts.confirm(lines))) {
232
+ return { exitCode: 1, planLines, purged: false, lines: [...lines, ``, `Cancelled — nothing was changed.`] };
233
+ }
234
+ }
235
+
236
+ lines.push(``);
237
+ const failures: string[] = [];
238
+
239
+ // STEP 1, and it must stay step 1. See the header: from here the machine can
240
+ // only fail open, so every remaining step is cleanup rather than a step that
241
+ // can lock anybody out by failing halfway.
242
+ if (found.daemonConfigured) {
243
+ try {
244
+ setDaemonConfigured(false);
245
+ lines.push(`✓ policies now evaluate in-process (this machine no longer requires the daemon)`);
246
+ } catch (err) {
247
+ // Uniquely fatal: everything after this assumes the flag is down. Removing
248
+ // the service while it is still up is the lockout this command exists to
249
+ // prevent, so stop rather than press on.
250
+ return {
251
+ exitCode: 2,
252
+ planLines,
253
+ purged: false,
254
+ lines: [
255
+ ...lines,
256
+ `✗ could not update ${failproofaiHome()}/config.json: ${err instanceof Error ? err.message : String(err)}`,
257
+ ``,
258
+ `Stopped before touching the service. Removing it while this machine still`,
259
+ `requires it would deny every tool call. Fix the file's permissions and re-run.`,
260
+ ],
261
+ };
262
+ }
263
+ }
264
+
265
+ if (found.clis.length > 0) {
266
+ try {
267
+ // Scope "all" and every cli that HAS entries — the same list the plan
268
+ // named. `removeCustomHooks` clears the configured custom-policy paths as
269
+ // well; leaving them behind would point a reinstall at files this command
270
+ // may be about to purge.
271
+ await removeHooks(undefined, "all", opts.cwd, {
272
+ cli: found.clis,
273
+ removeCustomHooks: true,
274
+ source: "uninstall_command",
275
+ });
276
+ lines.push(`✓ removed hook entries from ${found.clis.length} agent CLI${found.clis.length === 1 ? "" : "s"}`);
277
+ } catch (err) {
278
+ failures.push(
279
+ `hook entries: ${err instanceof Error ? err.message : String(err)} ` +
280
+ `(finish with \`failproofai policies --uninstall --scope all\`)`,
281
+ );
282
+ }
283
+ }
284
+
285
+ if (found.serviceInstalled) {
286
+ // PURGE ALWAYS REMOVES IT, and is not asked. `--purge` deletes
287
+ // `~/.failproofai`, which is where the daemon BINARY lives
288
+ // (`bin/failproofaid-<version>`) — so keeping an enabled unit whose ExecStart
289
+ // has just been deleted leaves the machine crash-looping the service at every
290
+ // boot. "Keep the daemon" is not an option purge can offer, because purge has
291
+ // already destroyed what the daemon needs to run.
292
+ //
293
+ // A plain uninstall is the opposite: the home survives, the binary survives,
294
+ // and someone clearing hooks before a reinstall has no reason to tear down a
295
+ // system service and re-type their password. So it asks, and a missing answer
296
+ // means KEEP — declining to remove a service is recoverable, removing one
297
+ // nobody asked about is not.
298
+ //
299
+ // `--yes` also removes it, and that is deliberate rather than incidental: the
300
+ // flag means "yes to the plan", the plan has always included the service, and
301
+ // scripted uninstalls rely on it. Making `--yes` keep the daemon would silently
302
+ // start leaving a service behind on every automated run — a behaviour change
303
+ // nobody asked for, in the direction of leaving more behind.
304
+ //
305
+ // So the only case that keeps it is an INTERACTIVE run where the person said no
306
+ // (or could not be asked).
307
+ const removeDaemon =
308
+ opts.purge || opts.yes ? true : ((await opts.confirmDaemon?.()) ?? false);
309
+
310
+ if (!removeDaemon) {
311
+ lines.push(
312
+ `• kept the daemon service at ${found.servicePath}`,
313
+ ` Remove it later with: failproofai uninstall --purge`,
314
+ );
315
+ } else {
316
+ // Ask for the password BEFORE trying, rather than failing on `sudo -n` and
317
+ // printing a unit file to delete by hand. `uninstallDaemonService()` is
318
+ // deliberately non-interactive — the wizard cannot prompt from under a
319
+ // full-screen TUI — but this command is plain line output and has a person
320
+ // in front of it, which is the same reasoning `failproofai update` follows.
321
+ // Best-effort: a refused or absent sudo still falls through to the existing
322
+ // "still there, here are the commands" path below.
323
+ const elevate = opts.elevate ?? primeElevation;
324
+ try {
325
+ elevate();
326
+ } catch {
327
+ // A failed prompt is not a reason to skip the attempt; `sudo -n` inside
328
+ // the removal will simply fail the same way it would have anyway.
329
+ }
330
+ try {
331
+ await uninstallDaemonService();
332
+ // uninstallDaemonService is best-effort by contract — it warns and
333
+ // returns rather than throwing when it cannot elevate — so the unit file
334
+ // is what gets believed here, not the absence of an exception.
335
+ if (found.servicePath && existsSync(found.servicePath)) {
336
+ failures.push(
337
+ `the service at ${found.servicePath} is still there (most often: no sudo). ` +
338
+ `Remove it with the commands below.`,
339
+ );
340
+ } else {
341
+ lines.push(`✓ stopped and removed the daemon service`);
342
+ }
343
+ } catch (err) {
344
+ failures.push(`daemon service: ${err instanceof Error ? err.message : String(err)}`);
345
+ }
346
+ }
347
+ }
348
+
349
+ if (opts.purge && found.homeExists) {
350
+ // Last, and only after the service is down: the daemon binary and its
351
+ // socket live here, and deleting them out from under a running unit is how
352
+ // a clean uninstall turns into a restart loop.
353
+ try {
354
+ rmSync(failproofaiHome(), { recursive: true, force: true });
355
+ purged = true;
356
+ lines.push(`✓ deleted ${failproofaiHome()}`);
357
+ } catch (err) {
358
+ failures.push(`${failproofaiHome()}: ${err instanceof Error ? err.message : String(err)}`);
359
+ }
360
+ }
361
+
362
+ if (failures.length > 0) {
363
+ lines.push(``, `Not everything could be removed:`);
364
+ for (const f of failures) lines.push(` ✗ ${f}`);
365
+ if (found.servicePath && existsSync(found.servicePath)) {
366
+ lines.push(
367
+ ``,
368
+ `To remove the service by hand:`,
369
+ ...manualServiceRemoval(found.servicePath),
370
+ ``,
371
+ `(\`${daemonStatusCommand()}\` shows its current state.)`,
372
+ );
373
+ }
374
+ // Exit 1, not 0: enforcement is off (step 1 succeeded, or there was nothing
375
+ // to clear), but something durable is still installed and the operator has
376
+ // to act. A 0 here is what makes people believe a machine is clean when a
377
+ // root-owned unit is still on it.
378
+ return { exitCode: 1, lines, planLines, purged };
379
+ }
380
+
381
+ lines.push(
382
+ ``,
383
+ `Done. failproofai no longer enforces anything on this machine.`,
384
+ ``,
385
+ `The npm package itself is still installed — npm runs no uninstall script, which`,
386
+ `is why this command exists. Remove it with:`,
387
+ ` npm rm -g failproofai`,
388
+ );
389
+ if (!opts.purge && found.homeExists) {
390
+ lines.push(``, `${failproofaiHome()} was kept. Delete it with \`failproofai uninstall --purge\`.`);
391
+ }
392
+ return { exitCode: 0, lines, planLines, purged };
393
+ }
394
+
395
+ /** The exact commands to finish a service removal that could not elevate. */
396
+ function manualServiceRemoval(servicePath: string): string[] {
397
+ if (process.platform === "darwin") {
398
+ return [` sudo launchctl unload -w ${servicePath}`, ` sudo rm -f ${servicePath}`];
399
+ }
400
+ const unit = servicePath.split("/").pop() ?? servicePath;
401
+ return [
402
+ ` sudo systemctl disable --now ${unit}`,
403
+ ` sudo rm -f ${servicePath}`,
404
+ ` sudo systemctl daemon-reload`,
405
+ ];
406
+ }
407
+
408
+ /** What `daemonServiceStatus()` says, for the status line the CLI prints. */
409
+ export function serviceStateLabel(): string {
410
+ const status = daemonServiceStatus();
411
+ if (status === "condition-failed") return "installed but skipped by systemd (a file it needs is missing)";
412
+ // Printing a bare "unknown" invites the reading "something is wrong". It is
413
+ // narrower than that: the service is installed and we could not read its
414
+ // state, because doing so on macOS needs root and no sudo credential was
415
+ // cached. Say which, so the reader knows there is nothing to fix.
416
+ if (status === "unknown") return "installed (state needs sudo to read)";
417
+ return status;
418
+ }
@@ -19,7 +19,7 @@
19
19
  * correct with zero changes; a worker_threads/process pool is a valid
20
20
  * future enhancement if this becomes a real throughput bottleneck.
21
21
  */
22
- import { createServer, type Server, type Socket } from "node:net";
22
+ import { createConnection, createServer, type Server, type Socket } from "node:net";
23
23
  import { existsSync, unlinkSync } from "node:fs";
24
24
  import { evaluateHookEvent } from "./handler";
25
25
  import type { IntegrationType } from "./types";
@@ -43,7 +43,15 @@ function isWorkerHookRequest(msg: unknown): msg is WorkerHookRequest {
43
43
  typeof m.hookEvent === "string" &&
44
44
  typeof m.cli === "string" &&
45
45
  typeof m.stdin === "string" &&
46
- (m.cwd === undefined || typeof m.cwd === "string")
46
+ // `null` as well as `undefined`. `cwd` is `Option<String>` on the wire, and
47
+ // the daemon forwards it with `json!({ "cwd": cwd })`, which serialises
48
+ // `None` as an explicit NULL rather than omitting the key. Accepting only
49
+ // `undefined` therefore rejected every request that legitimately carried no
50
+ // cwd — including the setup health probe, which sends none, so
51
+ // `failproofai config` aborted with "its worker process could not be run"
52
+ // against a daemon and worker that were both perfectly healthy. On a
53
+ // daemonConfigured machine the same mismatch denies the tool call.
54
+ (m.cwd === undefined || m.cwd === null || typeof m.cwd === "string")
47
55
  );
48
56
  }
49
57
 
@@ -54,18 +62,70 @@ function encodeFrame(value: unknown): Buffer {
54
62
  return Buffer.concat([header, body]);
55
63
  }
56
64
 
65
+ /**
66
+ * Last-resort deadline on a single queued task.
67
+ *
68
+ * Deliberately far above `daemon-client.ts`'s 30s `DAEMON_RESPONSE_TIMEOUT_MS`:
69
+ * by the time this fires the client has long since given up and fail-closed, so
70
+ * nothing legitimate is waiting. It exists only to detect a task that will never
71
+ * settle at all.
72
+ */
73
+ const TASK_DEADLINE_MS = 60_000;
74
+
57
75
  /**
58
76
  * Serializes every request across every connection through one chain, so
59
77
  * `evaluateHookEvent` calls never overlap. `.catch()` on each link keeps the
60
78
  * chain alive even if a handler throws unexpectedly — one bad request must
61
79
  * never wedge every request queued behind it.
80
+ *
81
+ * `.catch()` covers a task that REJECTS. It does nothing for one that never
82
+ * SETTLES, and that state is reachable: a policy file with a top-level `await`
83
+ * that never resolves hangs its `import()`, so the task neither resolves nor
84
+ * rejects and every hook queued behind it waits forever. Each client then burns
85
+ * its own 30s budget and fail-closed denies — every tool call, every CLI on the
86
+ * machine, until someone restarts the daemon. This is a class the warm worker
87
+ * creates: in the one-shot path the identical file hangs one hook process and
88
+ * the agent CLI's own timeout reaps it.
89
+ *
90
+ * `custom-hooks-loader.ts` bounds that import directly, which is the graceful
91
+ * fix and covers the known cause. This is the backstop for the ones nobody
92
+ * thought of, and it EXITS rather than continuing: the orphaned task is still
93
+ * running and still holds the `globalThis` policy registry this chain exists to
94
+ * serialize access to, so letting the next request proceed would trade a wedge
95
+ * for silent cross-request registry corruption. Exiting hands the supervisor a
96
+ * process it will respawn clean (`worker.rs`'s `ensure_started` respawns a
97
+ * worker whose `try_wait()` reports it gone), which costs one cold start and
98
+ * denies the in-flight requests — the same fail-closed outcome they were headed
99
+ * for anyway, but recovered on the next call instead of never.
62
100
  */
101
+ const WEDGED_EXIT_CODE = 75;
102
+
63
103
  let processingChain: Promise<void> = Promise.resolve();
64
104
  function enqueue(task: () => Promise<void>): void {
65
- processingChain = processingChain.then(task).catch(() => {});
105
+ processingChain = processingChain
106
+ .then(
107
+ () =>
108
+ new Promise<void>((settle) => {
109
+ const timer = setTimeout(() => {
110
+ hookLogWarn(
111
+ `worker: a request did not settle within ${TASK_DEADLINE_MS}ms; ` +
112
+ `exiting so the supervisor can respawn a clean worker`,
113
+ );
114
+ process.exit(WEDGED_EXIT_CODE);
115
+ }, TASK_DEADLINE_MS);
116
+ // Never let the deadline itself hold the event loop open.
117
+ timer.unref?.();
118
+ const done = () => {
119
+ clearTimeout(timer);
120
+ settle();
121
+ };
122
+ void task().then(done, done);
123
+ }),
124
+ )
125
+ .catch(() => {});
66
126
  }
67
127
 
68
- function handleConnection(socket: Socket): void {
128
+ function handleConnection(socket: Socket, shutdown: () => void): void {
69
129
  let recvBuf = Buffer.alloc(0);
70
130
  let declaredLen: number | null = null;
71
131
 
@@ -103,6 +163,15 @@ function handleConnection(socket: Socket): void {
103
163
  continue;
104
164
  }
105
165
 
166
+ if (
167
+ message &&
168
+ typeof message === "object" &&
169
+ (message as Record<string, unknown>).type === "shutdown"
170
+ ) {
171
+ socket.end(encodeFrame({ type: "shutdownAccepted" }), shutdown);
172
+ return;
173
+ }
174
+
106
175
  if (!isWorkerHookRequest(message)) {
107
176
  socket.write(encodeFrame({ type: "error", message: "unrecognized request shape" }));
108
177
  continue;
@@ -113,7 +182,9 @@ function handleConnection(socket: Socket): void {
113
182
  try {
114
183
  const result = await evaluateHookEvent(request.hookEvent, request.cli, request.stdin, {
115
184
  awaitTelemetryFlush: false,
116
- fallbackCwd: request.cwd,
185
+ // Normalised here so no consumer has to know the wire spells
186
+ // "absent" as null.
187
+ fallbackCwd: request.cwd ?? undefined,
117
188
  });
118
189
  socket.write(
119
190
  encodeFrame({
@@ -139,16 +210,64 @@ function handleConnection(socket: Socket): void {
139
210
  });
140
211
  }
141
212
 
142
- export function startWorkerServer(socketPath: string): Server {
143
- if (existsSync(socketPath)) {
144
- try {
145
- unlinkSync(socketPath);
146
- } catch {
147
- // Racing with something else clearing it — fine, bind will surface
148
- // any real problem.
213
+ const STALE_WORKER_SHUTDOWN_TIMEOUT_MS = 2_000;
214
+
215
+ function requestExistingWorkerShutdown(socketPath: string): Promise<"stale" | "stopped"> {
216
+ return new Promise((resolvePromise, reject) => {
217
+ const socket = createConnection({ path: socketPath });
218
+ let recvBuf = Buffer.alloc(0);
219
+ let settled = false;
220
+ const timer = setTimeout(() => finish(new Error("existing worker did not acknowledge shutdown")), STALE_WORKER_SHUTDOWN_TIMEOUT_MS);
221
+ timer.unref?.();
222
+
223
+ const finish = (result: Error | "stale" | "stopped") => {
224
+ if (settled) return;
225
+ settled = true;
226
+ clearTimeout(timer);
227
+ socket.destroy();
228
+ if (result instanceof Error) reject(result);
229
+ else resolvePromise(result);
230
+ };
231
+
232
+ socket.on("connect", () => socket.write(encodeFrame({ type: "shutdown" })));
233
+ socket.on("data", (chunk: Buffer) => {
234
+ recvBuf = Buffer.concat([recvBuf, chunk]);
235
+ if (recvBuf.length < 4) return;
236
+ const len = recvBuf.readUInt32BE(0);
237
+ if (len > MAX_FRAME_LEN || recvBuf.length < 4 + len) return;
238
+ try {
239
+ const response = JSON.parse(recvBuf.subarray(4, 4 + len).toString("utf8")) as { type?: unknown };
240
+ if (response.type === "shutdownAccepted") finish("stopped");
241
+ else finish(new Error("existing socket belongs to a process that does not support shutdown"));
242
+ } catch {
243
+ finish(new Error("existing worker returned a malformed shutdown response"));
244
+ }
245
+ });
246
+ socket.on("error", (err: NodeJS.ErrnoException) => {
247
+ if (err.code === "ECONNREFUSED" || err.code === "ENOENT") finish("stale");
248
+ else finish(err);
249
+ });
250
+ });
251
+ }
252
+
253
+ export function startWorkerServer(socketPath: string, shutdown?: () => void): Server {
254
+ const server = createServer((socket) =>
255
+ handleConnection(socket, shutdown ?? (() => server.close())),
256
+ );
257
+
258
+ const listen = () => {
259
+ if (existsSync(socketPath)) {
260
+ try {
261
+ unlinkSync(socketPath);
262
+ } catch {
263
+ // Racing with the old worker clearing it is harmless; listen surfaces
264
+ // any remaining conflict.
265
+ }
149
266
  }
150
- }
151
- const server = createServer(handleConnection);
152
- server.listen(socketPath);
267
+ server.listen(socketPath);
268
+ };
269
+
270
+ if (!existsSync(socketPath)) listen();
271
+ else void requestExistingWorkerShutdown(socketPath).then(listen, (err) => server.emit("error", err));
153
272
  return server;
154
273
  }
@@ -1,3 +0,0 @@
1
- module.exports=[14747,(e,o,r)=>{o.exports=e.x("path",()=>require("path"))},78500,(e,o,r)=>{o.exports=e.x("node:async_hooks",()=>require("node:async_hooks"))},1457,(e,o,r)=>{o.exports=e.x("node:readline",()=>require("node:readline"))}];
2
-
3
- //# sourceMappingURL=%5Bexternals%5D__14odj07._.js.map
@@ -1,3 +0,0 @@
1
- module.exports=[14747,(e,o,r)=>{o.exports=e.x("path",()=>require("path"))},2157,(e,o,r)=>{o.exports=e.x("node:fs",()=>require("node:fs"))},1457,(e,o,r)=>{o.exports=e.x("node:readline",()=>require("node:readline"))},78500,(e,o,r)=>{o.exports=e.x("node:async_hooks",()=>require("node:async_hooks"))}];
2
-
3
- //# sourceMappingURL=%5Bexternals%5D__1s61mel._.js.map