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
package/src/audit/cli.ts CHANGED
@@ -14,8 +14,14 @@
14
14
  * No arguments yet — a bare `failproofai audit` does a full scan (all CLIs, all
15
15
  * history). Flags (--since, --cli, --project, --port, --no-open) are easy
16
16
  * follow-ups against `RunAuditOptions`.
17
+ *
18
+ * `--scheduled` is the one exception, and it is not a flag on the interactive
19
+ * command so much as a second entry point sharing its name: it runs the same
20
+ * scan with no TTY, no browser and no server, and reports its outcome through
21
+ * an exit code. See `runScheduledAudit`.
17
22
  */
18
23
  import { runAudit } from "./index";
24
+ import { acquireAuditLock, type AuditLockInfo } from "./audit-lock";
19
25
  import { writeDashboardCache } from "./dashboard-cache";
20
26
  import type { AuditResult, RunAuditOptions } from "./types";
21
27
  import { trackHookEvent } from "../hooks/hook-telemetry";
@@ -28,6 +34,16 @@ import { brandAnsi, ANSI_RESET, ANSI_BOLD, ANSI_DIM } from "../hooks/tui";
28
34
  * for `start` mode, which `failproofai` (bare) already uses. */
29
35
  const DASHBOARD_PORT = 8020;
30
36
 
37
+ /**
38
+ * `EX_TEMPFAIL` from sysexits.h — "another audit already holds the lock".
39
+ *
40
+ * A distinct code rather than 1, because the scheduler that spawns the headless
41
+ * run has to tell the two apart: a failure deserves a report, while losing the
42
+ * lock means only "come back in fifteen minutes". Collapsing them would make a
43
+ * healthy machine that simply ran two audits close together look broken.
44
+ */
45
+ export const EXIT_AUDIT_ALREADY_RUNNING = 75;
46
+
31
47
  /**
32
48
  * Mirror of `app/audit/_components/run-progress.tsx`'s `STAGES`. Kept identical
33
49
  * so the CLI and the dashboard's in-progress view tell the same story — the
@@ -49,6 +65,12 @@ USAGE
49
65
  wasteful patterns, then open the audit dashboard.
50
66
  failproofai audit --help Show this help.
51
67
 
68
+ failproofai audit --scheduled
69
+ Headless run: scan, refresh the dashboard's cached
70
+ result, print one line, exit. No browser, no
71
+ server. This is what a scheduled audit runs; exit
72
+ 75 means another audit already had the lock.
73
+
52
74
  WHAT IT DOES
53
75
  1. Scans past sessions from every installed agent CLI (Claude, Codex, Cursor,
54
76
  Copilot, OpenCode, Pi) — entirely on your machine.
@@ -247,10 +269,13 @@ function printSummary(result: AuditResult): void {
247
269
 
248
270
  /**
249
271
  * Which entry point ran the audit. `onboarding` is the automatic post-setup run;
250
- * `cli` is an explicit `failproofai audit`. Carried on every cli_audit_* event so
251
- * the first audit a user ever runs is distinguishable from later deliberate ones.
272
+ * `cli` is an explicit `failproofai audit`; `scheduled` is the headless run the
273
+ * daemon spawns. Carried on every cli_audit_* event so the first audit a user
274
+ * ever runs, a deliberate one, and one nobody was present for stay distinct —
275
+ * without which an opt-in scheduled scan would silently inflate the counts that
276
+ * describe what people actually do by hand.
252
277
  */
253
- type AuditSource = "cli" | "onboarding";
278
+ type AuditSource = "cli" | "onboarding" | "scheduled";
254
279
 
255
280
  /** Shared so both entry points report cli_audit_completed identically. */
256
281
  function auditCompletedProps(source: AuditSource, result: AuditResult) {
@@ -263,6 +288,89 @@ function auditCompletedProps(source: AuditSource, result: AuditResult) {
263
288
  };
264
289
  }
265
290
 
291
+ /** One line naming whoever holds the lock, for a refusal message. */
292
+ function heldByLine(held: AuditLockInfo | null): string {
293
+ if (!held) return "another audit is already running";
294
+ const ageS = Math.max(0, Math.round((Date.now() - held.startedAt) / 1000));
295
+ return `another audit is already running (pid ${held.pid}, started by ${held.source} ${ageS}s ago)`;
296
+ }
297
+
298
+ // ── Headless (scheduled) audit ───────────────────────────────────────────────
299
+
300
+ /**
301
+ * Run the audit with nobody watching: no TTY animation, no browser, no prompts,
302
+ * no dashboard server left behind.
303
+ *
304
+ * This is the entry point the scheduler spawns, and it must be a SEPARATE
305
+ * short-lived process. `src/hooks/worker-server.ts` serialises every request
306
+ * through one promise chain that `crates/failproofaid/src/worker.rs` caps at
307
+ * 30s, and `daemon-client.ts` turns that timeout into a DENY — so a ~104-second
308
+ * audit on the warm worker would be a machine-wide fail-closed denial across
309
+ * all 12 CLIs for as long as it ran.
310
+ *
311
+ * Returns the exit code instead of exiting, so the caller owns the exit and
312
+ * this stays callable from a test:
313
+ * 0 the scan completed (whether or not it found anything)
314
+ * 1 the scan failed, or its result could not be persisted
315
+ * 75 another audit holds the lock — not an error, come back later
316
+ */
317
+ export async function runScheduledAudit(): Promise<number> {
318
+ const attempt = acquireAuditLock("scheduled");
319
+ if (!attempt.ok) {
320
+ // Deliberately ahead of cli_audit_started: a run that never started must
321
+ // not be counted as one that did.
322
+ process.stderr.write(`failproofai: ${heldByLine(attempt.heldBy)}; skipping this scheduled run\n`);
323
+ return EXIT_AUDIT_ALREADY_RUNNING;
324
+ }
325
+
326
+ const instanceId = getInstanceId();
327
+ try {
328
+ // Every event here is awaited, unlike runAuditCli's fire-and-forget
329
+ // `started`. Nothing keeps this process alive once the audit settles — no
330
+ // dashboard server, no user — and there is nobody waiting on latency
331
+ // either, so the bounded (5s, never-throws) send costs nothing that matters.
332
+ await trackHookEvent(instanceId, "cli_audit_started", { source: "scheduled" });
333
+
334
+ let result: AuditResult;
335
+ try {
336
+ result = await runAudit({});
337
+ } catch (err) {
338
+ await trackHookEvent(instanceId, "cli_audit_failed", {
339
+ source: "scheduled",
340
+ error_type: err instanceof Error ? err.name : "unknown",
341
+ error_message: sanitizeErrorMessage(err),
342
+ });
343
+ process.stderr.write(
344
+ `failproofai: scheduled audit failed: ${err instanceof Error ? err.message : String(err)}\n`,
345
+ );
346
+ return 1;
347
+ }
348
+
349
+ await trackHookEvent(instanceId, "cli_audit_completed", auditCompletedProps("scheduled", result));
350
+
351
+ // No cache write for an empty scan, matching both interactive paths — and it
352
+ // matters more here. This run is unattended, so overwriting a real earlier
353
+ // audit's cached result with an empty one (after a history rotation, or on a
354
+ // service unit whose HOME resolves somewhere else) would blank the dashboard
355
+ // with nobody present to notice why.
356
+ if (result.eventsScanned > 0 && !writeDashboardCache({}, result)) {
357
+ // The cache is the only channel by which this run's result reaches
358
+ // anyone, so failing to write it is a failed run, not a footnote.
359
+ process.stderr.write("failproofai: scheduled audit ran but its result could not be saved\n");
360
+ return 1;
361
+ }
362
+
363
+ process.stdout.write(
364
+ `failproofai: audit complete — ${num(result.eventsScanned)} tool calls across ` +
365
+ `${num(result.transcripts.scanned)} sessions, ${num(result.totals.hits)} hits\n`,
366
+ );
367
+
368
+ return 0;
369
+ } finally {
370
+ attempt.lock.release();
371
+ }
372
+ }
373
+
266
374
  // ── Post-setup background audit ────────────────────────────────────────────────
267
375
 
268
376
  /**
@@ -279,47 +387,67 @@ function auditCompletedProps(source: AuditSource, result: AuditResult) {
279
387
  export async function runPostSetupAudit(): Promise<void> {
280
388
  if (process.env.FAILPROOFAI_NO_AUTO_AUDIT === "1") return;
281
389
 
282
- const instanceId = getInstanceId();
283
- // Fire-and-forget, as in runAuditCli: the multi-second scan below keeps the
284
- // process alive long enough for this to land, and the completed/failed event
285
- // that follows is awaited.
286
- void trackHookEvent(instanceId, "cli_audit_started", { source: "onboarding" });
287
-
288
- process.stdout.write(
289
- `\n ${c(PINK, "✦")} ${c(BOLD, "failproofai audit now running")} ${c(DIM, "· ctrl+c to stop")}\n\n`,
290
- );
291
-
292
- let result: AuditResult;
293
- try {
294
- result = await runWithProgress({});
295
- } catch (err) {
296
- // Awaited: this function returns straight into the dashboard boot, and a
297
- // fire-and-forget fetch would race it.
298
- await trackHookEvent(instanceId, "cli_audit_failed", {
299
- source: "onboarding",
300
- error_type: err instanceof Error ? err.name : "unknown",
301
- error_message: sanitizeErrorMessage(err),
302
- });
390
+ // Take the same cross-process cache lock the scheduled run, `failproofai
391
+ // audit` and the dashboard re-run take. This onboarding scan writes the very
392
+ // same sha1-keyed per-transcript cache and single-slot dashboard cache, so it
393
+ // is the fourth writer and must serialise with the other three — a scheduled
394
+ // daemon child can already be mid-scan when setup finishes. Held ⇒ skip, best
395
+ // effort: the dashboard boots on whatever the holder's run leaves behind, and
396
+ // this is pre-warming, not a result anyone is waiting on. `onboarding` matches
397
+ // the telemetry source below and the lock source declared in audit-lock.ts.
398
+ const attempt = acquireAuditLock("onboarding");
399
+ if (!attempt.ok) {
303
400
  process.stdout.write(
304
- ` ${c(PINK, "!")} ${c(DIM, "audit couldn't finish — run")} ${c(CYAN, "failproofai audit")} ${c(DIM, "later.")}\n\n`,
401
+ `\n ${c(DIM, "an audit is already running — the dashboard will show its result.")}\n\n`,
305
402
  );
306
403
  return;
307
404
  }
308
405
 
309
- // Reported before the empty-history return below, so an onboarding audit that
310
- // finds nothing is still counted — matching runAuditCli, which reports
311
- // completed regardless of what the scan turned up.
312
- await trackHookEvent(instanceId, "cli_audit_completed", auditCompletedProps("onboarding", result));
406
+ try {
407
+ const instanceId = getInstanceId();
408
+ // Fire-and-forget, as in runAuditCli: the multi-second scan below keeps the
409
+ // process alive long enough for this to land, and the completed/failed event
410
+ // that follows is awaited.
411
+ void trackHookEvent(instanceId, "cli_audit_started", { source: "onboarding" });
313
412
 
314
- if (result.eventsScanned === 0) {
315
413
  process.stdout.write(
316
- `\n ${c(DIM, "no agent sessions to audit yet — come back after using your agent.")}\n\n`,
414
+ `\n ${c(PINK, "✦")} ${c(BOLD, "failproofai audit now running")} ${c(DIM, "· ctrl+c to stop")}\n\n`,
317
415
  );
318
- return;
416
+
417
+ let result: AuditResult;
418
+ try {
419
+ result = await runWithProgress({});
420
+ } catch (err) {
421
+ // Awaited: this function returns straight into the dashboard boot, and a
422
+ // fire-and-forget fetch would race it.
423
+ await trackHookEvent(instanceId, "cli_audit_failed", {
424
+ source: "onboarding",
425
+ error_type: err instanceof Error ? err.name : "unknown",
426
+ error_message: sanitizeErrorMessage(err),
427
+ });
428
+ process.stdout.write(
429
+ ` ${c(PINK, "!")} ${c(DIM, "audit couldn't finish — run")} ${c(CYAN, "failproofai audit")} ${c(DIM, "later.")}\n\n`,
430
+ );
431
+ return;
432
+ }
433
+
434
+ // Reported before the empty-history return below, so an onboarding audit that
435
+ // finds nothing is still counted — matching runAuditCli, which reports
436
+ // completed regardless of what the scan turned up.
437
+ await trackHookEvent(instanceId, "cli_audit_completed", auditCompletedProps("onboarding", result));
438
+
439
+ if (result.eventsScanned === 0) {
440
+ process.stdout.write(
441
+ `\n ${c(DIM, "no agent sessions to audit yet — come back after using your agent.")}\n\n`,
442
+ );
443
+ return;
444
+ }
445
+ writeDashboardCache({}, result);
446
+ printSummary(result);
447
+ process.stdout.write("\n");
448
+ } finally {
449
+ attempt.lock.release();
319
450
  }
320
- writeDashboardCache({}, result);
321
- printSummary(result);
322
- process.stdout.write("\n");
323
451
  }
324
452
 
325
453
  // ── Entry point ──────────────────────────────────────────────────────────────
@@ -329,6 +457,15 @@ export async function runAuditCli(args: string[]): Promise<void> {
329
457
  process.stdout.write(HELP);
330
458
  process.exit(0);
331
459
  }
460
+ // The headless path, spawned rather than typed. Handled ahead of the
461
+ // rejection below so that adding it costs the interactive path nothing: it
462
+ // still refuses every argument it has always refused.
463
+ if (args.includes("--scheduled")) {
464
+ const extra = args.find((a) => a !== "--scheduled");
465
+ if (extra) die(`\`audit --scheduled\` takes no other arguments (got: ${extra}).`);
466
+ process.exit(await runScheduledAudit());
467
+ }
468
+
332
469
  // No arguments supported yet — reject typos rather than silently doing a bare
333
470
  // audit, so a future `failproofai audit --since 7d` doesn't quietly no-op.
334
471
  const stray = args.find((a) => a !== "--help" && a !== "-h");
@@ -339,6 +476,18 @@ export async function runAuditCli(args: string[]): Promise<void> {
339
476
  );
340
477
  }
341
478
 
479
+ // Taken before any telemetry so a refused run is not counted as a started
480
+ // one, and released the moment the SCAN is done — see below, well before
481
+ // launch() parks this process on the dashboard.
482
+ const attempt = acquireAuditLock("cli");
483
+ if (!attempt.ok) {
484
+ process.stderr.write(
485
+ `Error: ${heldByLine(attempt.heldBy)}.\n` +
486
+ `Two audits write the same cache files, so this one won't start. Try again shortly.\n`,
487
+ );
488
+ process.exit(EXIT_AUDIT_ALREADY_RUNNING);
489
+ }
490
+
342
491
  const instanceId = getInstanceId();
343
492
  // Fire-and-forget is safe for `started`: the multi-second audit below (and the
344
493
  // awaited cli_audit_completed / cli_audit_failed) keep the process alive long
@@ -393,6 +542,13 @@ export async function runAuditCli(args: string[]): Promise<void> {
393
542
  );
394
543
  }
395
544
 
545
+ // Released here, not in a `finally`: the lock covers the SCAN, and launch()
546
+ // below keeps this process alive for as long as the user leaves the dashboard
547
+ // open. Holding it that long would block every scheduled run until the
548
+ // one-hour stale ceiling expired. The exit paths above (die(), the
549
+ // empty-history exit) are covered by the handle's own process-exit hook.
550
+ attempt.lock.release();
551
+
396
552
  const url = `http://localhost:${DASHBOARD_PORT}/audit`;
397
553
  process.stdout.write(
398
554
  `\n ${c(DIM, "starting the dashboard…")}\n` +
@@ -15,6 +15,7 @@ import { join } from "node:path";
15
15
  import { homedir } from "node:os";
16
16
  import { writeJsonAtomically } from "../../lib/atomic-write";
17
17
  import type { AuditResult, RunAuditOptions } from "./types";
18
+ import { auditDashboardFile } from "../hooks/fp-home";
18
19
 
19
20
  const DEFAULT_MAX_AGE_MINUTES = 30;
20
21
 
@@ -50,7 +51,7 @@ export interface DashboardCacheEntry {
50
51
  }
51
52
 
52
53
  function getCachePath(): string {
53
- return join(homedir(), ".failproofai", "audit-dashboard.json");
54
+ return auditDashboardFile();
54
55
  }
55
56
 
56
57
  /** Read the cache file. Returns null on missing/corrupt/unreadable file —
@@ -195,9 +195,106 @@ const CURL_PIPE_SH_RE = /(?:curl|wget)\s.*\|\s*(?:sh|bash|zsh|dash|ksh|csh|tcsh|
195
195
  * Matched against BOTH the raw command and its shell-unescaped form (see
196
196
  * `stripShellQuoting`), because a shell reconstructs the binary name from
197
197
  * fragments a regex over the raw string cannot see.
198
+ *
199
+ * ANCHORED ON COMMAND POSITION, which is the third thing an earlier version got
200
+ * wrong and the most expensive: with no anchor, the pattern fired on any string
201
+ * that merely CONTAINED the invocation, so `grep -rn "failproofai config
202
+ * --pause" docs/`, `git commit -m "docs: explain failproofai config --pause"`,
203
+ * `gh pr create --body "...failproofai config --pause"` and `git log --grep`
204
+ * were all denied. This policy is `defaultEnabled`, and this repo's own
205
+ * CHANGELOG and `docs/built-in-policies.mdx` contain that literal string, so
206
+ * the first thing it did on a real machine was block someone reading the
207
+ * documentation for it. Its sibling `FAILPROOFAI_CLI_RE` was anchored from the
208
+ * start; this one was not.
209
+ *
210
+ * The distinction that matters is structural, not textual: in a real
211
+ * invocation the binary sits where the shell will look for a COMMAND — first in
212
+ * a segment, or behind a runner (`npx`, `pnpm dlx`, `node`, `sh -c`) and its
213
+ * flags. In every false positive above it sits where an ARGUMENT goes.
198
214
  */
199
- const SELF_PAUSE_RE =
200
- /failproofai[^\s]*(?:\s+\S+)*?\s+(?:config|configure|setup)\b(?:\s+\S+)*?\s+--pause\b/;
215
+
216
+ /**
217
+ * Split a command into the pieces a shell would treat as separate commands.
218
+ *
219
+ * Includes `(`, `)` and backtick alongside the ordinary operators so that
220
+ * command substitution — `echo $(failproofai config --pause)` — puts the inner
221
+ * invocation in command position of its own segment rather than hiding it as an
222
+ * argument of `echo`.
223
+ */
224
+ const SEGMENT_SEPARATORS = /[;&|\n\r(){}`]+/;
225
+
226
+ /**
227
+ * Tokens that stand in front of the real binary without being it: package
228
+ * runners, interpreters, and the `exec`-alikes. Compared by basename, so
229
+ * `/usr/bin/env` and `env` behave identically.
230
+ */
231
+ const COMMAND_PREFIX_TOKENS = new Set([
232
+ "npx", "bunx", "pnpx", "npm", "pnpm", "yarn", "dlx", "exec", "run",
233
+ "node", "bun", "deno", "env", "command", "builtin", "nohup", "setsid",
234
+ "time", "timeout", "nice", "stdbuf", "xargs", "sudo", "doas",
235
+ "sh", "bash", "zsh", "dash", "ksh", "fish", "ash",
236
+ ]);
237
+
238
+ /** The binary itself, however it is pinned or pathed: `failproofai`,
239
+ * `failproofai@latest`, `/usr/local/bin/failproofai`,
240
+ * `./node_modules/.bin/failproofai`, `.../failproofai/bin/failproofai.mjs`. */
241
+ const SELF_BINARY_TOKEN_RE = /(?:^|\/)failproofai[^/]*$/;
242
+
243
+ /** `config`, and the two aliases the entrypoint normalizes to it. */
244
+ const CONFIG_SUBCOMMAND_RE = /^(?:config|configure|setup)$/;
245
+
246
+ /** `--pause`, with or without an `=value` tail. */
247
+ const PAUSE_FLAG_RE = /^--pause(?:=|$)/;
248
+
249
+ /** An `FOO=bar` prefix assignment, which a shell consumes before the command. */
250
+ const ENV_ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*=/;
251
+
252
+ /**
253
+ * A bare operand belonging to the runner in front of it — `timeout 30 …`,
254
+ * `nice -n 5 …`. Skipped so the runner's argument is not mistaken for the
255
+ * command. Safe to skip unconditionally: no binary is named `30` or `5m`.
256
+ */
257
+ const RUNNER_OPERAND_RE = /^\d+[a-z]*$/i;
258
+
259
+ /**
260
+ * True when `command` runs `failproofai config --pause` in command position.
261
+ *
262
+ * Residual false positive, deliberately accepted: a quoted argument that itself
263
+ * contains a shell operator AND the whole invocation — `git commit -m "x;
264
+ * failproofai config --pause"` — survives `stripShellQuoting` as two segments
265
+ * and denies. That is a far narrower miss than matching every mention, and it
266
+ * errs toward refusing rather than toward silently suspending enforcement.
267
+ */
268
+ function namesSelfPause(command: string): boolean {
269
+ for (const segment of command.split(SEGMENT_SEPARATORS)) {
270
+ const tokens = segment.split(/\s+/).filter(Boolean);
271
+ let i = 0;
272
+ // Walk off everything a shell resolves before it settles on the command:
273
+ // prefix assignments, redirections, runner flags, and the runners.
274
+ while (i < tokens.length) {
275
+ const token = tokens[i];
276
+ const isSkippable =
277
+ ENV_ASSIGNMENT_RE.test(token) ||
278
+ token.startsWith("-") ||
279
+ token.startsWith(">") ||
280
+ token.startsWith("<") ||
281
+ RUNNER_OPERAND_RE.test(token) ||
282
+ COMMAND_PREFIX_TOKENS.has(token.slice(token.lastIndexOf("/") + 1));
283
+ if (!isSkippable) break;
284
+ i++;
285
+ }
286
+ if (i >= tokens.length) continue;
287
+ if (!SELF_BINARY_TOKEN_RE.test(tokens[i])) continue;
288
+
289
+ // The binary IS the command here. Now require `config … --pause` in the
290
+ // argument order the CLI actually accepts.
291
+ const args = tokens.slice(i + 1);
292
+ const configAt = args.findIndex((a) => CONFIG_SUBCOMMAND_RE.test(a));
293
+ if (configAt === -1) continue;
294
+ if (args.slice(configAt + 1).some((a) => PAUSE_FLAG_RE.test(a))) return true;
295
+ }
296
+ return false;
297
+ }
201
298
 
202
299
  /**
203
300
  * Decode one ANSI-C `$'...'` body (the text between the quotes) to the bytes a
@@ -699,7 +796,7 @@ function blockSelfPause(ctx: PolicyContext): PolicyResult {
699
796
  // The raw command AND its shell-unescaped form: a shell strips quotes and
700
797
  // backslashes before running the binary, so `fail\proofai config --pause`
701
798
  // reaches the pause CLI even though the literal name is broken.
702
- if (SELF_PAUSE_RE.test(cmd) || SELF_PAUSE_RE.test(stripShellQuoting(cmd))) {
799
+ if (namesSelfPause(cmd) || namesSelfPause(stripShellQuoting(cmd))) {
703
800
  return deny(
704
801
  "Pausing failproofai enforcement is a human action, not an agent one. " +
705
802
  "If a policy is blocking legitimate work, say so and let the operator decide.",