failproofai 1.0.4-beta.4 → 1.0.4-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) 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 +2 -2
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  11. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  12. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  16. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  21. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
  22. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  23. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +5 -5
  32. package/.next/standalone/.next/server/app/audit/page.js +3 -3
  33. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  35. package/.next/standalone/.next/server/app/index.html +1 -1
  36. package/.next/standalone/.next/server/app/index.rsc +14 -14
  37. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
  38. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  39. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  40. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  41. package/.next/standalone/.next/server/app/page.js +2 -2
  42. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  44. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  45. package/.next/standalone/.next/server/app/policies/page.js +4 -4
  46. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  48. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  49. package/.next/standalone/.next/server/app/project/[name]/page.js +3 -3
  50. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  53. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  54. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +4 -4
  55. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  56. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  57. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  58. package/.next/standalone/.next/server/app/projects/page.js +3 -3
  59. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  60. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  61. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  62. package/.next/standalone/.next/server/app/settings/page.js +2 -2
  63. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  65. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0--lkk6._.js +1 -1
  66. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  67. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q0qzx1._.js +1 -1
  68. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
  69. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
  70. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +1 -1
  71. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  75. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +4 -4
  76. package/.next/standalone/.next/server/chunks/lib_factory-projects_ts_1eo_bk-._.js +1 -1
  77. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0p-5p8u._.js → [root-of-the-server]__0-4ja6q._.js} +3 -3
  79. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1p2otjt._.js → [root-of-the-server]__00cr3xu._.js} +3 -3
  80. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__013jr2b._.js → [root-of-the-server]__04px77e._.js} +3 -3
  81. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__190won8._.js → [root-of-the-server]__0dxf928._.js} +1 -1
  82. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0s740oi._.js → [root-of-the-server]__0egr5s1._.js} +3 -3
  83. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__01wy8d-._.js → [root-of-the-server]__0i0l1di._.js} +3 -3
  84. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0n_lxhg._.js → [root-of-the-server]__0kokc8z._.js} +1 -1
  85. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1ctpynv._.js → [root-of-the-server]__0n2txhk._.js} +1 -1
  86. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  87. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__02npjtd._.js → [root-of-the-server]__0r3c1_0._.js} +3 -3
  88. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ftmoxc._.js → [root-of-the-server]__17xnxlx._.js} +3 -3
  89. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0da85px._.js → [root-of-the-server]__1ckpmkk._.js} +3 -3
  90. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__00-s7h8._.js → [root-of-the-server]__1fvfw59._.js} +1 -1
  91. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0yl9wb5._.js → [root-of-the-server]__1ui5ur8._.js} +2 -2
  92. package/.next/standalone/.next/server/chunks/ssr/{_0guxnh-._.js → _02b3i98._.js} +2 -2
  93. package/.next/standalone/.next/server/chunks/ssr/_04ng_6x._.js +3 -0
  94. package/.next/standalone/.next/server/chunks/ssr/{_1u8-lu2._.js → _07t3jol._.js} +2 -2
  95. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  96. package/.next/standalone/.next/server/chunks/ssr/{_0h_douw._.js → _0s-77v5._.js} +1 -1
  97. package/.next/standalone/.next/server/chunks/ssr/_1ddpc5g._.js +1 -1
  98. package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +4 -14
  99. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
  100. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  101. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  102. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  105. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  106. package/.next/standalone/.next/server/pages/404.html +1 -1
  107. package/.next/standalone/.next/server/pages/500.html +1 -1
  108. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  109. package/.next/standalone/.next/server/server-reference-manifest.json +22 -22
  110. package/.next/standalone/.next/static/chunks/{364cqbpeg1y5i.js → 07c6l1lq5_69b.js} +1 -1
  111. package/.next/standalone/.next/static/chunks/{2bxr2_2h4kaqq.js → 12bsgz8czvcnn.js} +1 -1
  112. package/.next/standalone/.next/static/chunks/{38dqur43qthrl.js → 1gn08w2pgqh09.js} +1 -1
  113. package/.next/standalone/.next/static/chunks/{1i-i0pxg9q_8y.js → 1xgq22tri86an.js} +1 -1
  114. package/.next/standalone/.next/static/chunks/{17tfki9x5rd_7.js → 20xkiqzvfkuvm.js} +1 -1
  115. package/.next/standalone/.next/static/chunks/{42gwu2bsz4ske.js → 2a84ug32tzik2.js} +2 -2
  116. package/.next/standalone/.next/static/chunks/{0po8n4tyotjf2.js → 32m2ryfankf_2.js} +1 -1
  117. package/.next/standalone/.next/static/chunks/3ioub_lpen-sq.js +1 -0
  118. package/.next/standalone/.next/static/chunks/{323cyb3a2s06y.js → 3jxd133cw2vm3.js} +1 -1
  119. package/.next/standalone/.next/static/chunks/{0qx7gav1c2_vu.js → 44ncu88eq6z71.js} +1 -1
  120. package/.next/standalone/app/actions/get-leaks.ts +0 -5
  121. package/.next/standalone/app/actions/update-scheduled-audit.ts +4 -56
  122. package/.next/standalone/app/api/auth/status/route.ts +3 -2
  123. package/.next/standalone/app/settings/page.tsx +1 -1
  124. package/.next/standalone/app/settings/settings-client.tsx +13 -39
  125. package/.next/standalone/lib/auth/api-server-client.ts +6 -6
  126. package/.next/standalone/lib/codex-sessions.ts +20 -4
  127. package/.next/standalone/lib/copilot-sessions.ts +32 -1
  128. package/.next/standalone/lib/cursor-sessions.ts +62 -5
  129. package/.next/standalone/package.json +9 -9
  130. package/.next/standalone/server.js +1 -1
  131. package/dist/cli.mjs +952 -1063
  132. package/dist/worker.mjs +173 -380
  133. package/lib/auth/api-server-client.ts +6 -6
  134. package/lib/codex-sessions.ts +20 -4
  135. package/lib/copilot-sessions.ts +32 -1
  136. package/lib/cursor-sessions.ts +62 -5
  137. package/package.json +9 -9
  138. package/pi-extension/index.ts +10 -18
  139. package/src/audit/cli.ts +72 -81
  140. package/src/audit/harm-report.ts +12 -6
  141. package/src/audit/index.ts +2 -0
  142. package/src/audit/leak-fingerprint.ts +2 -4
  143. package/src/audit/leak-record.ts +13 -9
  144. package/src/audit/leak-scan.ts +1 -1
  145. package/src/audit/leak-store.ts +1 -46
  146. package/src/audit/machine-store.ts +8 -9
  147. package/src/audit/macos-notifier.ts +2 -2
  148. package/src/audit/redact-example.ts +2 -0
  149. package/src/audit/report-harm.ts +31 -46
  150. package/src/audit/schedule-cli.ts +17 -33
  151. package/src/audit/types.ts +5 -0
  152. package/src/hooks/configure-wizard.ts +28 -0
  153. package/src/hooks/fp-config.ts +29 -54
  154. package/src/hooks/handler.ts +1 -79
  155. package/src/hooks/integrations.ts +4 -15
  156. package/src/hooks/notice.ts +12 -179
  157. package/.next/standalone/.next/server/chunks/ssr/_0oq1dh6._.js +0 -3
  158. package/.next/standalone/.next/static/chunks/04o5zfl7p5lhx.js +0 -1
  159. package/src/audit/leak-notice.ts +0 -259
  160. /package/.next/standalone/.next/static/{P6icI2V2PvVuePJrqjHCr → aeM365dNTtUZOZvNkX8ba}/_buildManifest.js +0 -0
  161. /package/.next/standalone/.next/static/{P6icI2V2PvVuePJrqjHCr → aeM365dNTtUZOZvNkX8ba}/_clientMiddlewareManifest.js +0 -0
  162. /package/.next/standalone/.next/static/{P6icI2V2PvVuePJrqjHCr → aeM365dNTtUZOZvNkX8ba}/_ssgManifest.js +0 -0
@@ -18,7 +18,6 @@
18
18
  */
19
19
 
20
20
  import { readConfig, updateConfig } from "@/src/hooks/fp-config";
21
- import { readAuth, whoAmI } from "@/lib/auth/auth-store";
22
21
 
23
22
  /**
24
23
  * The outcome of trying to turn scheduling on.
@@ -34,70 +33,19 @@ import { readAuth, whoAmI } from "@/lib/auth/auth-store";
34
33
  * A returned discriminant survives the boundary, so the caller can open the
35
34
  * sign-in dialog for the one failure that has an obvious next step.
36
35
  */
37
- export type SetAutoAuditResult =
38
- | { ok: true; auto: boolean }
39
- | { ok: false; reason: "signed-out" }
40
- /**
41
- * The session is intact locally and the api-server could not be reached.
42
- *
43
- * Separated from `signed-out` because `whoAmI()` collapses them: it returns
44
- * null for a 401 AND for every transport failure, so an offline machine, a
45
- * proxy that blocks the host, and a genuinely expired token were one answer.
46
- * The user was told "that sign-in expired" and handed a code prompt that
47
- * cannot succeed either — the failure it names is not the failure they have,
48
- * and following its advice costs them a real, working session.
49
- */
50
- | { ok: false; reason: "unreachable" };
36
+ export type SetAutoAuditResult = { ok: true; auto: boolean };
51
37
 
52
38
  /**
53
39
  * Turn the scheduled scan on or off.
54
40
  *
55
- * Turning it ON is refused without a session. Scheduling and mailing are ONE
56
- * decision — the reason to put a scan on a timer is to be told what it found —
57
- * so a machine with the timer set and nobody to tell is a switch that reads as
58
- * on and produces nothing, discoverable only by noticing that no digest ever
59
- * arrives. The caller signs the user in first and retries.
60
- *
61
- * `whoAmI()` asks the SERVER, so this refuses in a case the page cannot see: a
62
- * session file that exists locally but whose refresh token the api-server has
63
- * rejected. The local file is what the page reads to show "reports go to …", so
64
- * the two disagree exactly when a session has expired or was minted against a
65
- * different server — and that disagreement is the common case, not an edge one.
66
- *
67
- * Turning it OFF never checks. An expired session must not be able to trap
68
- * somebody into keeping a feature they are trying to disable.
69
- *
70
- * Note this gates SETTING UP the timer, not the machine's ongoing work: a
71
- * session that later expires leaves the timer running and the local scan
72
- * working, and only the digest stops. See `report-harm.ts`.
41
+ * Scheduling is local and independent of authentication. A signed-in identity
42
+ * adds email delivery; without one the scan and desktop notification continue.
73
43
  *
74
44
  * Returns the value actually stored (re-read), so an optimistic UI can confirm
75
45
  * against the source of truth rather than assume its own guess landed.
76
46
  */
77
47
  export async function setAutoAuditAction(enabled: boolean): Promise<SetAutoAuditResult> {
78
- if (enabled) {
79
- const who = await whoAmI();
80
- if (!who) {
81
- // `whoAmI()` deletes the session on a 401 and leaves it alone on a
82
- // transport failure, so what is left ON DISK is the one thing that tells
83
- // the two apart — no extra request, and nothing new that can fail.
84
- const local = readAuth();
85
- const stillWithinWindow = local && local.refresh_expires_at * 1000 > Date.now();
86
- return { ok: false, reason: stillWithinWindow ? "unreachable" : "signed-out" };
87
- }
88
- }
89
- // Enabling stamps consent in the same write, because the `whoAmI()` above is
90
- // exactly what makes this a consent record rather than a guess: a person was
91
- // present, signed in, and looking at the panel that enumerates what gets
92
- // sent. `reportHarm` gates sending on the stamp, not on `auto`, so a machine
93
- // that inherited `auto` from a release where it meant "scan locally on a
94
- // timer" mails nothing until somebody passes through here or the CLI.
95
- //
96
- // Disabling leaves the stamp alone. It is a record of something that did
97
- // happen, and it grants nothing on its own — sending needs `auto` too.
98
- const next = updateConfig({
99
- audit: enabled ? { auto: true, reportsConsentedAt: Date.now() } : { auto: false },
100
- });
48
+ const next = updateConfig({ audit: { auto: enabled } });
101
49
  return { ok: true, auto: next.audit.auto };
102
50
  }
103
51
 
@@ -7,8 +7,9 @@
7
7
  * This keeps the dashboard UI and the CLI consistent regardless of whether
8
8
  * the api-server is reachable.
9
9
  *
10
- * Reminders are gone: the machine now audits itself on a timer and mails a
11
- * digest when it finds harm, so there is nothing to nudge anyone about. The
10
+ * Reminders are gone: the machine now audits itself on a timer and can mail an
11
+ * alert when it finds a credential leak, so there is nothing to nudge anyone
12
+ * about. The
12
13
  * scheduled-scan state lives in `getScheduledAuditAction`, which reads it from
13
14
  * the config and the daemon rather than from here.
14
15
  */
@@ -30,7 +30,7 @@ export const dynamic = "force-dynamic";
30
30
  export default async function SettingsPage() {
31
31
  // Same gate the audit, policies and projects pages carry. It was dropped in
32
32
  // the rewrite, and this is the page that least deserves to lose it: it shows
33
- // the address digests go to and can sign the machine out, on a dashboard an
33
+ // the address credential alerts go to and can sign the machine out, on a dashboard an
34
34
  // operator may deliberately be exposing beyond localhost.
35
35
  const disabled = (process.env.FAILPROOFAI_DISABLE_PAGES ?? "")
36
36
  .split(",").map((s) => s.trim()).filter(Boolean);
@@ -187,12 +187,9 @@ function Toggle({
187
187
  * What the scan does, as three labelled lines. This is the old footer paragraph
188
188
  * restructured — same claims, scannable instead of a wall.
189
189
  *
190
- * "sends" ENUMERATES rather than saying "only counts and redacted examples".
191
- * That was very nearly true, and very nearly true is the worse kind: the report
192
- * carries the machine's name too — its hostname, which routinely carries its
193
- * owner's. A list a person can check beats a stronger claim they cannot, and
194
- * this panel is the one place they would come to check. The digest email states
195
- * the same three, in the same order.
190
+ * "sends" names the exact remote payload. The scheduled scan itself stays
191
+ * local, and no report is attempted unless a credential leak exists and the
192
+ * machine has a verified email identity.
196
193
  */
197
194
  const HOW_IT_WORKS: ReadonlyArray<{ label: string; body: string }> = [
198
195
  {
@@ -202,7 +199,7 @@ const HOW_IT_WORKS: ReadonlyArray<{ label: string; body: string }> = [
202
199
  { label: "runs", body: "entirely on this machine. the transcripts never leave it." },
203
200
  {
204
201
  label: "sends",
205
- body: "counts, redacted examples, and this machine's name — and only when a scan finds something harmful.",
202
+ body: "only the newest masked credential exposure, its CLI, time, location, and this machine's name — only when email is configured.",
206
203
  },
207
204
  ];
208
205
 
@@ -312,26 +309,6 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
312
309
  setBusy(true);
313
310
  try {
314
311
  const res = await setAutoAuditAction(true);
315
- if (!res.ok) {
316
- setAuto(false);
317
- if (res.reason === "unreachable") {
318
- // The session is fine and the network is not. Offering a code prompt
319
- // here would name a failure the user does not have and hand them a
320
- // flow that cannot succeed either — and abandoning it mid-way is how
321
- // a working session gets replaced with none at all.
322
- toast("could not reach the server. check your connection and try again.");
323
- return;
324
- }
325
- // The server rejected the session this page had been showing an address
326
- // for — expired, or minted against a different api-server. The local
327
- // file is the only thing that said "signed in", and `whoAmI` has since
328
- // cleared it, so re-read before opening the dialog: otherwise the page
329
- // asks for an email while still displaying one.
330
- await reload();
331
- setAuthOpen(true);
332
- toast("that sign-in expired. one more code and it's on.");
333
- return;
334
- }
335
312
  setAuto(res.auto);
336
313
  toast("scheduled audits on.");
337
314
  await reload();
@@ -360,13 +337,8 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
360
337
  }
361
338
  return;
362
339
  }
363
- // Turning it on needs somewhere to send the digest.
364
- if (!signedIn) {
365
- setAuthOpen(true);
366
- return;
367
- }
368
340
  await enable();
369
- }, [auto, enable, reload, signedIn]);
341
+ }, [auto, enable, reload]);
370
342
 
371
343
  const commitInterval = useCallback(
372
344
  async (raw: number) => {
@@ -403,7 +375,7 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
403
375
  setBusy(true);
404
376
  try {
405
377
  await fetch("/api/auth/logout", { method: "POST" });
406
- toast("signed out. scans continue; digests pause.");
378
+ toast("signed out. scans and desktop notifications continue; email alerts pause.");
407
379
  await reload();
408
380
  } catch {
409
381
  toast("could not sign out.");
@@ -534,7 +506,9 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
534
506
  </span>
535
507
  <span className="set-dim">
536
508
  {auto
537
- ? "you'll get an email only when a scan finds something."
509
+ ? signedIn
510
+ ? "system notification on leaks; the latest masked leak is also emailed."
511
+ : "system notification on leaks. add an email below for masked alerts."
538
512
  : "off. nothing runs and nothing is sent."}
539
513
  </span>
540
514
  </div>
@@ -593,7 +567,7 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
593
567
  // from it at all.
594
568
  <>
595
569
  <span className="set-warn">
596
- signed out — scans continue, digests are paused.
570
+ signed out — scans and desktop notifications continue; email alerts are paused.
597
571
  </span>
598
572
  <button
599
573
  type="button"
@@ -601,13 +575,13 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
601
575
  disabled={busy}
602
576
  onClick={() => setAuthOpen(true)}
603
577
  >
604
- sign in to resume
578
+ add email alerts
605
579
  </button>
606
580
  </>
607
581
  ) : (
608
582
  <span className="set-dim">
609
- turning this on asks for an email, so there is somewhere to
610
- send the report.
583
+ scans and desktop notifications work without an account.
584
+ add email later if you want leak alerts in your inbox.
611
585
  </span>
612
586
  )}
613
587
  </div>
@@ -257,7 +257,7 @@ export interface AuditReportBody {
257
257
  * Credentials this machine found in its own transcripts.
258
258
  *
259
259
  * Optional on the wire, so a CLI newer than the api-server sends a field the
260
- * server ignores rather than failing the whole digest — the harmful counts
260
+ * server ignores rather than failing the whole report — the harmful counts
261
261
  * are the older half of the contract and must keep arriving either way.
262
262
  *
263
263
  * Every field here is a mask, a label, a length or a location. There is no
@@ -287,15 +287,15 @@ export interface AuditReportResult {
287
287
  report_id: string;
288
288
  /** Whether this report produced an email. */
289
289
  emailed: boolean;
290
- /** `below_threshold`, `cooldown`, `send_failed`, or null when mail went out. */
290
+ /** `no_leak`, `cooldown`, `send_failed`, or null when mail went out. */
291
291
  reason: string | null;
292
292
  /**
293
293
  * Where the next window starts, per the SERVER.
294
294
  *
295
295
  * Persisted verbatim rather than computed locally. The server anchors it on
296
- * the last DELIVERED digest, so a report held by the cooldown — or one whose
296
+ * the last DELIVERED alert, so a report held by the cooldown — or one whose
297
297
  * send failed — correctly leaves the watermark where it was, and its findings
298
- * turn up in the next digest instead of falling into a gap. A machine that
298
+ * turn up in the next alert instead of falling into a gap. A machine that
299
299
  * lost `machine.json` also resyncs here rather than re-reporting from the
300
300
  * beginning of time.
301
301
  */
@@ -303,11 +303,11 @@ export interface AuditReportResult {
303
303
  }
304
304
 
305
305
  /**
306
- * Submit one scheduled scan's harmful findings.
306
+ * Submit one scheduled scan's harmful findings and newest masked credential.
307
307
  *
308
308
  * Called only by the audit child, and only on `--scheduled`. The destination
309
309
  * address is never sent: the api-server takes it from the access-token claims,
310
- * so a report cannot name where its digest goes.
310
+ * so a report cannot name where its email alert goes.
311
311
  */
312
312
  export async function submitAuditReport(
313
313
  accessToken: string,
@@ -158,6 +158,21 @@ function joinTexts(blocks: CodexContentBlock[] | undefined, wantedType: "input_t
158
158
  .join("\n");
159
159
  }
160
160
 
161
+ /** Text carried by tool outputs. Newer Codex chronicles use an array of
162
+ * `input_text` blocks for `custom_tool_call_output`, while older releases use
163
+ * one string for `function_call_output`. */
164
+ function toolOutputText(output: unknown): string {
165
+ if (typeof output === "string") return output;
166
+ if (Array.isArray(output)) {
167
+ return output
168
+ .filter((block): block is CodexContentBlock => !!block && typeof block === "object")
169
+ .map((block) => typeof block.text === "string" ? block.text : "")
170
+ .filter(Boolean)
171
+ .join("\n");
172
+ }
173
+ return output === undefined ? "" : JSON.stringify(output);
174
+ }
175
+
161
176
  /**
162
177
  * Parse a Codex JSONL transcript into `LogEntry[]` plus the raw lines.
163
178
  * Yields to the event loop every 200 lines so big transcripts don't block.
@@ -250,10 +265,11 @@ export async function parseCodexLog(
250
265
  continue;
251
266
  }
252
267
 
253
- if (subType === "function_call") {
268
+ if (subType === "function_call" || subType === "custom_tool_call") {
254
269
  const callId = payload.call_id as string | undefined;
255
270
  const name = (payload.name as string | undefined) ?? "function_call";
256
- const input = safeJsonParse(payload.arguments as string | undefined);
271
+ const encodedInput = (payload.arguments ?? payload.input) as string | undefined;
272
+ const input = safeJsonParse(encodedInput);
257
273
  const toolUse: ToolUseBlock = {
258
274
  type: "tool_use",
259
275
  id: callId ?? `${timestamp}-${name}`,
@@ -273,7 +289,7 @@ export async function parseCodexLog(
273
289
  continue;
274
290
  }
275
291
 
276
- if (subType === "function_call_output") {
292
+ if (subType === "function_call_output" || subType === "custom_tool_call_output") {
277
293
  const callId = payload.call_id as string | undefined;
278
294
  const block = callId ? toolUseById.get(callId) : undefined;
279
295
  if (block) {
@@ -282,7 +298,7 @@ export async function parseCodexLog(
282
298
  block.result = {
283
299
  timestamp,
284
300
  timestampFormatted: formatTimestamp(date),
285
- content: typeof payload.output === "string" ? (payload.output as string) : JSON.stringify(payload.output),
301
+ content: toolOutputText(payload.output),
286
302
  durationMs: duration,
287
303
  durationFormatted: formatDuration(duration),
288
304
  };
@@ -151,6 +151,11 @@ interface CopilotToolResult {
151
151
  detailedContent?: string;
152
152
  }
153
153
 
154
+ interface CopilotHookToolResult {
155
+ textResultForLlm?: string;
156
+ sessionLog?: string;
157
+ }
158
+
154
159
  interface CopilotToolTelemetry {
155
160
  metrics?: { commandTimeMs?: number; durationMs?: number };
156
161
  properties?: Record<string, unknown>;
@@ -171,6 +176,8 @@ export async function parseCopilotLog(
171
176
  // toolCallId → tool_use block, so we can attach tool.execution_complete back.
172
177
  const toolUseById = new Map<string, ToolUseBlock>();
173
178
  const toolUseStartMs = new Map<string, number>();
179
+ let mostRecentToolUse: ToolUseBlock | undefined;
180
+ let mostRecentToolUseStartMs = 0;
174
181
  let cwd: string | undefined;
175
182
  let seenSessionStart = false;
176
183
 
@@ -273,9 +280,32 @@ export async function parseCopilotLog(
273
280
  toolUseById.set(callId, toolUse);
274
281
  toolUseStartMs.set(callId, date.getTime());
275
282
  }
283
+ mostRecentToolUse = toolUse;
284
+ mostRecentToolUseStartMs = date.getTime();
276
285
  continue;
277
286
  }
278
287
 
288
+ // Copilot CLI 1.0.39 can omit the command's real stdout from
289
+ // `tool.execution_complete` and expose it only to the PostToolUse hook as
290
+ // `input.toolResult.textResultForLlm`. That is the value the model saw and
291
+ // the value failproofai must audit. Keep it on the active tool block; the
292
+ // later completion record may still contribute timing, but must not replace
293
+ // this output with its shorter status-only summary.
294
+ if (recType === "hook.start" && data.hookType === "postToolUse" && mostRecentToolUse) {
295
+ const input = (data.input as Record<string, unknown> | undefined) ?? {};
296
+ const hookResult = (input.toolResult as CopilotHookToolResult | undefined) ?? {};
297
+ const hookContent = hookResult.textResultForLlm ?? hookResult.sessionLog;
298
+ if (typeof hookContent === "string" && hookContent.length > 0) {
299
+ mostRecentToolUse.result = {
300
+ timestamp,
301
+ timestampFormatted: formatTimestamp(date),
302
+ content: hookContent,
303
+ durationMs: Math.max(0, date.getTime() - mostRecentToolUseStartMs),
304
+ durationFormatted: formatDuration(Math.max(0, date.getTime() - mostRecentToolUseStartMs)),
305
+ };
306
+ }
307
+ }
308
+
279
309
  if (recType === "tool.execution_complete") {
280
310
  const callId = data.toolCallId as string | undefined;
281
311
  const block = callId ? toolUseById.get(callId) : undefined;
@@ -289,7 +319,8 @@ export async function parseCopilotLog(
289
319
  typeof reportedMs === "number" && reportedMs >= 0
290
320
  ? reportedMs
291
321
  : Math.max(0, date.getTime() - startMs);
292
- const content = result.detailedContent ?? result.content ?? "";
322
+ const completionContent = result.detailedContent ?? result.content ?? "";
323
+ const content = block.result?.content || completionContent;
293
324
  block.result = {
294
325
  timestamp,
295
326
  timestampFormatted: formatTimestamp(date),
@@ -127,7 +127,15 @@ interface CursorRecord {
127
127
  /** Cursor 2026-04+ transcript shape: `{role, message: {content: [...]}}`. */
128
128
  role?: "user" | "assistant" | "system" | string;
129
129
  message?: {
130
- content?: Array<{ type?: string; text?: string }>;
130
+ content?: Array<{
131
+ type?: string;
132
+ text?: string;
133
+ id?: string;
134
+ name?: string;
135
+ input?: Record<string, unknown>;
136
+ tool_use_id?: string;
137
+ content?: unknown;
138
+ }>;
131
139
  };
132
140
  }
133
141
 
@@ -183,11 +191,36 @@ export async function parseCursorLog(
183
191
  if (!raw.type && raw.role && raw.message?.content) {
184
192
  const synthDate = new Date(SYNTH_T0 + i);
185
193
  const synthTs = synthDate.toISOString();
186
- const textParts = raw.message.content
194
+ const messageContent = raw.message.content;
195
+ const textParts = messageContent
187
196
  .filter((c) => c?.type === "text" && typeof c.text === "string")
188
197
  .map((c) => c.text!)
189
198
  .join("");
190
199
  if (raw.role === "user") {
200
+ // Some Cursor builds emit Claude-style tool_result blocks in the next
201
+ // user record. Pair them before treating any remaining text as a user
202
+ // turn so command output is available to the credential scanner.
203
+ let attachedResult = false;
204
+ for (const block of messageContent) {
205
+ if (block?.type !== "tool_result") continue;
206
+ const callId = typeof block.tool_use_id === "string" ? block.tool_use_id : undefined;
207
+ const toolUse = callId ? toolUseById.get(callId) : undefined;
208
+ if (!toolUse) continue;
209
+ const durationMs = Math.max(
210
+ 0,
211
+ synthDate.getTime() - (toolUseStartMs.get(callId!) ?? synthDate.getTime()),
212
+ );
213
+ toolUse.result = {
214
+ timestamp: synthTs,
215
+ timestampFormatted: formatTimestamp(synthDate),
216
+ content: typeof block.content === "string"
217
+ ? block.content
218
+ : block.content == null ? "" : JSON.stringify(block.content),
219
+ durationMs,
220
+ durationFormatted: formatDuration(durationMs),
221
+ };
222
+ attachedResult = true;
223
+ }
191
224
  // Strip the synthesized `<timestamp>...</timestamp>\n<user_query>...\n</user_query>`
192
225
  // wrapper Cursor adds for context — keep just the user_query body.
193
226
  const queryMatch = /<user_query>\s*([\s\S]*?)\s*<\/user_query>/.exec(textParts);
@@ -198,13 +231,37 @@ export async function parseCursorLog(
198
231
  ...baseEntry(rawCopy, synthTs, synthDate, source),
199
232
  message: { role: "user", content: text },
200
233
  } satisfies UserEntry);
234
+ } else if (!attachedResult) {
235
+ entries.push({
236
+ type: "system",
237
+ ...baseEntry(rawCopy, synthTs, synthDate, source),
238
+ raw: rawCopy,
239
+ } satisfies GenericEntry);
201
240
  }
202
241
  continue;
203
242
  }
204
243
  if (raw.role === "assistant") {
205
- const blocks: ContentBlock[] = textParts
206
- ? [{ type: "text", text: textParts }]
207
- : [];
244
+ const blocks: ContentBlock[] = [];
245
+ if (textParts) blocks.push({ type: "text", text: textParts });
246
+ for (let j = 0; j < messageContent.length; j++) {
247
+ const contentBlock = messageContent[j];
248
+ if (contentBlock?.type !== "tool_use") continue;
249
+ const name = typeof contentBlock.name === "string" ? contentBlock.name : "tool";
250
+ const id = typeof contentBlock.id === "string" && contentBlock.id
251
+ ? contentBlock.id
252
+ : `cursor-${i}-${name}-${j}`;
253
+ const toolUse: ToolUseBlock = {
254
+ type: "tool_use",
255
+ id,
256
+ name,
257
+ input: contentBlock.input && typeof contentBlock.input === "object"
258
+ ? contentBlock.input
259
+ : {},
260
+ };
261
+ blocks.push(toolUse);
262
+ toolUseById.set(id, toolUse);
263
+ toolUseStartMs.set(id, synthDate.getTime());
264
+ }
208
265
  if (blocks.length === 0) {
209
266
  entries.push({
210
267
  type: "system",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "1.0.4-beta.4",
3
+ "version": "1.0.4-beta.6",
4
4
  "description": "Observability and enforcement for AI agent harnesses. 39 built-in policies hooked into 12 of them — Claude Code, Codex, Cursor, Hermes, OpenClaw and more — blocking the tool call before it runs. Local dashboard included, no account needed.",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs",
@@ -121,15 +121,15 @@
121
121
  "browserslist": "4.28.8"
122
122
  },
123
123
  "optionalDependencies": {
124
- "@failproofai/failproofaid-linux-x64": "1.0.4-beta.4",
125
- "@failproofai/failproofaid-linux-arm64": "1.0.4-beta.4",
126
- "@failproofai/failproofaid-darwin-x64": "1.0.4-beta.4",
127
- "@failproofai/failproofaid-darwin-arm64": "1.0.4-beta.4"
124
+ "@failproofai/failproofaid-linux-x64": "1.0.4-beta.6",
125
+ "@failproofai/failproofaid-linux-arm64": "1.0.4-beta.6",
126
+ "@failproofai/failproofaid-darwin-x64": "1.0.4-beta.6",
127
+ "@failproofai/failproofaid-darwin-arm64": "1.0.4-beta.6"
128
128
  },
129
129
  "failproofaidBinaries": {
130
- "linux-x64": "4487790ed9b881da2914b5985a23b5adca1f20e226d71c635d8170064e6b2340",
131
- "linux-arm64": "c903c5e201198213ccd8295fdab741c7d08d22c8dd5c276e52aa766595bedcc2",
132
- "darwin-x64": "f0933584abbf0f9ff95cd153f8c6c704e467af1d6ffc8f42f9c426bd44b1142a",
133
- "darwin-arm64": "387a3acbf13f667a557c8ac632fe67dee350cda078e3266e71edfcfacb9ecae6"
130
+ "linux-x64": "24118b8bd058cc5d8711053c4c77cdca9361c12dbf0f421a757d33f07596d428",
131
+ "linux-arm64": "b4278c34a288de2c747bcebcc623d8fd5bda3a1f1e55800926eb1a4b89f0a5e9",
132
+ "darwin-x64": "ecb66699f45f605b0d54ac9e4bac360ac11e4e136442dcccdd083514d77c7d42",
133
+ "darwin-arm64": "b3dbad58bda960ef9481f90bc5085ba8624b2472519cc0bf212a0e8451dd2db4"
134
134
  }
135
135
  }
@@ -9,7 +9,7 @@ const currentPort = parseInt(process.env.PORT, 10) || 3000
9
9
  const hostname = process.env.HOSTNAME || '0.0.0.0'
10
10
 
11
11
  let keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10)
12
- const nextConfig = {"env":{"NEXT_PUBLIC_APP_VERSION":"1.0.4-beta.4"},"typescript":{"ignoreBuildErrors":false},"typedRoutes":false,"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.ts","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"instrumentationClientInject":[],"poweredByHeader":true,"compress":true,"images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":14400,"formats":["image/webp"],"maximumRedirects":3,"maximumResponseBody":50000000,"dangerouslyAllowLocalIP":false,"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"attachment","localPatterns":[{"pathname":"**","search":""}],"remotePatterns":[],"qualities":[75],"unoptimized":true,"customCacheHandler":false},"devIndicators":{"position":"bottom-left"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"excludeDefaultMomentLocales":true,"reactProductionProfiling":false,"reactStrictMode":null,"reactMaxHeadersLength":6000,"httpAgentOptions":{"keepAlive":true},"logging":{"serverFunctions":true,"browserToTerminal":"warn"},"compiler":{},"expireTime":31536000,"staticPageGenerationTimeout":60,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"outputFileTracingRoot":"/home/runner/work/failproofai/failproofai","enablePrerenderSourceMaps":true,"cacheComponents":false,"cacheLife":{"default":{"stale":300,"revalidate":900,"expire":4294967294},"seconds":{"stale":30,"revalidate":1,"expire":60},"minutes":{"stale":300,"revalidate":60,"expire":3600},"hours":{"stale":300,"revalidate":3600,"expire":86400},"days":{"stale":300,"revalidate":86400,"expire":604800},"weeks":{"stale":300,"revalidate":604800,"expire":2592000},"max":{"stale":300,"revalidate":2592000,"expire":31536000}},"cacheHandlers":{},"experimental":{"appNewScrollHandler":true,"coldCacheBadge":false,"devValidationWorker":true,"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"cachedNavigations":false,"dynamicOnHover":false,"useOffline":false,"varyParams":true,"optimisticRouting":true,"instrumentationClientRouterTransitionEvents":false,"prefetchInlining":{"maxSize":2048,"maxBundleSize":10240},"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptOperationCache":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"devMemoryThresholdRestart":true,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"requestInsights":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"strictRouteTypes":false,"useTypeScriptCli":true,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":true,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":true,"serverComponentsHmrCache":true,"serverComponentsHmrCancellation":false,"staticGenerationMaxConcurrency":8,"staticGenerationMinPagesPerWorker":25,"transitionIndicator":false,"gestureTransition":false,"inlineCss":false,"useCache":false,"globalNotFound":false,"browserDebugInfoInTerminal":"warn","lockDistDir":true,"proxyClientMaxBodySize":10485760,"hideLogsAfterAbort":false,"mcpServer":true,"turbopackFileSystemCacheForDev":true,"turbopackFileSystemCacheForBuild":true,"turbopackInferModuleSideEffects":true,"turbopackPluginRuntimeStrategy":"childProcesses","turbopackMemoryEvictionMode":"auto","optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","effect","@effect/schema","@effect/platform","@effect/platform-node","@effect/platform-browser","@effect/platform-bun","@effect/sql","@effect/sql-mssql","@effect/sql-mysql2","@effect/sql-pg","@effect/sql-sqlite-node","@effect/sql-sqlite-bun","@effect/sql-sqlite-wasm","@effect/sql-sqlite-react-native","@effect/rpc","@effect/rpc-http","@effect/typeclass","@effect/experimental","@effect/opentelemetry","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"useCacheTimeout":54,"instantInsights":{"validationLevel":"warning"},"trustHostHeader":false,"isExperimentalCompile":false},"htmlLimitedBots":"[\\w-]+-Google|Google-[\\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight","bundlePagesRouterDependencies":false,"configFileName":"next.config.ts","outputFileTracingExcludes":{"*":["node_modules/@img/**","node_modules/sharp/**"]},"turbopack":{"root":"/home/runner/work/failproofai/failproofai"},"repoRoot":"/home/runner/work/failproofai/failproofai","distDirRoot":".next"}
12
+ const nextConfig = {"env":{"NEXT_PUBLIC_APP_VERSION":"1.0.4-beta.6"},"typescript":{"ignoreBuildErrors":false},"typedRoutes":false,"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.ts","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"instrumentationClientInject":[],"poweredByHeader":true,"compress":true,"images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":14400,"formats":["image/webp"],"maximumRedirects":3,"maximumResponseBody":50000000,"dangerouslyAllowLocalIP":false,"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"attachment","localPatterns":[{"pathname":"**","search":""}],"remotePatterns":[],"qualities":[75],"unoptimized":true,"customCacheHandler":false},"devIndicators":{"position":"bottom-left"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"excludeDefaultMomentLocales":true,"reactProductionProfiling":false,"reactStrictMode":null,"reactMaxHeadersLength":6000,"httpAgentOptions":{"keepAlive":true},"logging":{"serverFunctions":true,"browserToTerminal":"warn"},"compiler":{},"expireTime":31536000,"staticPageGenerationTimeout":60,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"outputFileTracingRoot":"/home/runner/work/failproofai/failproofai","enablePrerenderSourceMaps":true,"cacheComponents":false,"cacheLife":{"default":{"stale":300,"revalidate":900,"expire":4294967294},"seconds":{"stale":30,"revalidate":1,"expire":60},"minutes":{"stale":300,"revalidate":60,"expire":3600},"hours":{"stale":300,"revalidate":3600,"expire":86400},"days":{"stale":300,"revalidate":86400,"expire":604800},"weeks":{"stale":300,"revalidate":604800,"expire":2592000},"max":{"stale":300,"revalidate":2592000,"expire":31536000}},"cacheHandlers":{},"experimental":{"appNewScrollHandler":true,"coldCacheBadge":false,"devValidationWorker":true,"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"cachedNavigations":false,"dynamicOnHover":false,"useOffline":false,"varyParams":true,"optimisticRouting":true,"instrumentationClientRouterTransitionEvents":false,"prefetchInlining":{"maxSize":2048,"maxBundleSize":10240},"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptOperationCache":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"devMemoryThresholdRestart":true,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"requestInsights":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"strictRouteTypes":false,"useTypeScriptCli":true,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":true,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":true,"serverComponentsHmrCache":true,"serverComponentsHmrCancellation":false,"staticGenerationMaxConcurrency":8,"staticGenerationMinPagesPerWorker":25,"transitionIndicator":false,"gestureTransition":false,"inlineCss":false,"useCache":false,"globalNotFound":false,"browserDebugInfoInTerminal":"warn","lockDistDir":true,"proxyClientMaxBodySize":10485760,"hideLogsAfterAbort":false,"mcpServer":true,"turbopackFileSystemCacheForDev":true,"turbopackFileSystemCacheForBuild":true,"turbopackInferModuleSideEffects":true,"turbopackPluginRuntimeStrategy":"childProcesses","turbopackMemoryEvictionMode":"auto","optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","effect","@effect/schema","@effect/platform","@effect/platform-node","@effect/platform-browser","@effect/platform-bun","@effect/sql","@effect/sql-mssql","@effect/sql-mysql2","@effect/sql-pg","@effect/sql-sqlite-node","@effect/sql-sqlite-bun","@effect/sql-sqlite-wasm","@effect/sql-sqlite-react-native","@effect/rpc","@effect/rpc-http","@effect/typeclass","@effect/experimental","@effect/opentelemetry","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"useCacheTimeout":54,"instantInsights":{"validationLevel":"warning"},"trustHostHeader":false,"isExperimentalCompile":false},"htmlLimitedBots":"[\\w-]+-Google|Google-[\\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight","bundlePagesRouterDependencies":false,"configFileName":"next.config.ts","outputFileTracingExcludes":{"*":["node_modules/@img/**","node_modules/sharp/**"]},"turbopack":{"root":"/home/runner/work/failproofai/failproofai"},"repoRoot":"/home/runner/work/failproofai/failproofai","distDirRoot":".next"}
13
13
 
14
14
  process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig)
15
15