failproofai 1.0.4-beta.8 → 1.0.4

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 (245) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +5 -5
  3. package/.next/standalone/.next/prerender-manifest.json +4 -4
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
  6. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
  8. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  9. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  12. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  13. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  14. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +2 -2
  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 +2 -2
  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 +14 -14
  22. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  23. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
  24. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  25. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -4
  27. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  34. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
  36. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +4 -37
  37. package/.next/standalone/.next/server/app/audit/page.js +2 -2
  38. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  40. package/.next/standalone/.next/server/app/index.html +1 -1
  41. package/.next/standalone/.next/server/app/index.rsc +14 -14
  42. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
  43. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  44. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  45. package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
  46. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  47. package/.next/standalone/.next/server/app/page.js +2 -2
  48. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  50. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +2 -2
  51. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  52. package/.next/standalone/.next/server/app/policies/page.js +4 -4
  53. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  55. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +2 -2
  56. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/page.js +4 -4
  58. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +2 -2
  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 +5 -5
  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/build-manifest.json +2 -2
  67. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  68. package/.next/standalone/.next/server/app/projects/page.js +3 -3
  69. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +2 -2
  72. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -15
  73. package/.next/standalone/.next/server/app/settings/page.js +4 -4
  74. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  76. package/.next/standalone/.next/server/chunks/{[externals]__0hmcwgg._.js → [externals]__0-9ti7z._.js} +1 -1
  77. package/.next/standalone/.next/server/chunks/{[externals]__14b7cc6._.js → [externals]__0u3wrvq._.js} +1 -1
  78. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +3 -0
  79. package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +3 -0
  80. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08dchfu._.js +1 -1
  81. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  82. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
  83. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +3 -0
  85. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +5 -0
  87. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  89. package/.next/standalone/.next/server/chunks/{_1_eakl4._.js → _1-akg6w._.js} +2 -2
  90. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +10 -10
  91. package/.next/standalone/.next/server/chunks/_1vslkoe._.js +1 -1
  92. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
  93. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
  95. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__187jks6._.js → [root-of-the-server]__00-s7h8._.js} +1 -1
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
  97. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
  98. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
  99. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1fa1jd9._.js → [root-of-the-server]__09flfsx._.js} +2 -2
  100. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
  101. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
  102. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0kokc8z._.js → [root-of-the-server]__0n_lxhg._.js} +1 -1
  103. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
  105. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +6 -0
  106. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1kpd1za._.js → [root-of-the-server]__190won8._.js} +1 -1
  107. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +3 -0
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +3 -0
  109. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
  110. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__00y9ww2._.js → [root-of-the-server]__1sc84bb._.js} +2 -21
  111. package/.next/standalone/.next/server/chunks/ssr/_02yoeej._.js +3 -0
  112. package/.next/standalone/.next/server/chunks/ssr/{_00bqkhj._.js → _04rbnaq._.js} +2 -2
  113. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  114. package/.next/standalone/.next/server/chunks/ssr/{_0s-77v5._.js → _0h_douw._.js} +1 -1
  115. package/.next/standalone/.next/server/chunks/ssr/_1_ozgif._.js +1 -1
  116. package/.next/standalone/.next/server/chunks/ssr/_1ddpc5g._.js +1 -1
  117. package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +14 -51
  118. package/.next/standalone/.next/server/chunks/ssr/{_14c6q09._.js → _1if9ixi._.js} +2 -21
  119. package/.next/standalone/.next/server/chunks/ssr/{_07t3jol._.js → _1u8-lu2._.js} +2 -2
  120. package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +3 -0
  122. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
  123. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +69 -1
  124. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  125. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  127. package/.next/standalone/.next/server/chunks/ssr/lib_openclaw-projects_ts_0qxq3bp._.js +1 -1
  128. package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +1 -1
  129. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  131. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  133. package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
  134. package/.next/standalone/.next/server/pages/404.html +1 -1
  135. package/.next/standalone/.next/server/pages/500.html +1 -1
  136. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  137. package/.next/standalone/.next/server/server-reference-manifest.json +21 -65
  138. package/.next/standalone/.next/static/chunks/{1302duobnz07l.js → 0-tyrpwuc3lla.js} +1 -1
  139. package/.next/standalone/.next/static/chunks/0ca7octhod0g2.js +69 -0
  140. package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +1 -0
  141. package/.next/standalone/.next/static/chunks/{2n85g-crgcx5j.js → 1j6o9min_6qa9.js} +1 -1
  142. package/.next/standalone/.next/static/chunks/27cl8h-c2w7cd.js +1 -0
  143. package/.next/standalone/.next/static/chunks/{0jpd8dv2vk930.css → 29-iqnp9v_axt.css} +1 -1
  144. package/.next/standalone/.next/static/chunks/{3uyhmm01g64k4.css → 2k9f4tyv04809.css} +0 -1
  145. package/.next/standalone/.next/static/chunks/{1chm9piluggc-.js → 2lwipmv5etc8m.js} +1 -1
  146. package/.next/standalone/.next/static/chunks/2pd16tn5qmx13.js +1 -0
  147. package/.next/standalone/.next/static/chunks/{2ukucf6g-kbs5.js → 2u8vh73tpw28_.js} +1 -1
  148. package/.next/standalone/.next/static/chunks/{1_x7pqemw5n_w.js → 31u94yukw9fz-.js} +1 -1
  149. package/.next/standalone/.next/static/chunks/{2mizt96igig1y.js → 3p9_8x5ezce-9.js} +1 -1
  150. package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +1 -0
  151. package/.next/standalone/.next/static/chunks/{0vm4wdx3w-2_w.js → 3tjji451hrgc0.js} +1 -1
  152. package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +1 -0
  153. package/.next/standalone/.next/static/chunks/{turbopack-3s3c-u_u_l0u9.js → turbopack-3p73obe0xmrxm.js} +1 -1
  154. package/.next/standalone/.opencode/plugins/failproofai.mjs +27 -51
  155. package/.next/standalone/app/actions/get-scheduled-audit.ts +0 -5
  156. package/.next/standalone/app/actions/update-scheduled-audit.ts +57 -18
  157. package/.next/standalone/app/api/auth/status/route.ts +2 -3
  158. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +34 -140
  159. package/.next/standalone/app/audit/_components/audit-poster.tsx +10 -29
  160. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +1 -6
  161. package/.next/standalone/app/audit/_components/empty-state.tsx +1 -4
  162. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +5 -19
  163. package/.next/standalone/app/audit/_components/share-templates.ts +44 -110
  164. package/.next/standalone/app/audit/audit-styles.css +1 -132
  165. package/.next/standalone/app/settings/page.tsx +1 -1
  166. package/.next/standalone/app/settings/settings-client.tsx +45 -50
  167. package/.next/standalone/lib/auth/api-server-client.ts +5 -33
  168. package/.next/standalone/lib/claude-sessions.ts +27 -101
  169. package/.next/standalone/lib/codex-sessions.ts +4 -20
  170. package/.next/standalone/lib/copilot-sessions.ts +1 -32
  171. package/.next/standalone/lib/cursor-sessions.ts +5 -62
  172. package/.next/standalone/lib/download-session.ts +13 -58
  173. package/.next/standalone/lib/openclaw-projects.ts +18 -54
  174. package/.next/standalone/lib/openclaw-sessions.ts +17 -75
  175. package/.next/standalone/lib/sqlite-reader.ts +1 -78
  176. package/.next/standalone/package.json +9 -9
  177. package/.next/standalone/server.js +1 -1
  178. package/bin/failproofai.mjs +1 -1
  179. package/dist/cli.mjs +664 -2121
  180. package/dist/worker.mjs +105 -422
  181. package/lib/auth/api-server-client.ts +5 -33
  182. package/lib/claude-sessions.ts +27 -101
  183. package/lib/codex-sessions.ts +4 -20
  184. package/lib/copilot-sessions.ts +1 -32
  185. package/lib/cursor-sessions.ts +5 -62
  186. package/lib/download-session.ts +13 -58
  187. package/lib/openclaw-projects.ts +18 -54
  188. package/lib/openclaw-sessions.ts +17 -75
  189. package/lib/sqlite-reader.ts +1 -78
  190. package/package.json +9 -9
  191. package/pi-extension/index.ts +5 -50
  192. package/src/audit/cache.ts +0 -6
  193. package/src/audit/cli-adapters/claude.ts +0 -13
  194. package/src/audit/cli-login.ts +1 -4
  195. package/src/audit/cli.ts +7 -157
  196. package/src/audit/harm-report.ts +4 -111
  197. package/src/audit/index.ts +15 -286
  198. package/src/audit/machine-store.ts +9 -8
  199. package/src/audit/redact-example.ts +15 -324
  200. package/src/audit/report-harm.ts +37 -33
  201. package/src/audit/report.ts +3 -6
  202. package/src/audit/schedule-cli.ts +33 -54
  203. package/src/audit/scoring.ts +0 -49
  204. package/src/audit/types.ts +0 -52
  205. package/src/hooks/builtin-policies.ts +2 -67
  206. package/src/hooks/configure-wizard.ts +0 -55
  207. package/src/hooks/fp-config.ts +48 -83
  208. package/src/hooks/fp-home.ts +0 -27
  209. package/src/hooks/integrations.ts +14 -51
  210. package/src/hooks/uninstall-cli.ts +2 -17
  211. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0faqz9x._.js +0 -22
  212. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zyc_nm._.js +0 -3
  213. package/.next/standalone/.next/server/chunks/[root-of-the-server]__112b4go._.js +0 -24
  214. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1l71g3y._.js +0 -22
  215. package/.next/standalone/.next/server/chunks/lib_openclaw-projects_ts_0gc-p24._.js +0 -3
  216. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-4ja6q._.js +0 -4
  217. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00cr3xu._.js +0 -4
  218. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04px77e._.js +0 -4
  219. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0egr5s1._.js +0 -6
  220. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0i0l1di._.js +0 -4
  221. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n2txhk._.js +0 -3
  222. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0r3c1_0._.js +0 -4
  223. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17xnxlx._.js +0 -4
  224. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ckpmkk._.js +0 -4
  225. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kaotix._.js +0 -22
  226. package/.next/standalone/.next/server/chunks/ssr/_04ng_6x._.js +0 -3
  227. package/.next/standalone/.next/server/chunks/ssr/_0l2hi_d._.js +0 -3
  228. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_rerun-button_tsx_0blihgw._.js +0 -3
  229. package/.next/standalone/.next/static/chunks/0igq884ziuv-8.js +0 -1
  230. package/.next/standalone/.next/static/chunks/349jkb79v5g6z.js +0 -1
  231. package/.next/standalone/.next/static/chunks/3a4gth64hwtwf.js +0 -1
  232. package/.next/standalone/app/actions/get-leaks.ts +0 -104
  233. package/.next/standalone/app/audit/_components/leak-section.tsx +0 -192
  234. package/.next/standalone/lib/openclaw-db.ts +0 -271
  235. package/lib/openclaw-db.ts +0 -271
  236. package/src/audit/desktop-notify.ts +0 -420
  237. package/src/audit/leak-fingerprint.ts +0 -198
  238. package/src/audit/leak-record.ts +0 -248
  239. package/src/audit/leak-scan.ts +0 -268
  240. package/src/audit/leak-store.ts +0 -220
  241. package/src/audit/macos-notifier.ts +0 -298
  242. package/src/hooks/notice.ts +0 -24
  243. /package/.next/standalone/.next/static/{DNhaAH8iPGH0tIuxee5_2 → dg8VQ7Ejlo0Qsh_x92oIt}/_buildManifest.js +0 -0
  244. /package/.next/standalone/.next/static/{DNhaAH8iPGH0tIuxee5_2 → dg8VQ7Ejlo0Qsh_x92oIt}/_clientMiddlewareManifest.js +0 -0
  245. /package/.next/standalone/.next/static/{DNhaAH8iPGH0tIuxee5_2 → dg8VQ7Ejlo0Qsh_x92oIt}/_ssgManifest.js +0 -0
package/src/audit/cli.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * the dashboard to view it.
4
4
  *
5
5
  * failproofai audit Scan, then launch the dashboard at /audit.
6
- * failproofai audit --schedule Put local scans and notifications on a timer.
6
+ * failproofai audit --schedule Put scans on a timer and mail the findings.
7
7
  * failproofai audit --no-schedule Stop the timer.
8
8
  * failproofai audit --status What this machine is scheduled to do.
9
9
  * failproofai audit -h, --help Show usage.
@@ -35,11 +35,6 @@ import { getInstanceId } from "../../lib/telemetry-id";
35
35
  import { sanitizeErrorMessage } from "../../lib/telemetry-sanitize";
36
36
  import { openWhenReady } from "./open-browser";
37
37
  import { describeOutcome, reportHarm } from "./report-harm";
38
- import { notifyDesktop } from "./desktop-notify";
39
- import { macNotifierInstalled, pruneMacNotifyQueue, queueMacNotification } from "./macos-notifier";
40
- import { desktopLeakNotice, type DesktopLeakEmailState } from "../hooks/notice";
41
- import { readConfig } from "../hooks/fp-config";
42
- import { readAuth } from "../../lib/auth/auth-store";
43
38
  import { brandAnsi, ANSI_RESET, ANSI_BOLD, ANSI_DIM, helpScreen, helpOptsFor } from "../hooks/tui";
44
39
  import { version } from "../../package.json";
45
40
 
@@ -97,36 +92,26 @@ export function helpText(): string {
97
92
  {
98
93
  command: "audit",
99
94
  version,
100
- tagline: "find credentials your agents leaked into their own transcripts",
95
+ tagline: "review your agent CLIs for risky and wasteful patterns",
101
96
  sections: [
102
97
  {
103
98
  label: "usage",
104
99
  entries: [
105
- ["(bare)", `Scan your session history for leaked keys, then open http://localhost:${DASHBOARD_PORT}/audit`],
106
- ["--schedule [days]", "Scan on a timer and notify you on this machine. Default 7 days, range 1-90. No account required."],
100
+ ["(bare)", `Scan your session history, then open http://localhost:${DASHBOARD_PORT}/audit`],
101
+ ["--schedule [days]", "Scan on a timer and email the findings. Default 7 days, range 1-90. Signs you in the first time."],
107
102
  // Its own row now, rather than a clause inside --schedule's. It
108
103
  // does not stand alone, which is why it used to be a clause — but
109
104
  // a clause wraps, and `--email <address>` landing with the flag at
110
105
  // the end of one line and its placeholder at the start of the next
111
106
  // is not a flag anybody can read or copy.
112
- ["--email <address>", "With --schedule, add email alerts for the most recent masked credential leak. A sign-in code is emailed to you."],
107
+ ["--email <address>", "With --schedule, skips the sign-in prompt."],
113
108
  ["--no-schedule", "Stop the timer. Leaves you signed in."],
114
- // Its own pair of flags rather than a clause on --schedule: whether
115
- // this machine scans and whether it may interrupt you are separate
116
- // decisions, and nobody silencing a banner should have to re-state
117
- // their schedule to do it.
118
- ["--notify", "Show a desktop notification when a scan finds a credential. On by default."],
119
- ["--no-notify", "Stop desktop notifications. Scheduled scans and optional email alerts are unaffected."],
120
109
  ["--status", "Whether scheduling is on, where reports go, the daemon's state, and when the next scan is due."],
121
110
  ["-h, --help", "Show this help."],
122
111
  ],
123
112
  },
124
113
  ],
125
- footer: [
126
- "Every scan runs on this machine. Leak alerts appear as desktop system",
127
- "notifications, never inside agent sessions. If you add email, only the",
128
- "newest masked credential exposure and its metadata leave the machine.",
129
- ],
114
+ footer: ["Everything runs on this machine; only a scheduled digest ever leaves it."],
130
115
  },
131
116
  helpOptsFor(process.stdout),
132
117
  );
@@ -214,18 +199,6 @@ function startProgress(): Progress {
214
199
  const lines = Array.from({ length: n }, (_, i) => lineFor(i));
215
200
  // Move the cursor back up over the previously-drawn block, then clear and
216
201
  // rewrite each line in place.
217
- //
218
- // INVARIANT: nothing else may write to the terminal while this is running.
219
- // The cursor-up is a fixed count, so any stray line pushes the cursor down
220
- // and the next redraw repaints the block lower — stranding the top of the
221
- // old frame above it, which reads as the audit having run twice. That is
222
- // not hypothetical: Node's "SQLite is an experimental feature" warning did
223
- // exactly this on every first run, two lines of it, and it is why
224
- // `lib/sqlite-reader.ts` now filters that one warning at the source.
225
- //
226
- // There is no defensive fix from inside here — erasing to end of screen
227
- // still leaves the stranded lines ABOVE the cursor. Keep the terminal
228
- // quiet instead.
229
202
  if (printed) process.stdout.write(`\x1b[${n}A`);
230
203
  process.stdout.write(lines.map((l) => `\x1b[2K${l}`).join("\n") + "\n");
231
204
  printed = true;
@@ -361,116 +334,6 @@ function heldByLine(held: AuditLockInfo | null): string {
361
334
  return `another audit is already running (pid ${held.pid}, started by ${held.source} ${ageS}s ago)`;
362
335
  }
363
336
 
364
- /**
365
- * Raise a desktop banner for credentials this scan found and nothing has
366
- * announced yet.
367
- *
368
- * Scheduled runs only, and the reason is the same one that keeps `reportHarm`
369
- * here: an interactive `failproofai audit` prints its findings to a person who
370
- * is already looking at them, so a banner on top is noise. The scheduled run is
371
- * the one with nobody watching, which is exactly when a notification is the
372
- * only thing that reaches anyone.
373
- *
374
- * Every failure is swallowed on purpose. This is an announcement about a
375
- * completed scan, not part of the scan: a machine with no desktop, a locked
376
- * session, a bus that hung — none of those make the audit itself a failure, and
377
- * turning a good scan into exit 1 would make the scheduler back off from
378
- * running the thing that actually matters.
379
- */
380
- async function announceLeaksOnDesktop(
381
- result: AuditResult,
382
- report: Awaited<ReturnType<typeof reportHarm>>,
383
- ): Promise<void> {
384
- try {
385
- await announceLeaksOrThrow(result, report);
386
- } catch (err) {
387
- // The catch is not decoration. `notifyDesktop` documents that it never
388
- // throws, but it is doing socket I/O, and so are the config read and the
389
- // marker write beside it — depending on three modules' promises to keep a
390
- // completed scan from reporting failure is a dependency this does not need
391
- // to have.
392
- process.stderr.write(
393
- `failproofai: could not announce a leak on the desktop: ` +
394
- `${err instanceof Error ? err.message : String(err)}\n`,
395
- );
396
- }
397
- }
398
-
399
- async function announceLeaksOrThrow(
400
- result: AuditResult,
401
- report: Awaited<ReturnType<typeof reportHarm>>,
402
- ): Promise<void> {
403
- // `leakIds` includes credentials this scan encountered before. A scheduled
404
- // run is a fresh safety check, so it must raise a fresh system notification
405
- // when the leak is still present; the old per-finding marker is why only the
406
- // first test run ever produced a banner. Older cached results fall back to
407
- // first-seen ids.
408
- const ids = result.leakIds ?? result.newLeakIds ?? [];
409
- if (ids.length === 0) return;
410
- // Read at fire time, not at scan time: the switch answers "should this
411
- // interrupt me", and it is the user's most recent answer that counts.
412
- if (!readConfig().audit.notify) return;
413
-
414
- const n = ids.length === 1 ? "a possible credential exposure" : `${ids.length} possible credential exposures`;
415
- // Fixed text with no scanned content and no count. A finding's own text comes
416
- // from a repository this machine cloned, and notification bodies render
417
- // markup on several Linux desktops. The count is intentionally left to the
418
- // report too: a banner must not turn detector candidates into a claim that
419
- // the user leaked N real credentials.
420
- //
421
- // No action buttons, deliberately. `Notify` supports them, but a server
422
- // delivers the click back as an `ActionInvoked` signal to the sender — and
423
- // this process exits as soon as the scan finishes, so the button would be
424
- // dead. A plain command the user can copy beats a button that does nothing.
425
- const emailState: DesktopLeakEmailState =
426
- report.kind === "sent"
427
- ? "sent"
428
- : report.kind === "signed-out" || !readAuth()
429
- ? "missing"
430
- : report.kind === "failed"
431
- ? "failed"
432
- : "held";
433
- const { title, body } = desktopLeakNotice(emailState);
434
-
435
- // The two platforms need opposite things, and the split is not cosmetic. On
436
- // Linux this process can reach the session bus itself. On macOS it cannot
437
- // reach Notification Center at all — it is a child of a LaunchDaemon, outside
438
- // the GUI session — so it hands the message to an agent that lives inside
439
- // one. See `macos-notifier.ts` for why that agent exists.
440
- if (process.platform === "darwin") {
441
- pruneMacNotifyQueue();
442
- if (!queueMacNotification(ids[0], title, body)) {
443
- process.stderr.write(`failproofai: found ${n} but could not queue a notification\n`);
444
- } else if (!macNotifierInstalled()) {
445
- // Queued, but nothing currently collects it. The payload is durable and
446
- // will be consumed when the LaunchAgent returns.
447
- // Worth a line, because the immediate symptom is otherwise silence.
448
- process.stderr.write(
449
- `failproofai: found ${n}; the notifier is not installed, so nothing will show it ` +
450
- `(run \`failproofai config\`)\n`,
451
- );
452
- }
453
- return;
454
- }
455
-
456
- const outcome = await notifyDesktop(
457
- title,
458
- body,
459
- // `0` asks the notification server for a fresh id. Each scheduled scan that
460
- // still sees a leak is a new reminder, rather than replacing or suppressing
461
- // the first banner forever.
462
- 0,
463
- );
464
- if (!outcome.ok) {
465
- // stderr, so it lands in the journal beside the run it belongs to. Not a
466
- // failure of the audit — but "we found a key and could not tell you" is
467
- // exactly the sentence someone debugging this needs to find.
468
- process.stderr.write(
469
- `failproofai: found ${n} but could not raise a desktop notification (${outcome.reason})\n`,
470
- );
471
- }
472
- }
473
-
474
337
  // ── Headless (scheduled) audit ───────────────────────────────────────────────
475
338
 
476
339
  /**
@@ -541,9 +404,7 @@ export async function runScheduledAudit(): Promise<number> {
541
404
  `${num(result.transcripts.scanned)} sessions, ${num(result.totals.hits)} hits\n`,
542
405
  );
543
406
 
544
- // Email is optional and never controls the local scan. Run it first only so
545
- // the desktop notification can truthfully say whether this particular
546
- // alert was also emailed; every failure remains a non-fatal outcome.
407
+ // Report harmful findings upstream, if the user switched emailed reports on.
547
408
  //
548
409
  // AFTER the dashboard cache is written and AFTER the success line, because
549
410
  // the scan is the product and this is an optional extra on top of it.
@@ -557,7 +418,6 @@ export async function runScheduledAudit(): Promise<number> {
557
418
  // would also make the manual command do a network call that
558
419
  // `audit --help` promises it does not.
559
420
  const outcome = await reportHarm(result);
560
- await announceLeaksOnDesktop(result, outcome);
561
421
  const line = describeOutcome(outcome);
562
422
  if (line) {
563
423
  // Anything other than a successful send goes to stderr: on a scheduled run
@@ -692,16 +552,6 @@ export async function runAuditCli(args: string[]): Promise<void> {
692
552
  process.exit(0);
693
553
  }
694
554
 
695
- for (const [flag, enable] of [["--notify", true], ["--no-notify", false]] as const) {
696
- if (args.includes(flag)) {
697
- const extra = args.find((a) => a !== flag);
698
- if (extra) die(`\`audit ${flag}\` takes no other arguments (got: ${extra}).`);
699
- const { runNotifyToggle } = await import("./schedule-cli");
700
- runNotifyToggle(enable);
701
- process.exit(0);
702
- }
703
- }
704
-
705
555
  if (args.includes("--no-schedule")) {
706
556
  const extra = args.find((a) => a !== "--no-schedule");
707
557
  if (extra) die(`\`audit --no-schedule\` takes no other arguments (got: ${extra}).`);
@@ -1,11 +1,10 @@
1
1
  /**
2
2
  * Turning an audit result into a harm report the api-server can act on.
3
3
  *
4
- * Runs only after a SCHEDULED scan (`failproofai audit --scheduled`) and only
5
- * ever from the audit child never the daemon, which holds no human credential
6
- * precisely so that refresh rotation stays inside the audit lock. Without an
7
- * email identity the scan and desktop notification still work; this module
8
- * simply reports `signed-out` and sends nothing.
4
+ * Runs only after a SCHEDULED scan (`failproofai audit --scheduled`), only when
5
+ * the user has switched emailed reports on, and only ever from the audit child —
6
+ * never the daemon, which holds no human credential precisely so that refresh
7
+ * rotation stays inside the audit lock. See `crates/failproofaid/src/audit_lane.rs`.
9
8
  *
10
9
  * ## What counts as harm
11
10
  *
@@ -49,7 +48,6 @@
49
48
  */
50
49
  import type { AuditCount, AuditResult } from "./types";
51
50
  import { redactExample } from "./redact-example";
52
- import type { LeakFinding } from "./leak-record";
53
51
 
54
52
  /** Severities that mean "the engine would have stopped this". */
55
53
  const HARMFUL_SEVERITIES = new Set(["deny", "sanitize"]);
@@ -74,58 +72,10 @@ export interface ReportedPolicy {
74
72
  examples: string[];
75
73
  }
76
74
 
77
- /**
78
- * One leaked credential, as the digest carries it.
79
- *
80
- * **Nothing here can contain the secret**, and that is a structural property
81
- * rather than a promise this function keeps. `LeakFinding` has no field holding
82
- * the raw value — `recordLeaks` fingerprints at the moment of detection and
83
- * stores only the mask — so there is no raw value on disk for this to leak even
84
- * by accident. The most a bug here can send is a mask, a length and a label.
85
- *
86
- * The identifier NAME (`COMPOSIO_API_KEY`) is sent, and is not a secret: it is
87
- * the left-hand side of an assignment, and for the first-party class — 230 of
88
- * the 237 secret-named assignments the census measured, which no vendor console
89
- * can revoke — it is the only actionable thing in the finding. Without it the
90
- * digest can say a key leaked and not which one.
91
- */
92
- export interface ReportedLeak {
93
- /** The machine-local salted id, so a reader can act on the same row twice.
94
- * An HMAC under a salt that never leaves the machine — it names the finding
95
- * without describing it. */
96
- id: string;
97
- /** WHAT. `ghp_••••••••4f2a`, or `[13-char password]` when nothing was minted. */
98
- display: string;
99
- label: string;
100
- length: number;
101
- /** False means "we know it leaked, not who issued it" — which changes the
102
- * advice from "revoke at this console" to "find where this came from". */
103
- attributed: boolean;
104
- /** The identifier it was assigned to, when there was one. */
105
- name: string | null;
106
- confidence: string;
107
- first_seen: string;
108
- last_seen: string;
109
- occurrences: number;
110
- /** WHO — the harness whose transcript carried it. */
111
- cli: string;
112
- /** WHERE — home-shortened already, like every path in `examples`. */
113
- project: string;
114
- /** HOW — "read from ~/…/.env". */
115
- mechanism: string;
116
- /** Input means the agent SENT it (deniable at PreToolUse next time); result
117
- * means the agent RECEIVED it, which no gate can undo. */
118
- direction: "input" | "result";
119
- }
120
-
121
75
  export interface HarmReport {
122
76
  window_from?: string;
123
77
  window_to: string;
124
78
  harmful: ReportedPolicy[];
125
- /** Credentials seen in this window. Separate from `harmful` because it is a
126
- * different kind of claim: `harmful` counts policy activity, this names a
127
- * specific object that needs rotating. */
128
- leaks: ReportedLeak[];
129
79
  }
130
80
 
131
81
  /** `failproofai/block-rm-rf` → `block-rm-rf`. */
@@ -276,64 +226,10 @@ export function selectHarmful(
276
226
  * lower bound", so a policy carrying no usable timestamps is reported once on a
277
227
  * new machine rather than silently dropped by the bound this now always sets.
278
228
  */
279
- /**
280
- * The leaks worth putting in this window's digest.
281
- *
282
- * Windowed on `lastSeen`, not `firstSeen`: a key first seen months ago and used
283
- * again yesterday is live, and reporting it only in the window it debuted would
284
- * mean the digest goes quiet on exactly the credentials still in circulation.
285
- *
286
- * Dismissed findings never appear. A person who looked at a row and said "that
287
- * is not a secret" has given the only judgement available that beats ours, and
288
- * mailing it to them weekly afterwards is how a tool teaches people to filter
289
- * it out of their inbox.
290
- */
291
- export function selectLeaks(findings: LeakFinding[], from: Date, to: Date): ReportedLeak[] {
292
- const lo = from.getTime();
293
- const hi = to.getTime();
294
- const out: ReportedLeak[] = [];
295
- for (const f of findings) {
296
- if (f.dismissedAt) continue;
297
- const last = ts(f.lastSeen);
298
- // No usable timestamp means it cannot be placed in a window. Included
299
- // rather than dropped, for the same reason `includeUnplaceable` exists
300
- // above: silence about a credential is the failure that costs something.
301
- if (last !== null && (last < lo || last > hi)) continue;
302
- // The most recent exposure is the one worth describing: it is where the key
303
- // is now, not where it was first noticed.
304
- // Defensive even though `readLeakRecord` already sanitises: this is
305
- // exported and takes whatever a caller hands it.
306
- const seen = f.sightings?.reduce((latest, candidate) => {
307
- if (!latest) return candidate;
308
- return (ts(candidate.at) ?? 0) >= (ts(latest.at) ?? 0) ? candidate : latest;
309
- }, undefined as LeakFinding["sightings"][number] | undefined);
310
- out.push({
311
- id: f.id,
312
- display: f.fingerprint?.display ?? "[credential]",
313
- label: f.fingerprint?.label ?? "secret",
314
- length: f.fingerprint?.length ?? 0,
315
- attributed: f.fingerprint?.attributed === true,
316
- name: f.name,
317
- confidence: f.confidence,
318
- first_seen: f.firstSeen,
319
- last_seen: f.lastSeen,
320
- occurrences: f.occurrences,
321
- cli: seen?.cli ?? "unknown",
322
- project: seen?.cwd ?? "unknown",
323
- mechanism: seen?.mechanism?.summary ?? "seen in a transcript",
324
- direction: seen?.mechanism?.direction ?? "result",
325
- });
326
- }
327
- // Most recently seen first: the digest's first row should be the one whose
328
- // key is most likely still in use.
329
- return out.sort((a, b) => (ts(b.last_seen) ?? 0) - (ts(a.last_seen) ?? 0));
330
- }
331
-
332
229
  export function buildHarmReport(
333
230
  result: AuditResult,
334
231
  lastReportedAt: string | undefined,
335
232
  intervalDays: number,
336
- leakFindings: LeakFinding[] = [],
337
233
  ): HarmReport {
338
234
  const to = new Date(Date.parse(result.scannedAt));
339
235
  const windowTo = Number.isFinite(to.getTime()) ? to : new Date();
@@ -360,8 +256,5 @@ export function buildHarmReport(
360
256
  window_from: from.toISOString(),
361
257
  window_to: windowTo.toISOString(),
362
258
  harmful: selectHarmful(result, from, windowTo, { includeUnplaceable: isFirstReport }),
363
- // One email, one actionable credential. Sending every row turns a leak
364
- // alert into a backlog digest and obscures the event that just happened.
365
- leaks: selectLeaks(leakFindings, from, windowTo).slice(0, 1),
366
259
  };
367
260
  }