failproofai 1.0.4-beta.6 → 1.0.4-beta.7

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 (118) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  12. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
  18. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
  20. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  21. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +5 -5
  30. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  32. package/.next/standalone/.next/server/app/index.html +1 -1
  33. package/.next/standalone/.next/server/app/index.rsc +14 -14
  34. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
  35. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  36. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  37. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  38. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  40. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  41. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  43. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  44. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  47. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  48. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  50. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  51. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  53. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +15 -4
  54. package/.next/standalone/.next/server/app/settings/page.js +2 -2
  55. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  56. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  57. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  58. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  59. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  60. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +10 -10
  61. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  62. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-4ja6q._.js +2 -2
  63. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00cr3xu._.js +2 -2
  64. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04px77e._.js +2 -2
  65. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0egr5s1._.js +2 -2
  66. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0i0l1di._.js +2 -2
  67. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  68. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0r3c1_0._.js +2 -2
  69. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1ui5ur8._.js → [root-of-the-server]__12hbpha._.js} +2 -2
  70. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17xnxlx._.js +2 -2
  71. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ckpmkk._.js +2 -2
  72. package/.next/standalone/.next/server/chunks/ssr/_04ng_6x._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/ssr/_07t3jol._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  75. package/.next/standalone/.next/server/chunks/ssr/{_02b3i98._.js → _0a0zlpr._.js} +2 -2
  76. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
  77. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  79. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  80. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  81. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  82. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  83. package/.next/standalone/.next/server/pages/404.html +1 -1
  84. package/.next/standalone/.next/server/pages/500.html +1 -1
  85. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  86. package/.next/standalone/.next/server/server-reference-manifest.json +33 -22
  87. package/.next/standalone/.next/static/chunks/{20xkiqzvfkuvm.js → 083fk73jtv9a0.js} +1 -1
  88. package/.next/standalone/.next/static/chunks/{1xgq22tri86an.js → 0divvxng208xo.js} +1 -1
  89. package/.next/standalone/.next/static/chunks/{1gn08w2pgqh09.js → 0f1pznnc7zlub.js} +1 -1
  90. package/.next/standalone/.next/static/chunks/{3jxd133cw2vm3.js → 0ntdfr2552x-h.js} +1 -1
  91. package/.next/standalone/.next/static/chunks/{32m2ryfankf_2.js → 0t6ea7d3bkmdv.js} +1 -1
  92. package/.next/standalone/.next/static/chunks/{07c6l1lq5_69b.js → 15uon66hpwbgg.js} +1 -1
  93. package/.next/standalone/.next/static/chunks/{2a84ug32tzik2.js → 1csubx9gwhsf6.js} +1 -1
  94. package/.next/standalone/.next/static/chunks/27du9n7ssja7l.js +1 -0
  95. package/.next/standalone/.next/static/chunks/2h_pcb1y8_jde.js +1 -0
  96. package/.next/standalone/.next/static/chunks/{12bsgz8czvcnn.js → 2pwfrn6_mw-1u.js} +1 -1
  97. package/.next/standalone/app/actions/get-scheduled-audit.ts +5 -0
  98. package/.next/standalone/app/actions/update-scheduled-audit.ts +14 -1
  99. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +1 -1
  100. package/.next/standalone/app/audit/_components/empty-state.tsx +1 -1
  101. package/.next/standalone/app/audit/_components/leak-section.tsx +1 -1
  102. package/.next/standalone/app/settings/settings-client.tsx +42 -11
  103. package/.next/standalone/package.json +9 -9
  104. package/.next/standalone/server.js +1 -1
  105. package/dist/cli.mjs +69 -68
  106. package/dist/worker.mjs +3 -3
  107. package/package.json +9 -9
  108. package/src/audit/index.ts +1 -1
  109. package/src/audit/leak-record.ts +24 -12
  110. package/src/audit/macos-notifier.ts +54 -66
  111. package/src/hooks/builtin-policies.ts +2 -2
  112. package/src/hooks/configure-wizard.ts +8 -1
  113. package/src/hooks/uninstall-cli.ts +3 -3
  114. package/.next/standalone/.next/static/chunks/3ioub_lpen-sq.js +0 -1
  115. package/.next/standalone/.next/static/chunks/44ncu88eq6z71.js +0 -1
  116. /package/.next/standalone/.next/static/{aeM365dNTtUZOZvNkX8ba → YiPsVpjrc-pEQoN7XvhZ0}/_buildManifest.js +0 -0
  117. /package/.next/standalone/.next/static/{aeM365dNTtUZOZvNkX8ba → YiPsVpjrc-pEQoN7XvhZ0}/_clientMiddlewareManifest.js +0 -0
  118. /package/.next/standalone/.next/static/{aeM365dNTtUZOZvNkX8ba → YiPsVpjrc-pEQoN7XvhZ0}/_ssgManifest.js +0 -0
@@ -24,13 +24,13 @@
24
24
  * would install for every account on the box, which is not what a per-user
25
25
  * notification is.
26
26
  *
27
- * **An app bundle, not a bare `osascript -e 'display notification'`.** A raw
28
- * osascript notification is attributed to whatever host ran it — in practice
29
- * "Script Editor" — so it inherits Script Editor's notification permission,
30
- * appears under Script Editor in System Settings, and is indistinguishable from
31
- * any other script on the machine. Compiling a tiny applet with `osacompile`
32
- * gives it its own bundle identifier, so the banner says failproofai, and the
33
- * user gets a real entry they can allow, silence or deny like any other app.
27
+ * **A user-session `osascript`, not an applet binary launched directly.** The
28
+ * compiled applet path looked attractive because it could carry a FailproofAI
29
+ * bundle identifier, but launching `Contents/MacOS/applet` directly from
30
+ * launchd is not a dependable Notification Center client on current macOS.
31
+ * `osascript` from a user LaunchAgent is the same mechanism ordinary scheduled
32
+ * jobs use successfully. Delivery matters more than owning the label shown by
33
+ * Notification Center, so the runner uses that proven path.
34
34
  *
35
35
  * **A watched drop directory, not a resident process.** The agent is not a
36
36
  * daemon: `WatchPaths` makes launchd start it only when a file appears, and it
@@ -45,25 +45,29 @@
45
45
  * the thing that lasts.
46
46
  */
47
47
  import { execFileSync } from "node:child_process";
48
- import { existsSync, mkdirSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
48
+ import { chmodSync, existsSync, mkdirSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
49
49
  import { homedir } from "node:os";
50
50
  import { resolve } from "node:path";
51
51
 
52
52
  import { runDir } from "../hooks/fp-home";
53
53
  import { isFindingId } from "./leak-fingerprint";
54
54
 
55
- /** The bundle identifier the banner is attributed to, and the agent's label. */
55
+ /** Stable label used to load, inspect, and remove the per-user LaunchAgent. */
56
56
  export const MAC_NOTIFIER_LABEL = "ai.failproof.notifier";
57
57
 
58
58
  /** Where a queued notification waits for the agent to pick it up. */
59
59
  export const macNotifyDir = (home?: string) => resolve(runDir(home), "notify");
60
60
 
61
- /** The compiled applet. Under `~/.failproofai` rather than `/Applications`,
62
- * because it is a delivery mechanism, not something to launch. */
61
+ /** Legacy compiled applet path, retained so upgrades and uninstall remove it. */
63
62
  export function macNotifierAppPath(home?: string): string {
64
63
  return resolve(runDir(home), "..", "bin", "FailproofAI Notifier.app");
65
64
  }
66
65
 
66
+ /** User-session runner invoked by launchd when a notification is queued. */
67
+ export function macNotifierRunnerPath(home?: string): string {
68
+ return resolve(runDir(home), "..", "bin", "failproofai-notifier.zsh");
69
+ }
70
+
67
71
  export function macNotifierPlistPath(): string {
68
72
  return resolve(homedir(), "Library", "LaunchAgents", `${MAC_NOTIFIER_LABEL}.plist`);
69
73
  }
@@ -73,11 +77,11 @@ function escapeXml(s: string): string {
73
77
  }
74
78
 
75
79
  /**
76
- * The AppleScript the applet runs: drain the directory, say each line, exit.
80
+ * The user-session runner: drain the directory, say each notification, exit.
77
81
  *
78
- * Payload format is two lines — title, then body — chosen over JSON because
79
- * AppleScript has no JSON parser and shelling out to one would put a second
80
- * interpreter in the path of a two-field message.
82
+ * Payload format is two lines — title, then body. Values are passed to
83
+ * AppleScript as argv, never interpolated into source, so notification text
84
+ * cannot become AppleScript code.
81
85
  *
82
86
  * The file is REMOVED BEFORE the notification is posted, and that order is
83
87
  * deliberate: `WatchPaths` fires on every change to the directory, so a file
@@ -88,32 +92,25 @@ function escapeXml(s: string): string {
88
92
  */
89
93
  export function notifierScript(home?: string): string {
90
94
  const dir = macNotifyDir(home);
91
- return `on run
92
- set dropDir to ${JSON.stringify(dir + "/")}
93
- set listing to ""
94
- try
95
- set listing to do shell script "ls -1 " & quoted form of dropDir & " 2>/dev/null"
96
- end try
97
- repeat with entryName in paragraphs of listing
98
- set entryPath to dropDir & (entryName as string)
99
- if (entryName as string) is not "" then
100
- try
101
- set payload to do shell script "cat " & quoted form of entryPath & " 2>/dev/null"
102
- do shell script "rm -f " & quoted form of entryPath
103
- set lines_ to paragraphs of payload
104
- if (count of lines_) is greater than 1 then
105
- set noteTitle to item 1 of lines_
106
- set noteBody to ""
107
- repeat with i from 2 to (count of lines_)
108
- if noteBody is not "" then set noteBody to noteBody & " "
109
- set noteBody to noteBody & (item i of lines_)
110
- end repeat
111
- display notification noteBody with title noteTitle
112
- end if
113
- end try
114
- end if
115
- end repeat
95
+ const quotedDir = `'${dir.replace(/'/g, `'\\''`)}'`;
96
+ return `#!/bin/zsh
97
+ set -u
98
+ setopt NULL_GLOB
99
+
100
+ drop_dir=${quotedDir}
101
+
102
+ for entry_path in "$drop_dir"/*; do
103
+ [ -f "$entry_path" ] || continue
104
+ note_title=$(/usr/bin/sed -n '1p' "$entry_path")
105
+ note_body=$(/usr/bin/sed -n '2,$p' "$entry_path" | /usr/bin/tr '\\n' ' ')
106
+ /bin/rm -f "$entry_path"
107
+ [ -n "$note_title" ] || continue
108
+ /usr/bin/osascript - "$note_title" "$note_body" <<'APPLESCRIPT'
109
+ on run argv
110
+ display notification (item 2 of argv) with title (item 1 of argv)
116
111
  end run
112
+ APPLESCRIPT
113
+ done
117
114
  `;
118
115
  }
119
116
 
@@ -122,7 +119,7 @@ end run
122
119
  * assert this file's shape from a Linux CI runner, which is every CI runner we
123
120
  * have.
124
121
  */
125
- export function notifierPlistContents(appPath: string, watchDir: string): string {
122
+ export function notifierPlistContents(runnerPath: string, watchDir: string): string {
126
123
  return `<?xml version="1.0" encoding="UTF-8"?>
127
124
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
128
125
  <plist version="1.0">
@@ -131,7 +128,7 @@ export function notifierPlistContents(appPath: string, watchDir: string): string
131
128
  <string>${escapeXml(MAC_NOTIFIER_LABEL)}</string>
132
129
  <key>ProgramArguments</key>
133
130
  <array>
134
- <string>${escapeXml(resolve(appPath, "Contents", "MacOS", "applet"))}</string>
131
+ <string>${escapeXml(runnerPath)}</string>
135
132
  </array>
136
133
  <!-- Started only when something is queued, and it exits when the queue is
137
134
  empty. RunAtLoad would fire it once at login with nothing to say. -->
@@ -173,36 +170,26 @@ function run(cmd: string, args: string[]): void {
173
170
  * machine scan, and may it interrupt), and both of those are real settings.
174
171
  *
175
172
  * Returns rather than throws. Notifications are one channel of several, and a
176
- * Mac that cannot compile an applet must still finish setting up its daemon,
177
- * its hooks and its policies — the parts that actually enforce anything.
173
+ * Mac that cannot load the user LaunchAgent must still finish setting up its
174
+ * daemon, hooks and policies — the parts that actually enforce anything.
178
175
  */
179
176
  export function installMacNotifier(home?: string): MacNotifierResult {
180
177
  if (process.platform !== "darwin") return { installed: false, reason: "not macOS" };
181
178
  try {
182
- const app = macNotifierAppPath(home);
179
+ const runner = macNotifierRunnerPath(home);
183
180
  const watch = macNotifyDir(home);
184
181
  mkdirSync(watch, { recursive: true, mode: 0o700 });
185
- mkdirSync(resolve(app, ".."), { recursive: true });
186
-
187
- const scriptPath = resolve(watch, "..", "notifier.applescript");
188
- writeFileSync(scriptPath, notifierScript(home), { mode: 0o600 });
189
- // osacompile refuses to overwrite an existing bundle, so a reinstall (a
190
- // second `failproofai config`, or an upgrade) has to clear it first.
191
- rmSync(app, { recursive: true, force: true });
192
- run("osacompile", ["-o", app, scriptPath]);
193
- rmSync(scriptPath, { force: true });
194
-
195
- // Give the bundle its own identity, so the banner is attributed to
196
- // failproofai instead of to whatever compiled it, and so the user gets one
197
- // entry in System Settings > Notifications they can turn off themselves.
198
- const plist = resolve(app, "Contents", "Info.plist");
199
- run("defaults", ["write", plist, "CFBundleIdentifier", MAC_NOTIFIER_LABEL]);
200
- run("defaults", ["write", plist, "CFBundleName", "failproofai"]);
201
- // `defaults write` leaves the file 0600 and owned by us but in binary
202
- // format; launchd and LaunchServices both read either, so no conversion.
182
+ mkdirSync(resolve(runner, ".."), { recursive: true });
183
+ writeFileSync(runner, notifierScript(home), { mode: 0o700 });
184
+ // writeFileSync preserves an existing file's mode. Repair it explicitly on
185
+ // upgrades from a partially-created or hand-edited runner.
186
+ chmodSync(runner, 0o700);
187
+ // Remove the applet used by older builds. The LaunchAgent below no longer
188
+ // references it, and leaving it behind makes diagnosis ambiguous.
189
+ rmSync(macNotifierAppPath(home), { recursive: true, force: true });
203
190
 
204
191
  mkdirSync(resolve(macNotifierPlistPath(), ".."), { recursive: true });
205
- writeFileSync(macNotifierPlistPath(), notifierPlistContents(app, watch), { mode: 0o644 });
192
+ writeFileSync(macNotifierPlistPath(), notifierPlistContents(runner, watch), { mode: 0o644 });
206
193
 
207
194
  const target = `gui/${process.getuid?.() ?? 0}`;
208
195
  // bootout first: bootstrap fails outright on an already-loaded label, and
@@ -220,7 +207,7 @@ export function installMacNotifier(home?: string): MacNotifierResult {
220
207
  }
221
208
 
222
209
  /**
223
- * Remove the agent and the bundle.
210
+ * Remove the agent and its runner, including the applet used by older builds.
224
211
  *
225
212
  * Called from `failproofai uninstall --purge`. Unconditional and quiet: an
226
213
  * agent left behind after an uninstall is a plist launchd keeps trying to start
@@ -236,6 +223,7 @@ export function uninstallMacNotifier(home?: string): void {
236
223
  }
237
224
  rmSync(macNotifierPlistPath(), { force: true });
238
225
  rmSync(macNotifierAppPath(home), { recursive: true, force: true });
226
+ rmSync(macNotifierRunnerPath(home), { force: true });
239
227
  rmSync(macNotifyDir(home), { recursive: true, force: true });
240
228
  }
241
229
 
@@ -281,7 +269,7 @@ export function queueMacNotification(
281
269
 
282
270
  /** True when an agent is installed and could pick a queued file up. */
283
271
  export function macNotifierInstalled(home?: string): boolean {
284
- return existsSync(macNotifierPlistPath()) && existsSync(macNotifierAppPath(home));
272
+ return existsSync(macNotifierPlistPath()) && existsSync(macNotifierRunnerPath(home));
285
273
  }
286
274
 
287
275
  /**
@@ -138,10 +138,10 @@ const API_KEY_PATTERNS: Array<[RegExp, string]> = [
138
138
  // this rule's run would bridge the hyphen in `sk-proj-` and report every
139
139
  // project key a second time under the wrong label.
140
140
  [/sk-proj-[A-Za-z0-9\-_]{20,}/, "OpenAI project API key"],
141
- [/sk-svcacct-[A-Za-z0-9\-_]{20,}/, "OpenAI service-account key"],
141
+ [/(?<!\w)sk-svcacct-[A-Za-z0-9\-_]{20,}/, "OpenAI service-account key"],
142
142
  // Highest blast radius in the family: it authenticates the organisation
143
143
  // admin surface, and lives at a DIFFERENT console from the other keys.
144
- [/sk-admin-[A-Za-z0-9\-_]{20,}/, "OpenAI admin key"],
144
+ [/(?<!\w)sk-admin-[A-Za-z0-9\-_]{20,}/, "OpenAI admin key"],
145
145
 
146
146
  // ── OpenAI-compatible gateways ──
147
147
  [/sk-or-v1-[A-Za-z0-9\-_]{20,}/, "OpenRouter API key"],
@@ -1489,7 +1489,14 @@ export async function runConfigureWizard(
1489
1489
  // missing or launchd refuses the job must still finish installing its
1490
1490
  // daemon, its hooks and its policies, which are the parts that enforce
1491
1491
  // anything; the notice in-CLI still reaches them either way.
1492
- installMacNotifier();
1492
+ const notifier = installMacNotifier();
1493
+ if (process.platform === "darwin" && !notifier.installed) {
1494
+ hookLogWarn(`macOS notification helper was not installed: ${notifier.reason ?? "unknown error"}`);
1495
+ stdout.write(
1496
+ "\nSystem notifications could not be installed. Scheduled scans and email alerts\n" +
1497
+ "still work; re-run `failproofai config` after checking the local LaunchAgent.\n\n",
1498
+ );
1499
+ }
1493
1500
  }
1494
1501
 
1495
1502
  // Telemetry runs concurrently with the install (never rejects, 5s-bounded) so
@@ -193,6 +193,7 @@ export async function runUninstallCommand(opts: UninstallOptions = {}): Promise<
193
193
  if (found.daemonConfigured) {
194
194
  plan.push(` • stop requiring the daemon (policies go back to evaluating in-process)`);
195
195
  }
196
+ let removeDaemon = opts.purge === true;
196
197
  if (found.serviceInstalled) {
197
198
  plan.push(
198
199
  opts.purge || opts.yes
@@ -305,8 +306,7 @@ export async function runUninstallCommand(opts: UninstallOptions = {}): Promise<
305
306
  //
306
307
  // So the only case that keeps it is an INTERACTIVE run where the person said no
307
308
  // (or could not be asked).
308
- const removeDaemon =
309
- opts.purge || opts.yes ? true : ((await opts.confirmDaemon?.()) ?? false);
309
+ removeDaemon = opts.purge || opts.yes ? true : ((await opts.confirmDaemon?.()) ?? false);
310
310
 
311
311
  if (!removeDaemon) {
312
312
  lines.push(
@@ -357,7 +357,7 @@ export async function runUninstallCommand(opts: UninstallOptions = {}): Promise<
357
357
  // No-ops on every other platform, and needs no elevation on macOS: a per-user
358
358
  // agent is the user's own to load and unload. Tied to the daemon's removal
359
359
  // because it exists only to deliver what the daemon's scheduled audit finds.
360
- if (opts.purge || (found.serviceInstalled && (opts.purge || opts.yes))) {
360
+ if (opts.purge || removeDaemon) {
361
361
  uninstallMacNotifier();
362
362
  }
363
363
 
@@ -1 +0,0 @@
1
- (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,86616,29054,e=>{"use strict";function t(e,a={},s=15e3){let n=AbortSignal.timeout(s),i=a.signal?AbortSignal.any([a.signal,n]):n;return fetch(e,{...a,signal:i})}function a(e){if(null===e||"object"!=typeof e)return!1;let t=e.name;return"AbortError"===t||"TimeoutError"===t}e.s(["fetchWithTimeout",0,t,"isAbortError",0,a],29054);class s extends Error{kind;constructor(e,t){super(t),this.kind=e,this.name="RerunError"}}async function n(e){try{let a=await t("/api/audit/run",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({cli:e.cli.length>0?e.cli:void 0,since:"all"===e.since?void 0:e.since,noCache:!!e.noCache||void 0})});if(!a.ok&&409!==a.status){let e=await a.text().catch(()=>"");throw console.error("audit run failed:",a.status,e),new s("post_failed",`audit run failed (${a.status})`)}}catch(e){if(e instanceof s)throw e;throw console.error("audit run request failed:",e),new s(a(e)?"timeout":"network","audit run request failed")}let n=0;for(;;){await new Promise(e=>setTimeout(e,1e3));let e=null;try{let a=await t("/api/audit/status",{cache:"no-store"});a.ok&&(e=await a.json())}catch{}if(null===e){if(++n>=10)throw new s("network","audit status poll lost the server");continue}if(n=0,!e.running){if(e.error)throw new s("post_failed",e.error);return}}}e.s(["RerunError",0,s,"triggerRun",0,n],86616)},93237,e=>{"use strict";var t=e.i(43476),a=e.i(71645),s=e.i(95187);let n=(0,s.createServerReference)("003aa798ad2911b2624d7ae688194c8ac75c58befe",s.callServer,void 0,s.findSourceMapURL,"getScheduledAuditAction"),i=(0,s.createServerReference)("400be872c57a0a05d9c3c32baca938364d393ab573",s.callServer,void 0,s.findSourceMapURL,"setAutoAuditAction"),l=(0,s.createServerReference)("40d8dabf2f67b78ed18c25cea502c2bf2dd913d73b",s.callServer,void 0,s.findSourceMapURL,"setAuditIntervalAction");var r=e.i(86616),o=e.i(12344),c=e.i(29054);let d=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;function u(e){if((0,c.isAbortError)(e))return"request timed out. check your network and try again.";let t=e instanceof Error?e.message:String(e);return`network error: ${t}`}function h({open:e,headline:s="where to route the reminder?",subhead:n="we'll send a one-time code to confirm.",onClose:i,onAuthed:l,source:r="unknown"}){let{capture:m}=(0,o.usePostHog)(),[f,b]=(0,a.useState)({kind:"email",error:null}),[p,x]=(0,a.useState)(!1),v=(0,a.useRef)(!1),y=(0,a.useRef)(null),w=(0,a.useRef)(null);(0,a.useEffect)(()=>{e&&(b({kind:"email",error:null}),x(!1),v.current=!1,m("audit_auth_dialog_opened",{source:r}))},[m,e,r]);let g=(0,a.useRef)(!1);(0,a.useEffect)(()=>{if(e){g.current=!0;return}g.current&&!v.current&&m("audit_auth_dialog_dismissed",{source:r,step:f.kind}),g.current=!1},[m,e,r,f.kind]),(0,a.useEffect)(()=>{if(!e)return;let t=setTimeout(()=>{"email"===f.kind?y.current?.focus():"code"===f.kind&&w.current?.focus()},50);return()=>clearTimeout(t)},[e,f.kind]),(0,a.useEffect)(()=>{if(!e)return;let t=e=>{"Escape"!==e.key||p||i()};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[e,p,i]);let j="code"===f.kind&&f.resendIn>0;(0,a.useEffect)(()=>{if(!j)return;let e=setInterval(()=>{b(e=>"code"===e.kind?{...e,resendIn:Math.max(0,e.resendIn-1)}:e)},1e3);return()=>clearInterval(e)},[j]);let k=(0,a.useCallback)(async(e,t={})=>{let{isResend:a=!1}=t,s=e=>{a?b(t=>"code"===t.kind?{...t,error:e}:t):b({kind:"email",error:e})};x(!0);try{let t=await (0,c.fetchWithTimeout)("/api/auth/login-request",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({email:e})}),a=await t.json().catch(()=>({}));if(!t.ok){let e=a.message??"could not send code.";"rate_limited"===a.code&&void 0!==a.retry_after_secs?e=`too many tries. wait ${a.retry_after_secs}s and try again.`:"upstream_unreachable"===a.code&&(e="api-server unreachable. check your network."),s(e);return}b({kind:"code",email:e,error:null,expiresIn:a.expires_in??600,resendIn:a.resend_available_in??30})}catch(e){s(u(e))}finally{x(!1)}},[]),N=(0,a.useCallback)(async(e,t)=>{x(!0);try{let a=await (0,c.fetchWithTimeout)("/api/auth/login-verify",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({email:e,code:t})}),s=await a.json().catch(()=>({}));if(!a.ok||!s.authenticated||!s.user){let e=s.message??"invalid code.";"invalid_code"===s.code&&(e="wrong code, or it expired. try again."),b(t=>"code"===t.kind?{...t,error:e}:t);return}v.current=!0,m("audit_auth_dialog_succeeded",{source:r}),b({kind:"done",user:s.user}),l(s.user)}catch(t){let e=u(t);b(t=>"code"===t.kind?{...t,error:e}:t)}finally{x(!1)}},[m,l,r]),S=(0,a.useCallback)(async e=>{if(e.preventDefault(),p||"email"!==f.kind)return;let t=String(new FormData(e.currentTarget).get("email")??"").trim().toLowerCase();d.test(t)?await k(t):b({kind:"email",error:"that doesn't look like an email."})},[p,f,k]),C=(0,a.useCallback)(async e=>{if(e.preventDefault(),p||"code"!==f.kind)return;let t=String(new FormData(e.currentTarget).get("code")??"").trim();t.length<4||t.length>12?b(e=>"code"===e.kind?{...e,error:"code is 4–12 characters."}:e):await N(f.email,t)},[p,f,N]),M=(0,a.useCallback)(async()=>{"code"!==f.kind||f.resendIn>0||p||await k(f.email,{isResend:!0})},[f,p,k]);return e?(0,t.jsx)("div",{className:"auth-dialog-backdrop",role:"dialog","aria-modal":"true","aria-labelledby":"auth-dialog-title",onClick:e=>{p||e.target!==e.currentTarget||i()},children:(0,t.jsxs)("div",{className:"auth-dialog",children:[(0,t.jsx)("button",{type:"button",className:"auth-close",onClick:i,disabled:p,"aria-label":"close",children:"×"}),(0,t.jsx)("h2",{id:"auth-dialog-title",className:"auth-headline",children:s}),"email"===f.kind&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("p",{className:"auth-sub",children:n}),(0,t.jsxs)("form",{onSubmit:S,className:"auth-form",children:[(0,t.jsx)("input",{ref:y,id:"auth-dialog-email",name:"email",type:"email",autoComplete:"email",inputMode:"email",spellCheck:!1,placeholder:"you@yourdomain.com",disabled:p,className:"auth-input",required:!0}),f.error&&(0,t.jsx)("div",{className:"auth-error",children:f.error}),(0,t.jsxs)("div",{className:"auth-actions",children:[(0,t.jsx)("button",{type:"submit",className:"auth-btn primary",disabled:p,children:p?"sending…":"send code"}),(0,t.jsx)("button",{type:"button",className:"auth-btn",onClick:i,disabled:p,children:"cancel"})]})]})]}),"code"===f.kind&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("p",{className:"auth-sub",children:["code sent to ",(0,t.jsx)("span",{className:"auth-email",children:f.email}),". expires in ",Math.ceil(f.expiresIn/60)," min."]}),(0,t.jsxs)("form",{onSubmit:C,className:"auth-form",children:[(0,t.jsx)("input",{ref:w,id:"auth-dialog-code",name:"code",type:"text",inputMode:"numeric",autoComplete:"one-time-code",spellCheck:!1,placeholder:"123456",disabled:p,className:"auth-input auth-input-code",maxLength:12,required:!0}),f.error&&(0,t.jsx)("div",{className:"auth-error",children:f.error}),(0,t.jsxs)("div",{className:"auth-actions",children:[(0,t.jsx)("button",{type:"submit",className:"auth-btn primary",disabled:p,children:p?"verifying…":"verify"}),(0,t.jsx)("button",{type:"button",className:"auth-btn",onClick:M,disabled:p||f.resendIn>0,children:f.resendIn>0?`resend in ${f.resendIn}s`:"resend code"})]}),(0,t.jsx)("button",{type:"button",className:"auth-back",onClick:()=>b({kind:"email",error:null}),disabled:p,children:"← use a different email"})]})]}),"done"===f.kind&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("p",{className:"auth-sub",children:[(0,t.jsx)("span",{className:"auth-ok",children:"✓"})," signed in as"," ",(0,t.jsx)("span",{className:"auth-email",children:f.user.email}),"."]}),(0,t.jsx)("div",{className:"auth-actions",children:(0,t.jsx)("button",{type:"button",className:"auth-btn primary",onClick:i,children:"continue"})})]})]})}):null}var m=e.i(55574),f=e.i(34713);function b(e){return new Date(e).toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}function p(e,t){let a=e-t;if(a<=0)return"now";let s=Math.floor(a/864e5),n=Math.floor(a%864e5/36e5);return s>0?n>0?`${s}d ${n}h`:`${s}d`:n>0?`${n}h`:`${Math.max(1,Math.floor(a/6e4))}m`}function x(e){return e.toLocaleString("en-US")}function v({label:e,value:a,sub:s,tone:n}){return(0,t.jsxs)("div",{className:"set-stat",children:[(0,t.jsx)("div",{className:"set-stat-label",children:e}),(0,t.jsx)("div",{className:`set-stat-value${n?` ${n}`:""}`,children:a}),(0,t.jsx)("div",{className:"set-stat-sub",children:s??" "})]})}function y({lastRunAtMs:e,nextDueAtMs:a,now:s}){if(null==e||null==a||a<=e)return null;let n=Math.min(100,Math.max(0,(s-e)/(a-e)*100));return(0,t.jsxs)("div",{className:"tape","aria-hidden":"true",children:[(0,t.jsxs)("div",{className:"tape-rail",children:[(0,t.jsx)("div",{className:"tape-fill",style:{width:`${n}%`}}),(0,t.jsx)("div",{className:"tape-now",style:{left:`${n}%`}})]}),(0,t.jsxs)("div",{className:"tape-ends",children:[(0,t.jsxs)("span",{children:["last scan · ",b(e)]}),(0,t.jsxs)("span",{className:"tape-next",children:["next · ",p(a,s)]})]})]})}function w({enabled:e,onChange:a,disabled:s,label:n}){return(0,t.jsx)("button",{type:"button",role:"switch","aria-checked":e,"aria-label":n,disabled:s,onClick:a,className:"sw","data-on":e?"true":"false",children:(0,t.jsx)("span",{className:"sw-knob"})})}let g=[{label:"reads",body:"every session transcript on disk — your prompts, the files your agents read and wrote, and command output."},{label:"runs",body:"entirely on this machine. the transcripts never leave it."},{label:"sends",body:"only the newest masked credential exposure, its CLI, time, location, and this machine's name — only when email is configured."}];e.s(["default",0,function({initial:e}){let[s,o]=(0,a.useState)(e),[c,d]=(0,a.useState)(e?.auto??!1),[u,j]=(0,a.useState)(e?.intervalDays??7),[k,N]=(0,a.useState)(!1),[S,C]=(0,a.useState)(!1),[M,$]=(0,a.useState)(!1),[E,A]=(0,a.useState)(null===e),[D,R]=(0,a.useState)(0),_=(0,a.useRef)(!0),T=(0,a.useRef)(null!==e),I=(0,a.useCallback)(async()=>{try{let e=await n();if(!_.current)return;o(e),T.current=!0,d(e.auto),j(e.intervalDays),R(Date.now()),A(!1)}catch{_.current&&!T.current&&A(!0)}},[]);(0,a.useEffect)(()=>(_.current=!0,I(),()=>{_.current=!1}),[I]),(0,a.useEffect)(()=>{let e=()=>void I();return window.addEventListener("focus",e),document.addEventListener("visibilitychange",e),()=>{window.removeEventListener("focus",e),document.removeEventListener("visibilitychange",e)}},[I]);let L=s?.signedInAs??null,F=s?.daemon==="running",O=s?.daemon==="unsupported-platform",P=s?.schedule??null,q=s?.lastScan??null,U=P?.lastExitCode!=null&&0!==P.lastExitCode&&75!==P.lastExitCode,B=(0,a.useCallback)(async()=>{N(!0);try{let e=await i(!0);d(e.auto),(0,m.toast)("scheduled audits on."),await I()}catch{d(!1),(0,m.toast)("could not turn that on.")}finally{N(!1)}},[I]),J=(0,a.useCallback)(async()=>{if(c){N(!0);try{let e=await i(!1);e.ok&&d(e.auto),(0,m.toast)("scheduled audits off."),await I()}catch{(0,m.toast)("could not turn that off.")}finally{N(!1)}return}await B()},[c,B,I]),W=(0,a.useCallback)(async e=>{N(!0);try{let t=await l(e);j(t.intervalDays),o(e=>e?{...e,intervalDays:t.intervalDays}:e),(0,m.toast)(`scanning every ${t.intervalDays} day${1===t.intervalDays?"":"s"}.`)}catch{j(s?.intervalDays??7),(0,m.toast)("could not save that.")}finally{N(!1)}},[s?.intervalDays]),K=(0,a.useCallback)(async()=>{N(!0);try{await fetch("/api/auth/logout",{method:"POST"}),(0,m.toast)("signed out. scans and desktop notifications continue; email alerts pause."),await I()}catch{(0,m.toast)("could not sign out.")}finally{N(!1)}},[I]),H=(0,a.useCallback)(async()=>{if(!S){C(!0);try{await (0,r.triggerRun)({cli:[],since:"all",noCache:!1}),(0,m.toast)("scan complete."),await I()}catch(e){(0,m.toast)(e instanceof r.RerunError&&"timeout"===e.kind?"the scan is taking a while — it will finish in the background.":"the scan could not be completed.")}finally{C(!1)}}},[I,S]),z=(0,a.useCallback)(async e=>{$(!1),await I(),await B()},[B,I]),G=s?F?{value:"running",sub:null!=s.daemonStartedAtMs&&D>0?`up ${function(e){let t=Math.floor(e/864e5);if(t>0)return`${t}d`;let a=Math.floor(e/36e5);return a>0?`${a}h`:`${Math.max(1,Math.floor(e/6e4))}m`}(Math.max(0,D-s.daemonStartedAtMs))}`:"",tone:"ok"}:O?{value:"unavailable",sub:"not on this platform"}:"not-installed"===s.daemon?{value:"not installed",sub:"run failproofai config",tone:"warn"}:"unknown"===s.daemon?{value:"unknown",sub:"could not read it"}:{value:"stopped",sub:"run failproofai config",tone:"warn"}:{value:"…",sub:"reading"},Q=c?P?.nextDueAtMs==null?{value:"pending",sub:"after the next scan"}:{value:D>0?p(P.nextDueAtMs,D):"—",sub:b(P.nextDueAtMs)}:{value:"off",sub:"nothing scheduled"},V=q?{value:(0,f.formatRelativeTime)(Date.parse(q.finishedAt)),sub:null!=q.sessionsScanned?`${x(q.sessionsScanned)} sessions`:"scanned"}:{value:"none yet",sub:"run one below"},X=q?null==q.findings?{value:"—",sub:"unreadable"}:{value:x(q.findings),sub:"this scan",...q.findings>0?{tone:"warn"}:{}}:{value:"—",sub:"no scan yet"};return(0,t.jsxs)("main",{className:"report",children:[(0,t.jsxs)("section",{className:"section set-sec",children:[(0,t.jsxs)("div",{className:"set-mast",children:[(0,t.jsx)("h1",{className:"set-title",children:"settings"}),(0,t.jsx)("p",{className:"set-lede",children:"keeping watch, so you don't have to."})]}),E?(0,t.jsx)("div",{className:"set-console",children:(0,t.jsx)("div",{className:"set-cell",children:(0,t.jsxs)("p",{className:"set-warn",children:["could not read this machine's settings. check that the dashboard can reach ",(0,t.jsx)("code",{children:"~/.failproofai/config.json"}),"."]})})}):(0,t.jsxs)("div",{className:"set-console",children:[(0,t.jsxs)("div",{className:"set-stats",children:[(0,t.jsx)(v,{label:"daemon",...G}),(0,t.jsx)(v,{label:"next scan",...Q}),(0,t.jsx)(v,{label:"last scan",...V}),(0,t.jsx)(v,{label:"findings",...X})]}),(0,t.jsxs)("div",{className:"set-cols",children:[(0,t.jsxs)("div",{className:"set-cell set-config",children:[(0,t.jsx)("h2",{className:"set-cell-title",children:"scheduled audit"}),(0,t.jsxs)("div",{className:"set-row set-row-main",children:[(0,t.jsx)(w,{enabled:c,disabled:k||null===s,onChange:()=>void J(),label:c?"turn off scheduled audits":"turn on scheduled audits"}),(0,t.jsxs)("div",{className:"set-row-copy",children:[(0,t.jsx)("span",{className:"set-strong",children:c?"scanning on a schedule.":"scan on a schedule."}),(0,t.jsx)("span",{className:"set-dim",children:c?L?"system notification on leaks; the latest masked leak is also emailed.":"system notification on leaks. add an email below for masked alerts.":"off. nothing runs and nothing is sent."})]})]}),(0,t.jsxs)("div",{className:"set-row set-row-interval",children:[(0,t.jsx)("span",{className:"set-dim",children:"scan every"}),(0,t.jsx)("input",{type:"number",className:"set-num",min:1,max:90,value:u,disabled:k||null===s,"aria-label":"days between scheduled scans",onChange:e=>j(Number(e.target.value)),onBlur:e=>{let t=Number(e.target.value);Number.isFinite(t)?t!==s?.intervalDays&&W(t):j(s?.intervalDays??7)}}),(0,t.jsx)("span",{className:"set-dim",children:"days"}),(0,t.jsxs)("span",{className:"set-hint",children:[1,"–",90]})]}),(0,t.jsx)("div",{className:"set-rule"}),(0,t.jsx)("div",{className:"set-row set-row-identity",children:L?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{className:"set-dim",children:"reports go to"}),(0,t.jsx)("span",{className:"set-email",children:L.email}),(0,t.jsx)("button",{type:"button",className:"set-link",disabled:k,onClick:()=>void K(),children:"sign out"})]}):c?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{className:"set-warn",children:"signed out — scans and desktop notifications continue; email alerts are paused."}),(0,t.jsx)("button",{type:"button",className:"set-link",disabled:k,onClick:()=>$(!0),children:"add email alerts"})]}):(0,t.jsx)("span",{className:"set-dim",children:"scans and desktop notifications work without an account. add email later if you want leak alerts in your inbox."})}),c&&s&&!F&&(0,t.jsx)("p",{className:"set-warn",children:O?"the background service isn't available on this platform, so scheduled scans can't run here. you can still run one now.":"not-installed"===s.daemon?"the background service isn't installed, so nothing will run on the timer. install it with `failproofai config`.":"the background service is stopped, so nothing will run until it starts. repair it with `failproofai config`."}),U&&(0,t.jsxs)("p",{className:"set-warn",children:["the last scheduled scan exited ",P?.lastExitCode,". the next one will still run."]}),(0,t.jsx)("div",{className:"set-actions",children:(0,t.jsx)("button",{type:"button",className:"btn btn-press",disabled:S,onClick:()=>void H(),children:S?"[ scanning… ]":"[ run a scan now ]"})})]}),(0,t.jsxs)("div",{className:"set-cell set-how",children:[(0,t.jsx)("h2",{className:"set-cell-title",children:"how it works"}),(0,t.jsx)("dl",{className:"set-how-list",children:g.map(e=>(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:e.label}),(0,t.jsx)("dd",{className:"set-how-body",children:e.body})]},e.label))})]})]})]}),c&&D>0&&(0,t.jsx)(y,{lastRunAtMs:P?.lastRunAtMs??null,nextDueAtMs:P?.nextDueAtMs??null,now:D})]}),(0,t.jsx)(h,{open:M,source:"settings_scheduled_audit",headline:"where should the report go?",subhead:"we'll send a one-time code to confirm.",onClose:()=>$(!1),onAuthed:e=>void z(e)})]})}],93237)},34713,e=>{"use strict";e.s(["formatDuration",0,function(e){if(e<1e3)return`${e}ms`;let t=Math.round(e/100);if(t<600)return`${(t/10).toFixed(1)}s`;let a=Math.round(e/1e3);if(a<3600){let e=Math.floor(a/60);return`${e}m ${a%60}s`}let s=Math.round(e/6e4),n=Math.floor(s/60);return`${n}h ${s%60}m`},"formatRelativeTime",0,function(e){let t=Date.now()-e;return t<6e4?`${Math.max(1,Math.floor(t/1e3))}s ago`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}])}]);
@@ -1 +0,0 @@
1
- (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,24358,e=>{"use strict";var s=e.i(43476),a=e.i(71645),n=e.i(95187);let t=(0,n.createServerReference)("009ad545620d7158fc4c18ddc9854cfa5473399e64",n.callServer,void 0,n.findSourceMapURL,"getAuditResultAction");var r=e.i(12344);function i({cachedAt:e,fixed:a=!1}){return(0,s.jsxs)("footer",{className:a?"report-footer report-footer--fixed":"report-footer",children:[(0,s.jsx)("img",{src:"/logo.svg",alt:"failproof_ai",style:{height:18,display:"inline-block",verticalAlign:"middle"}}),(0,s.jsx)("span",{style:{margin:"0 12px",color:"var(--line-2)"},children:"·"}),"audit v1.0",(0,s.jsx)("span",{style:{margin:"0 12px",color:"var(--line-2)"},children:"·"}),"generated ",function(e){if(!e)return"—";let s=new Date(e);if(Number.isNaN(s.getTime()))return"—";let a=s.getUTCDate().toString().padStart(2,"0"),n=["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"][s.getUTCMonth()],t=s.getUTCFullYear(),r=s.getUTCHours().toString().padStart(2,"0"),i=s.getUTCMinutes().toString().padStart(2,"0");return`${a} ${n} ${t}, ${r}:${i} utc`}(e),(0,s.jsx)("span",{style:{margin:"0 12px",color:"var(--line-2)"},children:"·"}),(0,s.jsx)("span",{style:{color:"var(--ink-2)"},children:"auto-healing for your agents."})]})}let l=(0,n.createServerReference)("40627fe0bb35d1aea2f59f4ed5f39912b524a8cc1c",n.callServer,void 0,n.findSourceMapURL,"dismissLeakAction"),c=(0,n.createServerReference)("004c0a92f30b3875d89d14068f7fbdb7ecce772dc3",n.callServer,void 0,n.findSourceMapURL,"getLeaksAction"),d=(0,n.createServerReference)("40dbb536ad33353d440386d51c0242e56cd808cfbc",n.callServer,void 0,n.findSourceMapURL,"setLeakNotifyAction");function o(e){let s=Date.parse(e);if(!Number.isFinite(s))return"unknown";let a=Math.floor((Date.now()-s)/864e5);return a<=0?"today":1===a?"yesterday":a<30?`${a}d ago`:new Date(s).toLocaleDateString(void 0,{month:"short",day:"numeric"})}function u(){let[e,n]=(0,a.useState)(null),[t,r]=(0,a.useState)(!0),[i,u]=(0,a.useState)(null),h=(0,a.useCallback)(async()=>{let e=await c();n(e.rows),r(e.notify)},[]);(0,a.useEffect)(()=>{h()},[h]);let p=(0,a.useCallback)(async e=>{u(e);try{await l(e)&&n(s=>s?.filter(s=>s.id!==e)??null)}finally{u(null)}},[]),m=(0,a.useCallback)(async()=>{let e=!t;r(e),await d(e)||r(!e)},[t]);return null===e?null:(0,s.jsxs)("section",{className:"audit-sec","data-screen-label":"02 Leaks",children:[(0,s.jsxs)("div",{className:"audit-sec-head",children:[(0,s.jsxs)("span",{className:"audit-sec-eyebrow",children:[(0,s.jsx)("span",{className:"ix",children:"02"}),"// credentials"]}),(0,s.jsx)("button",{type:"button",className:"cbb-toggle leak-notify","data-on":t,onClick:m,"aria-label":t?"turn desktop notifications off":"turn desktop notifications on",title:"desktop notification when a new credential is found",children:(0,s.jsx)("span",{className:"cbb-toggle-knob"})})]}),0===e.length?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("h2",{className:"audit-sec-title",children:"no credentials found"}),(0,s.jsxs)("div",{className:"audit-sec-sub",children:["// nothing in this machine's transcripts matched a known key format",(0,s.jsx)("br",{}),"// or a secret-named assignment."]})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("h2",{className:"audit-sec-title",children:[e.length," credential",1===e.length?"":"s"," in your transcripts"]}),(0,s.jsx)("div",{className:"audit-sec-sub",children:"// one row per distinct key — an exposure count is not a key count."}),(0,s.jsx)("ul",{className:"leak-list",children:e.map(e=>(0,s.jsxs)("li",{className:"leak-row","data-attributed":e.attributed,children:[(0,s.jsxs)("div",{className:"leak-row-top",children:[(0,s.jsx)("code",{className:"leak-key",children:e.display}),(0,s.jsx)("span",{className:"leak-label",children:e.label}),e.name?(0,s.jsx)("span",{className:"leak-name",children:e.name}):null,(0,s.jsx)("button",{type:"button",className:"leak-dismiss",onClick:()=>void p(e.id),disabled:i===e.id,children:"not a secret"})]}),(0,s.jsxs)("div",{className:"leak-how",children:[(0,s.jsx)("span",{className:"leak-verb",children:e.mechanism})," in ",(0,s.jsx)("span",{className:"leak-where",children:e.project})," via ",(0,s.jsx)("span",{className:"leak-who",children:e.cli})," · leaked ",(0,s.jsx)("span",{className:"leak-when",children:o(e.firstSeen)}),o(e.firstSeen)!==o(e.lastSeen)?(0,s.jsxs)(s.Fragment,{children:[" · last seen ",(0,s.jsx)("span",{className:"leak-when",children:o(e.lastSeen)})]}):null]}),(0,s.jsxs)("div",{className:"leak-foot",children:[(0,s.jsxs)("span",{className:"leak-meta",children:[e.occurrences," exposure",1===e.occurrences?"":"s",e.sessions>1?` \xb7 ${e.sessions} sessions`:""," · ",e.length," chars"]}),(0,s.jsx)("span",{className:"leak-dir",children:"input"===e.direction?"sent by the agent — blockable at PreToolUse":"returned to the agent — already in the transcript"}),(0,s.jsx)("span",{className:"leak-fix",children:e.attributed?`rotate it in the ${e.label.replace(/ (API )?(key|token).*$/i,"")} console`:e.name?`find what reads ${e.name}, then rotate it there`:"trace it to its owner, then rotate it"})]})]},e.id))})]})]})}var h=e.i(86616);function p({mode:e,running:a,onStarted:n,onCompleted:t}){let{capture:i}=(0,r.usePostHog)(),l=async()=>{i("audit_first_run_clicked",{source:"empty_state",mode:e}),n();try{await (0,h.triggerRun)({cli:[],since:"all"})}catch(e){console.error("audit first run failed:",e)}finally{await t()}};return"no-cache"===e?(0,s.jsxs)("section",{className:"section empty-section","data-screen-label":"00 Empty",children:[(0,s.jsxs)("div",{className:"section-mast",children:[(0,s.jsxs)("div",{className:"section-label",children:[(0,s.jsx)("span",{className:"glyph",children:"━━"})," audit"," ",(0,s.jsx)("span",{className:"sep",children:"·"})," first run"]}),(0,s.jsxs)("div",{className:"section-meta",children:[(0,s.jsx)("span",{style:{color:"var(--dim)"},children:"○"})," no cache yet"]})]}),(0,s.jsx)("h2",{className:"section-h",children:"scan and see."}),(0,s.jsxs)("div",{className:"panel empty-panel",children:[(0,s.jsxs)("div",{className:"empty-glyph","aria-hidden":"true",children:[(0,s.jsx)("div",{className:"empty-glyph-grid",children:Array.from({length:36}).map((e,a)=>{let n=["1","5","8","10","13","17","20","23","25","30","32"].includes(String(a));return(0,s.jsx)("span",{className:"px"+(n?" on":"")},a)})}),(0,s.jsx)("div",{className:"empty-glyph-label",children:"▮▮ no audit data yet"})]}),(0,s.jsx)("h3",{className:"empty-headline",children:"run your first audit."}),(0,s.jsxs)("p",{className:"empty-sub",children:["we'll walk every transcript across your installed CLIs — Claude Code, Codex, Copilot, Cursor, OpenCode, Pi — and count every wasteful","or risky action. you'll get your agent's archetype and a punch-list."]}),(0,s.jsxs)("div",{className:"empty-actions",children:[(0,s.jsx)("button",{type:"button",className:"btn btn-primary btn-press empty-cta",onClick:l,disabled:a,children:a?"[ scanning… ]":"[ run audit ]"}),(0,s.jsx)("span",{className:"empty-meta",children:"scans all sessions · all installed CLIs · may take a while"})]})]})]}):(0,s.jsxs)("section",{className:"section empty-section","data-screen-label":"00 Empty",children:[(0,s.jsxs)("div",{className:"section-mast",children:[(0,s.jsxs)("div",{className:"section-label",children:[(0,s.jsx)("span",{className:"glyph",children:"━━"})," audit"," ",(0,s.jsx)("span",{className:"sep",children:"·"})," zero transcripts"]}),(0,s.jsxs)("div",{className:"section-meta",children:[(0,s.jsx)("span",{style:{color:"var(--amber)"},children:"●"})," hooks not installed"]})]}),(0,s.jsx)("h2",{className:"section-h",children:"nothing to read."}),(0,s.jsxs)("div",{className:"panel empty-panel",children:[(0,s.jsxs)("div",{className:"empty-glyph","aria-hidden":"true",children:[(0,s.jsx)("div",{className:"empty-glyph-grid",children:Array.from({length:36}).map((e,a)=>{let n=["2","4","9","11","16","18","23","25","27","30","33"].includes(String(a));return(0,s.jsx)("span",{className:"px"+(n?" on":"")},a)})}),(0,s.jsx)("div",{className:"empty-glyph-label",children:"▮▮ no sessions found"})]}),(0,s.jsx)("h3",{className:"empty-headline",children:"install hooks first."}),(0,s.jsx)("p",{className:"empty-sub",children:"failproofai couldn't find any transcripts to scan on this machine. install the hooks for at least one CLI and come back."}),(0,s.jsxs)("div",{className:"empty-actions",children:[(0,s.jsx)("a",{href:"https://docs.befailproof.ai/getting-started",target:"_blank",rel:"noopener noreferrer",className:"btn btn-primary btn-press empty-cta",onClick:()=>{i("audit_install_guide_clicked",{source:"empty_state",mode:e})},children:"[ install guide → ]"}),(0,s.jsx)("span",{className:"empty-meta",children:"takes about 30 seconds · one command per CLI"})]})]})]})}let m=[{label:"discovering transcripts",detail:"walking ~/.claude, ~/.codex, ~/.cursor, …"},{label:"parsing session logs",detail:"reading JSONL + sqlite session stores"},{label:"running policy checks",detail:"replaying every policy against each tool call"},{label:"aggregating results",detail:"counting hits, ranking by frequency"}];function g(){let[e,n]=(0,a.useState)(0),[t,r]=(0,a.useState)(0);(0,a.useEffect)(()=>{let e=setInterval(()=>{n(e=>Math.min(e+1,m.length-1))},4e3),s=setInterval(()=>r(e=>(e+1)%4),350);return()=>{clearInterval(e),clearInterval(s)}},[]);let i=".".repeat(t+1),l=e===m.length-1,c=Math.round(100*Math.min(.9,(e+1)/m.length*.9));return(0,s.jsxs)("section",{className:"section running-section","data-screen-label":"00 Running",children:[(0,s.jsxs)("div",{className:"section-mast",children:[(0,s.jsxs)("div",{className:"section-label",children:[(0,s.jsx)("span",{className:"glyph",children:"━━"})," audit"," ",(0,s.jsx)("span",{className:"sep",children:"·"})," in progress"]}),(0,s.jsxs)("div",{className:"section-meta",children:[(0,s.jsx)("span",{style:{color:"var(--accent-pink)"},children:"●"})," scanning"]})]}),(0,s.jsxs)("h2",{className:"section-h",children:["scanning sessions",i]}),(0,s.jsxs)("div",{className:"panel running-panel",children:[(0,s.jsxs)("div",{className:"running-header",children:[(0,s.jsx)("span",{className:"running-prompt",children:"$"}),(0,s.jsx)("span",{className:"running-cmd",children:"failproofai audit"}),(0,s.jsx)("span",{className:"running-cursor","aria-hidden":"true",children:"▮"})]}),(0,s.jsx)("ul",{className:"running-stages",children:m.map((a,n)=>{let r=n<e,i=n===e;return(0,s.jsxs)("li",{className:"running-stage"+(r?" done":i?" active":" queued"),children:[(0,s.jsx)("span",{className:"running-marker","aria-hidden":"true",children:r?"✓":i?"▮▮":"○"}),(0,s.jsxs)("div",{className:"running-stage-body",children:[(0,s.jsx)("div",{className:"running-stage-label",children:a.label}),i&&(0,s.jsx)("div",{className:"running-stage-detail",children:l?"finishing up…":a.detail})]}),i&&(0,s.jsx)("span",{className:"running-stage-spin","aria-hidden":"true",children:["⠋","⠙","⠹","⠸"][t]})]},n)})}),(0,s.jsxs)("div",{className:"running-bar-label",children:[(0,s.jsx)("span",{children:"progress"}),(0,s.jsxs)("span",{style:{color:"var(--dim)"},children:[c,"%"]})]}),(0,s.jsx)("div",{className:"running-bar-track",children:(0,s.jsx)("div",{className:"running-bar-fill",style:{width:`${c}%`}})}),(0,s.jsx)("p",{className:"running-foot",children:"this can take a while depending on how much session history you have."})]})]})}let x={timeout:"audit took too long — try again",network:"network hiccup — check your connection",post_failed:"audit failed — try again"};function f({status:e,onDismiss:n}){let[t,r]=(0,a.useState)(()=>Date.now());if((0,a.useEffect)(()=>{if("running"!==e.kind)return;let s=setInterval(()=>r(Date.now()),1e3);return()=>clearInterval(s)},[e.kind]),"idle"===e.kind)return null;if("running"===e.kind){let a=Math.max(0,t-e.startedAt);return(0,s.jsxs)("div",{className:"audit-progress-strip",role:"status","aria-live":"polite",children:[(0,s.jsxs)("div",{className:"audit-progress-strip__inner",children:[(0,s.jsxs)("span",{className:"audit-progress-strip__label",children:[(0,s.jsx)("span",{className:"audit-progress-strip__spinner","aria-hidden":"true"}),"re-auditing your sessions"]}),(0,s.jsx)("span",{className:"audit-progress-strip__elapsed",children:function(e){if(e<0)return"00:00";let s=Math.floor(e/1e3),a=Math.floor(s/60).toString().padStart(2,"0"),n=(s%60).toString().padStart(2,"0");return`${a}:${n}`}(a)})]}),(0,s.jsx)("div",{className:"audit-progress-strip__pulse","aria-hidden":"true"})]})}return(0,s.jsx)("div",{className:"audit-progress-strip audit-progress-strip--failed",role:"alert",children:(0,s.jsxs)("div",{className:"audit-progress-strip__inner",children:[(0,s.jsxs)("span",{className:"audit-progress-strip__label",children:[(0,s.jsx)("span",{className:"audit-progress-strip__x","aria-hidden":"true",children:"×"}),x[e.reason]]}),(0,s.jsx)("button",{type:"button",className:"audit-progress-strip__dismiss",onClick:n,"aria-label":"dismiss",children:"[ dismiss ]"})]})})}function j({result:e,cachedAt:n,projectFromUrl:t,isRunning:l,rerunStatus:c,onRerun:d,onDismissRerun:o}){let{capture:h}=(0,r.usePostHog)(),{detectorsTriggered:p,missing:m}=(0,a.useMemo)(()=>{let s=0,a=0;for(let n of e.results)n.hits>0&&s++,"builtin"===n.source&&!n.enabledInConfig&&n.hits>0&&a++;return{detectorsTriggered:s,missing:a}},[e]),g=(0,a.useRef)(!1);return(0,a.useEffect)(()=>{g.current||(g.current=!0,h("audit_dashboard_viewed",{missing:m,transcripts_scanned:e.transcripts.scanned,results_count:e.results.length,detectors_triggered:p}))},[h,m,e.transcripts.scanned,e.results.length,p]),(0,s.jsxs)("div",{className:"app",children:[(0,s.jsx)(f,{status:c,onDismiss:o}),(0,s.jsxs)("div",{className:"app-shell",children:[(0,s.jsxs)("div",{className:"report",children:[(0,s.jsx)(y,{transcripts:e.transcripts.scanned,events:e.eventsScanned??0,projects:e.projectsScanned.length,isRunning:l||"running"===c.kind,onRerun:()=>d("scan_header")}),(0,s.jsx)(u,{})]}),(0,s.jsx)(i,{cachedAt:n})]})]})}function y({transcripts:e,events:a,projects:n,isRunning:t,onRerun:r}){return(0,s.jsxs)("section",{className:"audit-sec","data-screen-label":"01 Scan",children:[(0,s.jsxs)("div",{className:"audit-sec-head",children:[(0,s.jsxs)("span",{className:"audit-sec-eyebrow",children:[(0,s.jsx)("span",{className:"ix",children:"01"}),"// scan"]}),(0,s.jsx)("button",{type:"button",className:"scan-rerun",onClick:r,disabled:t,children:t?"scanning…":"run again"})]}),(0,s.jsx)("h2",{className:"audit-sec-title",children:"scan complete"}),(0,s.jsxs)("div",{className:"audit-sec-sub",children:[a.toLocaleString()," tool call",1===a?"":"s"," across ",e.toLocaleString()," ","transcript",1===e?"":"s",n>0?` \xb7 ${n.toLocaleString()} project${1===n?"":"s"}`:""]}),(0,s.jsxs)("div",{className:"audit-sec-sub",children:["// the report is being rebuilt around leak detection. the scan, the",(0,s.jsx)("br",{}),"// 12-CLI transcript reader and the emailed digest are unaffected."]})]})}function N({running:e,mode:a="no-cache",rerunStatus:n,onDismissRerun:t,onStarted:r,onCompleted:l}){return(0,s.jsxs)("div",{className:"app",children:[(0,s.jsx)(f,{status:n,onDismiss:t}),(0,s.jsxs)("div",{className:"app-shell",children:[(0,s.jsx)("div",{className:"report",children:e?(0,s.jsx)(g,{}):(0,s.jsx)(p,{mode:a,running:e,onStarted:r,onCompleted:l})}),(0,s.jsx)(i,{cachedAt:null,fixed:!0})]})]})}e.s(["AuditDashboard",0,function({initial:e,projectFromUrl:n,totalCatalogSize:i}){let[l,c]=(0,a.useState)(e),[d,o]=(0,a.useState)(!1),[u,p]=(0,a.useState)({kind:"idle"}),{capture:m}=(0,r.usePostHog)(),g=(0,a.useRef)(null),x=(0,a.useRef)(!1),f="cached"===l.status?l.result.transcripts.scanned:0,y="cached"===l.status?l.result.results.length:0,b=(0,a.useCallback)(async()=>{c(await t())},[]),v=(0,a.useCallback)(async e=>{if(d)return;m("audit_rerun_clicked",{source:e,since:"all"}),o(!0);let s=Date.now();p({kind:"running",startedAt:s});try{await (0,h.triggerRun)({cli:[],since:"all",noCache:!0}),await b(),m("audit_rerun_succeeded",{source:e,since:"all",duration_ms:Date.now()-s}),p({kind:"idle"})}catch(n){let a=n instanceof h.RerunError?n.kind:"network";m("audit_rerun_failed",{kind:a,source:e,since:"all",cli_filter:"all",duration_ms:Date.now()-s}),p({kind:"failed",reason:a,failedAt:Date.now()})}finally{o(!1)}},[m,b,d]),k=(0,a.useCallback)(()=>{p({kind:"idle"})},[]);if((0,a.useEffect)(()=>(document.body.classList.add("audit-body"),()=>document.body.classList.remove("audit-body")),[]),(0,a.useEffect)(()=>{let e=d?"running":"empty"===l.status?"empty":0===f?"zero_sessions":"report";g.current!==e&&(g.current=e,m("audit_page_viewed",{state:e,has_cache:"cached"===l.status}))},[l.status,m,d,f]),(0,a.useEffect)(()=>{x.current=!1},[l.status,d,f]),(0,a.useEffect)(()=>{if(d||"cached"!==l.status||0===f)return;let e=0,s=()=>{if(e=0,x.current)return;let s=document.documentElement.scrollHeight-window.innerHeight;s<=0||window.scrollY>=s-24&&(x.current=!0,m("audit_page_scrolled_to_end",{results_count:y,transcripts_scanned:f}))},a=()=>{0===e&&(e=requestAnimationFrame(s))};return s(),window.addEventListener("scroll",a,{passive:!0}),()=>{window.removeEventListener("scroll",a),0!==e&&cancelAnimationFrame(e)}},[l.status,m,y,d,f]),"empty"===l.status&&!d)return(0,s.jsx)(N,{running:!1,rerunStatus:u,onDismissRerun:k,onStarted:()=>o(!0),onCompleted:async()=>{o(!1),await b()}});if("empty"===l.status&&d)return(0,s.jsx)(N,{running:!0,rerunStatus:u,onDismissRerun:k,onStarted:()=>{},onCompleted:async()=>{o(!1),await b()}});let w="cached"===l.status?l.result:null;if(!w)return null;let S="cached"===l.status?l.cachedAt:null;return 0===w.transcripts.scanned?(0,s.jsx)(N,{running:d,mode:"zero-sessions",rerunStatus:u,onDismissRerun:k,onStarted:()=>o(!0),onCompleted:async()=>{o(!1),await b()}}):(0,s.jsx)(j,{result:w,cachedAt:S,projectFromUrl:n,isRunning:d,rerunStatus:u,onRerun:e=>v(e),onDismissRerun:k})}],24358)},86616,29054,e=>{"use strict";function s(e,a={},n=15e3){let t=AbortSignal.timeout(n),r=a.signal?AbortSignal.any([a.signal,t]):t;return fetch(e,{...a,signal:r})}function a(e){if(null===e||"object"!=typeof e)return!1;let s=e.name;return"AbortError"===s||"TimeoutError"===s}e.s(["fetchWithTimeout",0,s,"isAbortError",0,a],29054);class n extends Error{kind;constructor(e,s){super(s),this.kind=e,this.name="RerunError"}}async function t(e){try{let a=await s("/api/audit/run",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({cli:e.cli.length>0?e.cli:void 0,since:"all"===e.since?void 0:e.since,noCache:!!e.noCache||void 0})});if(!a.ok&&409!==a.status){let e=await a.text().catch(()=>"");throw console.error("audit run failed:",a.status,e),new n("post_failed",`audit run failed (${a.status})`)}}catch(e){if(e instanceof n)throw e;throw console.error("audit run request failed:",e),new n(a(e)?"timeout":"network","audit run request failed")}let t=0;for(;;){await new Promise(e=>setTimeout(e,1e3));let e=null;try{let a=await s("/api/audit/status",{cache:"no-store"});a.ok&&(e=await a.json())}catch{}if(null===e){if(++t>=10)throw new n("network","audit status poll lost the server");continue}if(t=0,!e.running){if(e.error)throw new n("post_failed",e.error);return}}}e.s(["RerunError",0,n,"triggerRun",0,t],86616)}]);