failproofai 1.0.0 → 1.0.1-beta.1

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 (238) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +0 -1
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +5 -5
  5. package/.next/standalone/.next/required-server-files.json +1 -1
  6. package/.next/standalone/.next/routes-manifest.json +0 -6
  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 +4 -3
  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 +6 -6
  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/_tree.segment.rsc +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page.js +4 -3
  18. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  22. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  23. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
  24. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  25. package/.next/standalone/.next/server/app/api/audit/invite/route.js +2 -2
  26. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/audit/run/route.js +3 -3
  28. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/audit/status/route.js +2 -2
  30. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +4 -3
  32. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +2 -2
  34. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -2
  36. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/status/route.js +4 -3
  38. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  41. package/.next/standalone/.next/server/app/audit/page.js +5 -3
  42. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  44. package/.next/standalone/.next/server/app/index.html +1 -1
  45. package/.next/standalone/.next/server/app/index.rsc +15 -15
  46. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
  47. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  48. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  49. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  50. package/.next/standalone/.next/server/app/page.js +4 -3
  51. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  53. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +10 -10
  54. package/.next/standalone/.next/server/app/policies/page.js +6 -5
  55. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  56. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  58. package/.next/standalone/.next/server/app/project/[name]/page.js +5 -4
  59. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  60. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  61. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  62. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  63. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
  64. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  66. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  67. package/.next/standalone/.next/server/app/projects/page.js +5 -4
  68. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  69. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  70. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  71. package/.next/standalone/.next/server/app/settings/page.js +6 -4
  72. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  73. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  74. package/.next/standalone/.next/server/app-paths-manifest.json +0 -1
  75. package/.next/standalone/.next/server/chunks/[root-of-the-server]__05ja_05._.js +3 -0
  76. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0agdw2a._.js +3 -0
  77. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d1u_na._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0nne229._.js +3 -0
  79. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ny9q5d._.js +1 -1
  80. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +3 -0
  81. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +3 -0
  82. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +3 -0
  83. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1hstmft._.js +3 -0
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p8poxd._.js +3 -0
  85. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +1 -1
  87. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
  89. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_0jx6cp6._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0f-x2b0._.js} +1 -1
  90. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_1f290jp._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0hwvl7e._.js} +1 -1
  91. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_1g2ji1t._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_1ctjdzl._.js} +1 -1
  92. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  93. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +3 -0
  94. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__03eykvd._.js +5 -0
  95. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__07175vb._.js → [root-of-the-server]__07oh8b5._.js} +1 -1
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cxe_2_._.js +2 -2
  97. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kgy9da._.js +4 -0
  98. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0m98kzy._.js +4 -0
  99. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +3 -0
  100. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0sjzdjg._.js +3 -0
  101. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +2 -2
  102. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0v5_91z._.js +4 -0
  103. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-a3135._.js +4 -0
  104. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1hvwiw8._.js → [root-of-the-server]__11z_299._.js} +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1apc90t._.js → [root-of-the-server]__15c_z97._.js} +1 -1
  106. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1wmno1q._.js → [root-of-the-server]__17d_ffl._.js} +1 -1
  107. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_dfocn._.js +4 -0
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1e5g_ur._.js +4 -0
  109. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1hmqefd._.js +4 -0
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1jiwfsj._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kzapg5._.js +6 -0
  112. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zvtylo._.js +3 -0
  113. package/.next/standalone/.next/server/chunks/ssr/_04j57fb._.js +3 -0
  114. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/ssr/_0hom1s1._.js +3 -0
  116. package/.next/standalone/.next/server/chunks/ssr/_0qk91ba._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/ssr/_1q7j05x._.js +3 -0
  118. package/.next/standalone/.next/server/chunks/ssr/_1wboum1._.js +23 -0
  119. package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +3 -0
  120. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
  121. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  122. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  123. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js +1 -1
  125. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +4 -0
  127. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +3 -0
  128. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  129. package/.next/standalone/.next/server/pages/404.html +1 -1
  130. package/.next/standalone/.next/server/pages/500.html +1 -1
  131. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  132. package/.next/standalone/.next/server/server-reference-manifest.json +15 -15
  133. package/.next/standalone/.next/static/chunks/{2676v-lyzgo91.js → 01b0vu6t3wtfh.js} +1 -1
  134. package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +1 -0
  135. package/.next/standalone/.next/static/chunks/0w-2enf8raksk.js +1 -0
  136. package/.next/standalone/.next/static/chunks/{2kopjq_or743g.js → 13pe0rgzztjxa.js} +2 -2
  137. package/.next/standalone/.next/static/chunks/{3xvtgz6arztp_.js → 1cn6xl9lfp3ei.js} +1 -1
  138. package/.next/standalone/.next/static/chunks/{0zi8zjav5a4rq.js → 1e_2h_-zvxnke.js} +1 -1
  139. package/.next/standalone/.next/static/chunks/1eyl7fzkmnkf_.js +1 -0
  140. package/.next/standalone/.next/static/chunks/{0zm3_8jpqitxy.js → 1x9238y5kw9vi.js} +22 -22
  141. package/.next/standalone/.next/static/chunks/1xajksrefvav8.js +1 -0
  142. package/.next/standalone/.next/static/chunks/{1hfocb0sdl85c.js → 25c48grb44a-n.js} +1 -1
  143. package/.next/standalone/.next/static/chunks/{2ouq44jv_0x04.js → 28bdm54bngvc7.js} +1 -1
  144. package/.next/standalone/.next/static/chunks/29-iqnp9v_axt.css +1 -0
  145. package/.next/standalone/.next/static/chunks/{31k3yxpy57_fy.js → 2hhselnqeo4ll.js} +1 -1
  146. package/.next/standalone/.next/static/chunks/2k9f4tyv04809.css +1 -0
  147. package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +1 -0
  148. package/.next/standalone/.next/static/chunks/{0km-jn9o5xyqs.css → 3wq9x0c-7ld5d.css} +1 -1
  149. package/.next/standalone/app/actions/get-scheduled-audit.ts +65 -6
  150. package/.next/standalone/app/actions/update-scheduled-audit.ts +90 -17
  151. package/.next/standalone/app/api/auth/status/route.ts +7 -17
  152. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +2 -6
  153. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +101 -281
  154. package/.next/standalone/app/audit/_components/empty-state.tsx +2 -2
  155. package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
  156. package/.next/standalone/app/audit/audit-styles.css +150 -26
  157. package/.next/standalone/app/globals.css +39 -1
  158. package/.next/standalone/app/settings/page.tsx +38 -21
  159. package/.next/standalone/app/settings/settings-client.tsx +583 -392
  160. package/.next/standalone/app/settings/settings.css +354 -0
  161. package/.next/standalone/components/navbar.tsx +33 -4
  162. package/.next/standalone/lib/auth/api-server-client.ts +51 -29
  163. package/.next/standalone/lib/auth/auth-store.ts +44 -59
  164. package/.next/standalone/lib/telemetry.ts +45 -40
  165. package/.next/standalone/package.json +12 -10
  166. package/.next/standalone/server.js +1 -1
  167. package/bin/failproofai.mjs +6 -0
  168. package/bin/failproofaid-shim.mjs +0 -0
  169. package/dist/cli.mjs +1915 -472
  170. package/dist/worker.mjs +27 -5
  171. package/lib/auth/api-server-client.ts +51 -29
  172. package/lib/auth/auth-store.ts +44 -59
  173. package/lib/telemetry.ts +45 -40
  174. package/package.json +12 -10
  175. package/scripts/docs-audit.ts +493 -0
  176. package/scripts/translate-docs/cli.ts +16 -2
  177. package/scripts/translate-docs/mdx-translator.ts +6 -1
  178. package/scripts/translate-docs/readme-translator.ts +4 -2
  179. package/src/audit/cli-login.ts +330 -0
  180. package/src/audit/cli.ts +164 -31
  181. package/src/audit/dashboard-cache.ts +44 -8
  182. package/src/audit/harm-report.ts +260 -0
  183. package/src/audit/machine-store.ts +120 -0
  184. package/src/audit/redact-example.ts +312 -0
  185. package/src/audit/report-harm.ts +167 -0
  186. package/src/audit/schedule-cli.ts +352 -0
  187. package/src/hooks/builtin-policies.ts +26 -0
  188. package/src/hooks/daemon-service.ts +76 -0
  189. package/src/hooks/fp-config.ts +96 -5
  190. package/src/hooks/fp-home.ts +118 -8
  191. package/src/hooks/fp-reset.ts +79 -9
  192. package/src/hooks/hook-telemetry.ts +3 -2
  193. package/src/hooks/migrations.ts +241 -4
  194. package/src/hooks/tui.ts +61 -3
  195. package/.next/standalone/.next/server/app/api/auth/reminder/route/app-paths-manifest.json +0 -3
  196. package/.next/standalone/.next/server/app/api/auth/reminder/route/build-manifest.json +0 -12
  197. package/.next/standalone/.next/server/app/api/auth/reminder/route/server-reference-manifest.json +0 -4
  198. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +0 -9
  199. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.map +0 -5
  200. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +0 -1
  201. package/.next/standalone/.next/server/app/api/auth/reminder/route_client-reference-manifest.js +0 -3
  202. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-xggru._.js +0 -3
  203. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00ds05_._.js +0 -3
  204. package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +0 -3
  205. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1dxagv_._.js +0 -3
  206. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fgqj1_._.js +0 -3
  207. package/.next/standalone/.next/server/chunks/_084toou._.js +0 -3
  208. package/.next/standalone/.next/server/chunks/_0g4t8c9._.js +0 -3
  209. package/.next/standalone/.next/server/chunks/_0km521a._.js +0 -3
  210. package/.next/standalone/.next/server/chunks/_0t8kefc._.js +0 -3
  211. package/.next/standalone/.next/server/chunks/_16dwkfn._.js +0 -3
  212. package/.next/standalone/.next/server/chunks/_1phm9ui._.js +0 -3
  213. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_auth_reminder_route_actions_1kjgxf8.js +0 -3
  214. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0dk8r_l._.js +0 -3
  215. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kviq2m._.js +0 -3
  216. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ogyhu0._.js +0 -3
  217. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-rfu32._.js +0 -5
  218. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mmefsr._.js +0 -5
  219. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1t62w37._.js +0 -3
  220. package/.next/standalone/.next/server/chunks/ssr/_032wo7w._.js +0 -3
  221. package/.next/standalone/.next/server/chunks/ssr/_08840em._.js +0 -3
  222. package/.next/standalone/.next/server/chunks/ssr/_0gl2ujp._.js +0 -3
  223. package/.next/standalone/.next/server/chunks/ssr/_0j0ggkc._.js +0 -3
  224. package/.next/standalone/.next/server/chunks/ssr/_0kbv5-q._.js +0 -3
  225. package/.next/standalone/.next/server/chunks/ssr/_0r1jgvp._.js +0 -3
  226. package/.next/standalone/.next/server/chunks/ssr/_0t5xq1b._.js +0 -3
  227. package/.next/standalone/.next/server/chunks/ssr/_0w_be0g._.js +0 -3
  228. package/.next/standalone/.next/server/chunks/ssr/_15gba46._.js +0 -5
  229. package/.next/standalone/.next/server/chunks/ssr/_1qyhb9r._.js +0 -23
  230. package/.next/standalone/.next/server/chunks/ssr/_1vel2ey._.js +0 -3
  231. package/.next/standalone/.next/static/chunks/0wd503tv801gy.js +0 -1
  232. package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +0 -1
  233. package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +0 -2
  234. package/.next/standalone/.next/static/chunks/2ynl0-6aqng_b.js +0 -1
  235. package/.next/standalone/app/api/auth/reminder/route.ts +0 -213
  236. /package/.next/standalone/.next/static/{Aulh3DRWwRfwh2POCvZR_ → zMC0Yqypg3GF82-3KN6ul}/_buildManifest.js +0 -0
  237. /package/.next/standalone/.next/static/{Aulh3DRWwRfwh2POCvZR_ → zMC0Yqypg3GF82-3KN6ul}/_clientMiddlewareManifest.js +0 -0
  238. /package/.next/standalone/.next/static/{Aulh3DRWwRfwh2POCvZR_ → zMC0Yqypg3GF82-3KN6ul}/_ssgManifest.js +0 -0
package/dist/cli.mjs CHANGED
@@ -19,7 +19,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
19
19
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
20
20
 
21
21
  // package.json
22
- var version2 = "1.0.0", package_default;
22
+ var version2 = "1.0.1-beta.1", package_default;
23
23
  var init_package = __esm(() => {
24
24
  package_default = {
25
25
  name: "failproofai",
@@ -63,7 +63,8 @@ var init_package = __esm(() => {
63
63
  "translate:docs": "bun scripts/translate-docs/cli.ts --docs-only",
64
64
  "translate:dry-run": "bun scripts/translate-docs/cli.ts --dry-run",
65
65
  "translate:validate": "bun scripts/translate-docs/cli.ts --validate",
66
- "validate:mdx": "bun scripts/validate-mdx.ts"
66
+ "validate:mdx": "bun scripts/validate-mdx.ts",
67
+ "docs:audit": "bun scripts/docs-audit.ts"
67
68
  },
68
69
  keywords: [
69
70
  "claude",
@@ -127,6 +128,7 @@ var init_package = __esm(() => {
127
128
  yaml: "^2.9.0"
128
129
  },
129
130
  overrides: {
131
+ nanoid: "3.3.18",
130
132
  postcss: "8.5.26",
131
133
  "eslint-plugin-react-hooks": "7.0.1",
132
134
  vite: "8.0.16",
@@ -135,16 +137,16 @@ var init_package = __esm(() => {
135
137
  sharp: "0.35.0"
136
138
  },
137
139
  optionalDependencies: {
138
- "@failproofai/failproofaid-linux-x64": "1.0.0",
139
- "@failproofai/failproofaid-linux-arm64": "1.0.0",
140
- "@failproofai/failproofaid-darwin-x64": "1.0.0",
141
- "@failproofai/failproofaid-darwin-arm64": "1.0.0"
140
+ "@failproofai/failproofaid-linux-x64": "1.0.1-beta.1",
141
+ "@failproofai/failproofaid-linux-arm64": "1.0.1-beta.1",
142
+ "@failproofai/failproofaid-darwin-x64": "1.0.1-beta.1",
143
+ "@failproofai/failproofaid-darwin-arm64": "1.0.1-beta.1"
142
144
  },
143
145
  failproofaidBinaries: {
144
- "linux-x64": "67d9404812145b8b13dc97debd33cd667a5de347989b2c9c47bbdfcb5e99344c",
145
- "linux-arm64": "ca003a2ca755f5d696e2d6f4ddd8eedc0f8c4914f972101aa9803986ec409000",
146
- "darwin-x64": "53f1ed8710da69c2e3d3668d3b62380383a46328471870093936f53fc0a4fb05",
147
- "darwin-arm64": "682c22084906877760285e925b232d3563a1874a7a1f42c4c27deb1b7b74fb6f"
146
+ "linux-x64": "cf623441aa67363e423a1dba7db734d3e9243f0abff5e63a9ed7ddc096213e51",
147
+ "linux-arm64": "339281280ae8e60d03415da88a372ff4d3fb356c375f0e1f9eea17063233d52b",
148
+ "darwin-x64": "b972c37a9ca83b085d11d1312b3fa8487dead09dbc81e1e115000d2614051248",
149
+ "darwin-arm64": "597cbdae5f564fd0c7c917b2b0b94a91ba8465e568487da0d1cac0508b94cee5"
148
150
  }
149
151
  };
150
152
  });
@@ -175,6 +177,7 @@ function retiredLayoutPaths() {
175
177
  legacy.lastVersion(),
176
178
  legacy.auditDashboard(),
177
179
  legacy.auditCacheDir(),
180
+ legacy.auditReminder(),
178
181
  legacy.codexSessionPaths(),
179
182
  legacy.spoolDir(),
180
183
  legacy.failedDir(),
@@ -186,7 +189,7 @@ function retiredLayoutPaths() {
186
189
  legacy.localPoliciesDir()
187
190
  ];
188
191
  }
189
- var LAYOUT_VERSION = 3, at = (...parts) => resolve(failproofaiHome(), ...parts), atHome = (home, ...parts) => home ? resolve(failproofaiHome(home), ...parts) : at(...parts), versionFile = (home) => atHome(home, "VERSION"), configFile = (home) => atHome(home, "config.json"), credentialsFile = (home) => atHome(home, "credentials.json"), binDir = (home) => atHome(home, "bin"), policiesDir = (home) => atHome(home, "policies"), customPoliciesDir, globalPolicyConfigFile = (home) => atHome(home, "policies-config.json"), cloudPoliciesDir = (home) => resolve(policiesDir(home), "cloud-policies"), cursorsDir = (source, home) => source ? resolve(atHome(home, "cursors"), source) : atHome(home, "cursors"), customAgentsDir = (home) => atHome(home, "custom-agents"), auditDir = (home) => atHome(home, "audit"), auditDashboardFile = (home) => resolve(auditDir(home), "dashboard.json"), auditCacheDir = (home) => resolve(auditDir(home), "cache"), hookActivityDir = (home) => atHome(home, "hook-activity"), runDir = (home) => atHome(home, "run"), daemonSocket = () => process.env.FAILPROOFAI_DAEMON_SOCKET || resolve(runDir(), "failproofaid.sock"), auditLockFile = (home) => resolve(runDir(home), "audit.lock"), stateDir = (home) => atHome(home, "state"), shimsDir = (home) => resolve(stateDir(home), "shims"), spoolDir = (home) => resolve(stateDir(home), "spool"), failedDir = (home) => resolve(stateDir(home), "failed"), collectorHealthFile = (home) => resolve(stateDir(home), "collector-health.json"), sessionPauseDir = () => resolve(stateDir(), "sessions"), auditScheduleFile = (home) => resolve(stateDir(home), "audit-schedule.json"), telemetryIdFile = (home) => resolve(stateDir(home), "telemetry-id"), launcherMarker = (home) => resolve(stateDir(home), "launcher-configured"), onboardingAttemptFile = (home) => resolve(stateDir(home), "onboarding-attempt.json"), lastVersionFile = (home) => resolve(stateDir(home), "last-version"), onboardingLockFile = (home) => resolve(stateDir(home), "onboarding.lock"), codexSessionPathsFile = (home) => resolve(stateDir(home), "codex-session-paths.json"), logsDir = (home) => atHome(home, "logs"), migrationsDir = (home) => atHome(home, "migrations"), migrationLedgerFile = (home) => resolve(migrationsDir(home), "applied.json"), migrationBackupDir = (layout, home) => resolve(migrationsDir(home), `backup-layout${layout}`), HOME_CLASSES, legacy;
192
+ var LAYOUT_VERSION = 4, at = (...parts) => resolve(failproofaiHome(), ...parts), atHome = (home, ...parts) => home ? resolve(failproofaiHome(home), ...parts) : at(...parts), versionFile = (home) => atHome(home, "VERSION"), configFile = (home) => atHome(home, "config.json"), credentialsFile = (home) => atHome(home, "credentials.json"), binDir = (home) => atHome(home, "bin"), policiesDir = (home) => atHome(home, "policies"), customPoliciesDir, globalPolicyConfigFile = (home) => atHome(home, "policies-config.json"), cloudPoliciesDir = (home) => resolve(policiesDir(home), "cloud-policies"), cursorsDir = (source, home) => source ? resolve(atHome(home, "cursors"), source) : atHome(home, "cursors"), customAgentsDir = (home) => atHome(home, "custom-agents"), auditDir = (home) => atHome(home, "audit"), auditDashboardFile = (home) => resolve(auditDir(home), "dashboard.json"), auditCacheDir = (home) => resolve(auditDir(home), "cache"), auditSessionFile = (home) => resolve(auditDir(home), "session.json"), auditMachineFile = (home) => resolve(auditDir(home), "machine.json"), hookActivityDir = (home) => atHome(home, "hook-activity"), runDir = (home) => atHome(home, "run"), daemonSocket = () => process.env.FAILPROOFAI_DAEMON_SOCKET || resolve(runDir(), "failproofaid.sock"), auditLockFile = (home) => resolve(runDir(home), "audit.lock"), stateDir = (home) => atHome(home, "state"), shimsDir = (home) => resolve(stateDir(home), "shims"), spoolDir = (home) => resolve(stateDir(home), "spool"), failedDir = (home) => resolve(stateDir(home), "failed"), collectorHealthFile = (home) => resolve(stateDir(home), "collector-health.json"), sessionPauseDir = () => resolve(stateDir(), "sessions"), auditScheduleFile = (home) => resolve(auditDir(home), "schedule.json"), telemetryIdFile = (home) => resolve(stateDir(home), "telemetry-id"), launcherMarker = (home) => resolve(stateDir(home), "launcher-configured"), onboardingAttemptFile = (home) => resolve(stateDir(home), "onboarding-attempt.json"), lastVersionFile = (home) => resolve(stateDir(home), "last-version"), onboardingLockFile = (home) => resolve(stateDir(home), "onboarding.lock"), codexSessionPathsFile = (home) => resolve(stateDir(home), "codex-session-paths.json"), logsDir = (home) => atHome(home, "logs"), migrationsDir = (home) => atHome(home, "migrations"), migrationLedgerFile = (home) => resolve(migrationsDir(home), "applied.json"), migrationBackupDir = (layout, home) => resolve(migrationsDir(home), `backup-layout${layout}`), HOME_CLASSES, legacy;
190
193
  var init_fp_home = __esm(() => {
191
194
  customPoliciesDir = policiesDir;
192
195
  HOME_CLASSES = [
@@ -194,6 +197,7 @@ var init_fp_home = __esm(() => {
194
197
  { path: configFile, class: "user-typed" },
195
198
  { path: globalPolicyConfigFile, class: "user-typed" },
196
199
  { path: policiesDir, class: "user-typed" },
200
+ { path: auditSessionFile, class: "user-typed" },
197
201
  { path: spoolDir, class: "undelivered" },
198
202
  { path: failedDir, class: "undelivered" },
199
203
  { path: customAgentsDir, class: "undelivered" },
@@ -205,9 +209,11 @@ var init_fp_home = __esm(() => {
205
209
  { path: (home) => resolve(stateDir(home), "daemon-telemetry-id"), class: "identity" },
206
210
  { path: versionFile, class: "identity" },
207
211
  { path: migrationsDir, class: "identity" },
208
- { path: auditDir, class: "derived" },
209
- { path: collectorHealthFile, class: "derived" },
212
+ { path: auditMachineFile, class: "identity" },
213
+ { path: auditDashboardFile, class: "derived" },
214
+ { path: auditCacheDir, class: "derived" },
210
215
  { path: auditScheduleFile, class: "derived" },
216
+ { path: collectorHealthFile, class: "derived" },
211
217
  { path: codexSessionPathsFile, class: "derived" },
212
218
  { path: shimsDir, class: "derived" },
213
219
  { path: sessionPauseDir, class: "derived" },
@@ -233,6 +239,10 @@ var init_fp_home = __esm(() => {
233
239
  launcherMarker: () => at(".launcher-configured"),
234
240
  lastVersion: () => at("last-version"),
235
241
  auditDashboard: () => at("audit-dashboard.json"),
242
+ authJson: () => at("auth.json"),
243
+ nextAudit: () => at("next-audit.json"),
244
+ auditReminder: () => at("audit", "reminder.json"),
245
+ auditSchedule: () => at("state", "audit-schedule.json"),
236
246
  cacheDir: () => at("cache"),
237
247
  hookActivityDir: () => at("cache", "hook-activity"),
238
248
  auditCacheDir: () => at("cache", "audit"),
@@ -257,10 +267,17 @@ function detectLayout() {
257
267
  return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
258
268
  }
259
269
  if (existsSync(configFile())) {
270
+ const layoutThreePositions = [
271
+ legacy.authJson(),
272
+ legacy.nextAudit(),
273
+ legacy.auditSchedule()
274
+ ];
275
+ if (layoutThreePositions.some((p) => existsSync(p)))
276
+ return { kind: "stale", found: 3 };
260
277
  return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
261
278
  }
262
279
  if (existsSync(legacy.configToml()))
263
- return { kind: "stale", found: LAYOUT_VERSION - 1 };
280
+ return { kind: "stale", found: 2 };
264
281
  const layoutOneMarkers = [
265
282
  legacy.policyConfig(),
266
283
  legacy.cacheDir(),
@@ -303,7 +320,7 @@ function readVersionFile() {
303
320
  function writeVersionFile(v = {}) {
304
321
  const existing = readVersionFile();
305
322
  const next = {
306
- layout: LAYOUT_VERSION,
323
+ layout: v.layout ?? LAYOUT_VERSION,
307
324
  cli: v.cli ?? version2,
308
325
  daemon: v.clearDaemon ? undefined : v.daemon ?? existing?.daemon
309
326
  };
@@ -372,7 +389,11 @@ function projectConfig(parsed) {
372
389
  sources: readSources(collector.sources)
373
390
  },
374
391
  telemetry: { enabled: telemetry.enabled !== false },
375
- audit: { auto: audit.auto === true, intervalDays: readIntervalDays(audit.interval_days) }
392
+ audit: {
393
+ auto: audit.auto === true,
394
+ intervalDays: readIntervalDays(audit.interval_days),
395
+ reportsConsentedAt: typeof audit.reports_consented_at === "number" && Number.isFinite(audit.reports_consented_at) ? audit.reports_consented_at : undefined
396
+ }
376
397
  };
377
398
  }
378
399
  }
@@ -421,7 +442,11 @@ function writeConfig(config, raw) {
421
442
  } : {}
422
443
  },
423
444
  ...config.telemetry.enabled ? {} : { telemetry: { enabled: false } },
424
- audit: { auto: config.audit.auto, interval_days: config.audit.intervalDays }
445
+ audit: {
446
+ auto: config.audit.auto,
447
+ interval_days: config.audit.intervalDays,
448
+ ...config.audit.reportsConsentedAt === undefined ? {} : { reports_consented_at: config.audit.reportsConsentedAt }
449
+ }
425
450
  };
426
451
  const merged = structuredClone(raw ?? readConfigRaw().raw);
427
452
  for (const path of OWNED_CONFIG_KEYS)
@@ -567,7 +592,8 @@ var init_fp_config = __esm(() => {
567
592
  ["collector", "sources", "*", "extra_paths"],
568
593
  ["telemetry", "enabled"],
569
594
  ["audit", "auto"],
570
- ["audit", "interval_days"]
595
+ ["audit", "interval_days"],
596
+ ["audit", "reports_consented_at"]
571
597
  ];
572
598
  OWNED_CREDENTIAL_KEYS = [
573
599
  ["cloud", "url"],
@@ -2910,7 +2936,7 @@ function registerBuiltinPolicies(enabledNames) {
2910
2936
  }
2911
2937
  }
2912
2938
  }
2913
- var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, SEGMENT_SEPARATORS, COMMAND_PREFIX_TOKENS, SELF_BINARY_TOKEN_RE, CONFIG_SUBCOMMAND_RE, PAUSE_FLAG_RE, ENV_ASSIGNMENT_RE, RUNNER_OPERAND_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, EVAL_FLAG_RE, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
2939
+ var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SECRET_PATTERNS, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, SEGMENT_SEPARATORS, COMMAND_PREFIX_TOKENS, SELF_BINARY_TOKEN_RE, CONFIG_SUBCOMMAND_RE, PAUSE_FLAG_RE, ENV_ASSIGNMENT_RE, RUNNER_OPERAND_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, EVAL_FLAG_RE, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
2914
2940
  var init_builtin_policies = __esm(() => {
2915
2941
  init_hook_logger();
2916
2942
  isClaudeInternalPath = isAgentInternalPath;
@@ -2932,6 +2958,13 @@ var init_builtin_policies = __esm(() => {
2932
2958
  CONNECTION_STRING_RE = /(?:postgresql|postgres|mysql|mongodb(?:\+srv)?|redis|amqps?|smtps?):\/\/[^@\s]+@/;
2933
2959
  PRIVATE_KEY_RE = /-----BEGIN (?:[A-Z]+ )?PRIVATE KEY-----/;
2934
2960
  BEARER_TOKEN_RE = /Authorization:\s*Bearer\s+[A-Za-z0-9\-._~+/]{20,}/i;
2961
+ SECRET_PATTERNS = [
2962
+ [PRIVATE_KEY_RE, "private key"],
2963
+ [JWT_RE, "JWT"],
2964
+ [BEARER_TOKEN_RE, "bearer token"],
2965
+ [CONNECTION_STRING_RE, "database credentials"],
2966
+ ...API_KEY_PATTERNS
2967
+ ];
2935
2968
  SQL_TOOL_RE = /\b(?:psql|mysql|sqlite3|pgcli|clickhouse-client)\b/;
2936
2969
  DESTRUCTIVE_SQL_RE = /\b(?:DROP\s+(?:TABLE|DATABASE|SCHEMA)|TRUNCATE\b)/i;
2937
2970
  DELETE_NO_WHERE_RE = /\bDELETE\s+FROM\b/i;
@@ -11432,7 +11465,7 @@ function readCarriedLegacyCredentials() {
11432
11465
  const creds = projectCredentials(raw);
11433
11466
  return Object.keys(creds).length > 0 ? creds : null;
11434
11467
  }
11435
- function resetHome(from) {
11468
+ function resetHome(from, to = LAYOUT_VERSION) {
11436
11469
  const migrated = migrateConventionPolicies();
11437
11470
  const activity = migrateHookActivity();
11438
11471
  const pendingPolicyConfig = readCarriedPolicyConfig();
@@ -11457,7 +11490,7 @@ function resetHome(from) {
11457
11490
  writeConfig(pendingConfig);
11458
11491
  if (telemetryOptOut)
11459
11492
  updateConfig({ telemetry: { enabled: false } });
11460
- writeVersionFile();
11493
+ writeVersionFile({ layout: to });
11461
11494
  return { removed, migrated, activity, policyConfig, spooled, from };
11462
11495
  }
11463
11496
  function layoutWarningForHook() {
@@ -12033,14 +12066,71 @@ __export(exports_migrations, {
12033
12066
  runMigrations: () => runMigrations,
12034
12067
  restoreBackup: () => restoreBackup,
12035
12068
  readLedger: () => readLedger,
12069
+ pruneMigratedCredentials: () => pruneMigratedCredentials,
12036
12070
  planMigration: () => planMigration,
12037
12071
  migrationCoverageGap: () => migrationCoverageGap,
12038
12072
  describePlan: () => describePlan,
12039
12073
  backupBeforeMigrating: () => backupBeforeMigrating,
12040
12074
  MIGRATIONS: () => MIGRATIONS
12041
12075
  });
12042
- import { copyFileSync as copyFileSync2, existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as readFileSync16, rmSync as rmSync7, writeFileSync as writeFileSync10 } from "node:fs";
12043
- import { basename as basename4, dirname as dirname10, resolve as resolve16 } from "node:path";
12076
+ import {
12077
+ chmodSync as chmodSync4,
12078
+ copyFileSync as copyFileSync2,
12079
+ existsSync as existsSync21,
12080
+ mkdirSync as mkdirSync12,
12081
+ readFileSync as readFileSync16,
12082
+ renameSync as renameSync6,
12083
+ rmSync as rmSync7,
12084
+ writeFileSync as writeFileSync10
12085
+ } from "node:fs";
12086
+ import { basename as basename4, dirname as dirname10, join as join29, resolve as resolve16 } from "node:path";
12087
+ function migrateToLayout4() {
12088
+ const moves = [
12089
+ { from: legacy.authJson(), to: auditSessionFile() },
12090
+ { from: legacy.nextAudit(), to: legacy.auditReminder() },
12091
+ { from: legacy.auditSchedule(), to: auditScheduleFile() }
12092
+ ];
12093
+ const authDirOverride = process.env.FAILPROOFAI_AUTH_DIR;
12094
+ if (authDirOverride) {
12095
+ moves.push({ from: join29(authDirOverride, "auth.json"), to: join29(authDirOverride, "session.json") }, {
12096
+ from: join29(authDirOverride, "next-audit.json"),
12097
+ to: join29(authDirOverride, "reminder.json")
12098
+ });
12099
+ }
12100
+ const migrated = [];
12101
+ for (const { from, to } of moves) {
12102
+ if (!existsSync21(from))
12103
+ continue;
12104
+ if (existsSync21(to)) {
12105
+ rmSync7(from, { force: true });
12106
+ continue;
12107
+ }
12108
+ mkdirSync12(dirname10(to), { recursive: true });
12109
+ try {
12110
+ renameSync6(from, to);
12111
+ } catch {
12112
+ try {
12113
+ copyFileSync2(from, to);
12114
+ } catch (err) {
12115
+ try {
12116
+ rmSync7(to, { force: true });
12117
+ } catch {}
12118
+ throw err;
12119
+ }
12120
+ rmSync7(from, { force: true });
12121
+ }
12122
+ migrated.push(`${basename4(from)} → audit/${basename4(to)}`);
12123
+ }
12124
+ for (const secret of [auditSessionFile()]) {
12125
+ if (!existsSync21(secret))
12126
+ continue;
12127
+ try {
12128
+ chmodSync4(secret, 384);
12129
+ } catch {}
12130
+ }
12131
+ writeVersionFile({ layout: 4 });
12132
+ return { removed: [], migrated, activity: [], policyConfig: [], spooled: [], from: 3 };
12133
+ }
12044
12134
  function planMigration(from, registry = MIGRATIONS) {
12045
12135
  if (from >= LAYOUT_VERSION)
12046
12136
  return [];
@@ -12095,6 +12185,23 @@ function appendLedger(entry) {
12095
12185
  function backupNameOf(f) {
12096
12186
  return f.as ?? basename4(f.at());
12097
12187
  }
12188
+ function pruneMigratedCredentials(from) {
12189
+ const pruned = [];
12190
+ for (let layout = from;layout < LAYOUT_VERSION; layout++) {
12191
+ for (const { as, landsAt } of PRUNED_AFTER_SUCCESS) {
12192
+ try {
12193
+ if (!existsSync21(landsAt()))
12194
+ continue;
12195
+ const copy = resolve16(migrationBackupDir(layout), as);
12196
+ if (!existsSync21(copy))
12197
+ continue;
12198
+ rmSync7(copy, { force: true });
12199
+ pruned.push(as);
12200
+ } catch {}
12201
+ }
12202
+ }
12203
+ return pruned;
12204
+ }
12098
12205
  function filesToBackUp() {
12099
12206
  const seen = new Map;
12100
12207
  for (const f of [...BACKED_UP, ...BACKED_UP_LEGACY]) {
@@ -12156,6 +12263,11 @@ function runMigrations(from, chain = planMigration(from)) {
12156
12263
  });
12157
12264
  } catch (err) {
12158
12265
  steps.push({ from: step.from, to: step.to, ok: false });
12266
+ try {
12267
+ const marker = readVersionFile();
12268
+ if (marker && marker.layout >= LAYOUT_VERSION)
12269
+ writeVersionFile({ layout: step.from });
12270
+ } catch {}
12159
12271
  failed = {
12160
12272
  from: step.from,
12161
12273
  to: step.to,
@@ -12172,6 +12284,8 @@ function runMigrations(from, chain = planMigration(from)) {
12172
12284
  break;
12173
12285
  }
12174
12286
  }
12287
+ if (!failed)
12288
+ pruneMigratedCredentials(from);
12175
12289
  return { from, steps, backedUp, outcome, failed };
12176
12290
  }
12177
12291
  function describePlan(from) {
@@ -12214,23 +12328,30 @@ function restoreBackup(layout) {
12214
12328
  }
12215
12329
  return restored.sort((a, b) => a.localeCompare(b));
12216
12330
  }
12217
- var MIGRATIONS, BACKED_UP, BACKED_UP_LEGACY;
12331
+ var MIGRATIONS, BACKED_UP, BACKED_UP_LEGACY, PRUNED_AFTER_SUCCESS;
12218
12332
  var init_migrations = __esm(() => {
12219
12333
  init_package();
12220
12334
  init_fp_home();
12335
+ init_fp_config();
12221
12336
  init_fp_reset();
12222
12337
  MIGRATIONS = [
12223
12338
  {
12224
12339
  from: 1,
12225
12340
  to: 3,
12226
12341
  describe: "layout 1 → 3: carry the decision log out of cache/, keep the policy config in place, drop the layout-1 credential files",
12227
- run: () => resetHome(1)
12342
+ run: () => resetHome(1, 3)
12228
12343
  },
12229
12344
  {
12230
12345
  from: 2,
12231
12346
  to: 3,
12232
12347
  describe: "layout 2 → 3: carry config.toml and credentials.toml into JSON, move custom-policies/ back up into policies/, nest the policy config at the root",
12233
- run: () => resetHome(2)
12348
+ run: () => resetHome(2, 3)
12349
+ },
12350
+ {
12351
+ from: 3,
12352
+ to: 4,
12353
+ describe: "layout 3 → 4: gather the audit's files under audit/ — auth.json becomes audit/session.json, next-audit.json becomes audit/reminder.json, state/audit-schedule.json becomes audit/schedule.json",
12354
+ run: migrateToLayout4
12234
12355
  }
12235
12356
  ];
12236
12357
  BACKED_UP = [
@@ -12245,7 +12366,13 @@ var init_migrations = __esm(() => {
12245
12366
  { at: legacy.policyConfig },
12246
12367
  { at: legacy.localPolicyConfig, as: "local-policies-policies-config.json" },
12247
12368
  { at: legacy.cloudCredentials },
12248
- { at: legacy.ingestCredentials }
12369
+ { at: legacy.ingestCredentials },
12370
+ { at: legacy.authJson },
12371
+ { at: legacy.nextAudit },
12372
+ { at: legacy.auditSchedule }
12373
+ ];
12374
+ PRUNED_AFTER_SUCCESS = [
12375
+ { as: basename4(legacy.authJson()), landsAt: auditSessionFile }
12249
12376
  ];
12250
12377
  });
12251
12378
 
@@ -12272,7 +12399,7 @@ import {
12272
12399
  mkdirSync as mkdirSync13,
12273
12400
  readFileSync as readFileSync17,
12274
12401
  readdirSync as readdirSync16,
12275
- renameSync as renameSync6,
12402
+ renameSync as renameSync7,
12276
12403
  rmSync as rmSync8,
12277
12404
  rmdirSync as rmdirSync2,
12278
12405
  statSync as statSync17,
@@ -12304,7 +12431,7 @@ function migrateConventionPolicies2() {
12304
12431
  const name = `${prefix}${entry.name}`;
12305
12432
  if (!existsSync22(target)) {
12306
12433
  try {
12307
- renameSync6(source, target);
12434
+ renameSync7(source, target);
12308
12435
  moved.push(name);
12309
12436
  } catch {}
12310
12437
  continue;
@@ -12341,7 +12468,7 @@ function migrateLegacySpool2() {
12341
12468
  while (existsSync22(resolve17(to, name)))
12342
12469
  name = `legacy-${n++}-${entry.name}`;
12343
12470
  try {
12344
- renameSync6(resolve17(from, entry.name), resolve17(to, name));
12471
+ renameSync7(resolve17(from, entry.name), resolve17(to, name));
12345
12472
  moved.push(name);
12346
12473
  } catch {
12347
12474
  try {
@@ -12376,7 +12503,7 @@ function migrateHookActivity2() {
12376
12503
  target = resolve17(to, name);
12377
12504
  }
12378
12505
  try {
12379
- renameSync6(source, target);
12506
+ renameSync7(source, target);
12380
12507
  moved.push(name);
12381
12508
  } catch {
12382
12509
  try {
@@ -12584,7 +12711,7 @@ function readCarriedLegacyCredentials2() {
12584
12711
  const creds = projectCredentials(raw);
12585
12712
  return Object.keys(creds).length > 0 ? creds : null;
12586
12713
  }
12587
- function resetHome2(from) {
12714
+ function resetHome2(from, to = LAYOUT_VERSION) {
12588
12715
  const migrated = migrateConventionPolicies2();
12589
12716
  const activity = migrateHookActivity2();
12590
12717
  const pendingPolicyConfig = readCarriedPolicyConfig2();
@@ -12609,7 +12736,7 @@ function resetHome2(from) {
12609
12736
  writeConfig(pendingConfig);
12610
12737
  if (telemetryOptOut)
12611
12738
  updateConfig({ telemetry: { enabled: false } });
12612
- writeVersionFile();
12739
+ writeVersionFile({ layout: to });
12613
12740
  return { removed, migrated, activity, policyConfig, spooled, from };
12614
12741
  }
12615
12742
  async function drainSpoolAfterMigrating() {
@@ -12647,6 +12774,7 @@ async function checkLayoutForCli() {
12647
12774
  const run = runMigrations2(state.found);
12648
12775
  const { removed, migrated, activity } = run.outcome;
12649
12776
  const pending3 = await drainSpoolAfterMigrating();
12777
+ const daemonHint = staleDaemonHint();
12650
12778
  return {
12651
12779
  state,
12652
12780
  fatal: false,
@@ -12678,7 +12806,8 @@ async function checkLayoutForCli() {
12678
12806
  `Step ${run.failed.from} → ${run.failed.to} did not finish: ${run.failed.error}`,
12679
12807
  `The home is still marked layout ${state.found} and will be retried. Copies of`,
12680
12808
  `your settings and enrolment were saved first, in ${migrationBackupDir(state.found)}.`
12681
- ] : []
12809
+ ] : [],
12810
+ ...daemonHint.length > 0 ? ["", ...daemonHint] : []
12682
12811
  ]
12683
12812
  };
12684
12813
  }
@@ -12743,8 +12872,23 @@ function staleDaemonHint() {
12743
12872
  const skew = daemonVersionSkew();
12744
12873
  if (!skew)
12745
12874
  return [];
12875
+ let requiresDaemon = false;
12876
+ try {
12877
+ requiresDaemon = readConfig().daemon.configured;
12878
+ } catch {}
12879
+ if (requiresDaemon) {
12880
+ return [
12881
+ `[failproofai] daemon is ${skew.installed}, CLI is ${skew.expected}.`,
12882
+ `This machine is configured to REQUIRE the daemon. A daemon built against a`,
12883
+ `different on-disk layout refuses to start, and this version moved it — so the`,
12884
+ `next reboot or restart can leave the service down, which denies every tool`,
12885
+ `call until it is fixed.`,
12886
+ `Run \`failproofai update\` now to bring the daemon in line.`,
12887
+ ``
12888
+ ];
12889
+ }
12746
12890
  return [
12747
- `[failproofai] daemon is ${skew.installed}, CLI is ${skew.expected} — ` + `run \`failproofai config\` to update it.`,
12891
+ `[failproofai] daemon is ${skew.installed}, CLI is ${skew.expected} — ` + `run \`failproofai update\` to update it.`,
12748
12892
  ``
12749
12893
  ];
12750
12894
  } catch {
@@ -12785,13 +12929,13 @@ import { createHmac, randomUUID as randomUUID2 } from "node:crypto";
12785
12929
  import { execSync as execSync4 } from "node:child_process";
12786
12930
  import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, mkdirSync as mkdirSync14 } from "node:fs";
12787
12931
  import { homedir as homedir20, platform, arch, hostname, cpus } from "node:os";
12788
- import { join as join29 } from "node:path";
12932
+ import { join as join30 } from "node:path";
12789
12933
  function hashToId3(raw) {
12790
12934
  return createHmac("sha256", NAMESPACE3).update(raw).digest("hex");
12791
12935
  }
12792
12936
  function getInstanceId3() {
12793
- const idDir = join29(homedir20(), ".failproofai");
12794
- const idFile = join29(idDir, "instance-id");
12937
+ const idDir = join30(homedir20(), ".failproofai");
12938
+ const idFile = join30(idDir, "instance-id");
12795
12939
  try {
12796
12940
  const existing = readFileSync18(idFile, "utf-8").trim();
12797
12941
  if (existing)
@@ -13187,6 +13331,27 @@ function renderLaunchBanner(version3, stdout = process.stdout) {
13187
13331
  ""
13188
13332
  ];
13189
13333
  }
13334
+ function step(message, summary, stdout = process.stdout) {
13335
+ const c = paint(colorsEnabled(stdout));
13336
+ const rows = summary === undefined ? [] : Array.isArray(summary) ? summary : [summary];
13337
+ if (!stdout.isTTY) {
13338
+ if (rows.length)
13339
+ stdout.write(`${message}: ${rows.join(" ")}
13340
+ `);
13341
+ return;
13342
+ }
13343
+ const cols = stdout.columns || 80;
13344
+ const lines = [c.dim(BAR), truncate(`${c.dim(STEP_DONE)} ${message}`, cols - 1)];
13345
+ for (const r of rows)
13346
+ lines.push(truncate(`${c.dim(BAR)} ${c.dim(r)}`, cols - 1));
13347
+ writeLines(stdout, lines);
13348
+ }
13349
+ function stepOpen(message, stdout = process.stdout) {
13350
+ const c = paint(colorsEnabled(stdout));
13351
+ if (!stdout.isTTY)
13352
+ return;
13353
+ writeLines(stdout, [c.dim(BAR), `${c.guide(STEP_ACTIVE)} ${c.bold(message)}`]);
13354
+ }
13190
13355
  function outro(message, opts = {}, stdout = process.stdout) {
13191
13356
  const c = paint(colorsEnabled(stdout));
13192
13357
  const ok = opts.ok !== false;
@@ -13449,10 +13614,10 @@ function promptText(opts) {
13449
13614
  const draw = (error) => {
13450
13615
  const cols = stdout.columns || 80;
13451
13616
  const shown = opts.mask ? "•".repeat(value.length) : value;
13452
- const hint = opts.hint ? ` ${c.dim(opts.hint)}` : "";
13453
- const line = truncate(`${c.bold(opts.message)} ${shown}${hint}`, cols - 1);
13617
+ const hint = opts.hint && value.length === 0 ? ` ${c.dim(opts.hint)}` : "";
13618
+ const line = truncate(`${opts.prefix ?? ""}${c.bold(opts.message)} ${shown}${hint}`, cols - 1);
13454
13619
  const err = error ? `
13455
- ${truncate(c.warn(error), cols - 3)}` : "";
13620
+ ${opts.prefix ?? " "}${truncate(c.warn(error), cols - 3)}` : "";
13456
13621
  stdout.write(`\r\x1B[2K${line}${err}`);
13457
13622
  if (err)
13458
13623
  stdout.write("\x1B[1A");
@@ -23469,7 +23634,7 @@ var init_onboarding_lock = __esm(() => {
23469
23634
 
23470
23635
  // lib/claude-sessions.ts
23471
23636
  import { readdirSync as readdirSync17, statSync as statSync19, existsSync as existsSync29 } from "node:fs";
23472
- import { join as join30, basename as basename6 } from "node:path";
23637
+ import { join as join31, basename as basename6 } from "node:path";
23473
23638
  function getClaudeProjectsRoot() {
23474
23639
  return getClaudeProjectsPath();
23475
23640
  }
@@ -23484,7 +23649,7 @@ function listClaudeProjects() {
23484
23649
  return entries.filter((e) => e.isDirectory()).map((e) => ({
23485
23650
  name: e.name,
23486
23651
  cwd: decodeFolderName(e.name),
23487
- path: join30(root, e.name)
23652
+ path: join31(root, e.name)
23488
23653
  }));
23489
23654
  }
23490
23655
  function listClaudeTranscripts(project) {
@@ -23500,7 +23665,7 @@ function listClaudeTranscripts(project) {
23500
23665
  const sessionId = entry.name.slice(0, -".jsonl".length);
23501
23666
  if (!UUID_RE2.test(sessionId))
23502
23667
  continue;
23503
- const transcriptPath = join30(project.path, entry.name);
23668
+ const transcriptPath = join31(project.path, entry.name);
23504
23669
  try {
23505
23670
  const s = statSync19(transcriptPath);
23506
23671
  out.push({
@@ -23514,7 +23679,7 @@ function listClaudeTranscripts(project) {
23514
23679
  });
23515
23680
  } catch {}
23516
23681
  } else if (entry.isDirectory() && UUID_RE2.test(entry.name)) {
23517
- const subDir = join30(project.path, entry.name, "subagents");
23682
+ const subDir = join31(project.path, entry.name, "subagents");
23518
23683
  if (!existsSync29(subDir))
23519
23684
  continue;
23520
23685
  let subEntries;
@@ -23527,7 +23692,7 @@ function listClaudeTranscripts(project) {
23527
23692
  if (!sub.isFile() || !sub.name.endsWith(".jsonl"))
23528
23693
  continue;
23529
23694
  const agentId = sub.name.slice(0, -".jsonl".length);
23530
- const transcriptPath = join30(subDir, sub.name);
23695
+ const transcriptPath = join31(subDir, sub.name);
23531
23696
  try {
23532
23697
  const s = statSync19(transcriptPath);
23533
23698
  out.push({
@@ -24790,8 +24955,8 @@ var init_features = __esm(() => {
24790
24955
 
24791
24956
  // src/audit/cache.ts
24792
24957
  import { createHash as createHash6 } from "node:crypto";
24793
- import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync24, writeFileSync as writeFileSync17, chmodSync as chmodSync4 } from "node:fs";
24794
- import { join as join31 } from "node:path";
24958
+ import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync24, writeFileSync as writeFileSync17, chmodSync as chmodSync5 } from "node:fs";
24959
+ import { join as join32 } from "node:path";
24795
24960
  function getEngineVersion() {
24796
24961
  if (cachedEngineVersion)
24797
24962
  return cachedEngineVersion;
@@ -24811,7 +24976,7 @@ function getDetectorVersion() {
24811
24976
  function getCachePathFor(transcriptPath) {
24812
24977
  const root = auditCacheDir();
24813
24978
  const key = createHash6("sha1").update(transcriptPath).digest("hex");
24814
- return join31(root, `${key}.json`);
24979
+ return join32(root, `${key}.json`);
24815
24980
  }
24816
24981
  function readCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes) {
24817
24982
  if (sizeBytes === 0)
@@ -24856,7 +25021,7 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result)
24856
25021
  };
24857
25022
  writeFileSync17(cachePath, JSON.stringify(entry), { encoding: "utf-8", mode: 384 });
24858
25023
  try {
24859
- chmodSync4(cachePath, 384);
25024
+ chmodSync5(cachePath, 384);
24860
25025
  } catch {}
24861
25026
  } catch {}
24862
25027
  }
@@ -25343,6 +25508,7 @@ var init_audit_lock = __esm(() => {
25343
25508
  });
25344
25509
 
25345
25510
  // src/audit/dashboard-cache.ts
25511
+ import { existsSync as existsSync31, readFileSync as readFileSync26 } from "node:fs";
25346
25512
  function getCachePath() {
25347
25513
  return auditDashboardFile();
25348
25514
  }
@@ -25360,6 +25526,27 @@ function writeDashboardCache(params, result) {
25360
25526
  return false;
25361
25527
  }
25362
25528
  }
25529
+ function readDashboardCacheMeta() {
25530
+ const cachePath = getCachePath();
25531
+ if (!existsSync31(cachePath))
25532
+ return null;
25533
+ try {
25534
+ const raw = readFileSync26(cachePath, "utf-8");
25535
+ const entry = JSON.parse(raw);
25536
+ if (!entry || entry.schemaVersion !== DASHBOARD_CACHE_SCHEMA_VERSION || typeof entry.cachedAt !== "string" || Number.isNaN(new Date(entry.cachedAt).getTime()))
25537
+ return null;
25538
+ const result = entry.result;
25539
+ const count = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
25540
+ return {
25541
+ cachedAt: entry.cachedAt,
25542
+ findings: count(result?.totals?.hits),
25543
+ sessionsScanned: count(result?.transcripts?.scanned),
25544
+ eventsScanned: count(result?.eventsScanned)
25545
+ };
25546
+ } catch {
25547
+ return null;
25548
+ }
25549
+ }
25363
25550
  var DASHBOARD_CACHE_TTL_MINUTES, DASHBOARD_CACHE_SCHEMA_VERSION = 2;
25364
25551
  var init_dashboard_cache = __esm(() => {
25365
25552
  init_atomic_write();
@@ -25434,160 +25621,1095 @@ function openWhenReady(port, path3) {
25434
25621
  var POLL_INTERVAL_MS = 150, POLL_TIMEOUT_MS = 30000;
25435
25622
  var init_open_browser = () => {};
25436
25623
 
25437
- // scripts/parse-script-args.ts
25438
- import { resolve as resolve22 } from "path";
25439
- function parseStringFlag(flagName, errorLabel, inlineValue, args, index, options) {
25440
- const raw = inlineValue ?? args[index + 1];
25441
- if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
25442
- console.error(`Error: ${flagName} requires ${errorLabel}`);
25443
- process.exit(1);
25444
- }
25445
- const value = options?.resolve ? resolve22(raw) : raw;
25446
- return { value, spliceCount: inlineValue !== null ? 1 : 2 };
25624
+ // lib/telemetry.ts
25625
+ function trackEvent(name, properties) {
25626
+ if (!isTelemetryEnabled())
25627
+ return;
25628
+ const client = globalThis.__FAILPROOFAI_POSTHOG__;
25629
+ if (!client)
25630
+ return;
25631
+ client.capture({
25632
+ distinctId: getInstanceId2(),
25633
+ event: name,
25634
+ properties: { ...properties, $lib: "failproofai", failproofai_version: version2, product: POSTHOG_PRODUCT }
25635
+ });
25447
25636
  }
25448
- function parseScriptArgs(argv) {
25449
- const args = [...argv];
25450
- let loggingLevel;
25451
- let disableTelemetry = false;
25452
- let allowedDevOrigins;
25453
- let host;
25454
- for (let i = 0;i < args.length; i++) {
25455
- const arg = args[i];
25456
- const eqIdx = arg.indexOf("=");
25457
- const flag = eqIdx >= 0 ? arg.slice(0, eqIdx) : arg;
25458
- const inlineValue = eqIdx >= 0 ? arg.slice(eqIdx + 1) : null;
25459
- if (flag === "--logging") {
25460
- const raw = inlineValue ?? args[i + 1];
25461
- if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
25462
- console.error("Error: --logging requires a level (info, warn, error)");
25463
- process.exit(1);
25464
- }
25465
- const val = raw.toLowerCase();
25466
- if (val !== "info" && val !== "warn" && val !== "error") {
25467
- console.error("Error: --logging must be one of: info, warn, error");
25468
- process.exit(1);
25469
- }
25470
- loggingLevel = val;
25471
- args.splice(i, inlineValue !== null ? 1 : 2);
25472
- i--;
25473
- continue;
25474
- }
25475
- if (flag === "--disable-telemetry") {
25476
- disableTelemetry = true;
25477
- args.splice(i, 1);
25478
- i--;
25479
- continue;
25480
- }
25481
- if (flag === "--allowed-origins") {
25482
- const { value, spliceCount } = parseStringFlag(flag, "a comma-separated list of origins", inlineValue, args, i);
25483
- allowedDevOrigins = value.split(",").map((s) => s.trim()).filter(Boolean);
25484
- args.splice(i, spliceCount);
25485
- i--;
25486
- continue;
25637
+ var init_telemetry = __esm(() => {
25638
+ init_telemetry_id2();
25639
+ init_telemetry_enabled();
25640
+ init_package();
25641
+ });
25642
+
25643
+ // lib/auth/api-server-client.ts
25644
+ function getApiBase() {
25645
+ const raw = process.env.FAILPROOF_API_URL ?? process.env.FAILPROOFAI_API_URL ?? DEFAULT_API_BASE;
25646
+ return raw.replace(/\/+$/, "");
25647
+ }
25648
+ async function parseError(res) {
25649
+ let body = {};
25650
+ try {
25651
+ body = await res.json();
25652
+ } catch {}
25653
+ const code = body.code ?? `http_${res.status}`;
25654
+ const message = body.message ?? body.detail ?? res.statusText ?? "request failed";
25655
+ let retryAfterSecs = body.retry_after_secs;
25656
+ if (retryAfterSecs === undefined) {
25657
+ const h = res.headers.get("retry-after");
25658
+ if (h) {
25659
+ const n = Number(h);
25660
+ if (Number.isFinite(n))
25661
+ retryAfterSecs = n;
25487
25662
  }
25488
- if (flag === "--host" || flag === "-H" || flag === "--hostname") {
25489
- const { value, spliceCount } = parseStringFlag(flag, "an address to bind (e.g. 127.0.0.1)", inlineValue, args, i);
25490
- host = value;
25491
- args.splice(i, spliceCount);
25492
- i--;
25493
- continue;
25663
+ }
25664
+ if (retryAfterSecs !== undefined) {
25665
+ retryAfterSecs = Math.max(0, Math.min(86400, retryAfterSecs));
25666
+ }
25667
+ return new AuthApiError(res.status, code, message, retryAfterSecs);
25668
+ }
25669
+ function timeoutSignal(extra) {
25670
+ const t = AbortSignal.timeout(REQUEST_TIMEOUT_MS);
25671
+ if (!extra)
25672
+ return t;
25673
+ const anyFn = AbortSignal.any;
25674
+ if (anyFn)
25675
+ return anyFn([t, extra]);
25676
+ const composed = new AbortController;
25677
+ const onAbort = (s) => composed.abort(s.reason);
25678
+ if (t.aborted)
25679
+ onAbort(t);
25680
+ else
25681
+ t.addEventListener("abort", () => onAbort(t), { once: true });
25682
+ if (extra.aborted)
25683
+ onAbort(extra);
25684
+ else
25685
+ extra.addEventListener("abort", () => onAbort(extra), { once: true });
25686
+ return composed.signal;
25687
+ }
25688
+ function pathFromUrl(url) {
25689
+ try {
25690
+ return new URL(url).pathname;
25691
+ } catch {
25692
+ return url;
25693
+ }
25694
+ }
25695
+ async function fetchWithTimeout2(url, init) {
25696
+ try {
25697
+ return await fetch(url, { ...init, signal: timeoutSignal(init.signal ?? undefined) });
25698
+ } catch (err) {
25699
+ const isTimeout = isAbortError(err);
25700
+ trackEvent("api_server_unreachable", {
25701
+ kind: isTimeout ? "timeout" : "network",
25702
+ path: pathFromUrl(url),
25703
+ method: typeof init.method === "string" ? init.method : "GET"
25704
+ });
25705
+ if (isTimeout) {
25706
+ throw new AuthApiError(0, "timeout", `request to ${url} timed out after ${REQUEST_TIMEOUT_MS}ms`);
25494
25707
  }
25708
+ throw err;
25495
25709
  }
25496
- return { loggingLevel, disableTelemetry, allowedDevOrigins, host, remainingArgs: args };
25497
25710
  }
25498
- var init_parse_script_args = () => {};
25711
+ async function postJson(path3, body, init) {
25712
+ const headers = { "content-type": "application/json" };
25713
+ if (init?.accessToken)
25714
+ headers["authorization"] = `Bearer ${init.accessToken}`;
25715
+ const res = await fetchWithTimeout2(`${getApiBase()}${path3}`, {
25716
+ method: "POST",
25717
+ headers,
25718
+ body: JSON.stringify(body)
25719
+ });
25720
+ if (res.status === 204)
25721
+ return;
25722
+ if (!res.ok)
25723
+ throw await parseError(res);
25724
+ return await res.json();
25725
+ }
25726
+ async function requestLoginCode(email) {
25727
+ return postJson("/v0/auth/login/request", { email });
25728
+ }
25729
+ async function verifyLoginCode(email, code) {
25730
+ return postJson("/v0/auth/login/verify", { email, code });
25731
+ }
25732
+ async function refreshAccessToken(refreshToken) {
25733
+ return postJson("/v0/auth/token/refresh", {
25734
+ refresh_token: refreshToken
25735
+ });
25736
+ }
25737
+ async function submitAuditReport(accessToken, body) {
25738
+ return postJson("/v0/audit-reports", body, { accessToken });
25739
+ }
25740
+ var DEFAULT_API_BASE = "https://api.befailproof.ai", AuthApiError, REQUEST_TIMEOUT_MS = 1e4;
25741
+ var init_api_server_client = __esm(() => {
25742
+ init_telemetry();
25743
+ AuthApiError = class AuthApiError extends Error {
25744
+ status;
25745
+ code;
25746
+ retryAfterSecs;
25747
+ constructor(status, code, message, retryAfterSecs) {
25748
+ super(message);
25749
+ this.status = status;
25750
+ this.code = code;
25751
+ this.retryAfterSecs = retryAfterSecs;
25752
+ this.name = "AuthApiError";
25753
+ }
25754
+ };
25755
+ });
25499
25756
 
25500
- // scripts/install-diagnosis.mjs
25501
- import { existsSync as existsSync31, readFileSync as readFileSync26, realpathSync as realpathSync3 } from "node:fs";
25502
- import { dirname as dirname16, resolve as resolve23 } from "node:path";
25503
- import { homedir as homedir25, platform as platform4 } from "node:os";
25504
- import { spawnSync } from "node:child_process";
25505
- function findPackageRoot(start) {
25757
+ // lib/auth/auth-store.ts
25758
+ import { existsSync as existsSync32, readFileSync as readFileSync27, readdirSync as readdirSync18, rmSync as rmSync12 } from "node:fs";
25759
+ import { join as join33, resolve as resolve22 } from "node:path";
25760
+ function getAuthFilePath() {
25761
+ const override = process.env.FAILPROOFAI_AUTH_DIR;
25762
+ return override ? join33(override, "session.json") : auditSessionFile();
25763
+ }
25764
+ function readAuth() {
25765
+ const p = getAuthFilePath();
25766
+ if (!existsSync32(p))
25767
+ return null;
25506
25768
  try {
25507
- let dir = realpathSync3(start);
25508
- if (existsSync31(dir) && !existsSync31(resolve23(dir, "package.json"))) {
25509
- dir = dirname16(dir);
25769
+ const raw = readFileSync27(p, "utf-8");
25770
+ const parsed = JSON.parse(raw);
25771
+ if (typeof parsed.access_token !== "string" || typeof parsed.refresh_token !== "string" || typeof parsed.access_expires_at !== "number" || typeof parsed.user !== "object" || !parsed.user || typeof parsed.user.id !== "string" || typeof parsed.user.email !== "string") {
25772
+ return null;
25510
25773
  }
25511
- while (true) {
25512
- const pkgPath = resolve23(dir, "package.json");
25513
- if (existsSync31(pkgPath)) {
25514
- try {
25515
- const pkg = JSON.parse(readFileSync26(pkgPath, "utf8"));
25516
- if (pkg.name === PKG_NAME)
25517
- return dir;
25518
- } catch {}
25774
+ return {
25775
+ access_token: parsed.access_token,
25776
+ refresh_token: parsed.refresh_token,
25777
+ access_expires_at: parsed.access_expires_at,
25778
+ refresh_expires_at: typeof parsed.refresh_expires_at === "number" ? parsed.refresh_expires_at : parsed.access_expires_at,
25779
+ user: {
25780
+ id: parsed.user.id,
25781
+ email: parsed.user.email
25519
25782
  }
25520
- const parent = dirname16(dir);
25521
- if (parent === dir)
25522
- return null;
25523
- dir = parent;
25524
- }
25783
+ };
25525
25784
  } catch {
25526
25785
  return null;
25527
25786
  }
25528
25787
  }
25529
- function readPackageVersion(packageRoot) {
25530
- if (!packageRoot)
25531
- return null;
25788
+ function writeAuth(auth) {
25789
+ writeJsonAtomically(getAuthFilePath(), auth);
25790
+ }
25791
+ function deleteAuth() {
25792
+ const p = getAuthFilePath();
25793
+ if (existsSync32(p))
25794
+ rmSync12(p, { force: true });
25532
25795
  try {
25533
- const pkg = JSON.parse(readFileSync26(resolve23(packageRoot, "package.json"), "utf8"));
25534
- return typeof pkg.version === "string" ? pkg.version : null;
25535
- } catch {
25536
- return null;
25796
+ const root = migrationsDir();
25797
+ if (!existsSync32(root))
25798
+ return;
25799
+ for (const entry of readdirSync18(root)) {
25800
+ if (!entry.startsWith("backup-layout"))
25801
+ continue;
25802
+ const copy = resolve22(root, entry, "auth.json");
25803
+ if (existsSync32(copy))
25804
+ rmSync12(copy, { force: true });
25805
+ }
25806
+ } catch {}
25807
+ }
25808
+ function authFromTokenResponse(token, existingUser) {
25809
+ const now = Math.floor(Date.now() / 1000);
25810
+ const user = token.user ?? existingUser;
25811
+ if (!user) {
25812
+ throw new Error("authFromTokenResponse: missing user identity");
25537
25813
  }
25814
+ return {
25815
+ access_token: token.access_token,
25816
+ refresh_token: token.refresh_token,
25817
+ access_expires_at: now + token.access_expires_in,
25818
+ refresh_expires_at: now + token.refresh_expires_in,
25819
+ user
25820
+ };
25538
25821
  }
25539
- function resolvePathFirstBinary() {
25822
+ async function dedupedRefresh(auth) {
25823
+ const existing = inFlightRefreshes.get(auth.refresh_token);
25824
+ if (existing)
25825
+ return existing;
25826
+ const p = (async () => {
25827
+ const refreshed = await refreshAccessToken(auth.refresh_token);
25828
+ const next = authFromTokenResponse(refreshed, auth.user);
25829
+ writeAuth(next);
25830
+ return next;
25831
+ })();
25832
+ inFlightRefreshes.set(auth.refresh_token, p);
25540
25833
  try {
25541
- const isWin = platform4() === "win32";
25542
- const res = isWin ? spawnSync("where", [PKG_NAME], { encoding: "utf8" }) : spawnSync("sh", ["-c", `command -v ${PKG_NAME}`], { encoding: "utf8" });
25543
- if (res.status !== 0)
25544
- return null;
25545
- const first = (res.stdout || "").split(/\r?\n/).find((l) => l.trim().length > 0);
25546
- return first ? first.trim() : null;
25547
- } catch {
25548
- return null;
25834
+ return await p;
25835
+ } finally {
25836
+ inFlightRefreshes.delete(auth.refresh_token);
25549
25837
  }
25550
25838
  }
25551
- function locateNpmGlobal() {
25839
+ async function getValidAccessToken() {
25840
+ const auth = readAuth();
25841
+ if (!auth)
25842
+ return null;
25843
+ const now = Math.floor(Date.now() / 1000);
25844
+ if (auth.access_expires_at - now > REFRESH_LEEWAY_SECS)
25845
+ return auth;
25552
25846
  try {
25553
- const res = spawnSync("npm", ["root", "-g"], { encoding: "utf8" });
25554
- if (res.status !== 0)
25555
- return null;
25556
- const root = (res.stdout || "").trim();
25557
- if (!root)
25847
+ return await dedupedRefresh(auth);
25848
+ } catch (err) {
25849
+ if (err instanceof AuthApiError && err.status === 401) {
25850
+ deleteAuth();
25558
25851
  return null;
25559
- const candidate = resolve23(root, PKG_NAME);
25560
- return existsSync31(resolve23(candidate, "package.json")) ? candidate : null;
25561
- } catch {
25852
+ }
25562
25853
  return null;
25563
25854
  }
25564
25855
  }
25565
- function locateBunGlobal() {
25566
- try {
25567
- const candidate = resolve23(homedir25(), ".bun", "install", "global", "node_modules", PKG_NAME);
25568
- return existsSync31(resolve23(candidate, "package.json")) ? candidate : null;
25569
- } catch {
25570
- return null;
25856
+ var REFRESH_LEEWAY_SECS = 60, inFlightRefreshes;
25857
+ var init_auth_store = __esm(() => {
25858
+ init_atomic_write();
25859
+ init_fp_home();
25860
+ init_api_server_client();
25861
+ inFlightRefreshes = new Map;
25862
+ });
25863
+
25864
+ // src/audit/redact-example.ts
25865
+ import { homedir as homedir25 } from "node:os";
25866
+ function maskTruncatedSecret(input) {
25867
+ for (const [pattern, label] of SECRET_PREFIXES) {
25868
+ if (pattern.test(input)) {
25869
+ return input.replace(pattern, `[REDACTED: ${label}]`);
25870
+ }
25571
25871
  }
25872
+ return input;
25572
25873
  }
25573
- function buildRecommendation(pathFirstBin) {
25574
- if (!pathFirstBin)
25575
- return null;
25576
- const bunBinPrefix = resolve23(homedir25(), ".bun", "bin") + "/";
25577
- const bunGlobalPrefix = resolve23(homedir25(), ".bun", "install", "global") + "/";
25578
- const isBun = pathFirstBin.startsWith(bunBinPrefix) || pathFirstBin.startsWith(bunGlobalPrefix);
25579
- if (isBun) {
25580
- return `rm -f ~/.bun/bin/${PKG_NAME} && rm -rf ~/.bun/install/global/node_modules/${PKG_NAME}`;
25874
+ function isSecretName(name) {
25875
+ const upper = name.toUpperCase();
25876
+ if (SECRET_NAME_SUBSTRINGS.some((word) => upper.includes(word)))
25877
+ return true;
25878
+ return upper.split("_").some((part) => SECRET_NAME_COMPONENTS.includes(part));
25879
+ }
25880
+ function maskAssignedSecrets(input) {
25881
+ let out = input.replace(ASSIGNMENT_RE, (match, name, value) => {
25882
+ if (!isSecretName(name))
25883
+ return match;
25884
+ if (value.startsWith("[REDACTED"))
25885
+ return match;
25886
+ return `${name}=[REDACTED: assigned secret]`;
25887
+ });
25888
+ out = out.replace(URL_CREDENTIALS_RE, "$1[REDACTED: URL credentials]@");
25889
+ out = out.replace(BASIC_AUTH_FLAG_RE, "$1[REDACTED: basic auth]");
25890
+ return out;
25891
+ }
25892
+ function maskSecrets(input) {
25893
+ let out = input;
25894
+ for (const [pattern, label] of SECRET_PATTERNS) {
25895
+ const global = new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
25896
+ out = out.replace(global, `[REDACTED: ${label}]`);
25581
25897
  }
25582
- return `npm rm -g ${PKG_NAME}`;
25898
+ return out;
25583
25899
  }
25584
- function diagnoseShadow(self) {
25585
- const selfPackageRoot = (() => {
25586
- try {
25587
- return self?.selfPackageRoot ? realpathSync3(self.selfPackageRoot) : null;
25588
- } catch {
25589
- return self?.selfPackageRoot ?? null;
25590
- }
25900
+ function shortenPaths(input, home = homedir25()) {
25901
+ const homeRoot = home.replace(/\/+$/, "");
25902
+ return input.replace(ABSOLUTE_PATH_RE, (match, offset, whole) => {
25903
+ if (PUBLIC_PATH_ROOTS.some((root2) => match.startsWith(root2)))
25904
+ return match;
25905
+ if (offset > 0 && whole[offset - 1] === "/" && /[a-z][a-z0-9+.\-]*:$/i.test(whole.slice(0, offset - 1))) {
25906
+ const urlSegments = match.split("/").filter(Boolean);
25907
+ if (urlSegments.length <= 1)
25908
+ return match;
25909
+ const host = urlSegments[0];
25910
+ const leaf = urlSegments[urlSegments.length - 1];
25911
+ const elided = urlSegments.length > 2 ? "/…" : "";
25912
+ return `/${host}${elided}/${leaf}${match.endsWith("/") ? "/" : ""}`;
25913
+ }
25914
+ const trailingSlash = match.endsWith("/");
25915
+ const segments = match.split("/").filter(Boolean);
25916
+ if (segments.length === 0)
25917
+ return match;
25918
+ const basename7 = segments[segments.length - 1];
25919
+ const kept = segments.slice(Math.max(0, segments.length - 1 - KEPT_PARENT_SEGMENTS), segments.length - 1);
25920
+ const matchRoot = match.replace(/\/+$/, "");
25921
+ const underHome = homeRoot.length > 0 && (matchRoot === homeRoot || matchRoot.startsWith(`${homeRoot}/`));
25922
+ if (matchRoot === homeRoot && homeRoot.length > 0) {
25923
+ return trailingSlash ? "~/" : "~";
25924
+ }
25925
+ const root = underHome ? "~" : "";
25926
+ const middle = segments.length - kept.length - 1 > 0 ? "/…" : "";
25927
+ const tail = [...kept, basename7].join("/");
25928
+ return `${root}${middle}/${tail}${trailingSlash ? "/" : ""}`;
25929
+ });
25930
+ }
25931
+ function redactExample(input, home = homedir25()) {
25932
+ const masked = maskAssignedSecrets(maskTruncatedSecret(maskSecrets(input)));
25933
+ const shortened = shortenPaths(masked, home);
25934
+ const collapsed = shortened.replace(/\s+/g, " ").trim();
25935
+ return collapsed.length > REDACTED_EXAMPLE_MAX_CHARS ? `${collapsed.slice(0, REDACTED_EXAMPLE_MAX_CHARS - 1)}…` : collapsed;
25936
+ }
25937
+ var REDACTED_EXAMPLE_MAX_CHARS = 160, KEPT_PARENT_SEGMENTS = 0, ABSOLUTE_PATH_RE, PUBLIC_PATH_ROOTS, SECRET_PREFIXES, SECRET_NAME_SUBSTRINGS, SECRET_NAME_COMPONENTS, ASSIGNMENT_RE, URL_CREDENTIALS_RE, BASIC_AUTH_FLAG_RE;
25938
+ var init_redact_example = __esm(() => {
25939
+ init_builtin_policies();
25940
+ ABSOLUTE_PATH_RE = /(?:\/[\w.\-@+]+){2,}\/?/g;
25941
+ PUBLIC_PATH_ROOTS = ["/dev/", "/proc/", "/sys/"];
25942
+ SECRET_PREFIXES = [
25943
+ [/(?<!\w)(?:Authorization:\s*)?Bearer\s+\S*$/i, "bearer token"],
25944
+ [/(?<!\w)sk-ant-\S*$/, "Anthropic API key"],
25945
+ [/(?<!\w)sk-proj-\S*$/, "OpenAI project API key"],
25946
+ [/(?<!\w)sk-\S*$/, "OpenAI API key"],
25947
+ [/(?<!\w)ghp_\S*$/, "GitHub personal access token"],
25948
+ [/(?<!\w)github_pat_\S*$/, "GitHub fine-grained token"],
25949
+ [/(?<!\w)AKIA\S*$/, "AWS access key ID"],
25950
+ [/(?<!\w)sk_live_\S*$/, "Stripe live secret key"],
25951
+ [/(?<!\w)sk_test_\S*$/, "Stripe test secret key"],
25952
+ [/(?<!\w)AIza\S*$/, "Google API key"],
25953
+ [/-----BEGIN\s[A-Z ]*$/, "private key"]
25954
+ ];
25955
+ SECRET_NAME_SUBSTRINGS = [
25956
+ "TOKEN",
25957
+ "SECRET",
25958
+ "PASSWORD",
25959
+ "PASSWD",
25960
+ "CREDENTIAL",
25961
+ "APIKEY",
25962
+ "PRIVATEKEY"
25963
+ ];
25964
+ SECRET_NAME_COMPONENTS = ["KEY", "PASS", "AUTH", "PAT", "SIG", "SIGNATURE", "SESSION", "COOKIE"];
25965
+ ASSIGNMENT_RE = /\b([A-Za-z_][A-Za-z0-9_]*)=("[^"]*"|'[^']*'|[^\s;|&"']+)/g;
25966
+ URL_CREDENTIALS_RE = /([a-z][a-z0-9+.\-]*:\/\/)[^\s/:@]+:[^\s/@]+@/gi;
25967
+ BASIC_AUTH_FLAG_RE = /((?:^|\s)(?:-u|--user)[\s=])\S+:\S+/g;
25968
+ });
25969
+
25970
+ // src/audit/harm-report.ts
25971
+ function shortName2(name) {
25972
+ const slash = name.indexOf("/");
25973
+ return slash === -1 ? name : name.slice(slash + 1);
25974
+ }
25975
+ function isHarmful(count) {
25976
+ if (count.source !== "builtin")
25977
+ return false;
25978
+ const short = shortName2(count.name);
25979
+ return HARMFUL_SEVERITIES.has(count.severity) || ALSO_HARMFUL.has(short);
25980
+ }
25981
+ function ts(value) {
25982
+ if (!value)
25983
+ return null;
25984
+ const n = Date.parse(value);
25985
+ return Number.isFinite(n) ? n : null;
25986
+ }
25987
+ function selectHarmful(result, from, to, opts = {}) {
25988
+ const includeUnplaceable = opts.includeUnplaceable ?? from === undefined;
25989
+ const fromMs = from ? from.getTime() : null;
25990
+ const toMs2 = to.getTime();
25991
+ const out = [];
25992
+ for (const count of result.results) {
25993
+ if (!isHarmful(count))
25994
+ continue;
25995
+ const last = ts(count.lastSeen);
25996
+ const first = ts(count.firstSeen);
25997
+ if (fromMs !== null && last !== null && last <= fromMs)
25998
+ continue;
25999
+ if (first !== null && first > toMs2)
26000
+ continue;
26001
+ const inWindow = count.examples.filter((e) => {
26002
+ const at2 = ts(e.timestamp);
26003
+ if (at2 === null)
26004
+ return includeUnplaceable;
26005
+ if (fromMs !== null && at2 <= fromMs)
26006
+ return false;
26007
+ return at2 <= toMs2;
26008
+ });
26009
+ const unplaceable = last === null && first === null;
26010
+ if (unplaceable && !includeUnplaceable)
26011
+ continue;
26012
+ const afterLowerEdge = fromMs === null || first !== null && first > fromMs;
26013
+ const beforeUpperEdge = last !== null && last <= toMs2;
26014
+ const wholly = unplaceable || afterLowerEdge && beforeUpperEdge;
26015
+ const floor = beforeUpperEdge ? 1 : 0;
26016
+ const hits = wholly ? count.hits : Math.max(inWindow.length, floor);
26017
+ if (hits <= 0)
26018
+ continue;
26019
+ out.push({
26020
+ policy: shortName2(count.name),
26021
+ category: count.category,
26022
+ title: count.displayTitle ?? "",
26023
+ hits,
26024
+ first_seen: count.firstSeen,
26025
+ last_seen: count.lastSeen,
26026
+ examples: inWindow.map((e) => redactExample(e.example)).filter((e) => e.length > 0)
26027
+ });
26028
+ }
26029
+ out.sort((a, b) => b.hits - a.hits);
26030
+ return out;
26031
+ }
26032
+ function buildHarmReport(result, lastReportedAt, intervalDays) {
26033
+ const to = new Date(Date.parse(result.scannedAt));
26034
+ const windowTo = Number.isFinite(to.getTime()) ? to : new Date;
26035
+ const watermark = ts(lastReportedAt);
26036
+ const isFirstReport = watermark === null;
26037
+ const oneInterval = Math.max(1, intervalDays) * 86400000;
26038
+ const fallbackFrom = windowTo.getTime() - oneInterval;
26039
+ const from = new Date(isFirstReport || watermark >= windowTo.getTime() ? fallbackFrom : watermark);
26040
+ return {
26041
+ window_from: from.toISOString(),
26042
+ window_to: windowTo.toISOString(),
26043
+ harmful: selectHarmful(result, from, windowTo, { includeUnplaceable: isFirstReport })
26044
+ };
26045
+ }
26046
+ var HARMFUL_SEVERITIES, ALSO_HARMFUL;
26047
+ var init_harm_report = __esm(() => {
26048
+ init_redact_example();
26049
+ HARMFUL_SEVERITIES = new Set(["deny", "sanitize"]);
26050
+ ALSO_HARMFUL = new Set(["protect-env-vars"]);
26051
+ });
26052
+
26053
+ // src/audit/machine-store.ts
26054
+ import { existsSync as existsSync33, readFileSync as readFileSync28, rmSync as rmSync13 } from "node:fs";
26055
+ import { hostname as hostname5 } from "node:os";
26056
+ import { randomUUID as randomUUID4 } from "node:crypto";
26057
+ function readMachineIdentity(home) {
26058
+ const path3 = auditMachineFile(home);
26059
+ if (!existsSync33(path3))
26060
+ return null;
26061
+ try {
26062
+ const parsed = JSON.parse(readFileSync28(path3, "utf8"));
26063
+ if (typeof parsed.machine_id !== "string" || !parsed.machine_id)
26064
+ return null;
26065
+ return {
26066
+ machine_id: parsed.machine_id,
26067
+ last_reported_at: typeof parsed.last_reported_at === "string" ? parsed.last_reported_at : undefined,
26068
+ created_at: typeof parsed.created_at === "string" ? parsed.created_at : new Date(0).toISOString()
26069
+ };
26070
+ } catch {
26071
+ return null;
26072
+ }
26073
+ }
26074
+ function ensureMachineIdentity(home) {
26075
+ const existing = readMachineIdentity(home);
26076
+ if (existing)
26077
+ return existing;
26078
+ const fresh = {
26079
+ machine_id: randomUUID4(),
26080
+ created_at: new Date().toISOString()
26081
+ };
26082
+ writeJsonAtomically(auditMachineFile(home), fresh);
26083
+ return fresh;
26084
+ }
26085
+ function recordReportWatermark(nextWindowFrom, home) {
26086
+ const current = ensureMachineIdentity(home);
26087
+ writeJsonAtomically(auditMachineFile(home), {
26088
+ ...current,
26089
+ last_reported_at: nextWindowFrom
26090
+ });
26091
+ }
26092
+ function machineLabel() {
26093
+ try {
26094
+ const h = hostname5().trim();
26095
+ return h.length > 0 ? h : undefined;
26096
+ } catch {
26097
+ return;
26098
+ }
26099
+ }
26100
+ var init_machine_store = __esm(() => {
26101
+ init_atomic_write();
26102
+ init_fp_home();
26103
+ });
26104
+
26105
+ // src/audit/report-harm.ts
26106
+ async function reportHarm(result) {
26107
+ let auto = false;
26108
+ let consentedAt;
26109
+ let intervalDays = 7;
26110
+ try {
26111
+ const config = readConfig();
26112
+ auto = config.audit.auto;
26113
+ consentedAt = config.audit.reportsConsentedAt;
26114
+ intervalDays = config.audit.intervalDays;
26115
+ } catch {
26116
+ return { kind: "disabled" };
26117
+ }
26118
+ if (!auto)
26119
+ return { kind: "disabled" };
26120
+ if (consentedAt === undefined)
26121
+ return { kind: "consent-required" };
26122
+ const auth = await getValidAccessToken();
26123
+ if (!auth) {
26124
+ return { kind: "signed-out" };
26125
+ }
26126
+ let identity2;
26127
+ try {
26128
+ identity2 = ensureMachineIdentity();
26129
+ } catch (err) {
26130
+ return { kind: "failed", error: err instanceof Error ? err.message : String(err) };
26131
+ }
26132
+ const report = buildHarmReport(result, identity2.last_reported_at, intervalDays);
26133
+ const hits = report.harmful.reduce((n, p) => n + p.hits, 0);
26134
+ try {
26135
+ const res = await submitAuditReport(auth.access_token, {
26136
+ machine_id: identity2.machine_id,
26137
+ label: machineLabel(),
26138
+ platform: process.platform,
26139
+ window_from: report.window_from,
26140
+ window_to: report.window_to,
26141
+ harmful: report.harmful
26142
+ });
26143
+ try {
26144
+ recordReportWatermark(res.next_window_from);
26145
+ } catch {}
26146
+ return res.emailed ? { kind: "sent", hits } : { kind: "held", hits, reason: res.reason ?? "not_sent" };
26147
+ } catch (err) {
26148
+ const error = err instanceof AuthApiError ? `${err.code}: ${err.message}` : err instanceof Error ? err.message : String(err);
26149
+ return { kind: "failed", error };
26150
+ }
26151
+ }
26152
+ function describeOutcome2(outcome) {
26153
+ switch (outcome.kind) {
26154
+ case "disabled":
26155
+ return null;
26156
+ case "consent-required":
26157
+ return "failproofai: scheduled scans are on, but emailing what they find needs a fresh opt-in — run `failproofai audit --schedule` (or visit /settings) to turn digests on";
26158
+ case "signed-out":
26159
+ return "failproofai: emailed reports are on but this machine is signed out — run `failproofai audit --schedule` or visit /settings to resume them";
26160
+ case "sent":
26161
+ return `failproofai: emailed a harm digest (${outcome.hits} finding${outcome.hits === 1 ? "" : "s"})`;
26162
+ case "held":
26163
+ return `failproofai: ${outcome.hits} finding${outcome.hits === 1 ? "" : "s"} reported, no email (${outcome.reason})`;
26164
+ case "failed":
26165
+ return `failproofai: could not send the harm report: ${outcome.error}`;
26166
+ }
26167
+ }
26168
+ var init_report_harm = __esm(() => {
26169
+ init_auth_store();
26170
+ init_api_server_client();
26171
+ init_fp_config();
26172
+ init_harm_report();
26173
+ init_machine_store();
26174
+ });
26175
+
26176
+ // src/audit/audit-schedule.ts
26177
+ import { readFileSync as readFileSync29 } from "node:fs";
26178
+ function readAuditSchedule(home) {
26179
+ let raw;
26180
+ try {
26181
+ raw = readFileSync29(auditScheduleFile(home), "utf8");
26182
+ } catch {
26183
+ return null;
26184
+ }
26185
+ let parsed;
26186
+ try {
26187
+ parsed = JSON.parse(raw);
26188
+ } catch {
26189
+ return null;
26190
+ }
26191
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
26192
+ return null;
26193
+ const obj = parsed;
26194
+ const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
26195
+ const schema = num(obj.schema);
26196
+ return {
26197
+ schema,
26198
+ nextDueAtMs: num(obj.next_due_at_ms),
26199
+ lastAttemptAtMs: num(obj.last_attempt_at_ms),
26200
+ lastRunAtMs: num(obj.last_run_at_ms),
26201
+ lastExitCode: num(obj.last_exit_code),
26202
+ schemaAhead: schema !== null && schema > AUDIT_SCHEDULE_SCHEMA
26203
+ };
26204
+ }
26205
+ var AUDIT_SCHEDULE_SCHEMA = 1;
26206
+ var init_audit_schedule = __esm(() => {
26207
+ init_fp_home();
26208
+ });
26209
+
26210
+ // src/audit/cli-login.ts
26211
+ var exports_cli_login = {};
26212
+ __export(exports_cli_login, {
26213
+ runLogin: () => runLogin,
26214
+ invalidEmail: () => invalidEmail,
26215
+ extractCode: () => extractCode,
26216
+ ensureSignedIn: () => ensureSignedIn,
26217
+ canPrompt: () => canPrompt,
26218
+ LoginError: () => LoginError
26219
+ });
26220
+ function spine() {
26221
+ return colorsEnabled(process.stdout) ? `${ANSI_DIM_BAR}${BAR}${ANSI_RESET} ` : "";
26222
+ }
26223
+ function extractCode(raw) {
26224
+ const trimmed = raw.trim();
26225
+ if (/^\d+$/.test(trimmed))
26226
+ return trimmed;
26227
+ const runs = trimmed.match(/\d+/g) ?? [];
26228
+ if (runs.length === 0)
26229
+ return trimmed;
26230
+ const whole = runs.find((run) => run.length >= CODE_MIN);
26231
+ if (whole)
26232
+ return whole;
26233
+ return runs.join("");
26234
+ }
26235
+ function canPrompt() {
26236
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
26237
+ }
26238
+ function sessionStillValid(auth) {
26239
+ if (!auth)
26240
+ return null;
26241
+ return auth.refresh_expires_at * 1000 > Date.now() ? auth : null;
26242
+ }
26243
+ async function ensureSignedIn(preset) {
26244
+ const existing = sessionStillValid(readAuth());
26245
+ if (existing) {
26246
+ if (preset && !sameAddress(preset, existing.user.email)) {
26247
+ throw new LoginError(`This machine is already signed in as ${existing.user.email}.
26248
+ ` + `To report as ${preset.trim().toLowerCase()} instead, sign out first — ` + `from the dashboard, or by removing ~/.failproofai/audit/session.json.`);
26249
+ }
26250
+ return { user: existing.user, prompted: false };
26251
+ }
26252
+ if (!canPrompt()) {
26253
+ throw new LoginError(`Signing in needs an interactive terminal, and this one is not.
26254
+ ` + "Run `failproofai audit --schedule` from a shell you are sitting at,\n" + "or sign in from the dashboard — both write the same session file.");
26255
+ }
26256
+ return { user: await runLogin(preset), prompted: true };
26257
+ }
26258
+ function sameAddress(a, b) {
26259
+ return a.trim().toLowerCase() === b.trim().toLowerCase();
26260
+ }
26261
+ function invalidEmail(address) {
26262
+ return EMAIL_RE.test(address.trim()) ? null : `\`--email\` needs an email address (got: ${address}).`;
26263
+ }
26264
+ async function runLogin(preset) {
26265
+ intro("scheduled audits need somewhere to send the report");
26266
+ let address;
26267
+ if (preset) {
26268
+ address = preset.trim().toLowerCase();
26269
+ step("your email", address);
26270
+ } else {
26271
+ const email = await promptText({
26272
+ prefix: spine(),
26273
+ message: "your email",
26274
+ hint: "you@yourdomain.com",
26275
+ validate: (v) => EMAIL_RE.test(v.trim()) ? null : "that doesn't look like an email"
26276
+ });
26277
+ if (email === null) {
26278
+ outro("Cancelled — nothing was changed.", { ok: false });
26279
+ throw new LoginError("Cancelled.");
26280
+ }
26281
+ address = email.trim().toLowerCase();
26282
+ }
26283
+ let expiresInMin = 10;
26284
+ try {
26285
+ const sent = await requestLoginCode(address);
26286
+ expiresInMin = Math.max(1, Math.ceil(sent.expires_in / 60));
26287
+ } catch (err) {
26288
+ outro("Could not send a login code.", { ok: false });
26289
+ throw new LoginError(describeAuthError(err, "Could not send a login code"));
26290
+ }
26291
+ step("code sent", `to ${address} · expires in ${expiresInMin} min`);
26292
+ const ATTEMPTS = 3;
26293
+ for (let attempt = 1;attempt <= ATTEMPTS; attempt += 1) {
26294
+ stepOpen(attempt === 1 ? "the code from that email" : "try that code again");
26295
+ const typed = await promptText({
26296
+ prefix: spine(),
26297
+ message: "code",
26298
+ hint: attempt === 1 ? "123456 · paste the whole line if you like" : `attempt ${attempt} of ${ATTEMPTS}`,
26299
+ validate: (v) => {
26300
+ if (!/\d/.test(v))
26301
+ return "a code is digits — paste the line from the email";
26302
+ const code = extractCode(v);
26303
+ if (code.length < CODE_MIN)
26304
+ return "that looks too short to be the code";
26305
+ if (code.length > CODE_MAX)
26306
+ return "that looks too long — paste just the code";
26307
+ return null;
26308
+ }
26309
+ });
26310
+ if (typed === null) {
26311
+ outro("Cancelled — nothing was changed.", { ok: false });
26312
+ throw new LoginError("Cancelled.");
26313
+ }
26314
+ try {
26315
+ const tokens = await verifyLoginCode(address, extractCode(typed));
26316
+ writeAuth(authFromTokenResponse(tokens));
26317
+ step("signed in", tokens.user.email);
26318
+ return { id: tokens.user.id, email: tokens.user.email };
26319
+ } catch (err) {
26320
+ const wrongCode = err instanceof AuthApiError && err.code === "invalid_code";
26321
+ if (wrongCode && attempt < ATTEMPTS) {
26322
+ step("that code was wrong or expired", `${ATTEMPTS - attempt} more ${ATTEMPTS - attempt === 1 ? "try" : "tries"} before it asks for a new one`);
26323
+ continue;
26324
+ }
26325
+ outro("Could not verify that code.", { ok: false });
26326
+ throw new LoginError(describeAuthError(err, "Could not verify that code"));
26327
+ }
26328
+ }
26329
+ outro("Too many wrong codes.", { ok: false });
26330
+ throw new LoginError("Too many wrong codes. Run the command again for a fresh one.");
26331
+ }
26332
+ function describeAuthError(err, prefix) {
26333
+ if (err instanceof AuthApiError) {
26334
+ if (err.code === "rate_limited") {
26335
+ const wait = err.retryAfterSecs;
26336
+ return `${prefix}: too many attempts.${wait ? ` Try again in ${wait}s.` : ""}`;
26337
+ }
26338
+ if (err.status === 0 || err.code === "timeout") {
26339
+ return `${prefix}: the api-server did not respond.
26340
+ ` + `It is at ${apiBaseForMessage()} — check that it is running,
26341
+ ` + `or set FAILPROOF_API_URL to point somewhere else.`;
26342
+ }
26343
+ return `${prefix}: ${err.message}`;
26344
+ }
26345
+ return `${prefix}: ${err instanceof Error ? err.message : String(err)}`;
26346
+ }
26347
+ function apiBaseForMessage() {
26348
+ return process.env.FAILPROOF_API_URL ?? process.env.FAILPROOFAI_API_URL ?? "https://api.befailproof.ai";
26349
+ }
26350
+ var EMAIL_RE, CODE_MIN = 4, CODE_MAX = 12, ANSI_DIM_BAR = "\x1B[2m", LoginError;
26351
+ var init_cli_login = __esm(() => {
26352
+ init_api_server_client();
26353
+ init_auth_store();
26354
+ init_tui();
26355
+ EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
26356
+ LoginError = class LoginError extends Error {
26357
+ };
26358
+ });
26359
+
26360
+ // src/audit/schedule-cli.ts
26361
+ var exports_schedule_cli = {};
26362
+ __export(exports_schedule_cli, {
26363
+ runScheduleStatus: () => runScheduleStatus,
26364
+ runScheduleOn: () => runScheduleOn,
26365
+ runScheduleOff: () => runScheduleOff,
26366
+ asScheduleError: () => asScheduleError,
26367
+ ScheduleCliError: () => ScheduleCliError
26368
+ });
26369
+ function row(label, value, note) {
26370
+ const gap = " ".repeat(Math.max(1, LABEL_WIDTH - label.length));
26371
+ return `${rail()} ${dim(label)}${gap}${value}${note ? ` ${dim(note)}` : ""}`;
26372
+ }
26373
+ async function runScheduleOn(daysArg, emailArg) {
26374
+ let days;
26375
+ if (daysArg !== undefined) {
26376
+ const parsed = Number(daysArg);
26377
+ if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) {
26378
+ throw new ScheduleCliError(`\`--schedule\` takes a whole number of days (got: ${daysArg}).`);
26379
+ }
26380
+ if (parsed < MIN_DAYS || parsed > MAX_DAYS) {
26381
+ throw new ScheduleCliError(`\`--schedule\` must be between ${MIN_DAYS} and ${MAX_DAYS} days (got: ${parsed}).`);
26382
+ }
26383
+ days = parsed;
26384
+ }
26385
+ if (emailArg !== undefined) {
26386
+ const bad = invalidEmail(emailArg);
26387
+ if (bad)
26388
+ throw new ScheduleCliError(bad);
26389
+ }
26390
+ const { user, prompted } = await ensureSignedIn(emailArg);
26391
+ const next = updateConfig({
26392
+ audit: {
26393
+ auto: true,
26394
+ reportsConsentedAt: Date.now(),
26395
+ ...days !== undefined ? { intervalDays: days } : {}
26396
+ }
26397
+ });
26398
+ const interval = next.audit.intervalDays;
26399
+ const summary = [
26400
+ `every ${interval} day${interval === 1 ? "" : "s"} · reports to ${user.email}`,
26401
+ "you only hear from it when a scan finds something harmful",
26402
+ "each report sends: finding counts, redacted example commands, this machine's name"
26403
+ ];
26404
+ if (prompted) {
26405
+ step("scheduled audits are on", summary);
26406
+ } else {
26407
+ process.stdout.write(`
26408
+ ${green("✓")} ${bold("scheduled audits are on")}
26409
+ ` + summary.map((r) => ` ${dim(r)}
26410
+ `).join(""));
26411
+ }
26412
+ warnIfDaemonWontRun();
26413
+ if (prompted) {
26414
+ outro("failproofai audit --status · when the next scan is due");
26415
+ } else {
26416
+ process.stdout.write(dim(`
26417
+ failproofai audit --status when the next scan is due
26418
+
26419
+ `));
26420
+ }
26421
+ }
26422
+ function runScheduleOff() {
26423
+ const before = readConfig().audit.auto;
26424
+ const next = updateConfig({ audit: { auto: false } });
26425
+ if (next.audit.auto) {
26426
+ process.stdout.write(`Could not turn scheduled audits off.
26427
+ `);
26428
+ return;
26429
+ }
26430
+ if (!before) {
26431
+ process.stdout.write(dim(`
26432
+ Scheduled audits were already off.
26433
+
26434
+ `));
26435
+ return;
26436
+ }
26437
+ process.stdout.write(`
26438
+ ${green("✓")} ${bold("scheduled audits are off")}
26439
+ ` + ` ${dim("nothing runs on a timer and nothing is sent")}
26440
+ ` + ` ${dim("your session is untouched — sign out from the dashboard for that")}
26441
+
26442
+ `);
26443
+ }
26444
+ function runScheduleStatus() {
26445
+ const config = readConfig();
26446
+ const auth = readAuth();
26447
+ const sched = readAuditSchedule();
26448
+ const meta = readDashboardCacheMeta();
26449
+ const machine = readMachineIdentity();
26450
+ const daemon = daemonServiceStatus();
26451
+ const on = config.audit.auto;
26452
+ const out = [""];
26453
+ out.push(`${rail()} ${bold("scheduled audit")} ${on ? green("on") : dim("off")}` + (on ? dim(` every ${config.audit.intervalDays} days`) : ""));
26454
+ out.push(rail());
26455
+ const live = auth && auth.refresh_expires_at * 1000 > Date.now() ? auth : null;
26456
+ out.push(row("reports to", live ? live.user.email : dim("— signed out")));
26457
+ if (on && !live) {
26458
+ out.push(row("", pink("scans continue; digests are paused until you sign in")));
26459
+ } else if (on && config.audit.reportsConsentedAt === undefined) {
26460
+ out.push(row("", pink("scans continue; digests need a fresh opt-in — run `--schedule` to turn them on")));
26461
+ }
26462
+ out.push(row("daemon", describeDaemon(daemon)));
26463
+ if (sched?.nextDueAtMs != null && on) {
26464
+ out.push(row("next scan", untilPhrase(sched.nextDueAtMs)));
26465
+ }
26466
+ if (sched?.lastRunAtMs != null) {
26467
+ const exit = sched.lastExitCode;
26468
+ out.push(row("last scheduled", agoPhrase(sched.lastRunAtMs), exit != null && exit !== 0 && exit !== 75 ? pink(`exit ${exit}`) : undefined));
26469
+ }
26470
+ out.push(row("last result", meta?.cachedAt ? agoPhrase(Date.parse(meta.cachedAt)) : dim("none yet")));
26471
+ if (machine?.last_reported_at) {
26472
+ out.push(row("last reported", agoPhrase(Date.parse(machine.last_reported_at))));
26473
+ }
26474
+ out.push("");
26475
+ process.stdout.write(out.join(`
26476
+ `));
26477
+ }
26478
+ function describeDaemon(status) {
26479
+ switch (status) {
26480
+ case "running":
26481
+ return green("running");
26482
+ case "stopped":
26483
+ return pink("stopped — run `failproofai config` to repair it");
26484
+ case "not-installed":
26485
+ return pink("not installed — run `failproofai config`");
26486
+ case "condition-failed":
26487
+ return pink("installed but its binary is missing — run `failproofai config`");
26488
+ default:
26489
+ return isDaemonSupportedPlatform() ? dim(String(status)) : dim("unavailable on this platform");
26490
+ }
26491
+ }
26492
+ function warnIfDaemonWontRun() {
26493
+ const status = daemonServiceStatus();
26494
+ if (status === "running")
26495
+ return;
26496
+ if (status === "unknown")
26497
+ return;
26498
+ if (!isDaemonSupportedPlatform()) {
26499
+ process.stderr.write(`
26500
+ ${pink("!")} The background service is not available on this platform,
26501
+ ` + ` so nothing will run on the timer here. \`failproofai audit\` still works.
26502
+ `);
26503
+ return;
26504
+ }
26505
+ process.stderr.write(`
26506
+ ${pink("!")} The background service is ${status.replace("-", " ")}, ` + `so nothing will run on the timer yet.
26507
+ ` + ` Run \`failproofai config\` to install or repair it.
26508
+ `);
26509
+ }
26510
+ function untilPhrase(ms) {
26511
+ const diff = ms - Date.now();
26512
+ if (diff <= 0)
26513
+ return "due now";
26514
+ const d = Math.floor(diff / 86400000);
26515
+ const h = Math.floor(diff % 86400000 / 3600000);
26516
+ if (d > 0)
26517
+ return `in ${d}d${h > 0 ? ` ${h}h` : ""}`;
26518
+ if (h > 0)
26519
+ return `in ${h}h`;
26520
+ return `in ${Math.max(1, Math.floor(diff / 60000))}m`;
26521
+ }
26522
+ function agoPhrase(ms) {
26523
+ if (!Number.isFinite(ms))
26524
+ return "unknown";
26525
+ const diff = Date.now() - ms;
26526
+ if (diff < 0)
26527
+ return "just now";
26528
+ const d = Math.floor(diff / 86400000);
26529
+ const h = Math.floor(diff / 3600000);
26530
+ const m = Math.floor(diff / 60000);
26531
+ if (d > 0)
26532
+ return `${d}d ago`;
26533
+ if (h > 0)
26534
+ return `${h}h ago`;
26535
+ if (m > 0)
26536
+ return `${m}m ago`;
26537
+ return "just now";
26538
+ }
26539
+ function asScheduleError(err) {
26540
+ if (err instanceof LoginError || err instanceof ScheduleCliError) {
26541
+ throw new ScheduleCliError(err.message);
26542
+ }
26543
+ throw err;
26544
+ }
26545
+ var MIN_DAYS = 1, MAX_DAYS = 90, ScheduleCliError, styled = () => colorsEnabled(process.stdout), wrap = (open2, s) => styled() ? `${open2}${s}${ANSI_RESET}` : s, pink = (s) => wrap(brandAnsi("pink"), s), green = (s) => wrap(brandAnsi("guide"), s), dim = (s) => wrap(ANSI_DIM, s), bold = (s) => wrap(ANSI_BOLD, s), rail = () => " ", LABEL_WIDTH = 15;
26546
+ var init_schedule_cli = __esm(() => {
26547
+ init_fp_config();
26548
+ init_daemon_service();
26549
+ init_auth_store();
26550
+ init_audit_schedule();
26551
+ init_dashboard_cache();
26552
+ init_machine_store();
26553
+ init_cli_login();
26554
+ init_tui();
26555
+ ScheduleCliError = class ScheduleCliError extends Error {
26556
+ };
26557
+ });
26558
+
26559
+ // scripts/parse-script-args.ts
26560
+ import { resolve as resolve23 } from "path";
26561
+ function parseStringFlag(flagName, errorLabel, inlineValue, args, index, options) {
26562
+ const raw = inlineValue ?? args[index + 1];
26563
+ if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
26564
+ console.error(`Error: ${flagName} requires ${errorLabel}`);
26565
+ process.exit(1);
26566
+ }
26567
+ const value = options?.resolve ? resolve23(raw) : raw;
26568
+ return { value, spliceCount: inlineValue !== null ? 1 : 2 };
26569
+ }
26570
+ function parseScriptArgs(argv) {
26571
+ const args = [...argv];
26572
+ let loggingLevel;
26573
+ let disableTelemetry = false;
26574
+ let allowedDevOrigins;
26575
+ let host;
26576
+ for (let i = 0;i < args.length; i++) {
26577
+ const arg = args[i];
26578
+ const eqIdx = arg.indexOf("=");
26579
+ const flag = eqIdx >= 0 ? arg.slice(0, eqIdx) : arg;
26580
+ const inlineValue = eqIdx >= 0 ? arg.slice(eqIdx + 1) : null;
26581
+ if (flag === "--logging") {
26582
+ const raw = inlineValue ?? args[i + 1];
26583
+ if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
26584
+ console.error("Error: --logging requires a level (info, warn, error)");
26585
+ process.exit(1);
26586
+ }
26587
+ const val = raw.toLowerCase();
26588
+ if (val !== "info" && val !== "warn" && val !== "error") {
26589
+ console.error("Error: --logging must be one of: info, warn, error");
26590
+ process.exit(1);
26591
+ }
26592
+ loggingLevel = val;
26593
+ args.splice(i, inlineValue !== null ? 1 : 2);
26594
+ i--;
26595
+ continue;
26596
+ }
26597
+ if (flag === "--disable-telemetry") {
26598
+ disableTelemetry = true;
26599
+ args.splice(i, 1);
26600
+ i--;
26601
+ continue;
26602
+ }
26603
+ if (flag === "--allowed-origins") {
26604
+ const { value, spliceCount } = parseStringFlag(flag, "a comma-separated list of origins", inlineValue, args, i);
26605
+ allowedDevOrigins = value.split(",").map((s) => s.trim()).filter(Boolean);
26606
+ args.splice(i, spliceCount);
26607
+ i--;
26608
+ continue;
26609
+ }
26610
+ if (flag === "--host" || flag === "-H" || flag === "--hostname") {
26611
+ const { value, spliceCount } = parseStringFlag(flag, "an address to bind (e.g. 127.0.0.1)", inlineValue, args, i);
26612
+ host = value;
26613
+ args.splice(i, spliceCount);
26614
+ i--;
26615
+ continue;
26616
+ }
26617
+ }
26618
+ return { loggingLevel, disableTelemetry, allowedDevOrigins, host, remainingArgs: args };
26619
+ }
26620
+ var init_parse_script_args = () => {};
26621
+
26622
+ // scripts/install-diagnosis.mjs
26623
+ import { existsSync as existsSync34, readFileSync as readFileSync30, realpathSync as realpathSync3 } from "node:fs";
26624
+ import { dirname as dirname16, resolve as resolve24 } from "node:path";
26625
+ import { homedir as homedir26, platform as platform4 } from "node:os";
26626
+ import { spawnSync } from "node:child_process";
26627
+ function findPackageRoot(start) {
26628
+ try {
26629
+ let dir = realpathSync3(start);
26630
+ if (existsSync34(dir) && !existsSync34(resolve24(dir, "package.json"))) {
26631
+ dir = dirname16(dir);
26632
+ }
26633
+ while (true) {
26634
+ const pkgPath = resolve24(dir, "package.json");
26635
+ if (existsSync34(pkgPath)) {
26636
+ try {
26637
+ const pkg = JSON.parse(readFileSync30(pkgPath, "utf8"));
26638
+ if (pkg.name === PKG_NAME)
26639
+ return dir;
26640
+ } catch {}
26641
+ }
26642
+ const parent = dirname16(dir);
26643
+ if (parent === dir)
26644
+ return null;
26645
+ dir = parent;
26646
+ }
26647
+ } catch {
26648
+ return null;
26649
+ }
26650
+ }
26651
+ function readPackageVersion(packageRoot) {
26652
+ if (!packageRoot)
26653
+ return null;
26654
+ try {
26655
+ const pkg = JSON.parse(readFileSync30(resolve24(packageRoot, "package.json"), "utf8"));
26656
+ return typeof pkg.version === "string" ? pkg.version : null;
26657
+ } catch {
26658
+ return null;
26659
+ }
26660
+ }
26661
+ function resolvePathFirstBinary() {
26662
+ try {
26663
+ const isWin = platform4() === "win32";
26664
+ const res = isWin ? spawnSync("where", [PKG_NAME], { encoding: "utf8" }) : spawnSync("sh", ["-c", `command -v ${PKG_NAME}`], { encoding: "utf8" });
26665
+ if (res.status !== 0)
26666
+ return null;
26667
+ const first = (res.stdout || "").split(/\r?\n/).find((l) => l.trim().length > 0);
26668
+ return first ? first.trim() : null;
26669
+ } catch {
26670
+ return null;
26671
+ }
26672
+ }
26673
+ function locateNpmGlobal() {
26674
+ try {
26675
+ const res = spawnSync("npm", ["root", "-g"], { encoding: "utf8" });
26676
+ if (res.status !== 0)
26677
+ return null;
26678
+ const root = (res.stdout || "").trim();
26679
+ if (!root)
26680
+ return null;
26681
+ const candidate = resolve24(root, PKG_NAME);
26682
+ return existsSync34(resolve24(candidate, "package.json")) ? candidate : null;
26683
+ } catch {
26684
+ return null;
26685
+ }
26686
+ }
26687
+ function locateBunGlobal() {
26688
+ try {
26689
+ const candidate = resolve24(homedir26(), ".bun", "install", "global", "node_modules", PKG_NAME);
26690
+ return existsSync34(resolve24(candidate, "package.json")) ? candidate : null;
26691
+ } catch {
26692
+ return null;
26693
+ }
26694
+ }
26695
+ function buildRecommendation(pathFirstBin) {
26696
+ if (!pathFirstBin)
26697
+ return null;
26698
+ const bunBinPrefix = resolve24(homedir26(), ".bun", "bin") + "/";
26699
+ const bunGlobalPrefix = resolve24(homedir26(), ".bun", "install", "global") + "/";
26700
+ const isBun = pathFirstBin.startsWith(bunBinPrefix) || pathFirstBin.startsWith(bunGlobalPrefix);
26701
+ if (isBun) {
26702
+ return `rm -f ~/.bun/bin/${PKG_NAME} && rm -rf ~/.bun/install/global/node_modules/${PKG_NAME}`;
26703
+ }
26704
+ return `npm rm -g ${PKG_NAME}`;
26705
+ }
26706
+ function diagnoseShadow(self) {
26707
+ const selfPackageRoot = (() => {
26708
+ try {
26709
+ return self?.selfPackageRoot ? realpathSync3(self.selfPackageRoot) : null;
26710
+ } catch {
26711
+ return self?.selfPackageRoot ?? null;
26712
+ }
25591
26713
  })();
25592
26714
  const selfVersion = self?.selfVersion ?? null;
25593
26715
  const pathFirstBin = resolvePathFirstBinary();
@@ -25649,8 +26771,8 @@ function makeSkewLogFilter() {
25649
26771
  }
25650
26772
 
25651
26773
  // lib/dashboard-host.ts
25652
- function isLoopbackHostname(hostname5) {
25653
- const h = hostname5.trim().toLowerCase();
26774
+ function isLoopbackHostname(hostname6) {
26775
+ const h = hostname6.trim().toLowerCase();
25654
26776
  if (!h)
25655
26777
  return false;
25656
26778
  if (LOOPBACK_HOSTNAMES.has(h))
@@ -25672,8 +26794,8 @@ __export(exports_launch, {
25672
26794
  launch: () => launch
25673
26795
  });
25674
26796
  import { spawn as spawn2 } from "child_process";
25675
- import { realpathSync as realpathSync4, existsSync as existsSync32 } from "node:fs";
25676
- import { resolve as resolve24, dirname as dirname17 } from "node:path";
26797
+ import { realpathSync as realpathSync4, existsSync as existsSync35 } from "node:fs";
26798
+ import { resolve as resolve25, dirname as dirname17 } from "node:path";
25677
26799
  import { fileURLToPath as fileURLToPath2 } from "node:url";
25678
26800
  import { createInterface } from "node:readline";
25679
26801
  function launch(mode) {
@@ -25697,9 +26819,9 @@ function launch(mode) {
25697
26819
  process.env.PORT = port;
25698
26820
  process.env.HOSTNAME = bindHost;
25699
26821
  cmd = "node";
25700
- const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve24(dirname17(realpathSync4(fileURLToPath2(import.meta.url))), "..");
25701
- const serverJsPath = resolve24(packageRoot, ".next/standalone/server.js");
25702
- if (!existsSync32(serverJsPath)) {
26822
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve25(dirname17(realpathSync4(fileURLToPath2(import.meta.url))), "..");
26823
+ const serverJsPath = resolve25(packageRoot, ".next/standalone/server.js");
26824
+ if (!existsSync35(serverJsPath)) {
25703
26825
  let shadowMessage = null;
25704
26826
  try {
25705
26827
  const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
@@ -25783,10 +26905,43 @@ __export(exports_cli, {
25783
26905
  runScheduledAudit: () => runScheduledAudit,
25784
26906
  runPostSetupAudit: () => runPostSetupAudit,
25785
26907
  runAuditCli: () => runAuditCli,
26908
+ helpText: () => helpText,
25786
26909
  buildSummary: () => buildSummary,
25787
26910
  EXIT_AUDIT_ALREADY_RUNNING: () => EXIT_AUDIT_ALREADY_RUNNING,
25788
26911
  AUDIT_STAGES: () => AUDIT_STAGES
25789
26912
  });
26913
+ function helpText() {
26914
+ const row2 = (command, lines) => {
26915
+ const pad = " ".repeat(Math.max(1, HELP_DESC_COL - 2 - command.length));
26916
+ return lines.map((line, i) => i === 0 ? ` ${c(CYAN, command)}${pad}${line}` : `${" ".repeat(HELP_DESC_COL)}${line}`).join(`
26917
+ `);
26918
+ };
26919
+ return [
26920
+ `${c(BOLD, "failproofai audit")} — review your agent CLIs for risky and wasteful patterns.`,
26921
+ c(DIM, "Everything runs on this machine; only a scheduled digest ever leaves it."),
26922
+ "",
26923
+ c(BOLD, "USAGE"),
26924
+ row2("failproofai audit", [
26925
+ "Scan your session history, then open",
26926
+ `http://localhost:${DASHBOARD_PORT}/audit`
26927
+ ]),
26928
+ row2("failproofai audit --schedule [days]", [
26929
+ "Scan on a timer and email the findings.",
26930
+ "Default 7 days, range 1-90.",
26931
+ "Signs you in the first time; add",
26932
+ "--email <address> to skip a prompt."
26933
+ ]),
26934
+ row2("failproofai audit --no-schedule", ["Stop the timer. Leaves you signed in."]),
26935
+ row2("failproofai audit --status", [
26936
+ "Whether scheduling is on, where reports",
26937
+ "go, the daemon's state, and when the",
26938
+ "next scan is due."
26939
+ ]),
26940
+ row2("failproofai audit -h, --help", ["Show this help."]),
26941
+ ""
26942
+ ].join(`
26943
+ `);
26944
+ }
25790
26945
  function colorOn() {
25791
26946
  if (process.env.NO_COLOR && process.env.NO_COLOR !== "")
25792
26947
  return false;
@@ -25961,6 +27116,13 @@ async function runScheduledAudit() {
25961
27116
  }
25962
27117
  process.stdout.write(`failproofai: audit complete — ${num(result.eventsScanned)} tool calls across ` + `${num(result.transcripts.scanned)} sessions, ${num(result.totals.hits)} hits
25963
27118
  `);
27119
+ const outcome = await reportHarm(result);
27120
+ const line = describeOutcome2(outcome);
27121
+ if (line) {
27122
+ const stream = outcome.kind === "sent" ? process.stdout : process.stderr;
27123
+ stream.write(`${line}
27124
+ `);
27125
+ }
25964
27126
  return 0;
25965
27127
  } finally {
25966
27128
  attempt.lock.release();
@@ -26016,7 +27178,7 @@ async function runPostSetupAudit() {
26016
27178
  }
26017
27179
  async function runAuditCli(args) {
26018
27180
  if (args.includes("--help") || args.includes("-h")) {
26019
- process.stdout.write(HELP);
27181
+ process.stdout.write(helpText());
26020
27182
  process.exit(0);
26021
27183
  }
26022
27184
  if (args.includes("--scheduled")) {
@@ -26025,15 +27187,71 @@ async function runAuditCli(args) {
26025
27187
  die(`\`audit --scheduled\` takes no other arguments (got: ${extra}).`);
26026
27188
  process.exit(await runScheduledAudit());
26027
27189
  }
27190
+ if (args.includes("--status")) {
27191
+ const extra = args.find((a) => a !== "--status");
27192
+ if (extra)
27193
+ die(`\`audit --status\` takes no other arguments (got: ${extra}).`);
27194
+ const { runScheduleStatus: runScheduleStatus2 } = await Promise.resolve().then(() => (init_schedule_cli(), exports_schedule_cli));
27195
+ runScheduleStatus2();
27196
+ process.exit(0);
27197
+ }
27198
+ if (args.includes("--no-schedule")) {
27199
+ const extra = args.find((a) => a !== "--no-schedule");
27200
+ if (extra)
27201
+ die(`\`audit --no-schedule\` takes no other arguments (got: ${extra}).`);
27202
+ const { runScheduleOff: runScheduleOff2 } = await Promise.resolve().then(() => (init_schedule_cli(), exports_schedule_cli));
27203
+ runScheduleOff2();
27204
+ process.exit(0);
27205
+ }
27206
+ const scheduleAt = args.indexOf("--schedule");
27207
+ if (scheduleAt !== -1) {
27208
+ let days;
27209
+ let email;
27210
+ for (let i = 0;i < args.length; i += 1) {
27211
+ const a = args[i];
27212
+ if (a === "--schedule") {
27213
+ const next = args[i + 1];
27214
+ if (next !== undefined && !next.startsWith("-")) {
27215
+ days = next;
27216
+ i += 1;
27217
+ }
27218
+ continue;
27219
+ }
27220
+ if (a === "--email" || a.startsWith("--email=")) {
27221
+ if (a.startsWith("--email=")) {
27222
+ email = a.slice("--email=".length);
27223
+ } else {
27224
+ email = args[i + 1];
27225
+ i += 1;
27226
+ }
27227
+ if (email === undefined || email.length === 0 || email.startsWith("-")) {
27228
+ die("`--email` needs an address, e.g. `--email you@yourdomain.com`.");
27229
+ }
27230
+ continue;
27231
+ }
27232
+ die(`\`audit --schedule\` does not take ${a}.`);
27233
+ }
27234
+ const { runScheduleOn: runScheduleOn2, ScheduleCliError: ScheduleCliError2 } = await Promise.resolve().then(() => (init_schedule_cli(), exports_schedule_cli));
27235
+ const { LoginError: LoginError2 } = await Promise.resolve().then(() => (init_cli_login(), exports_cli_login));
27236
+ try {
27237
+ await runScheduleOn2(days, email);
27238
+ } catch (err) {
27239
+ if (err instanceof ScheduleCliError2 || err instanceof LoginError2)
27240
+ die(err.message);
27241
+ throw err;
27242
+ }
27243
+ process.exit(0);
27244
+ }
26028
27245
  const stray = args.find((a) => a !== "--help" && a !== "-h");
26029
27246
  if (stray) {
26030
- die(`\`audit\` takes no arguments yet (got: ${stray}).
26031
- ` + `Run \`failproofai audit\` to scan your history and open the dashboard.`);
27247
+ die(`\`audit\` does not take ${stray}.
27248
+ Run \`failproofai audit\` to scan your history and open the dashboard,
27249
+ or \`failproofai audit --help\` for the scheduling commands.`);
26032
27250
  }
26033
27251
  const attempt = acquireAuditLock("cli");
26034
27252
  if (!attempt.ok) {
26035
27253
  process.stderr.write(`Error: ${heldByLine(attempt.heldBy)}.
26036
- ` + `Two audits write the same cache files, so this one won't start. Try again shortly.
27254
+ Two audits write the same cache files, so this one won't start. Try again shortly.
26037
27255
  `);
26038
27256
  process.exit(EXIT_AUDIT_ALREADY_RUNNING);
26039
27257
  }
@@ -26057,7 +27275,7 @@ async function runAuditCli(args) {
26057
27275
  if (result.eventsScanned === 0) {
26058
27276
  process.stdout.write(`
26059
27277
  ${c(DIM, "no agent sessions found yet.")}
26060
- ` + ` install hooks with ${c(CYAN, "failproofai policies --install")} ` + `${c(DIM, "and come back after using your agent.")}
27278
+ install hooks with ${c(CYAN, "failproofai policies --install")} ${c(DIM, "and come back after using your agent.")}
26061
27279
 
26062
27280
  `);
26063
27281
  process.exit(0);
@@ -26072,15 +27290,15 @@ async function runAuditCli(args) {
26072
27290
  const url = `http://localhost:${DASHBOARD_PORT}/audit`;
26073
27291
  process.stdout.write(`
26074
27292
  ${c(DIM, "starting the dashboard…")}
26075
- ` + ` ${c(PINK, "✦")} ${c(BOLD, "here's your audit")} ${c(DIM, "→")} ${c(CYAN, url)}
26076
- ` + ` ${c(DIM, "(opening in your browser — press Ctrl+C to stop the server)")}
27293
+ ${c(PINK, "✦")} ${c(BOLD, "here's your audit")} ${c(DIM, "→")} ${c(CYAN, url)}
27294
+ ${c(DIM, "(opening in your browser — press Ctrl+C to stop the server)")}
26077
27295
 
26078
27296
  `);
26079
27297
  openWhenReady(DASHBOARD_PORT, "/audit");
26080
27298
  const { launch: launch2 } = await Promise.resolve().then(() => (init_launch(), exports_launch));
26081
27299
  launch2("start");
26082
27300
  }
26083
- var DASHBOARD_PORT = 8020, EXIT_AUDIT_ALREADY_RUNNING = 75, AUDIT_STAGES, HELP, RESET, DIM, BOLD, PINK, GREEN, CYAN, SPINNER;
27301
+ var DASHBOARD_PORT = 8020, EXIT_AUDIT_ALREADY_RUNNING = 75, AUDIT_STAGES, HELP_DESC_COL = 40, RESET, DIM, BOLD, PINK, GREEN, CYAN, SPINNER;
26084
27302
  var init_cli = __esm(() => {
26085
27303
  init_audit();
26086
27304
  init_audit_lock();
@@ -26089,6 +27307,7 @@ var init_cli = __esm(() => {
26089
27307
  init_telemetry_id2();
26090
27308
  init_telemetry_sanitize();
26091
27309
  init_open_browser();
27310
+ init_report_harm();
26092
27311
  init_tui();
26093
27312
  AUDIT_STAGES = [
26094
27313
  { label: "discovering transcripts", detail: "walking ~/.claude, ~/.codex, ~/.cursor, …" },
@@ -26096,29 +27315,6 @@ var init_cli = __esm(() => {
26096
27315
  { label: "running policy checks", detail: "replaying through 30 builtin policies" },
26097
27316
  { label: "aggregating results", detail: "counting hits, ranking by frequency" }
26098
27317
  ];
26099
- HELP = `
26100
- failproofai audit — audit your AI agent's behavior, then open the dashboard
26101
-
26102
- USAGE
26103
- failproofai audit Scan your agent-CLI session history for risky and
26104
- wasteful patterns, then open the audit dashboard.
26105
- failproofai audit --help Show this help.
26106
-
26107
- failproofai audit --scheduled
26108
- Headless run: scan, refresh the dashboard's cached
26109
- result, print one line, exit. No browser, no
26110
- server. This is what a scheduled audit runs; exit
26111
- 75 means another audit already had the lock.
26112
-
26113
- WHAT IT DOES
26114
- 1. Scans past sessions from every installed agent CLI (Claude, Codex, Cursor,
26115
- Copilot, OpenCode, Pi) — entirely on your machine.
26116
- 2. Starts the local dashboard and opens
26117
- http://localhost:${DASHBOARD_PORT}/audit with your results.
26118
-
26119
- Runs fully offline — no account or network required. Press Ctrl+C to stop the
26120
- dashboard server when you're done.
26121
- `.trimStart();
26122
27318
  RESET = ANSI_RESET;
26123
27319
  DIM = ANSI_DIM;
26124
27320
  BOLD = ANSI_BOLD;
@@ -26147,16 +27343,16 @@ __export(exports_configure_wizard, {
26147
27343
  buildCompletionSummary: () => buildCompletionSummary,
26148
27344
  buildAgentChoices: () => buildAgentChoices
26149
27345
  });
26150
- import { existsSync as existsSync33, mkdirSync as mkdirSync21, writeFileSync as writeFileSync19, readFileSync as readFileSync27 } from "node:fs";
26151
- import { homedir as homedir26 } from "node:os";
26152
- import { dirname as dirname18, resolve as resolve25, sep as sep5 } from "node:path";
27346
+ import { existsSync as existsSync36, mkdirSync as mkdirSync21, writeFileSync as writeFileSync19, readFileSync as readFileSync31 } from "node:fs";
27347
+ import { homedir as homedir27 } from "node:os";
27348
+ import { dirname as dirname18, resolve as resolve26, sep as sep5 } from "node:path";
26153
27349
  async function emit(event, props) {
26154
27350
  try {
26155
27351
  await trackHookEvent2(getInstanceId2(), event, props);
26156
27352
  } catch {}
26157
27353
  }
26158
27354
  function homeify2(p) {
26159
- const home = homedir26();
27355
+ const home = homedir27();
26160
27356
  if (p === home)
26161
27357
  return "~";
26162
27358
  if (p.startsWith(home + sep5))
@@ -26280,8 +27476,8 @@ function setCustomPoliciesEnabled(scope, cwd, enabled) {
26280
27476
  const path3 = getConfigPathForScope(scope, cwd);
26281
27477
  let config = {};
26282
27478
  try {
26283
- if (existsSync33(path3))
26284
- config = JSON.parse(readFileSync27(path3, "utf8"));
27479
+ if (existsSync36(path3))
27480
+ config = JSON.parse(readFileSync31(path3, "utf8"));
26285
27481
  } catch {
26286
27482
  return;
26287
27483
  }
@@ -26321,7 +27517,7 @@ function describeCustomPolicies(cwd) {
26321
27517
  const scopes = [];
26322
27518
  let fileCount = 0;
26323
27519
  const dirs = [
26324
- { dir: resolve25(cwd, ".failproofai", "policies"), label: "project" },
27520
+ { dir: resolve26(cwd, ".failproofai", "policies"), label: "project" },
26325
27521
  { dir: customPoliciesDir(), label: "global" }
26326
27522
  ];
26327
27523
  for (const { dir, label } of dirs) {
@@ -26332,7 +27528,7 @@ function describeCustomPolicies(cwd) {
26332
27528
  fileCount += found.length;
26333
27529
  }
26334
27530
  for (const name of findSkippedPolicyFiles(dir)) {
26335
- warnings.push(`! ${homeify2(resolve25(dir, name))} is NOT loaded — rename to ` + `${name.replace(/\.(js|mjs|ts)$/, "-policies.$1")}`);
27531
+ warnings.push(`! ${homeify2(resolve26(dir, name))} is NOT loaded — rename to ` + `${name.replace(/\.(js|mjs|ts)$/, "-policies.$1")}`);
26336
27532
  }
26337
27533
  }
26338
27534
  return { active, warnings, fileCount, scopes };
@@ -26438,7 +27634,7 @@ function firstRunMarkerPath() {
26438
27634
  return launcherMarker();
26439
27635
  }
26440
27636
  function hasSeenLauncher() {
26441
- return existsSync33(firstRunMarkerPath());
27637
+ return existsSync36(firstRunMarkerPath());
26442
27638
  }
26443
27639
  function markLauncherSeen() {
26444
27640
  try {
@@ -26740,7 +27936,7 @@ refreshed. Re-run \`failproofai config\` once you can use sudo.
26740
27936
  let url = null;
26741
27937
  let token = null;
26742
27938
  let machineId = resolveMachineId();
26743
- let machineLabel = existing?.machineLabel ?? resolveMachineLabel();
27939
+ let machineLabel2 = existing?.machineLabel ?? resolveMachineLabel();
26744
27940
  if (existing) {
26745
27941
  const reuse = await selectOne({
26746
27942
  message: `Use this machine's existing connection to ${existing.url}?`,
@@ -26761,7 +27957,7 @@ refreshed. Re-run \`failproofai config\` once you can use sudo.
26761
27957
  url = existing.url;
26762
27958
  token = existing.token;
26763
27959
  machineId = existing.machineId;
26764
- machineLabel = existing.machineLabel ?? machineLabel;
27960
+ machineLabel2 = existing.machineLabel ?? machineLabel2;
26765
27961
  }
26766
27962
  }
26767
27963
  if (url === null) {
@@ -26815,13 +28011,13 @@ That did not work: ${probe.reason}
26815
28011
  if (retry === "skip") {
26816
28012
  stdout.write("Staying local. Connect later with `failproofai config --connect`.\n\n");
26817
28013
  } else {
26818
- connect = { url, token, machineId, machineLabel };
28014
+ connect = { url, token, machineId, machineLabel: machineLabel2 };
26819
28015
  }
26820
28016
  } else {
26821
28017
  stdout.write(`looks good.
26822
28018
 
26823
28019
  `);
26824
- connect = { url, token, machineId, machineLabel };
28020
+ connect = { url, token, machineId, machineLabel: machineLabel2 };
26825
28021
  }
26826
28022
  }
26827
28023
  }
@@ -27024,17 +28220,17 @@ __export(exports_flush_cli2, {
27024
28220
  flushRequestPath: () => flushRequestPath2,
27025
28221
  DEFAULT_FLUSH_TIMEOUT_SECS: () => DEFAULT_FLUSH_TIMEOUT_SECS2
27026
28222
  });
27027
- import { existsSync as existsSync34, mkdirSync as mkdirSync22, writeFileSync as writeFileSync20, readdirSync as readdirSync18 } from "node:fs";
27028
- import { dirname as dirname19, join as join32 } from "node:path";
28223
+ import { existsSync as existsSync37, mkdirSync as mkdirSync22, writeFileSync as writeFileSync20, readdirSync as readdirSync19 } from "node:fs";
28224
+ import { dirname as dirname19, join as join34 } from "node:path";
27029
28225
  function flushRequestPath2(home) {
27030
- return join32(failproofaiHome(home), "state", "flush-request.json");
28226
+ return join34(failproofaiHome(home), "state", "flush-request.json");
27031
28227
  }
27032
28228
  function spoolDirs2(home) {
27033
28229
  const root = spoolDir(home);
27034
- if (!existsSync34(root))
28230
+ if (!existsSync37(root))
27035
28231
  return [];
27036
28232
  try {
27037
- return readdirSync18(root, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name !== "failed").map((e) => join32(root, e.name));
28233
+ return readdirSync19(root, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name !== "failed").map((e) => join34(root, e.name));
27038
28234
  } catch {
27039
28235
  return [];
27040
28236
  }
@@ -27043,7 +28239,7 @@ function pendingBatches2(home) {
27043
28239
  let n = 0;
27044
28240
  for (const dir of spoolDirs2(home)) {
27045
28241
  try {
27046
- n += readdirSync18(dir).filter((f) => f.endsWith(".jsonl")).length;
28242
+ n += readdirSync19(dir).filter((f) => f.endsWith(".jsonl")).length;
27047
28243
  } catch {}
27048
28244
  }
27049
28245
  return n;
@@ -27138,16 +28334,16 @@ __export(exports_backfill_cli, {
27138
28334
  backfillRequestPath: () => backfillRequestPath,
27139
28335
  DEFAULT_BACKFILL_DAYS: () => DEFAULT_BACKFILL_DAYS
27140
28336
  });
27141
- import { existsSync as existsSync35, mkdirSync as mkdirSync23, writeFileSync as writeFileSync21, readdirSync as readdirSync19, statSync as statSync24 } from "node:fs";
27142
- import { dirname as dirname20, join as join33 } from "node:path";
28337
+ import { existsSync as existsSync38, mkdirSync as mkdirSync23, writeFileSync as writeFileSync21, readdirSync as readdirSync20, statSync as statSync24 } from "node:fs";
28338
+ import { dirname as dirname20, join as join35 } from "node:path";
27143
28339
  function backfillRequestPath(home) {
27144
- return join33(failproofaiHome(home), "state", "backfill-request.json");
28340
+ return join35(failproofaiHome(home), "state", "backfill-request.json");
27145
28341
  }
27146
28342
  function surveySessions(sinceMs, homeDir) {
27147
28343
  const out = [];
27148
28344
  for (const { cli, dir } of SESSION_DIRS) {
27149
- const root = join33(homeDir, dir);
27150
- if (!existsSync35(root))
28345
+ const root = join35(homeDir, dir);
28346
+ if (!existsSync38(root))
27151
28347
  continue;
27152
28348
  let files = 0;
27153
28349
  let newest = 0;
@@ -27156,12 +28352,12 @@ function surveySessions(sinceMs, homeDir) {
27156
28352
  return;
27157
28353
  let entries;
27158
28354
  try {
27159
- entries = readdirSync19(d, { withFileTypes: true });
28355
+ entries = readdirSync20(d, { withFileTypes: true });
27160
28356
  } catch {
27161
28357
  return;
27162
28358
  }
27163
28359
  for (const e of entries) {
27164
- const p = join33(d, e.name);
28360
+ const p = join35(d, e.name);
27165
28361
  if (e.isDirectory()) {
27166
28362
  walk(p, depth + 1);
27167
28363
  continue;
@@ -27185,7 +28381,7 @@ function runBackfillCommand(opts = {}) {
27185
28381
  const now = opts.now ?? Date.now();
27186
28382
  const sinceMs = opts.sinceMs ?? now - DEFAULT_BACKFILL_DAYS * 24 * 60 * 60 * 1000;
27187
28383
  const home = failproofaiHome();
27188
- if (!existsSync35(home)) {
28384
+ if (!existsSync38(home)) {
27189
28385
  return {
27190
28386
  exitCode: 1,
27191
28387
  lines: [`No failproofai home at ${home}. Run \`failproofai config\` first.`]
@@ -27291,8 +28487,8 @@ __export(exports_harness_cli, {
27291
28487
  addPath: () => addPath,
27292
28488
  HARNESS_KEYS: () => HARNESS_KEYS
27293
28489
  });
27294
- import { homedir as homedir27 } from "node:os";
27295
- import { resolve as resolve26 } from "node:path";
28490
+ import { homedir as homedir28 } from "node:os";
28491
+ import { resolve as resolve27 } from "node:path";
27296
28492
  function unknownHarness(name) {
27297
28493
  return fail([
27298
28494
  `Unknown harness: ${name}`,
@@ -27335,8 +28531,8 @@ function comparableLabel(raw) {
27335
28531
  return raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
27336
28532
  }
27337
28533
  function comparablePath(raw) {
27338
- const home = homedir27();
27339
- const expanded = raw === "~" ? home : raw.startsWith("~/") ? resolve26(home, raw.slice(2)) : raw;
28534
+ const home = homedir28();
28535
+ const expanded = raw === "~" ? home : raw.startsWith("~/") ? resolve27(home, raw.slice(2)) : raw;
27340
28536
  return expanded.replace(/\/+$/, "");
27341
28537
  }
27342
28538
  function effectiveLabel(entry) {
@@ -27523,7 +28719,7 @@ __export(exports_fp_config, {
27523
28719
  DEFAULT_CONFIG: () => DEFAULT_CONFIG2,
27524
28720
  DEFAULT_AUDIT_INTERVAL_DAYS: () => DEFAULT_AUDIT_INTERVAL_DAYS2
27525
28721
  });
27526
- import { chmodSync as chmodSync5, existsSync as existsSync36, mkdirSync as mkdirSync24, readFileSync as readFileSync28, statSync as statSync25, writeFileSync as writeFileSync22 } from "node:fs";
28722
+ import { chmodSync as chmodSync6, existsSync as existsSync39, mkdirSync as mkdirSync24, readFileSync as readFileSync32, statSync as statSync25, writeFileSync as writeFileSync22 } from "node:fs";
27527
28723
  import { dirname as dirname21 } from "node:path";
27528
28724
  function detectLayout2() {
27529
28725
  const raw = readVersionFile2();
@@ -27532,11 +28728,18 @@ function detectLayout2() {
27532
28728
  return { kind: "current", version: raw };
27533
28729
  return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
27534
28730
  }
27535
- if (existsSync36(configFile())) {
28731
+ if (existsSync39(configFile())) {
28732
+ const layoutThreePositions = [
28733
+ legacy.authJson(),
28734
+ legacy.nextAudit(),
28735
+ legacy.auditSchedule()
28736
+ ];
28737
+ if (layoutThreePositions.some((p) => existsSync39(p)))
28738
+ return { kind: "stale", found: 3 };
27536
28739
  return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
27537
28740
  }
27538
- if (existsSync36(legacy.configToml()))
27539
- return { kind: "stale", found: LAYOUT_VERSION - 1 };
28741
+ if (existsSync39(legacy.configToml()))
28742
+ return { kind: "stale", found: 2 };
27540
28743
  const layoutOneMarkers = [
27541
28744
  legacy.policyConfig(),
27542
28745
  legacy.cacheDir(),
@@ -27546,7 +28749,7 @@ function detectLayout2() {
27546
28749
  legacy.ingestCredentials(),
27547
28750
  legacy.auditDashboard()
27548
28751
  ];
27549
- return layoutOneMarkers.some((p) => existsSync36(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
28752
+ return layoutOneMarkers.some((p) => existsSync39(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
27550
28753
  }
27551
28754
  function readLegacyTomlVersion2(text) {
27552
28755
  const layout = Number(/^\s*layout\s*=\s*(\d+)\s*$/m.exec(text)?.[1]);
@@ -27558,7 +28761,7 @@ function readLegacyTomlVersion2(text) {
27558
28761
  function readVersionFile2() {
27559
28762
  let text;
27560
28763
  try {
27561
- text = readFileSync28(versionFile(), "utf8");
28764
+ text = readFileSync32(versionFile(), "utf8");
27562
28765
  } catch {
27563
28766
  return null;
27564
28767
  }
@@ -27579,7 +28782,7 @@ function readVersionFile2() {
27579
28782
  function writeVersionFile2(v = {}) {
27580
28783
  const existing = readVersionFile2();
27581
28784
  const next = {
27582
- layout: LAYOUT_VERSION,
28785
+ layout: v.layout ?? LAYOUT_VERSION,
27583
28786
  cli: v.cli ?? version2,
27584
28787
  daemon: v.clearDaemon ? undefined : v.daemon ?? existing?.daemon
27585
28788
  };
@@ -27613,7 +28816,7 @@ function readSources2(raw) {
27613
28816
  function readConfigRaw2() {
27614
28817
  let raw = {};
27615
28818
  try {
27616
- const parsed = JSON.parse(readFileSync28(configFile(), "utf8"));
28819
+ const parsed = JSON.parse(readFileSync32(configFile(), "utf8"));
27617
28820
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
27618
28821
  raw = parsed;
27619
28822
  }
@@ -27622,7 +28825,7 @@ function readConfigRaw2() {
27622
28825
  }
27623
28826
  function readConfig2() {
27624
28827
  try {
27625
- const parsed = JSON.parse(readFileSync28(configFile(), "utf8"));
28828
+ const parsed = JSON.parse(readFileSync32(configFile(), "utf8"));
27626
28829
  return projectConfig2(parsed);
27627
28830
  } catch {
27628
28831
  return structuredClone(DEFAULT_CONFIG2);
@@ -27648,7 +28851,11 @@ function projectConfig2(parsed) {
27648
28851
  sources: readSources2(collector.sources)
27649
28852
  },
27650
28853
  telemetry: { enabled: telemetry.enabled !== false },
27651
- audit: { auto: audit.auto === true, intervalDays: readIntervalDays2(audit.interval_days) }
28854
+ audit: {
28855
+ auto: audit.auto === true,
28856
+ intervalDays: readIntervalDays2(audit.interval_days),
28857
+ reportsConsentedAt: typeof audit.reports_consented_at === "number" && Number.isFinite(audit.reports_consented_at) ? audit.reports_consented_at : undefined
28858
+ }
27652
28859
  };
27653
28860
  }
27654
28861
  }
@@ -27697,7 +28904,11 @@ function writeConfig2(config, raw) {
27697
28904
  } : {}
27698
28905
  },
27699
28906
  ...config.telemetry.enabled ? {} : { telemetry: { enabled: false } },
27700
- audit: { auto: config.audit.auto, interval_days: config.audit.intervalDays }
28907
+ audit: {
28908
+ auto: config.audit.auto,
28909
+ interval_days: config.audit.intervalDays,
28910
+ ...config.audit.reportsConsentedAt === undefined ? {} : { reports_consented_at: config.audit.reportsConsentedAt }
28911
+ }
27701
28912
  };
27702
28913
  const merged = structuredClone(raw ?? readConfigRaw2().raw);
27703
28914
  for (const path3 of OWNED_CONFIG_KEYS2)
@@ -27721,7 +28932,7 @@ function updateConfig2(patch) {
27721
28932
  function readCredentialsRaw2() {
27722
28933
  let raw = {};
27723
28934
  try {
27724
- const parsed = JSON.parse(readFileSync28(credentialsFile(), "utf8"));
28935
+ const parsed = JSON.parse(readFileSync32(credentialsFile(), "utf8"));
27725
28936
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
27726
28937
  raw = parsed;
27727
28938
  }
@@ -27730,7 +28941,7 @@ function readCredentialsRaw2() {
27730
28941
  }
27731
28942
  function readCredentials2() {
27732
28943
  try {
27733
- return projectCredentials2(JSON.parse(readFileSync28(credentialsFile(), "utf8")));
28944
+ return projectCredentials2(JSON.parse(readFileSync32(credentialsFile(), "utf8")));
27734
28945
  } catch {
27735
28946
  return {};
27736
28947
  }
@@ -27742,8 +28953,8 @@ function projectCredentials2(parsed) {
27742
28953
  const auth = parsed.auth;
27743
28954
  const out = {};
27744
28955
  if (cloud && typeof cloud.url === "string" && typeof cloud.machine_id === "string" && typeof cloud.token === "string" && cloud.url && cloud.token) {
27745
- const machineLabel = typeof cloud.machine_label === "string" && cloud.machine_label ? cloud.machine_label : undefined;
27746
- out.cloud = { url: cloud.url, machineId: cloud.machine_id, token: cloud.token, machineLabel };
28956
+ const machineLabel2 = typeof cloud.machine_label === "string" && cloud.machine_label ? cloud.machine_label : undefined;
28957
+ out.cloud = { url: cloud.url, machineId: cloud.machine_id, token: cloud.token, machineLabel: machineLabel2 };
27747
28958
  }
27748
28959
  const org = parsed.org;
27749
28960
  if (org) {
@@ -27802,12 +29013,12 @@ function writeCredentials2(creds, raw) {
27802
29013
  mkdirSync24(home, { recursive: true });
27803
29014
  try {
27804
29015
  if (statSync25(home).mode & 63)
27805
- chmodSync5(home, 448);
29016
+ chmodSync6(home, 448);
27806
29017
  } catch {}
27807
29018
  writeFileSync22(credentialsFile(), `${JSON.stringify(merged, null, 2)}
27808
29019
  `, { mode: 384 });
27809
29020
  try {
27810
- chmodSync5(credentialsFile(), 384);
29021
+ chmodSync6(credentialsFile(), 384);
27811
29022
  } catch {}
27812
29023
  }
27813
29024
  function hasCloudCredentials() {
@@ -27847,7 +29058,8 @@ var init_fp_config2 = __esm(() => {
27847
29058
  ["collector", "sources", "*", "extra_paths"],
27848
29059
  ["telemetry", "enabled"],
27849
29060
  ["audit", "auto"],
27850
- ["audit", "interval_days"]
29061
+ ["audit", "interval_days"],
29062
+ ["audit", "reports_consented_at"]
27851
29063
  ];
27852
29064
  OWNED_CREDENTIAL_KEYS2 = [
27853
29065
  ["cloud", "url"],
@@ -27906,7 +29118,9 @@ __export(exports_fp_home, {
27906
29118
  codexSessionPathsFile: () => codexSessionPathsFile2,
27907
29119
  cloudPoliciesDir: () => cloudPoliciesDir2,
27908
29120
  binDir: () => binDir2,
29121
+ auditSessionFile: () => auditSessionFile2,
27909
29122
  auditScheduleFile: () => auditScheduleFile2,
29123
+ auditMachineFile: () => auditMachineFile2,
27910
29124
  auditLockFile: () => auditLockFile2,
27911
29125
  auditDir: () => auditDir2,
27912
29126
  auditDashboardFile: () => auditDashboardFile2,
@@ -27914,12 +29128,12 @@ __export(exports_fp_home, {
27914
29128
  LAYOUT_VERSION: () => LAYOUT_VERSION2,
27915
29129
  HOME_CLASSES: () => HOME_CLASSES2
27916
29130
  });
27917
- import { homedir as homedir28 } from "node:os";
27918
- import { resolve as resolve27 } from "node:path";
29131
+ import { homedir as homedir29 } from "node:os";
29132
+ import { resolve as resolve28 } from "node:path";
27919
29133
  function failproofaiHome2(home) {
27920
29134
  if (home)
27921
- return resolve27(home, ".failproofai");
27922
- return process.env.FAILPROOFAI_HOME || resolve27(homedir28(), ".failproofai");
29135
+ return resolve28(home, ".failproofai");
29136
+ return process.env.FAILPROOFAI_HOME || resolve28(homedir29(), ".failproofai");
27923
29137
  }
27924
29138
  function resettablePaths2() {
27925
29139
  return [
@@ -27936,6 +29150,7 @@ function retiredLayoutPaths2() {
27936
29150
  legacy2.lastVersion(),
27937
29151
  legacy2.auditDashboard(),
27938
29152
  legacy2.auditCacheDir(),
29153
+ legacy2.auditReminder(),
27939
29154
  legacy2.codexSessionPaths(),
27940
29155
  legacy2.spoolDir(),
27941
29156
  legacy2.failedDir(),
@@ -27947,7 +29162,7 @@ function retiredLayoutPaths2() {
27947
29162
  legacy2.localPoliciesDir()
27948
29163
  ];
27949
29164
  }
27950
- var LAYOUT_VERSION2 = 3, at2 = (...parts) => resolve27(failproofaiHome2(), ...parts), atHome2 = (home, ...parts) => home ? resolve27(failproofaiHome2(home), ...parts) : at2(...parts), versionFile2 = (home) => atHome2(home, "VERSION"), configFile2 = (home) => atHome2(home, "config.json"), credentialsFile2 = (home) => atHome2(home, "credentials.json"), binDir2 = (home) => atHome2(home, "bin"), daemonBinary = (version3, home) => resolve27(binDir2(home), `failproofaid-${version3}`), policiesDir2 = (home) => atHome2(home, "policies"), customPoliciesDir2, globalPolicyConfigFile2 = (home) => atHome2(home, "policies-config.json"), cloudPoliciesDir2 = (home) => resolve27(policiesDir2(home), "cloud-policies"), cursorsDir2 = (source, home) => source ? resolve27(atHome2(home, "cursors"), source) : atHome2(home, "cursors"), customAgentsDir2 = (home) => atHome2(home, "custom-agents"), customAgentsEventsDir = (home) => resolve27(customAgentsDir2(home), "events"), customAgentsFailedDir = (home) => resolve27(customAgentsDir2(home), "failed"), auditDir2 = (home) => atHome2(home, "audit"), auditDashboardFile2 = (home) => resolve27(auditDir2(home), "dashboard.json"), auditCacheDir2 = (home) => resolve27(auditDir2(home), "cache"), hookActivityDir2 = (home) => atHome2(home, "hook-activity"), runDir2 = (home) => atHome2(home, "run"), daemonSocket2 = () => process.env.FAILPROOFAI_DAEMON_SOCKET || resolve27(runDir2(), "failproofaid.sock"), workerSocket = (home) => resolve27(runDir2(home), "worker.sock"), daemonLock = (home) => resolve27(runDir2(home), "failproofaid.lock"), auditLockFile2 = (home) => resolve27(runDir2(home), "audit.lock"), stateDir2 = (home) => atHome2(home, "state"), shimsDir2 = (home) => resolve27(stateDir2(home), "shims"), spoolDir2 = (home) => resolve27(stateDir2(home), "spool"), failedDir2 = (home) => resolve27(stateDir2(home), "failed"), collectorHealthFile2 = (home) => resolve27(stateDir2(home), "collector-health.json"), sessionPauseDir2 = () => resolve27(stateDir2(), "sessions"), auditScheduleFile2 = (home) => resolve27(stateDir2(home), "audit-schedule.json"), telemetryIdFile2 = (home) => resolve27(stateDir2(home), "telemetry-id"), launcherMarker2 = (home) => resolve27(stateDir2(home), "launcher-configured"), onboardingAttemptFile2 = (home) => resolve27(stateDir2(home), "onboarding-attempt.json"), lastVersionFile2 = (home) => resolve27(stateDir2(home), "last-version"), onboardingLockFile2 = (home) => resolve27(stateDir2(home), "onboarding.lock"), codexSessionPathsFile2 = (home) => resolve27(stateDir2(home), "codex-session-paths.json"), logsDir2 = (home) => atHome2(home, "logs"), migrationsDir2 = (home) => atHome2(home, "migrations"), migrationLedgerFile2 = (home) => resolve27(migrationsDir2(home), "applied.json"), migrationBackupDir2 = (layout, home) => resolve27(migrationsDir2(home), `backup-layout${layout}`), HOME_CLASSES2, legacy2;
29165
+ var LAYOUT_VERSION2 = 4, at2 = (...parts) => resolve28(failproofaiHome2(), ...parts), atHome2 = (home, ...parts) => home ? resolve28(failproofaiHome2(home), ...parts) : at2(...parts), versionFile2 = (home) => atHome2(home, "VERSION"), configFile2 = (home) => atHome2(home, "config.json"), credentialsFile2 = (home) => atHome2(home, "credentials.json"), binDir2 = (home) => atHome2(home, "bin"), daemonBinary = (version3, home) => resolve28(binDir2(home), `failproofaid-${version3}`), policiesDir2 = (home) => atHome2(home, "policies"), customPoliciesDir2, globalPolicyConfigFile2 = (home) => atHome2(home, "policies-config.json"), cloudPoliciesDir2 = (home) => resolve28(policiesDir2(home), "cloud-policies"), cursorsDir2 = (source, home) => source ? resolve28(atHome2(home, "cursors"), source) : atHome2(home, "cursors"), customAgentsDir2 = (home) => atHome2(home, "custom-agents"), customAgentsEventsDir = (home) => resolve28(customAgentsDir2(home), "events"), customAgentsFailedDir = (home) => resolve28(customAgentsDir2(home), "failed"), auditDir2 = (home) => atHome2(home, "audit"), auditDashboardFile2 = (home) => resolve28(auditDir2(home), "dashboard.json"), auditCacheDir2 = (home) => resolve28(auditDir2(home), "cache"), auditSessionFile2 = (home) => resolve28(auditDir2(home), "session.json"), auditMachineFile2 = (home) => resolve28(auditDir2(home), "machine.json"), hookActivityDir2 = (home) => atHome2(home, "hook-activity"), runDir2 = (home) => atHome2(home, "run"), daemonSocket2 = () => process.env.FAILPROOFAI_DAEMON_SOCKET || resolve28(runDir2(), "failproofaid.sock"), workerSocket = (home) => resolve28(runDir2(home), "worker.sock"), daemonLock = (home) => resolve28(runDir2(home), "failproofaid.lock"), auditLockFile2 = (home) => resolve28(runDir2(home), "audit.lock"), stateDir2 = (home) => atHome2(home, "state"), shimsDir2 = (home) => resolve28(stateDir2(home), "shims"), spoolDir2 = (home) => resolve28(stateDir2(home), "spool"), failedDir2 = (home) => resolve28(stateDir2(home), "failed"), collectorHealthFile2 = (home) => resolve28(stateDir2(home), "collector-health.json"), sessionPauseDir2 = () => resolve28(stateDir2(), "sessions"), auditScheduleFile2 = (home) => resolve28(auditDir2(home), "schedule.json"), telemetryIdFile2 = (home) => resolve28(stateDir2(home), "telemetry-id"), launcherMarker2 = (home) => resolve28(stateDir2(home), "launcher-configured"), onboardingAttemptFile2 = (home) => resolve28(stateDir2(home), "onboarding-attempt.json"), lastVersionFile2 = (home) => resolve28(stateDir2(home), "last-version"), onboardingLockFile2 = (home) => resolve28(stateDir2(home), "onboarding.lock"), codexSessionPathsFile2 = (home) => resolve28(stateDir2(home), "codex-session-paths.json"), logsDir2 = (home) => atHome2(home, "logs"), migrationsDir2 = (home) => atHome2(home, "migrations"), migrationLedgerFile2 = (home) => resolve28(migrationsDir2(home), "applied.json"), migrationBackupDir2 = (layout, home) => resolve28(migrationsDir2(home), `backup-layout${layout}`), HOME_CLASSES2, legacy2;
27951
29166
  var init_fp_home2 = __esm(() => {
27952
29167
  customPoliciesDir2 = policiesDir2;
27953
29168
  HOME_CLASSES2 = [
@@ -27955,20 +29170,23 @@ var init_fp_home2 = __esm(() => {
27955
29170
  { path: configFile2, class: "user-typed" },
27956
29171
  { path: globalPolicyConfigFile2, class: "user-typed" },
27957
29172
  { path: policiesDir2, class: "user-typed" },
29173
+ { path: auditSessionFile2, class: "user-typed" },
27958
29174
  { path: spoolDir2, class: "undelivered" },
27959
29175
  { path: failedDir2, class: "undelivered" },
27960
29176
  { path: customAgentsDir2, class: "undelivered" },
27961
29177
  { path: hookActivityDir2, class: "undelivered" },
27962
- { path: (home) => resolve27(stateDir2(home), "flush-request.json"), class: "undelivered" },
27963
- { path: (home) => resolve27(stateDir2(home), "backfill-request.json"), class: "undelivered" },
29178
+ { path: (home) => resolve28(stateDir2(home), "flush-request.json"), class: "undelivered" },
29179
+ { path: (home) => resolve28(stateDir2(home), "backfill-request.json"), class: "undelivered" },
27964
29180
  { path: cursorsDir2, class: "identity" },
27965
29181
  { path: telemetryIdFile2, class: "identity" },
27966
- { path: (home) => resolve27(stateDir2(home), "daemon-telemetry-id"), class: "identity" },
29182
+ { path: (home) => resolve28(stateDir2(home), "daemon-telemetry-id"), class: "identity" },
27967
29183
  { path: versionFile2, class: "identity" },
27968
29184
  { path: migrationsDir2, class: "identity" },
27969
- { path: auditDir2, class: "derived" },
27970
- { path: collectorHealthFile2, class: "derived" },
29185
+ { path: auditMachineFile2, class: "identity" },
29186
+ { path: auditDashboardFile2, class: "derived" },
29187
+ { path: auditCacheDir2, class: "derived" },
27971
29188
  { path: auditScheduleFile2, class: "derived" },
29189
+ { path: collectorHealthFile2, class: "derived" },
27972
29190
  { path: codexSessionPathsFile2, class: "derived" },
27973
29191
  { path: shimsDir2, class: "derived" },
27974
29192
  { path: sessionPauseDir2, class: "derived" },
@@ -27994,6 +29212,10 @@ var init_fp_home2 = __esm(() => {
27994
29212
  launcherMarker: () => at2(".launcher-configured"),
27995
29213
  lastVersion: () => at2("last-version"),
27996
29214
  auditDashboard: () => at2("audit-dashboard.json"),
29215
+ authJson: () => at2("auth.json"),
29216
+ nextAudit: () => at2("next-audit.json"),
29217
+ auditReminder: () => at2("audit", "reminder.json"),
29218
+ auditSchedule: () => at2("state", "audit-schedule.json"),
27997
29219
  cacheDir: () => at2("cache"),
27998
29220
  hookActivityDir: () => at2("cache", "hook-activity"),
27999
29221
  auditCacheDir: () => at2("cache", "audit"),
@@ -28013,26 +29235,83 @@ __export(exports_migrations2, {
28013
29235
  runMigrations: () => runMigrations2,
28014
29236
  restoreBackup: () => restoreBackup2,
28015
29237
  readLedger: () => readLedger2,
29238
+ pruneMigratedCredentials: () => pruneMigratedCredentials2,
28016
29239
  planMigration: () => planMigration2,
28017
29240
  migrationCoverageGap: () => migrationCoverageGap2,
28018
29241
  describePlan: () => describePlan2,
28019
29242
  backupBeforeMigrating: () => backupBeforeMigrating2,
28020
29243
  MIGRATIONS: () => MIGRATIONS2
28021
29244
  });
28022
- import { copyFileSync as copyFileSync4, existsSync as existsSync37, mkdirSync as mkdirSync25, readFileSync as readFileSync29, rmSync as rmSync12, writeFileSync as writeFileSync23 } from "node:fs";
28023
- import { basename as basename7, dirname as dirname22, resolve as resolve28 } from "node:path";
29245
+ import {
29246
+ chmodSync as chmodSync7,
29247
+ copyFileSync as copyFileSync4,
29248
+ existsSync as existsSync40,
29249
+ mkdirSync as mkdirSync25,
29250
+ readFileSync as readFileSync33,
29251
+ renameSync as renameSync8,
29252
+ rmSync as rmSync14,
29253
+ writeFileSync as writeFileSync23
29254
+ } from "node:fs";
29255
+ import { basename as basename7, dirname as dirname22, join as join36, resolve as resolve29 } from "node:path";
29256
+ function migrateToLayout42() {
29257
+ const moves = [
29258
+ { from: legacy.authJson(), to: auditSessionFile() },
29259
+ { from: legacy.nextAudit(), to: legacy.auditReminder() },
29260
+ { from: legacy.auditSchedule(), to: auditScheduleFile() }
29261
+ ];
29262
+ const authDirOverride = process.env.FAILPROOFAI_AUTH_DIR;
29263
+ if (authDirOverride) {
29264
+ moves.push({ from: join36(authDirOverride, "auth.json"), to: join36(authDirOverride, "session.json") }, {
29265
+ from: join36(authDirOverride, "next-audit.json"),
29266
+ to: join36(authDirOverride, "reminder.json")
29267
+ });
29268
+ }
29269
+ const migrated = [];
29270
+ for (const { from, to } of moves) {
29271
+ if (!existsSync40(from))
29272
+ continue;
29273
+ if (existsSync40(to)) {
29274
+ rmSync14(from, { force: true });
29275
+ continue;
29276
+ }
29277
+ mkdirSync25(dirname22(to), { recursive: true });
29278
+ try {
29279
+ renameSync8(from, to);
29280
+ } catch {
29281
+ try {
29282
+ copyFileSync4(from, to);
29283
+ } catch (err) {
29284
+ try {
29285
+ rmSync14(to, { force: true });
29286
+ } catch {}
29287
+ throw err;
29288
+ }
29289
+ rmSync14(from, { force: true });
29290
+ }
29291
+ migrated.push(`${basename7(from)} → audit/${basename7(to)}`);
29292
+ }
29293
+ for (const secret of [auditSessionFile()]) {
29294
+ if (!existsSync40(secret))
29295
+ continue;
29296
+ try {
29297
+ chmodSync7(secret, 384);
29298
+ } catch {}
29299
+ }
29300
+ writeVersionFile({ layout: 4 });
29301
+ return { removed: [], migrated, activity: [], policyConfig: [], spooled: [], from: 3 };
29302
+ }
28024
29303
  function planMigration2(from, registry = MIGRATIONS2) {
28025
29304
  if (from >= LAYOUT_VERSION)
28026
29305
  return [];
28027
29306
  const chain = [];
28028
29307
  let at3 = from;
28029
29308
  while (at3 < LAYOUT_VERSION) {
28030
- const step = registry.find((m) => m.from === at3);
28031
- if (!step) {
29309
+ const step2 = registry.find((m) => m.from === at3);
29310
+ if (!step2) {
28032
29311
  throw new Error(`no migration from layout ${at3} to ${LAYOUT_VERSION} — this build cannot upgrade that home`);
28033
29312
  }
28034
- chain.push(step);
28035
- at3 = step.to;
29313
+ chain.push(step2);
29314
+ at3 = step2.to;
28036
29315
  }
28037
29316
  return chain;
28038
29317
  }
@@ -28058,7 +29337,7 @@ function migrationCoverageGap2(registry = MIGRATIONS2) {
28058
29337
  }
28059
29338
  function readLedger2() {
28060
29339
  try {
28061
- const parsed = JSON.parse(readFileSync29(migrationLedgerFile(), "utf8"));
29340
+ const parsed = JSON.parse(readFileSync33(migrationLedgerFile(), "utf8"));
28062
29341
  return Array.isArray(parsed) ? parsed : [];
28063
29342
  } catch {
28064
29343
  return [];
@@ -28075,11 +29354,28 @@ function appendLedger2(entry) {
28075
29354
  function backupNameOf2(f) {
28076
29355
  return f.as ?? basename7(f.at());
28077
29356
  }
29357
+ function pruneMigratedCredentials2(from) {
29358
+ const pruned = [];
29359
+ for (let layout = from;layout < LAYOUT_VERSION; layout++) {
29360
+ for (const { as, landsAt } of PRUNED_AFTER_SUCCESS2) {
29361
+ try {
29362
+ if (!existsSync40(landsAt()))
29363
+ continue;
29364
+ const copy = resolve29(migrationBackupDir(layout), as);
29365
+ if (!existsSync40(copy))
29366
+ continue;
29367
+ rmSync14(copy, { force: true });
29368
+ pruned.push(as);
29369
+ } catch {}
29370
+ }
29371
+ }
29372
+ return pruned;
29373
+ }
28078
29374
  function filesToBackUp2() {
28079
29375
  const seen = new Map;
28080
29376
  for (const f of [...BACKED_UP2, ...BACKED_UP_LEGACY2]) {
28081
29377
  const path3 = f.at();
28082
- if (existsSync37(path3))
29378
+ if (existsSync40(path3))
28083
29379
  seen.set(path3, backupNameOf2(f));
28084
29380
  }
28085
29381
  return [...seen].map(([path3, as]) => ({ path: path3, as }));
@@ -28088,14 +29384,14 @@ function backupBeforeMigrating2(from) {
28088
29384
  const dir = migrationBackupDir(from);
28089
29385
  const saved = [];
28090
29386
  try {
28091
- rmSync12(dir, { recursive: true, force: true });
29387
+ rmSync14(dir, { recursive: true, force: true });
28092
29388
  mkdirSync25(dir, { recursive: true });
28093
29389
  } catch {
28094
29390
  return [];
28095
29391
  }
28096
29392
  for (const { path: source, as } of filesToBackUp2()) {
28097
29393
  try {
28098
- copyFileSync4(source, resolve28(dir, as));
29394
+ copyFileSync4(source, resolve29(dir, as));
28099
29395
  saved.push(as);
28100
29396
  } catch {}
28101
29397
  }
@@ -28113,19 +29409,19 @@ function runMigrations2(from, chain = planMigration2(from)) {
28113
29409
  from
28114
29410
  };
28115
29411
  let failed;
28116
- for (const step of chain) {
29412
+ for (const step2 of chain) {
28117
29413
  const startedAt = Date.now();
28118
29414
  try {
28119
- const result = step.run();
29415
+ const result = step2.run();
28120
29416
  outcome.removed.push(...result.removed);
28121
29417
  outcome.migrated.push(...result.migrated);
28122
29418
  outcome.activity.push(...result.activity);
28123
29419
  outcome.policyConfig.push(...result.policyConfig);
28124
29420
  outcome.spooled.push(...result.spooled);
28125
- steps.push({ from: step.from, to: step.to, ok: true });
29421
+ steps.push({ from: step2.from, to: step2.to, ok: true });
28126
29422
  appendLedger2({
28127
- from: step.from,
28128
- to: step.to,
29423
+ from: step2.from,
29424
+ to: step2.to,
28129
29425
  cli: version2,
28130
29426
  at: new Date(startedAt).toISOString(),
28131
29427
  durationMs: Date.now() - startedAt,
@@ -28135,15 +29431,20 @@ function runMigrations2(from, chain = planMigration2(from)) {
28135
29431
  activity: result.activity.length
28136
29432
  });
28137
29433
  } catch (err) {
28138
- steps.push({ from: step.from, to: step.to, ok: false });
29434
+ steps.push({ from: step2.from, to: step2.to, ok: false });
29435
+ try {
29436
+ const marker = readVersionFile();
29437
+ if (marker && marker.layout >= LAYOUT_VERSION)
29438
+ writeVersionFile({ layout: step2.from });
29439
+ } catch {}
28139
29440
  failed = {
28140
- from: step.from,
28141
- to: step.to,
29441
+ from: step2.from,
29442
+ to: step2.to,
28142
29443
  error: err instanceof Error ? err.message : String(err)
28143
29444
  };
28144
29445
  appendLedger2({
28145
- from: step.from,
28146
- to: step.to,
29446
+ from: step2.from,
29447
+ to: step2.to,
28147
29448
  cli: version2,
28148
29449
  at: new Date(startedAt).toISOString(),
28149
29450
  durationMs: Date.now() - startedAt,
@@ -28152,6 +29453,8 @@ function runMigrations2(from, chain = planMigration2(from)) {
28152
29453
  break;
28153
29454
  }
28154
29455
  }
29456
+ if (!failed)
29457
+ pruneMigratedCredentials2(from);
28155
29458
  return { from, steps, backedUp, outcome, failed };
28156
29459
  }
28157
29460
  function describePlan2(from) {
@@ -28178,13 +29481,13 @@ function describePlan2(from) {
28178
29481
  }
28179
29482
  function restoreBackup2(layout) {
28180
29483
  const dir = migrationBackupDir(layout);
28181
- if (!existsSync37(dir))
29484
+ if (!existsSync40(dir))
28182
29485
  return [];
28183
29486
  const restored = [];
28184
29487
  for (const f of [...BACKED_UP2, ...BACKED_UP_LEGACY2]) {
28185
29488
  const target = f.at();
28186
- const source = resolve28(dir, backupNameOf2(f));
28187
- if (!existsSync37(source))
29489
+ const source = resolve29(dir, backupNameOf2(f));
29490
+ if (!existsSync40(source))
28188
29491
  continue;
28189
29492
  try {
28190
29493
  mkdirSync25(dirname22(target), { recursive: true });
@@ -28194,23 +29497,30 @@ function restoreBackup2(layout) {
28194
29497
  }
28195
29498
  return restored.sort((a, b) => a.localeCompare(b));
28196
29499
  }
28197
- var MIGRATIONS2, BACKED_UP2, BACKED_UP_LEGACY2;
29500
+ var MIGRATIONS2, BACKED_UP2, BACKED_UP_LEGACY2, PRUNED_AFTER_SUCCESS2;
28198
29501
  var init_migrations2 = __esm(() => {
28199
29502
  init_package();
28200
29503
  init_fp_home();
29504
+ init_fp_config();
28201
29505
  init_fp_reset();
28202
29506
  MIGRATIONS2 = [
28203
29507
  {
28204
29508
  from: 1,
28205
29509
  to: 3,
28206
29510
  describe: "layout 1 → 3: carry the decision log out of cache/, keep the policy config in place, drop the layout-1 credential files",
28207
- run: () => resetHome(1)
29511
+ run: () => resetHome(1, 3)
28208
29512
  },
28209
29513
  {
28210
29514
  from: 2,
28211
29515
  to: 3,
28212
29516
  describe: "layout 2 → 3: carry config.toml and credentials.toml into JSON, move custom-policies/ back up into policies/, nest the policy config at the root",
28213
- run: () => resetHome(2)
29517
+ run: () => resetHome(2, 3)
29518
+ },
29519
+ {
29520
+ from: 3,
29521
+ to: 4,
29522
+ describe: "layout 3 → 4: gather the audit's files under audit/ — auth.json becomes audit/session.json, next-audit.json becomes audit/reminder.json, state/audit-schedule.json becomes audit/schedule.json",
29523
+ run: migrateToLayout42
28214
29524
  }
28215
29525
  ];
28216
29526
  BACKED_UP2 = [
@@ -28225,7 +29535,13 @@ var init_migrations2 = __esm(() => {
28225
29535
  { at: legacy.policyConfig },
28226
29536
  { at: legacy.localPolicyConfig, as: "local-policies-policies-config.json" },
28227
29537
  { at: legacy.cloudCredentials },
28228
- { at: legacy.ingestCredentials }
29538
+ { at: legacy.ingestCredentials },
29539
+ { at: legacy.authJson },
29540
+ { at: legacy.nextAudit },
29541
+ { at: legacy.auditSchedule }
29542
+ ];
29543
+ PRUNED_AFTER_SUCCESS2 = [
29544
+ { as: basename7(legacy.authJson()), landsAt: auditSessionFile }
28229
29545
  ];
28230
29546
  });
28231
29547
 
@@ -28237,6 +29553,7 @@ __export(exports_daemon_service, {
28237
29553
  upgradedServiceDefinition: () => upgradedServiceDefinition2,
28238
29554
  uninstallDaemonService: () => uninstallDaemonService2,
28239
29555
  systemdUnitContents: () => systemdUnitContents2,
29556
+ startedAtFromMonotonic: () => startedAtFromMonotonic,
28240
29557
  setDaemonConfigured: () => setDaemonConfigured2,
28241
29558
  resolveWorkerCommand: () => resolveWorkerCommand2,
28242
29559
  resolveFailproofaidBinaryPath: () => resolveFailproofaidBinaryPath2,
@@ -28253,6 +29570,7 @@ __export(exports_daemon_service, {
28253
29570
  ensureDaemonServiceCurrent: () => ensureDaemonServiceCurrent2,
28254
29571
  daemonVersionSkew: () => daemonVersionSkew2,
28255
29572
  daemonStatusCommand: () => daemonStatusCommand2,
29573
+ daemonStartedAtMs: () => daemonStartedAtMs,
28256
29574
  daemonServiceStatus: () => daemonServiceStatus2,
28257
29575
  daemonServiceNeedsUpgrade: () => daemonServiceNeedsUpgrade2,
28258
29576
  daemonServiceFilePath: () => daemonServiceFilePath2,
@@ -28260,16 +29578,16 @@ __export(exports_daemon_service, {
28260
29578
  canElevate: () => canElevate2
28261
29579
  });
28262
29580
  import {
28263
- existsSync as existsSync38,
29581
+ existsSync as existsSync41,
28264
29582
  mkdirSync as mkdirSync26,
28265
29583
  mkdtempSync as mkdtempSync2,
28266
- readFileSync as readFileSync30,
29584
+ readFileSync as readFileSync34,
28267
29585
  writeFileSync as writeFileSync24,
28268
29586
  unlinkSync as unlinkSync5,
28269
- rmSync as rmSync13
29587
+ rmSync as rmSync15
28270
29588
  } from "node:fs";
28271
- import { homedir as homedir29, tmpdir as tmpdir3, userInfo as userInfo2 } from "node:os";
28272
- import { resolve as resolve29 } from "node:path";
29589
+ import { homedir as homedir30, tmpdir as tmpdir3, userInfo as userInfo2 } from "node:os";
29590
+ import { resolve as resolve30 } from "node:path";
28273
29591
  import { execFileSync as execFileSync5 } from "node:child_process";
28274
29592
  function setDaemonConfigured2(value, installedVersion) {
28275
29593
  try {
@@ -28291,13 +29609,13 @@ function resolveFailproofaidBinaryPath2() {
28291
29609
  if (process.env.FAILPROOFAI_DAEMON_BINARY)
28292
29610
  return process.env.FAILPROOFAI_DAEMON_BINARY;
28293
29611
  const downloaded = installedBinaryPath();
28294
- if (existsSync38(downloaded))
29612
+ if (existsSync41(downloaded))
28295
29613
  return downloaded;
28296
29614
  const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
28297
29615
  if (packageRoot) {
28298
29616
  for (const profile of ["release", "debug"]) {
28299
- const candidate = resolve29(packageRoot, "target", profile, "failproofaid");
28300
- if (existsSync38(candidate))
29617
+ const candidate = resolve30(packageRoot, "target", profile, "failproofaid");
29618
+ if (existsSync41(candidate))
28301
29619
  return candidate;
28302
29620
  }
28303
29621
  }
@@ -28327,8 +29645,8 @@ function resolveWorkerCommand2() {
28327
29645
  const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
28328
29646
  if (!packageRoot)
28329
29647
  return null;
28330
- const workerScript = resolve29(packageRoot, "dist", "worker.mjs");
28331
- if (!existsSync38(workerScript))
29648
+ const workerScript = resolve30(packageRoot, "dist", "worker.mjs");
29649
+ if (!existsSync41(workerScript))
28332
29650
  return null;
28333
29651
  return `${shellQuote2(process.execPath)} ${shellQuote2(workerScript)}`;
28334
29652
  }
@@ -28338,8 +29656,8 @@ function resolveCliCommand2() {
28338
29656
  const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
28339
29657
  if (!packageRoot)
28340
29658
  return null;
28341
- const cliScript = resolve29(packageRoot, "dist", "cli.mjs");
28342
- if (!existsSync38(cliScript))
29659
+ const cliScript = resolve30(packageRoot, "dist", "cli.mjs");
29660
+ if (!existsSync41(cliScript))
28343
29661
  return null;
28344
29662
  return `${shellQuote2(process.execPath)} ${shellQuote2(cliScript)}`;
28345
29663
  }
@@ -28352,8 +29670,8 @@ function workerScriptPath2() {
28352
29670
  const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
28353
29671
  if (!packageRoot)
28354
29672
  return null;
28355
- const workerScript = resolve29(packageRoot, "dist", "worker.mjs");
28356
- return existsSync38(workerScript) ? workerScript : null;
29673
+ const workerScript = resolve30(packageRoot, "dist", "worker.mjs");
29674
+ return existsSync41(workerScript) ? workerScript : null;
28357
29675
  }
28358
29676
  function assertUnitSafe2(value, field) {
28359
29677
  if (UNIT_UNSAFE_RE2.test(value)) {
@@ -28379,10 +29697,10 @@ function systemdUnitDir2() {
28379
29697
  return process.env.FAILPROOFAI_SYSTEMD_DIR || "/etc/systemd/system";
28380
29698
  }
28381
29699
  function systemdUnitPath2(user = serviceUser2()) {
28382
- return resolve29(systemdUnitDir2(), systemdUnitName2(user));
29700
+ return resolve30(systemdUnitDir2(), systemdUnitName2(user));
28383
29701
  }
28384
29702
  function legacySystemdUserUnitPath2() {
28385
- return resolve29(homedir29(), ".config", "systemd", "user", "failproofaid.service");
29703
+ return resolve30(homedir30(), ".config", "systemd", "user", "failproofaid.service");
28386
29704
  }
28387
29705
  function launchdLabel2(user = serviceUser2()) {
28388
29706
  return `ai.failproof.failproofaid.${user}`;
@@ -28391,7 +29709,7 @@ function launchdPlistPath2(user = serviceUser2()) {
28391
29709
  return `/Library/LaunchDaemons/${launchdLabel2(user)}.plist`;
28392
29710
  }
28393
29711
  function legacyLaunchAgentPlistPath2() {
28394
- return resolve29(homedir29(), "Library", "LaunchAgents", "ai.failproof.failproofaid.plist");
29712
+ return resolve30(homedir30(), "Library", "LaunchAgents", "ai.failproof.failproofaid.plist");
28395
29713
  }
28396
29714
  function legacySharedLaunchDaemonPath2() {
28397
29715
  return "/Library/LaunchDaemons/ai.failproof.failproofaid.plist";
@@ -28439,20 +29757,20 @@ function runPrivileged2(command, args) {
28439
29757
  execFileSync5(cmd, argv, { stdio: "ignore", timeout: SERVICE_CMD_TIMEOUT_MS2 });
28440
29758
  }
28441
29759
  function writePrivilegedFile2(destination, contents, mode = "0644") {
28442
- const stagingDir = mkdtempSync2(resolve29(tmpdir3(), "failproofaid-stage-"));
28443
- const staging = resolve29(stagingDir, "service-definition");
29760
+ const stagingDir = mkdtempSync2(resolve30(tmpdir3(), "failproofaid-stage-"));
29761
+ const staging = resolve30(stagingDir, "service-definition");
28444
29762
  try {
28445
29763
  writeFileSync24(staging, contents, "utf8");
28446
29764
  runPrivileged2("install", ["-m", mode, staging, destination]);
28447
29765
  } finally {
28448
- rmSync13(stagingDir, { recursive: true, force: true });
29766
+ rmSync15(stagingDir, { recursive: true, force: true });
28449
29767
  }
28450
29768
  }
28451
29769
  function removeLegacyUserService2() {
28452
29770
  try {
28453
29771
  if (process.platform === "linux") {
28454
29772
  const legacy3 = legacySystemdUserUnitPath2();
28455
- if (!existsSync38(legacy3))
29773
+ if (!existsSync41(legacy3))
28456
29774
  return;
28457
29775
  try {
28458
29776
  execFileSync5("systemctl", ["--user", "disable", "--now", "failproofaid.service"], {
@@ -28469,7 +29787,7 @@ function removeLegacyUserService2() {
28469
29787
  } catch {}
28470
29788
  } else {
28471
29789
  const legacyAgent = legacyLaunchAgentPlistPath2();
28472
- if (existsSync38(legacyAgent)) {
29790
+ if (existsSync41(legacyAgent)) {
28473
29791
  try {
28474
29792
  execFileSync5("launchctl", ["unload", "-w", legacyAgent], {
28475
29793
  stdio: "ignore",
@@ -28479,7 +29797,7 @@ function removeLegacyUserService2() {
28479
29797
  unlinkSync5(legacyAgent);
28480
29798
  }
28481
29799
  const legacyDaemon = legacySharedLaunchDaemonPath2();
28482
- if (existsSync38(legacyDaemon)) {
29800
+ if (existsSync41(legacyDaemon)) {
28483
29801
  try {
28484
29802
  runPrivileged2("launchctl", ["unload", "-w", legacyDaemon]);
28485
29803
  } catch {}
@@ -28495,8 +29813,8 @@ function systemdUnitContents2(binaryPath, workerCmd, cliCmd = null) {
28495
29813
  `).join("");
28496
29814
  const user = assertUnitSafe2(serviceUser2(), "User");
28497
29815
  assertUnitSafe2(binaryPath, "ExecStart");
28498
- assertUnitSafe2(homedir29(), "HOME");
28499
- const conditionPaths = [binaryPath, workerScriptPath2()].filter((p) => typeof p === "string" && existsSync38(p));
29816
+ assertUnitSafe2(homedir30(), "HOME");
29817
+ const conditionPaths = [binaryPath, workerScriptPath2()].filter((p) => typeof p === "string" && existsSync41(p));
28500
29818
  const conditionLines = conditionPaths.map((p) => `ConditionPathExists=${assertUnitSafe2(p, "ConditionPathExists")}
28501
29819
  `).join("");
28502
29820
  return `[Unit]
@@ -28509,7 +29827,7 @@ User=${user}
28509
29827
  # Set explicitly rather than relying on systemd deriving it from User=:
28510
29828
  # failproofaid stores per-user state and refuses to start without HOME, so this
28511
29829
  # required variable is not left to a version-dependent default.
28512
- Environment="HOME=${homedir29()}"
29830
+ Environment="HOME=${homedir30()}"
28513
29831
  ${envLines}ExecStart=${binaryPath}
28514
29832
  Restart=on-failure
28515
29833
  RestartSec=2
@@ -28557,9 +29875,9 @@ ${env.map(([key, value]) => ` <key>${key}</key>
28557
29875
  <key>UserName</key>
28558
29876
  <string>${escapeXml2(serviceUser2())}</string>
28559
29877
  ${envBlock} <key>StandardOutPath</key>
28560
- <string>${escapeXml2(resolve29(logDir2, "failproofaid.log"))}</string>
29878
+ <string>${escapeXml2(resolve30(logDir2, "failproofaid.log"))}</string>
28561
29879
  <key>StandardErrorPath</key>
28562
- <string>${escapeXml2(resolve29(logDir2, "failproofaid.err.log"))}</string>
29880
+ <string>${escapeXml2(resolve30(logDir2, "failproofaid.err.log"))}</string>
28563
29881
  </dict>
28564
29882
  </plist>
28565
29883
  `;
@@ -28691,10 +30009,10 @@ function readInstalledServiceDefinition2() {
28691
30009
  if (!isDaemonSupportedPlatform2())
28692
30010
  return null;
28693
30011
  const path3 = daemonServiceFilePath2();
28694
- if (!path3 || !existsSync38(path3))
30012
+ if (!path3 || !existsSync41(path3))
28695
30013
  return null;
28696
30014
  try {
28697
- return readFileSync30(path3, "utf8");
30015
+ return readFileSync34(path3, "utf8");
28698
30016
  } catch {
28699
30017
  return null;
28700
30018
  }
@@ -28855,7 +30173,7 @@ async function uninstallDaemonService2() {
28855
30173
  try {
28856
30174
  if (process.platform === "linux") {
28857
30175
  const unitPath = systemdUnitPath2();
28858
- if (!existsSync38(unitPath))
30176
+ if (!existsSync41(unitPath))
28859
30177
  return;
28860
30178
  try {
28861
30179
  runPrivileged2("systemctl", ["disable", "--now", systemdUnitName2()]);
@@ -28866,7 +30184,7 @@ async function uninstallDaemonService2() {
28866
30184
  } catch {}
28867
30185
  } else {
28868
30186
  const plistPath = launchdPlistPath2();
28869
- if (!existsSync38(plistPath))
30187
+ if (!existsSync41(plistPath))
28870
30188
  return;
28871
30189
  try {
28872
30190
  runPrivileged2("launchctl", ["unload", "-w", plistPath]);
@@ -28883,7 +30201,7 @@ function daemonVersionSkew2() {
28883
30201
  const recorded = readVersionFile()?.daemon;
28884
30202
  if (!recorded)
28885
30203
  return null;
28886
- if (!existsSync38(installedBinaryPath(recorded)))
30204
+ if (!existsSync41(installedBinaryPath(recorded)))
28887
30205
  return null;
28888
30206
  return recorded === version2 ? null : { installed: recorded, expected: version2 };
28889
30207
  }
@@ -28901,7 +30219,7 @@ function daemonServiceStatus2() {
28901
30219
  if (!isDaemonSupportedPlatform2())
28902
30220
  return "unsupported-platform";
28903
30221
  if (process.platform === "linux") {
28904
- if (!existsSync38(systemdUnitPath2()))
30222
+ if (!existsSync41(systemdUnitPath2()))
28905
30223
  return "not-installed";
28906
30224
  try {
28907
30225
  const out = execFileSync5("systemctl", ["is-active", systemdUnitName2()], {
@@ -28914,7 +30232,7 @@ function daemonServiceStatus2() {
28914
30232
  }
28915
30233
  }
28916
30234
  const plistPath = launchdPlistPath2();
28917
- if (!existsSync38(plistPath))
30235
+ if (!existsSync41(plistPath))
28918
30236
  return "not-installed";
28919
30237
  try {
28920
30238
  const root = typeof process.getuid === "function" && process.getuid() === 0;
@@ -28933,6 +30251,28 @@ function daemonServiceStatus2() {
28933
30251
  return "stopped";
28934
30252
  }
28935
30253
  }
30254
+ function daemonStartedAtMs() {
30255
+ if (process.platform !== "linux")
30256
+ return null;
30257
+ if (!existsSync41(systemdUnitPath2()))
30258
+ return null;
30259
+ try {
30260
+ const raw = execFileSync5("systemctl", ["show", systemdUnitName2(), "-p", "ActiveEnterTimestampMonotonic", "--value"], { stdio: ["ignore", "pipe", "ignore"], timeout: SERVICE_CMD_TIMEOUT_MS2 }).toString().trim();
30261
+ return startedAtFromMonotonic(Number(raw), Math.floor(Number(process.hrtime.bigint()) / 1000), Date.now());
30262
+ } catch {
30263
+ return null;
30264
+ }
30265
+ }
30266
+ function startedAtFromMonotonic(activeEnterMonotonicUs, monotonicNowUs, nowMs) {
30267
+ if (!Number.isFinite(activeEnterMonotonicUs) || activeEnterMonotonicUs <= 0)
30268
+ return null;
30269
+ if (!Number.isFinite(monotonicNowUs) || monotonicNowUs <= 0)
30270
+ return null;
30271
+ const activeForMs = (monotonicNowUs - activeEnterMonotonicUs) / 1000;
30272
+ if (activeForMs < 0)
30273
+ return null;
30274
+ return Math.round(nowMs - activeForMs);
30275
+ }
28936
30276
  var SERVICE_CMD_TIMEOUT_MS2 = 1e4, SERVICE_START_TIMEOUT_MS2 = 5000, SERVICE_START_POLL_MS2 = 100, SERVICE_SETTLE_MS2 = 750, SUDO_PROMPT_TIMEOUT_MS2 = 120000, UNIT_UNSAFE_RE2, DAEMON_PROBE_TIMEOUT_MS2 = 5000, DAEMON_PROBE_READY_TIMEOUT_MS2 = 1e4, DAEMON_PROBE_RETRY_MS2 = 150;
28937
30277
  var init_daemon_service2 = __esm(() => {
28938
30278
  init_hook_logger();
@@ -28949,7 +30289,7 @@ __export(exports_uninstall_cli, {
28949
30289
  serviceStateLabel: () => serviceStateLabel,
28950
30290
  runUninstallCommand: () => runUninstallCommand
28951
30291
  });
28952
- import { existsSync as existsSync39, rmSync as rmSync14 } from "node:fs";
30292
+ import { existsSync as existsSync42, rmSync as rmSync16 } from "node:fs";
28953
30293
  function survey(cwd) {
28954
30294
  const clis = [];
28955
30295
  for (const id of listInstallableIds()) {
@@ -28970,9 +30310,9 @@ function survey(cwd) {
28970
30310
  return {
28971
30311
  clis,
28972
30312
  servicePath,
28973
- serviceInstalled: !!servicePath && existsSync39(servicePath),
30313
+ serviceInstalled: !!servicePath && existsSync42(servicePath),
28974
30314
  daemonConfigured,
28975
- homeExists: existsSync39(failproofaiHome())
30315
+ homeExists: existsSync42(failproofaiHome())
28976
30316
  };
28977
30317
  }
28978
30318
  async function runUninstallCommand(opts = {}) {
@@ -29070,7 +30410,7 @@ async function runUninstallCommand(opts = {}) {
29070
30410
  } catch {}
29071
30411
  try {
29072
30412
  await uninstallDaemonService();
29073
- if (found.servicePath && existsSync39(found.servicePath)) {
30413
+ if (found.servicePath && existsSync42(found.servicePath)) {
29074
30414
  failures.push(`the service at ${found.servicePath} is still there (most often: no sudo). ` + `Remove it with the commands below.`);
29075
30415
  } else {
29076
30416
  lines.push(`✓ stopped and removed the daemon service`);
@@ -29082,7 +30422,7 @@ async function runUninstallCommand(opts = {}) {
29082
30422
  }
29083
30423
  if (opts.purge && found.homeExists) {
29084
30424
  try {
29085
- rmSync14(failproofaiHome(), { recursive: true, force: true });
30425
+ rmSync16(failproofaiHome(), { recursive: true, force: true });
29086
30426
  purged = true;
29087
30427
  lines.push(`✓ deleted ${failproofaiHome()}`);
29088
30428
  } catch (err) {
@@ -29093,7 +30433,7 @@ async function runUninstallCommand(opts = {}) {
29093
30433
  lines.push(``, `Not everything could be removed:`);
29094
30434
  for (const f of failures)
29095
30435
  lines.push(` ✗ ${f}`);
29096
- if (found.servicePath && existsSync39(found.servicePath)) {
30436
+ if (found.servicePath && existsSync42(found.servicePath)) {
29097
30437
  lines.push(``, `To remove the service by hand:`, ...manualServiceRemoval(found.servicePath), ``, `(\`${daemonStatusCommand()}\` shows its current state.)`);
29098
30438
  }
29099
30439
  return { exitCode: 1, lines, planLines, purged };
@@ -29136,6 +30476,8 @@ var exports_tui = {};
29136
30476
  __export(exports_tui, {
29137
30477
  truncate: () => truncate2,
29138
30478
  summarize: () => summarize2,
30479
+ stepOpen: () => stepOpen2,
30480
+ step: () => step2,
29139
30481
  selectOne: () => selectOne2,
29140
30482
  renderLaunchBanner: () => renderLaunchBanner2,
29141
30483
  renderBrandLogo: () => renderBrandLogo2,
@@ -29172,11 +30514,11 @@ function truecolorEnabled2() {
29172
30514
  }
29173
30515
  function paint2(on) {
29174
30516
  const tc = on && truecolorEnabled2();
29175
- const mk = (h, bold = false) => (s) => {
30517
+ const mk = (h, bold2 = false) => (s) => {
29176
30518
  if (!on)
29177
30519
  return s;
29178
30520
  const code = tc ? `38;2;${h.rgb[0]};${h.rgb[1]};${h.rgb[2]}` : h.basic;
29179
- return `${ESC2}[${bold ? "1;" : ""}${code}m${s}${ESC2}[0m`;
30521
+ return `${ESC2}[${bold2 ? "1;" : ""}${code}m${s}${ESC2}[0m`;
29180
30522
  };
29181
30523
  return {
29182
30524
  bold: (s) => on ? `${ESC2}[1m${s}${ESC2}[0m` : s,
@@ -29282,19 +30624,40 @@ function intro2(message, stdout = process.stdout) {
29282
30624
  }
29283
30625
  function renderLaunchBanner2(version3, stdout = process.stdout) {
29284
30626
  const c2 = paint2(colorsEnabled2(stdout));
29285
- const row = (label, value) => ` ${c2.guide(label.padEnd(9))}${value}`;
30627
+ const row2 = (label, value) => ` ${c2.guide(label.padEnd(9))}${value}`;
29286
30628
  const header = stdout.isTTY ? renderBrandLogo2(stdout) : [" failproof ai", ` ${TAGLINE2}`];
29287
30629
  return [
29288
30630
  "",
29289
30631
  ...header,
29290
30632
  "",
29291
- row("version", c2.pink(version3)),
29292
- row("star", c2.dim("https://github.com/failproofai/failproofai")),
29293
- row("docs", c2.dim("https://docs.befailproof.ai/introduction")),
29294
- row("discord", c2.dim("https://discord.befailproof.ai/")),
30633
+ row2("version", c2.pink(version3)),
30634
+ row2("star", c2.dim("https://github.com/failproofai/failproofai")),
30635
+ row2("docs", c2.dim("https://docs.befailproof.ai/introduction")),
30636
+ row2("discord", c2.dim("https://discord.befailproof.ai/")),
29295
30637
  ""
29296
30638
  ];
29297
30639
  }
30640
+ function step2(message, summary, stdout = process.stdout) {
30641
+ const c2 = paint2(colorsEnabled2(stdout));
30642
+ const rows = summary === undefined ? [] : Array.isArray(summary) ? summary : [summary];
30643
+ if (!stdout.isTTY) {
30644
+ if (rows.length)
30645
+ stdout.write(`${message}: ${rows.join(" ")}
30646
+ `);
30647
+ return;
30648
+ }
30649
+ const cols = stdout.columns || 80;
30650
+ const lines = [c2.dim(BAR2), truncate2(`${c2.dim(STEP_DONE2)} ${message}`, cols - 1)];
30651
+ for (const r of rows)
30652
+ lines.push(truncate2(`${c2.dim(BAR2)} ${c2.dim(r)}`, cols - 1));
30653
+ writeLines2(stdout, lines);
30654
+ }
30655
+ function stepOpen2(message, stdout = process.stdout) {
30656
+ const c2 = paint2(colorsEnabled2(stdout));
30657
+ if (!stdout.isTTY)
30658
+ return;
30659
+ writeLines2(stdout, [c2.dim(BAR2), `${c2.guide(STEP_ACTIVE2)} ${c2.bold(message)}`]);
30660
+ }
29298
30661
  function outro2(message, opts = {}, stdout = process.stdout) {
29299
30662
  const c2 = paint2(colorsEnabled2(stdout));
29300
30663
  const ok2 = opts.ok !== false;
@@ -29364,11 +30727,11 @@ function runPrompt2(p) {
29364
30727
  lines.push(`${c2.dim(BAR2)} ${c2.dim(`↑ ${above} more`)}`);
29365
30728
  const budget = Math.max(6, cols - nameCol - 10);
29366
30729
  for (let ri = start;ri < end; ri++) {
29367
- const row = rows[ri];
29368
- if (row.kind === "header") {
29369
- lines.push(`${c2.dim(BAR2)} ${c2.dim(row.text)}`);
30730
+ const row2 = rows[ri];
30731
+ if (row2.kind === "header") {
30732
+ lines.push(`${c2.dim(BAR2)} ${c2.dim(row2.text)}`);
29370
30733
  } else {
29371
- lines.push(`${c2.dim(BAR2)} ${p.renderRow(row.index, row.index === cursor2, budget)}`);
30734
+ lines.push(`${c2.dim(BAR2)} ${p.renderRow(row2.index, row2.index === cursor2, budget)}`);
29372
30735
  }
29373
30736
  }
29374
30737
  if (below > 0)
@@ -29387,7 +30750,7 @@ function runPrompt2(p) {
29387
30750
  `${c2.dim(BAR2)} ${c2.dim(summary)}`
29388
30751
  ]);
29389
30752
  };
29390
- return new Promise((resolve30) => {
30753
+ return new Promise((resolve31) => {
29391
30754
  hideCursor2(stdout);
29392
30755
  repaint2(stdout, region, build());
29393
30756
  readline3.emitKeypressEvents(stdin);
@@ -29403,7 +30766,7 @@ function runPrompt2(p) {
29403
30766
  const finish = (result) => {
29404
30767
  cleanup();
29405
30768
  collapse(result);
29406
- resolve30(result);
30769
+ resolve31(result);
29407
30770
  };
29408
30771
  function onKey(_s, key) {
29409
30772
  if (!key)
@@ -29533,7 +30896,7 @@ function promptText2(opts) {
29533
30896
  return error ? { ok: false, error } : { ok: true, value };
29534
30897
  };
29535
30898
  if (!stdin.isTTY) {
29536
- return new Promise((resolve30) => {
30899
+ return new Promise((resolve31) => {
29537
30900
  let buf = "";
29538
30901
  const onData = (chunk) => {
29539
30902
  buf += String(chunk);
@@ -29546,21 +30909,21 @@ function promptText2(opts) {
29546
30909
  stdin.removeListener("end", done);
29547
30910
  const r = accept(buf.split(`
29548
30911
  `)[0] ?? "");
29549
- resolve30(r.ok ? r.value : null);
30912
+ resolve31(r.ok ? r.value : null);
29550
30913
  };
29551
30914
  stdin.on("data", onData);
29552
30915
  stdin.on("end", done);
29553
30916
  });
29554
30917
  }
29555
- return new Promise((resolve30) => {
30918
+ return new Promise((resolve31) => {
29556
30919
  let value = "";
29557
30920
  const draw = (error) => {
29558
30921
  const cols = stdout.columns || 80;
29559
30922
  const shown = opts.mask ? "•".repeat(value.length) : value;
29560
- const hint = opts.hint ? ` ${c2.dim(opts.hint)}` : "";
29561
- const line = truncate2(`${c2.bold(opts.message)} ${shown}${hint}`, cols - 1);
30923
+ const hint = opts.hint && value.length === 0 ? ` ${c2.dim(opts.hint)}` : "";
30924
+ const line = truncate2(`${opts.prefix ?? ""}${c2.bold(opts.message)} ${shown}${hint}`, cols - 1);
29562
30925
  const err = error ? `
29563
- ${truncate2(c2.warn(error), cols - 3)}` : "";
30926
+ ${opts.prefix ?? " "}${truncate2(c2.warn(error), cols - 3)}` : "";
29564
30927
  stdout.write(`\r\x1B[2K${line}${err}`);
29565
30928
  if (err)
29566
30929
  stdout.write("\x1B[1A");
@@ -29582,14 +30945,14 @@ function promptText2(opts) {
29582
30945
  return;
29583
30946
  if (key.ctrl && (key.name === "c" || key.name === "d") || key.name === "escape") {
29584
30947
  cleanup();
29585
- resolve30(null);
30948
+ resolve31(null);
29586
30949
  return;
29587
30950
  }
29588
30951
  if (key.name === "return" || key.name === "enter") {
29589
30952
  const r = accept(value);
29590
30953
  if (r.ok) {
29591
30954
  cleanup();
29592
- resolve30(r.value);
30955
+ resolve31(r.value);
29593
30956
  } else {
29594
30957
  draw(r.error);
29595
30958
  }
@@ -29657,9 +31020,9 @@ __export(exports_manager, {
29657
31020
  getSettingsPath: () => getSettingsPath2
29658
31021
  });
29659
31022
  import { execSync as execSync7 } from "node:child_process";
29660
- import { existsSync as existsSync40 } from "node:fs";
29661
- import { resolve as resolve30, basename as basename8 } from "node:path";
29662
- import { platform as platform5, arch as arch4, release as release3, hostname as hostname5 } from "node:os";
31023
+ import { existsSync as existsSync43 } from "node:fs";
31024
+ import { resolve as resolve31, basename as basename8 } from "node:path";
31025
+ import { platform as platform5, arch as arch4, release as release3, hostname as hostname6 } from "node:os";
29663
31026
  function getSettingsPath2(scope, cwd) {
29664
31027
  return claudeCode.getSettingsPath(scope, cwd);
29665
31028
  }
@@ -29766,9 +31129,9 @@ async function installHooksImpl2(policyNames, scope = "user", cwd, includeBeta =
29766
31129
  delete configToWrite.customPoliciesPath;
29767
31130
  delete configToWrite.customPoliciesPaths;
29768
31131
  } else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
29769
- const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve30(path3));
29770
- const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve30(path3)).filter((path3) => {
29771
- if (existsSync40(path3))
31132
+ const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve31(path3));
31133
+ const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve31(path3)).filter((path3) => {
31134
+ if (existsSync43(path3))
29772
31135
  return true;
29773
31136
  console.log(`Dropping custom policies path (file no longer exists): ${path3}`);
29774
31137
  return false;
@@ -29854,7 +31217,7 @@ Enabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}
29854
31217
  platform: platform5(),
29855
31218
  arch: arch4(),
29856
31219
  os_release: release3(),
29857
- hostname_hash: hashToId2(hostname5()),
31220
+ hostname_hash: hashToId2(hostname6()),
29858
31221
  has_custom_hooks_path: !!(configToWrite.customPoliciesPaths?.length || configToWrite.customPoliciesPath),
29859
31222
  has_policy_params: !!(configToWrite.policyParams && Object.keys(configToWrite.policyParams).length > 0),
29860
31223
  param_policy_names: configToWrite.policyParams ? Object.keys(configToWrite.policyParams) : [],
@@ -29945,7 +31308,7 @@ async function removeHooks2(policyNames, scope = "user", cwd, opts) {
29945
31308
  platform: platform5(),
29946
31309
  arch: arch4(),
29947
31310
  os_release: release3(),
29948
- hostname_hash: hashToId2(hostname5())
31311
+ hostname_hash: hashToId2(hostname6())
29949
31312
  });
29950
31313
  } catch {}
29951
31314
  console.log(`Disabled ${policyNames.length - notEnabled.length} policy(ies).`);
@@ -29960,7 +31323,7 @@ async function removeHooks2(policyNames, scope = "user", cwd, opts) {
29960
31323
  const scopesToRemove = scope === "all" ? [...integration.scopes] : integration.scopes.includes(scope) ? [scope] : [];
29961
31324
  for (const s of scopesToRemove) {
29962
31325
  const settingsPaths = settingsPathsFor(integration, s, cwd);
29963
- const existing = settingsPaths.filter((p) => existsSync40(p));
31326
+ const existing = settingsPaths.filter((p) => existsSync43(p));
29964
31327
  if (existing.length === 0) {
29965
31328
  if (scope !== "all" && selectedClis.length === 1) {
29966
31329
  console.log("No settings file found. Nothing to remove.");
@@ -30010,7 +31373,7 @@ async function removeHooks2(policyNames, scope = "user", cwd, opts) {
30010
31373
  platform: platform5(),
30011
31374
  arch: arch4(),
30012
31375
  os_release: release3(),
30013
- hostname_hash: hashToId2(hostname5())
31376
+ hostname_hash: hashToId2(hostname6())
30014
31377
  });
30015
31378
  } catch {}
30016
31379
  if (scope === "all") {
@@ -30109,16 +31472,16 @@ Failproof AI Hook Policies
30109
31472
  console.log(` ${"─".repeat(scopeHeaderWidth)}${"─".repeat(nameColWidth)}${"─".repeat(38)}`);
30110
31473
  const printMultiScopeRow = (policy) => {
30111
31474
  const enabled = enabledSet.has(policy.name);
30112
- let row = " ";
31475
+ let row2 = " ";
30113
31476
  for (const _scope of installedScopes) {
30114
31477
  if (enabled) {
30115
- row += `\x1B[32m✓ ON\x1B[0m` + " ".repeat(COL - 4);
31478
+ row2 += `\x1B[32m✓ ON\x1B[0m` + " ".repeat(COL - 4);
30116
31479
  } else {
30117
- row += " OFF" + " ".repeat(COL - 5);
31480
+ row2 += " OFF" + " ".repeat(COL - 5);
30118
31481
  }
30119
31482
  }
30120
- row += policy.name.padEnd(nameColWidth) + policy.description;
30121
- console.log(row);
31483
+ row2 += policy.name.padEnd(nameColWidth) + policy.description;
31484
+ console.log(row2);
30122
31485
  printParamsSummary(policy.name, ` ${" ".repeat(scopeHeaderWidth)}`);
30123
31486
  };
30124
31487
  for (const policy of regularPolicies)
@@ -30159,9 +31522,9 @@ Failproof AI Hook Policies
30159
31522
  console.log(`
30160
31523
  ── Custom Policies ───────────────────────`);
30161
31524
  for (const path3 of explicitPaths) {
30162
- const absPath = resolve30(findProjectConfigDir(cwd ?? process.cwd()), path3);
31525
+ const absPath = resolve31(findProjectConfigDir(cwd ?? process.cwd()), path3);
30163
31526
  console.log(` ${absPath}`);
30164
- if (!existsSync40(absPath)) {
31527
+ if (!existsSync43(absPath)) {
30165
31528
  console.log(` \x1B[31m✗ File not found: ${absPath}\x1B[0m`);
30166
31529
  continue;
30167
31530
  }
@@ -30180,7 +31543,7 @@ Failproof AI Hook Policies
30180
31543
  console.log();
30181
31544
  }
30182
31545
  const base = findProjectConfigDir(cwd ?? process.cwd());
30183
- const projectDir = resolve30(base, ".failproofai", "policies");
31546
+ const projectDir = resolve31(base, ".failproofai", "policies");
30184
31547
  const userDir = customPoliciesDir();
30185
31548
  const sameDir = userDir === projectDir;
30186
31549
  const conventionDirs = [
@@ -30393,32 +31756,32 @@ async function promptCliTargetSelection(detected, action = "install") {
30393
31756
  cursorHidden = false;
30394
31757
  }
30395
31758
  }
30396
- const { dim, bold, guide: teal, pink, pinkBold } = paint(!process.env.NO_COLOR);
31759
+ const { dim: dim2, bold: bold2, guide: teal, pink: pink2, pinkBold } = paint(!process.env.NO_COLOR);
30397
31760
  const heading = action === "uninstall" ? "Remove failproofai hooks" : "Install failproofai hooks";
30398
31761
  function render() {
30399
31762
  const cols = process.stdout.columns || 100;
30400
31763
  hideCursor3();
30401
- const g = dim(BAR);
30402
- const lines = [g, `${teal(STEP_ACTIVE)} ${bold(heading)}`];
30403
- for (const row of buildDisplayRows()) {
30404
- if (row.kind === "blank") {
31764
+ const g = dim2(BAR);
31765
+ const lines = [g, `${teal(STEP_ACTIVE)} ${bold2(heading)}`];
31766
+ for (const row2 of buildDisplayRows()) {
31767
+ if (row2.kind === "blank") {
30405
31768
  lines.push(g);
30406
31769
  continue;
30407
31770
  }
30408
- if (row.kind === "header") {
30409
- const hint = row.hint ? ` ${dim("· " + row.hint)}` : "";
30410
- lines.push(`${g} ${dim(row.title)}${hint}`);
31771
+ if (row2.kind === "header") {
31772
+ const hint = row2.hint ? ` ${dim2("· " + row2.hint)}` : "";
31773
+ lines.push(`${g} ${dim2(row2.title)}${hint}`);
30411
31774
  continue;
30412
31775
  }
30413
- const opt = row.option;
30414
- const isActive = row.itemIndex === cursor2;
30415
- const caret = isActive ? pink("❯") : " ";
30416
- const marker = opt.isAll ? teal("★") : opt.detected ? pink("●") : dim("○");
30417
- const label = isActive ? pinkBold(opt.label) : opt.detected ? opt.label : dim(opt.label);
31776
+ const opt = row2.option;
31777
+ const isActive = row2.itemIndex === cursor2;
31778
+ const caret = isActive ? pink2("❯") : " ";
31779
+ const marker = opt.isAll ? teal("★") : opt.detected ? pink2("●") : dim2("○");
31780
+ const label = isActive ? pinkBold(opt.label) : opt.detected ? opt.label : dim2(opt.label);
30418
31781
  lines.push(`${g} ${caret} ${marker} ${label}`);
30419
31782
  }
30420
31783
  lines.push(g);
30421
- lines.push(`${g} ${dim("↑/↓ move · ↵ select · esc cancel")}`);
31784
+ lines.push(`${g} ${dim2("↑/↓ move · ↵ select · esc cancel")}`);
30422
31785
  if (lastLineCount > 0) {
30423
31786
  process.stdout.write(`\x1B[${lastLineCount}A\x1B[J`);
30424
31787
  }
@@ -30428,7 +31791,7 @@ async function promptCliTargetSelection(detected, action = "install") {
30428
31791
  lastLineCount = lines.length;
30429
31792
  }
30430
31793
  const itemCount = options.length;
30431
- return new Promise((resolve31) => {
31794
+ return new Promise((resolve32) => {
30432
31795
  render();
30433
31796
  readline4.emitKeypressEvents(process.stdin);
30434
31797
  const wasRaw = process.stdin.isRaw;
@@ -30461,7 +31824,7 @@ async function promptCliTargetSelection(detected, action = "install") {
30461
31824
  cleanup();
30462
31825
  process.stdout.write(`
30463
31826
  `);
30464
- resolve31(options[cursor2].value);
31827
+ resolve32(options[cursor2].value);
30465
31828
  }
30466
31829
  }
30467
31830
  process.stdin.on("keypress", onKey);
@@ -30487,7 +31850,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
30487
31850
  const WINDOW_SIZE = 10;
30488
31851
  const useColor = !process.env.NO_COLOR;
30489
31852
  const c2 = paint(useColor);
30490
- const { dim, bold } = c2;
31853
+ const { dim: dim2, bold: bold2 } = c2;
30491
31854
  const stepMark = c2.guide;
30492
31855
  const activeName = c2.pinkBold;
30493
31856
  const selectedMark = c2.pink;
@@ -30556,24 +31919,24 @@ async function promptPolicySelection2(preSelected, options = {}) {
30556
31919
  if (shown > 0 && cursor2 >= shown)
30557
31920
  cursor2 = shown - 1;
30558
31921
  const selectedCount = items.filter((i) => i.selected).length;
30559
- const g = dim(BAR);
31922
+ const g = dim2(BAR);
30560
31923
  const lines = [];
30561
31924
  lines.push(g);
30562
- lines.push(`${stepMark(STEP_ACTIVE)} ${bold("Choose the policies to enable")} ${dim("·")} ` + `${stepMark(String(selectedCount))} ${dim("selected")}`);
31925
+ lines.push(`${stepMark(STEP_ACTIVE)} ${bold2("Choose the policies to enable")} ${dim2("·")} ` + `${stepMark(String(selectedCount))} ${dim2("selected")}`);
30563
31926
  const blockCursor = useColor ? "\x1B[7m \x1B[0m" : "_";
30564
- const countLabel = search ? dim(`${shown}/${total} shown`) : dim(`${total} policies`);
30565
- lines.push(`${g} ${dim("filter ›")} ${search}${blockCursor} ${countLabel}`);
31927
+ const countLabel = search ? dim2(`${shown}/${total} shown`) : dim2(`${total} policies`);
31928
+ lines.push(`${g} ${dim2("filter ›")} ${search}${blockCursor} ${countLabel}`);
30566
31929
  lines.push(g);
30567
31930
  if (shown === 0) {
30568
- lines.push(`${g} ${dim(`no policies match “${search}”`)}`);
31931
+ lines.push(`${g} ${dim2(`no policies match “${search}”`)}`);
30569
31932
  for (let i = 0;i < WINDOW_SIZE + 1; i++)
30570
31933
  lines.push(g);
30571
31934
  } else {
30572
31935
  const displayRows3 = buildDisplayRows(filtered);
30573
31936
  let cursorDisplayRow = 0;
30574
31937
  for (let i = 0;i < displayRows3.length; i++) {
30575
- const row = displayRows3[i];
30576
- if (row.kind === "item" && row.filteredIndex === cursor2) {
31938
+ const row2 = displayRows3[i];
31939
+ if (row2.kind === "item" && row2.filteredIndex === cursor2) {
30577
31940
  cursorDisplayRow = i;
30578
31941
  break;
30579
31942
  }
@@ -30583,30 +31946,30 @@ async function promptPolicySelection2(preSelected, options = {}) {
30583
31946
  windowStart = Math.min(windowStart, Math.max(0, displayRows3.length - WINDOW_SIZE));
30584
31947
  const windowEnd = Math.min(displayRows3.length, windowStart + WINDOW_SIZE);
30585
31948
  const aboveItems = displayRows3.slice(0, windowStart).filter((r) => r.kind === "item").length;
30586
- lines.push(aboveItems > 0 ? `${g} ${dim(`↑ ${aboveItems} more`)}` : g);
31949
+ lines.push(aboveItems > 0 ? `${g} ${dim2(`↑ ${aboveItems} more`)}` : g);
30587
31950
  for (let i = windowStart;i < windowEnd; i++) {
30588
- const row = displayRows3[i];
30589
- if (row.kind === "header") {
30590
- lines.push(`${g} ${dim(row.category.toUpperCase())} ${dim(`${row.enabledCount}/${row.totalCount}`)}`);
31951
+ const row2 = displayRows3[i];
31952
+ if (row2.kind === "header") {
31953
+ lines.push(`${g} ${dim2(row2.category.toUpperCase())} ${dim2(`${row2.enabledCount}/${row2.totalCount}`)}`);
30591
31954
  } else {
30592
- const item = row.item;
30593
- const active = row.filteredIndex === cursor2;
30594
- const box = item.selected ? selectedMark(CHECK_ON) : dim(CHECK_OFF);
31955
+ const item = row2.item;
31956
+ const active = row2.filteredIndex === cursor2;
31957
+ const box = item.selected ? selectedMark(CHECK_ON) : dim2(CHECK_OFF);
30595
31958
  const rawName = item.name.padEnd(NAME_COL);
30596
- const name = active ? activeName(rawName) : item.selected ? rawName : dim(rawName);
31959
+ const name = active ? activeName(rawName) : item.selected ? rawName : dim2(rawName);
30597
31960
  const beta = item.beta ? ` ${betaTag("beta")}` : "";
30598
31961
  const descWidth = Math.max(8, cols - NAME_COL - 8 - (item.beta ? 5 : 0));
30599
- const desc = dim(truncate(item.description, descWidth));
31962
+ const desc = dim2(truncate(item.description, descWidth));
30600
31963
  lines.push(`${g} ${box} ${name}${beta} ${desc}`);
30601
31964
  }
30602
31965
  }
30603
31966
  for (let i = windowEnd - windowStart;i < WINDOW_SIZE; i++)
30604
31967
  lines.push(g);
30605
31968
  const belowItems = displayRows3.slice(windowEnd).filter((r) => r.kind === "item").length;
30606
- lines.push(belowItems > 0 ? `${g} ${dim(`↓ ${belowItems} more`)}` : g);
31969
+ lines.push(belowItems > 0 ? `${g} ${dim2(`↓ ${belowItems} more`)}` : g);
30607
31970
  }
30608
31971
  lines.push(g);
30609
- lines.push(`${g} ${dim("↑/↓ move · space select · ctrl+a all · ↵ save · type to filter · esc clear")}`);
31972
+ lines.push(`${g} ${dim2("↑/↓ move · space select · ctrl+a all · ↵ save · type to filter · esc clear")}`);
30610
31973
  if (lastLineCount > 0)
30611
31974
  process.stdout.write(`\x1B[${lastLineCount}A\x1B[J`);
30612
31975
  process.stdout.write(lines.map((l) => truncate(l, cols)).join(`
@@ -30614,7 +31977,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
30614
31977
  `);
30615
31978
  lastLineCount = lines.length;
30616
31979
  }
30617
- return new Promise((resolve31) => {
31980
+ return new Promise((resolve32) => {
30618
31981
  render();
30619
31982
  process.stdin.setRawMode(true);
30620
31983
  process.stdin.resume();
@@ -30647,11 +32010,11 @@ async function promptPolicySelection2(preSelected, options = {}) {
30647
32010
  const selected = items.filter((i) => i.selected).map((i) => i.name);
30648
32011
  if (lastLineCount > 0)
30649
32012
  process.stdout.write(`\x1B[${lastLineCount}A\x1B[J`);
30650
- process.stdout.write(`${dim(BAR)}
30651
- ${dim(STEP_DONE)} Policies
30652
- ${dim(BAR)} ${dim(`${selected.length} selected`)}
32013
+ process.stdout.write(`${dim2(BAR)}
32014
+ ${dim2(STEP_DONE)} Policies
32015
+ ${dim2(BAR)} ${dim2(`${selected.length} selected`)}
30653
32016
  `);
30654
- resolve31(selected);
32017
+ resolve32(selected);
30655
32018
  } else if (key.name === "escape") {
30656
32019
  search = "";
30657
32020
  cursor2 = 0;
@@ -30696,10 +32059,43 @@ __export(exports_cli2, {
30696
32059
  runScheduledAudit: () => runScheduledAudit2,
30697
32060
  runPostSetupAudit: () => runPostSetupAudit2,
30698
32061
  runAuditCli: () => runAuditCli2,
32062
+ helpText: () => helpText2,
30699
32063
  buildSummary: () => buildSummary2,
30700
32064
  EXIT_AUDIT_ALREADY_RUNNING: () => EXIT_AUDIT_ALREADY_RUNNING2,
30701
32065
  AUDIT_STAGES: () => AUDIT_STAGES2
30702
32066
  });
32067
+ function helpText2() {
32068
+ const row2 = (command, lines) => {
32069
+ const pad = " ".repeat(Math.max(1, HELP_DESC_COL2 - 2 - command.length));
32070
+ return lines.map((line, i) => i === 0 ? ` ${c2(CYAN2, command)}${pad}${line}` : `${" ".repeat(HELP_DESC_COL2)}${line}`).join(`
32071
+ `);
32072
+ };
32073
+ return [
32074
+ `${c2(BOLD2, "failproofai audit")} — review your agent CLIs for risky and wasteful patterns.`,
32075
+ c2(DIM2, "Everything runs on this machine; only a scheduled digest ever leaves it."),
32076
+ "",
32077
+ c2(BOLD2, "USAGE"),
32078
+ row2("failproofai audit", [
32079
+ "Scan your session history, then open",
32080
+ `http://localhost:${DASHBOARD_PORT2}/audit`
32081
+ ]),
32082
+ row2("failproofai audit --schedule [days]", [
32083
+ "Scan on a timer and email the findings.",
32084
+ "Default 7 days, range 1-90.",
32085
+ "Signs you in the first time; add",
32086
+ "--email <address> to skip a prompt."
32087
+ ]),
32088
+ row2("failproofai audit --no-schedule", ["Stop the timer. Leaves you signed in."]),
32089
+ row2("failproofai audit --status", [
32090
+ "Whether scheduling is on, where reports",
32091
+ "go, the daemon's state, and when the",
32092
+ "next scan is due."
32093
+ ]),
32094
+ row2("failproofai audit -h, --help", ["Show this help."]),
32095
+ ""
32096
+ ].join(`
32097
+ `);
32098
+ }
30703
32099
  function colorOn2() {
30704
32100
  if (process.env.NO_COLOR && process.env.NO_COLOR !== "")
30705
32101
  return false;
@@ -30874,6 +32270,13 @@ async function runScheduledAudit2() {
30874
32270
  }
30875
32271
  process.stdout.write(`failproofai: audit complete — ${num2(result.eventsScanned)} tool calls across ` + `${num2(result.transcripts.scanned)} sessions, ${num2(result.totals.hits)} hits
30876
32272
  `);
32273
+ const outcome = await reportHarm(result);
32274
+ const line = describeOutcome2(outcome);
32275
+ if (line) {
32276
+ const stream = outcome.kind === "sent" ? process.stdout : process.stderr;
32277
+ stream.write(`${line}
32278
+ `);
32279
+ }
30877
32280
  return 0;
30878
32281
  } finally {
30879
32282
  attempt.lock.release();
@@ -30929,7 +32332,7 @@ async function runPostSetupAudit2() {
30929
32332
  }
30930
32333
  async function runAuditCli2(args) {
30931
32334
  if (args.includes("--help") || args.includes("-h")) {
30932
- process.stdout.write(HELP2);
32335
+ process.stdout.write(helpText2());
30933
32336
  process.exit(0);
30934
32337
  }
30935
32338
  if (args.includes("--scheduled")) {
@@ -30938,15 +32341,71 @@ async function runAuditCli2(args) {
30938
32341
  die2(`\`audit --scheduled\` takes no other arguments (got: ${extra}).`);
30939
32342
  process.exit(await runScheduledAudit2());
30940
32343
  }
32344
+ if (args.includes("--status")) {
32345
+ const extra = args.find((a) => a !== "--status");
32346
+ if (extra)
32347
+ die2(`\`audit --status\` takes no other arguments (got: ${extra}).`);
32348
+ const { runScheduleStatus: runScheduleStatus2 } = await Promise.resolve().then(() => (init_schedule_cli(), exports_schedule_cli));
32349
+ runScheduleStatus2();
32350
+ process.exit(0);
32351
+ }
32352
+ if (args.includes("--no-schedule")) {
32353
+ const extra = args.find((a) => a !== "--no-schedule");
32354
+ if (extra)
32355
+ die2(`\`audit --no-schedule\` takes no other arguments (got: ${extra}).`);
32356
+ const { runScheduleOff: runScheduleOff2 } = await Promise.resolve().then(() => (init_schedule_cli(), exports_schedule_cli));
32357
+ runScheduleOff2();
32358
+ process.exit(0);
32359
+ }
32360
+ const scheduleAt = args.indexOf("--schedule");
32361
+ if (scheduleAt !== -1) {
32362
+ let days;
32363
+ let email;
32364
+ for (let i = 0;i < args.length; i += 1) {
32365
+ const a = args[i];
32366
+ if (a === "--schedule") {
32367
+ const next = args[i + 1];
32368
+ if (next !== undefined && !next.startsWith("-")) {
32369
+ days = next;
32370
+ i += 1;
32371
+ }
32372
+ continue;
32373
+ }
32374
+ if (a === "--email" || a.startsWith("--email=")) {
32375
+ if (a.startsWith("--email=")) {
32376
+ email = a.slice("--email=".length);
32377
+ } else {
32378
+ email = args[i + 1];
32379
+ i += 1;
32380
+ }
32381
+ if (email === undefined || email.length === 0 || email.startsWith("-")) {
32382
+ die2("`--email` needs an address, e.g. `--email you@yourdomain.com`.");
32383
+ }
32384
+ continue;
32385
+ }
32386
+ die2(`\`audit --schedule\` does not take ${a}.`);
32387
+ }
32388
+ const { runScheduleOn: runScheduleOn2, ScheduleCliError: ScheduleCliError2 } = await Promise.resolve().then(() => (init_schedule_cli(), exports_schedule_cli));
32389
+ const { LoginError: LoginError2 } = await Promise.resolve().then(() => (init_cli_login(), exports_cli_login));
32390
+ try {
32391
+ await runScheduleOn2(days, email);
32392
+ } catch (err) {
32393
+ if (err instanceof ScheduleCliError2 || err instanceof LoginError2)
32394
+ die2(err.message);
32395
+ throw err;
32396
+ }
32397
+ process.exit(0);
32398
+ }
30941
32399
  const stray = args.find((a) => a !== "--help" && a !== "-h");
30942
32400
  if (stray) {
30943
- die2(`\`audit\` takes no arguments yet (got: ${stray}).
30944
- ` + `Run \`failproofai audit\` to scan your history and open the dashboard.`);
32401
+ die2(`\`audit\` does not take ${stray}.
32402
+ Run \`failproofai audit\` to scan your history and open the dashboard,
32403
+ or \`failproofai audit --help\` for the scheduling commands.`);
30945
32404
  }
30946
32405
  const attempt = acquireAuditLock("cli");
30947
32406
  if (!attempt.ok) {
30948
32407
  process.stderr.write(`Error: ${heldByLine2(attempt.heldBy)}.
30949
- ` + `Two audits write the same cache files, so this one won't start. Try again shortly.
32408
+ Two audits write the same cache files, so this one won't start. Try again shortly.
30950
32409
  `);
30951
32410
  process.exit(EXIT_AUDIT_ALREADY_RUNNING2);
30952
32411
  }
@@ -30970,7 +32429,7 @@ async function runAuditCli2(args) {
30970
32429
  if (result.eventsScanned === 0) {
30971
32430
  process.stdout.write(`
30972
32431
  ${c2(DIM2, "no agent sessions found yet.")}
30973
- ` + ` install hooks with ${c2(CYAN2, "failproofai policies --install")} ` + `${c2(DIM2, "and come back after using your agent.")}
32432
+ install hooks with ${c2(CYAN2, "failproofai policies --install")} ${c2(DIM2, "and come back after using your agent.")}
30974
32433
 
30975
32434
  `);
30976
32435
  process.exit(0);
@@ -30985,15 +32444,15 @@ async function runAuditCli2(args) {
30985
32444
  const url = `http://localhost:${DASHBOARD_PORT2}/audit`;
30986
32445
  process.stdout.write(`
30987
32446
  ${c2(DIM2, "starting the dashboard…")}
30988
- ` + ` ${c2(PINK2, "✦")} ${c2(BOLD2, "here's your audit")} ${c2(DIM2, "→")} ${c2(CYAN2, url)}
30989
- ` + ` ${c2(DIM2, "(opening in your browser — press Ctrl+C to stop the server)")}
32447
+ ${c2(PINK2, "✦")} ${c2(BOLD2, "here's your audit")} ${c2(DIM2, "→")} ${c2(CYAN2, url)}
32448
+ ${c2(DIM2, "(opening in your browser — press Ctrl+C to stop the server)")}
30990
32449
 
30991
32450
  `);
30992
32451
  openWhenReady(DASHBOARD_PORT2, "/audit");
30993
32452
  const { launch: launch2 } = await Promise.resolve().then(() => (init_launch(), exports_launch));
30994
32453
  launch2("start");
30995
32454
  }
30996
- var DASHBOARD_PORT2 = 8020, EXIT_AUDIT_ALREADY_RUNNING2 = 75, AUDIT_STAGES2, HELP2, RESET2, DIM2, BOLD2, PINK2, GREEN2, CYAN2, SPINNER2;
32455
+ var DASHBOARD_PORT2 = 8020, EXIT_AUDIT_ALREADY_RUNNING2 = 75, AUDIT_STAGES2, HELP_DESC_COL2 = 40, RESET2, DIM2, BOLD2, PINK2, GREEN2, CYAN2, SPINNER2;
30997
32456
  var init_cli2 = __esm(() => {
30998
32457
  init_audit();
30999
32458
  init_audit_lock();
@@ -31002,6 +32461,7 @@ var init_cli2 = __esm(() => {
31002
32461
  init_telemetry_id2();
31003
32462
  init_telemetry_sanitize();
31004
32463
  init_open_browser();
32464
+ init_report_harm();
31005
32465
  init_tui();
31006
32466
  AUDIT_STAGES2 = [
31007
32467
  { label: "discovering transcripts", detail: "walking ~/.claude, ~/.codex, ~/.cursor, …" },
@@ -31009,29 +32469,6 @@ var init_cli2 = __esm(() => {
31009
32469
  { label: "running policy checks", detail: "replaying through 30 builtin policies" },
31010
32470
  { label: "aggregating results", detail: "counting hits, ranking by frequency" }
31011
32471
  ];
31012
- HELP2 = `
31013
- failproofai audit — audit your AI agent's behavior, then open the dashboard
31014
-
31015
- USAGE
31016
- failproofai audit Scan your agent-CLI session history for risky and
31017
- wasteful patterns, then open the audit dashboard.
31018
- failproofai audit --help Show this help.
31019
-
31020
- failproofai audit --scheduled
31021
- Headless run: scan, refresh the dashboard's cached
31022
- result, print one line, exit. No browser, no
31023
- server. This is what a scheduled audit runs; exit
31024
- 75 means another audit already had the lock.
31025
-
31026
- WHAT IT DOES
31027
- 1. Scans past sessions from every installed agent CLI (Claude, Codex, Cursor,
31028
- Copilot, OpenCode, Pi) — entirely on your machine.
31029
- 2. Starts the local dashboard and opens
31030
- http://localhost:${DASHBOARD_PORT2}/audit with your results.
31031
-
31032
- Runs fully offline — no account or network required. Press Ctrl+C to stop the
31033
- dashboard server when you're done.
31034
- `.trimStart();
31035
32472
  RESET2 = ANSI_RESET;
31036
32473
  DIM2 = ANSI_DIM;
31037
32474
  BOLD2 = ANSI_BOLD;
@@ -31051,10 +32488,10 @@ __export(exports_cloud_enrollment_cli, {
31051
32488
  describeMachine: () => describeMachine,
31052
32489
  connectionStatusLines: () => connectionStatusLines
31053
32490
  });
31054
- function describeMachine(machineId, machineLabel, full = false) {
31055
- if (!machineLabel || machineLabel === machineId)
32491
+ function describeMachine(machineId, machineLabel2, full = false) {
32492
+ if (!machineLabel2 || machineLabel2 === machineId)
31056
32493
  return machineId;
31057
- return full ? `${machineLabel} (${machineId})` : `${machineLabel} (${machineId.slice(0, 8)})`;
32494
+ return full ? `${machineLabel2} (${machineId})` : `${machineLabel2} (${machineId.slice(0, 8)})`;
31058
32495
  }
31059
32496
  function daemonWarning(status) {
31060
32497
  if (status === "running")
@@ -31113,13 +32550,13 @@ async function runConnectCommand(opts) {
31113
32550
  if (!validated.ok)
31114
32551
  return { exitCode: 1, lines: [validated.reason] };
31115
32552
  const machineId = resolveMachineId(opts.machineId);
31116
- const machineLabel = resolveMachineLabel(opts.machineLabel ?? opts.defaultMachineId);
31117
- const shownAs = describeMachine(machineId, machineLabel);
32553
+ const machineLabel2 = resolveMachineLabel(opts.machineLabel ?? opts.defaultMachineId);
32554
+ const shownAs = describeMachine(machineId, machineLabel2);
31118
32555
  const outcome = await connectToCloud({
31119
32556
  url: validated.url,
31120
32557
  token: opts.token,
31121
32558
  machineId,
31122
- machineLabel,
32559
+ machineLabel: machineLabel2,
31123
32560
  sessions: opts.sessions,
31124
32561
  environment: opts.environment,
31125
32562
  verifyPolicy: opts.verify,
@@ -31365,8 +32802,8 @@ __export(exports_launch2, {
31365
32802
  launch: () => launch2
31366
32803
  });
31367
32804
  import { spawn as spawn3 } from "child_process";
31368
- import { realpathSync as realpathSync5, existsSync as existsSync41 } from "node:fs";
31369
- import { resolve as resolve31, dirname as dirname23 } from "node:path";
32805
+ import { realpathSync as realpathSync5, existsSync as existsSync44 } from "node:fs";
32806
+ import { resolve as resolve32, dirname as dirname23 } from "node:path";
31370
32807
  import { fileURLToPath as fileURLToPath3 } from "node:url";
31371
32808
  import { createInterface as createInterface2 } from "node:readline";
31372
32809
  function launch2(mode) {
@@ -31390,9 +32827,9 @@ function launch2(mode) {
31390
32827
  process.env.PORT = port;
31391
32828
  process.env.HOSTNAME = bindHost;
31392
32829
  cmd = "node";
31393
- const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve31(dirname23(realpathSync5(fileURLToPath3(import.meta.url))), "..");
31394
- const serverJsPath = resolve31(packageRoot, ".next/standalone/server.js");
31395
- if (!existsSync41(serverJsPath)) {
32830
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve32(dirname23(realpathSync5(fileURLToPath3(import.meta.url))), "..");
32831
+ const serverJsPath = resolve32(packageRoot, ".next/standalone/server.js");
32832
+ if (!existsSync44(serverJsPath)) {
31396
32833
  let shadowMessage = null;
31397
32834
  try {
31398
32835
  const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
@@ -31489,17 +32926,17 @@ var init_cli_error2 = __esm(() => {
31489
32926
 
31490
32927
  // bin/failproofai.mjs
31491
32928
  import { realpathSync as realpathSync6 } from "fs";
31492
- import { dirname as dirname24, resolve as resolve32 } from "path";
32929
+ import { dirname as dirname24, resolve as resolve33 } from "path";
31493
32930
  import { fileURLToPath as fileURLToPath4 } from "url";
31494
32931
  // package.json
31495
- var version = "1.0.0";
32932
+ var version = "1.0.1-beta.1";
31496
32933
 
31497
32934
  // bin/failproofai.mjs
31498
32935
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
31499
- process.env.FAILPROOFAI_PACKAGE_ROOT = resolve32(dirname24(realpathSync6(fileURLToPath4(import.meta.url))), "..");
32936
+ process.env.FAILPROOFAI_PACKAGE_ROOT = resolve33(dirname24(realpathSync6(fileURLToPath4(import.meta.url))), "..");
31500
32937
  }
31501
32938
  if (!process.env.FAILPROOFAI_DIST_PATH) {
31502
- process.env.FAILPROOFAI_DIST_PATH = resolve32(dirname24(realpathSync6(fileURLToPath4(import.meta.url))), "..", "dist");
32939
+ process.env.FAILPROOFAI_DIST_PATH = resolve33(dirname24(realpathSync6(fileURLToPath4(import.meta.url))), "..", "dist");
31503
32940
  }
31504
32941
  var args = process.argv.slice(2);
31505
32942
  if (args[0] === "p")
@@ -31682,6 +33119,12 @@ COMMANDS
31682
33119
 
31683
33120
  audit Audit your agent's behavior, then open the
31684
33121
  dashboard at http://localhost:8020/audit
33122
+ audit --schedule [days] Audit on a timer (default 7 days) and email you
33123
+ what it finds. Signs you in the first time;
33124
+ --email <address> skips that question
33125
+ audit --no-schedule Stop auditing on a timer
33126
+ audit --status Whether scheduling is on, where reports go, and
33127
+ when the next scan is due
31685
33128
  audit --help, -h Show this help for the audit command
31686
33129
 
31687
33130
  backfill Re-send history the collector already read past
@@ -32069,8 +33512,8 @@ Run \`failproofai migrate --help\` for usage.`);
32069
33512
  return;
32070
33513
  }
32071
33514
  const run = runMigrations3(from);
32072
- for (const step of run.steps) {
32073
- console.log(`${step.ok ? "migrated" : "FAILED "} layout ${step.from} \u2192 ${step.to}`);
33515
+ for (const step3 of run.steps) {
33516
+ console.log(`${step3.ok ? "migrated" : "FAILED "} layout ${step3.from} \u2192 ${step3.to}`);
32074
33517
  }
32075
33518
  if (run.backedUp.length > 0) {
32076
33519
  console.log(`Saved first: ${run.backedUp.join(", ")}`);
@@ -32140,8 +33583,8 @@ Run \`failproofai update --help\` for usage.`);
32140
33583
  const run = runMigrations3(state.found);
32141
33584
  migrationsRan = run.steps.length;
32142
33585
  migrationFailed = Boolean(run.failed);
32143
- for (const step of run.steps) {
32144
- console.log(`${step.ok ? "migrated" : "FAILED "} layout ${step.from} \u2192 ${step.to}`);
33586
+ for (const step3 of run.steps) {
33587
+ console.log(`${step3.ok ? "migrated" : "FAILED "} layout ${step3.from} \u2192 ${step3.to}`);
32145
33588
  }
32146
33589
  if (run.backedUp.length > 0)
32147
33590
  console.log(`Saved first: ${run.backedUp.join(", ")}`);
@@ -32678,14 +34121,14 @@ PAUSING ENFORCEMENT (one session, always time-boxed)
32678
34121
  const { runDisconnectCommand: runDisconnectCommand2 } = await Promise.resolve().then(() => (init_cloud_enrollment_cli(), exports_cloud_enrollment_cli));
32679
34122
  result2 = runDisconnectCommand2();
32680
34123
  } else {
32681
- const { hostname: hostname6 } = await import("os");
34124
+ const { hostname: hostname7 } = await import("os");
32682
34125
  const { runConnectCommand: runConnectCommand2 } = await Promise.resolve().then(() => (init_cloud_enrollment_cli(), exports_cloud_enrollment_cli));
32683
34126
  result2 = await runConnectCommand2({
32684
34127
  url: valueAfter("--connect"),
32685
34128
  token: valueAfter("--token"),
32686
34129
  machineId: valueAfter("--machine-id"),
32687
34130
  machineLabel: valueAfter("--machine-label"),
32688
- defaultMachineId: hostname6(),
34131
+ defaultMachineId: hostname7(),
32689
34132
  sessions: !args.includes("--no-transcripts")
32690
34133
  });
32691
34134
  }