failproofai 0.0.14-beta.1 → 0.0.14

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 (174) 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 +2 -2
  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/_head.segment.rsc +3 -3
  13. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  14. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  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/_head.segment.rsc +4 -4
  22. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +9 -9
  23. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  24. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  25. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  26. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  35. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  37. package/.next/standalone/.next/server/app/index.html +1 -1
  38. package/.next/standalone/.next/server/app/index.rsc +14 -14
  39. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  40. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  41. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  42. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +9 -9
  43. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  44. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  45. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  47. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  48. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  50. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  51. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  53. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  54. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  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.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0--lkk6._.js +1 -1
  61. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +1 -1
  62. package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +1 -1
  63. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06cuf1y._.js +1 -1
  64. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08w4wmd._.js +1 -1
  65. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g6bbuw._.js +1 -1
  66. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jnjf2t._.js +1 -1
  67. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
  68. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q0qzx1._.js +1 -1
  69. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q2nbsl._.js +1 -1
  70. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
  71. package/.next/standalone/.next/server/chunks/[root-of-the-server]__16le-kd._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ffkmds._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
  75. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
  76. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1mrihkj._.js +1 -1
  77. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qb590j._.js +1 -1
  79. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qxztj-._.js +1 -1
  80. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1shcqgr._.js +1 -1
  81. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1xuedea._.js +1 -1
  82. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +4 -4
  83. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__015_i4t._.js +2 -2
  87. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__030k0c6._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__03lv-pe._.js +2 -2
  89. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__05r_17v._.js +1 -1
  90. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e2l3c1._.js +1 -1
  91. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hci7t3._.js +1 -1
  92. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hdsupo._.js +2 -2
  93. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p84xee._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qeuy9c._.js +2 -2
  95. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s89hoe._.js +2 -2
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ynf7tx._.js +2 -2
  97. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0brjczq._.js → [root-of-the-server]__0zf8xlq._.js} +2 -2
  98. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__170799-._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17ai7sy._.js +2 -2
  100. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +1 -1
  101. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0wp9w6i._.js → [root-of-the-server]__1s2b6h0._.js} +2 -2
  102. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tkjqka._.js +2 -2
  103. package/.next/standalone/.next/server/chunks/ssr/_05whahf._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/_1kje4fm._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
  106. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  108. package/.next/standalone/.next/server/chunks/ssr/lib_0xkhw_v._.js +1 -1
  109. package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_12rjfea.js → node_modules_html-to-image_es_index_0gkzjaa.js} +1 -1
  110. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +1 -1
  112. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +1 -1
  113. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  114. package/.next/standalone/.next/server/pages/404.html +1 -1
  115. package/.next/standalone/.next/server/pages/500.html +1 -1
  116. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  117. package/.next/standalone/.next/server/server-reference-manifest.json +10 -10
  118. package/.next/standalone/.next/static/chunks/06z_ex1ts3n4s.js +1 -0
  119. package/.next/standalone/.next/static/chunks/0o0ut0ugd0_-s.js +1 -0
  120. package/.next/standalone/.next/static/chunks/0xl97x-874k-u.js +1 -0
  121. package/.next/standalone/.next/static/chunks/19d7hqx8udopz.js +1 -0
  122. package/.next/standalone/.next/static/chunks/{0emg30_6svycu.js → 29o4j32z643_a.js} +1 -1
  123. package/.next/standalone/.next/static/chunks/{1359o5785rmve.js → 2uh_4k4m296z5.js} +2 -2
  124. package/.next/standalone/.next/static/chunks/370sj2lgkw5ey.js +1 -0
  125. package/.next/standalone/.next/static/chunks/{0d5lypu1zs21r.js → 3s8aaig91937v.js} +1 -1
  126. package/.next/standalone/.next/static/chunks/3w7g0n0iwwnbs.js +1 -0
  127. package/.next/standalone/.next/static/chunks/{0o9wpnze1ey8y.js → 42oeva1-w0z2d.js} +1 -1
  128. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +11 -2
  129. package/.next/standalone/app/components/copy-button.tsx +22 -6
  130. package/.next/standalone/app/components/log-viewer/entry-row.tsx +8 -3
  131. package/.next/standalone/app/components/raw-log-viewer.tsx +33 -15
  132. package/.next/standalone/app/policies/hooks-client.tsx +1 -1
  133. package/.next/standalone/{ci/cli-integration → integration-suite}/README.md +25 -7
  134. package/.next/standalone/integration-suite/ci-entrypoint.sh +164 -0
  135. package/.next/standalone/{ci/cli-integration → integration-suite}/run.sh +4 -4
  136. package/.next/standalone/lib/entry-keys.ts +36 -0
  137. package/.next/standalone/lib/format-duration.ts +23 -9
  138. package/.next/standalone/package.json +5 -4
  139. package/.next/standalone/server.js +1 -1
  140. package/dist/cli.mjs +342 -106
  141. package/lib/entry-keys.ts +36 -0
  142. package/lib/format-duration.ts +23 -9
  143. package/package.json +5 -4
  144. package/scripts/translate-docs/cli.ts +6 -0
  145. package/scripts/translate-docs/mdx-translator.ts +30 -19
  146. package/scripts/translate-docs/readme-translator.ts +31 -21
  147. package/scripts/translate-docs/translator.ts +142 -7
  148. package/scripts/translate-docs/types.ts +6 -0
  149. package/scripts/translate-docs/validate-translation.ts +132 -0
  150. package/scripts/validate-mdx.ts +80 -11
  151. package/src/audit/cli.ts +12 -6
  152. package/src/auth/cli.ts +14 -5
  153. package/src/hooks/builtin-policies.ts +168 -50
  154. package/src/hooks/configure-wizard.ts +238 -22
  155. package/src/hooks/custom-hooks-loader.ts +70 -4
  156. package/src/hooks/handler.ts +4 -1
  157. package/src/hooks/policy-types.ts +8 -0
  158. package/src/hooks/tui.ts +95 -28
  159. package/.next/standalone/.next/static/chunks/20tuctw38jbgw.js +0 -1
  160. package/.next/standalone/.next/static/chunks/2ldhnaweu9ew3.js +0 -1
  161. package/.next/standalone/.next/static/chunks/2vav_x0_336ds.js +0 -1
  162. package/.next/standalone/.next/static/chunks/3cg7x0joi2b98.js +0 -1
  163. package/.next/standalone/.next/static/chunks/3k87p-tijud-4.js +0 -1
  164. package/.next/standalone/.next/static/chunks/3kmaqcp7rldad.js +0 -1
  165. /package/.next/standalone/.next/static/{LkN-JjmhKGmL-WklH7WVi → WQRKgSfFekLcC_pvyzp4S}/_buildManifest.js +0 -0
  166. /package/.next/standalone/.next/static/{LkN-JjmhKGmL-WklH7WVi → WQRKgSfFekLcC_pvyzp4S}/_clientMiddlewareManifest.js +0 -0
  167. /package/.next/standalone/.next/static/{LkN-JjmhKGmL-WklH7WVi → WQRKgSfFekLcC_pvyzp4S}/_ssgManifest.js +0 -0
  168. /package/.next/standalone/{ci/cli-integration → integration-suite}/Dockerfile +0 -0
  169. /package/.next/standalone/{ci/cli-integration → integration-suite}/canary-policies.mjs +0 -0
  170. /package/.next/standalone/{ci/cli-integration → integration-suite}/capture-tokens.sh +0 -0
  171. /package/.next/standalone/{ci/cli-integration → integration-suite}/inject-tokens.sh +0 -0
  172. /package/.next/standalone/{ci/cli-integration → integration-suite}/install-clis.sh +0 -0
  173. /package/.next/standalone/{ci/cli-integration → integration-suite}/probe-cli.sh +0 -0
  174. /package/.next/standalone/{ci/cli-integration → integration-suite}/report.js +0 -0
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env bash
2
+ # ─────────────────────────────────────────────────────────────────────────────
3
+ # failproofai integration suite — CI entrypoint (runs on the GitHub Actions runner).
4
+ #
5
+ # This is the suite's single front door for CI. The workflow
6
+ # (.github/workflows/integration-suite.yml) is a thin trigger: it checks out,
7
+ # installs bun, maps secrets into the environment, and calls this script. Every
8
+ # step that actually *does* something lives here, so the harness can be read —
9
+ # and run — without opening the YAML.
10
+ #
11
+ # Responsibilities, in order:
12
+ # 1. build failproofai under test (dist/index.js + dist/cli.mjs)
13
+ # 2. decode the base64 OAuth-token secrets into $TOKENS_DIR
14
+ # 3. build the sandbox image and create the per-run HOME volume
15
+ # 4. install all 12 CLIs @latest into that volume, then inject the tokens
16
+ # 5. assemble the gateway env-file
17
+ # 6. hand off to run.sh, which does the version-gating, probing and reporting
18
+ #
19
+ # Cleanup (env-file + docker volume) runs via trap, so it happens on failure and
20
+ # on early exit too — not just on the happy path.
21
+ #
22
+ # Inputs are all environment variables, so this is runnable outside GH Actions:
23
+ #
24
+ # GITHUB_WORKSPACE repo checkout (default: this script's parent dir)
25
+ # CANARY_LLM_API_KEY gateway credentials -> written into the env-file
26
+ # CANARY_LLM_BASE_URL (default https://models.aikin.club)
27
+ # CANARY_LLM_MODEL (default deepseek-v4-pro)
28
+ # CANARY_CLAUDE_MODEL (default claude-haiku-4-5)
29
+ # CANARY_PI_MODEL (default claude-haiku-4-5)
30
+ # COPILOT_GITHUB_TOKEN copilot PAT
31
+ # CURSOR_TOKEN_TGZ_B64 } base64 gzip-tars of each OAuth credential tree,
32
+ # DEVIN_TOKEN_TGZ_B64 } rooted at $HOME (see capture-tokens.sh)
33
+ # ANTIGRAVITY_TOKEN_TGZ_B64
34
+ # CANARY_STATE version-gate state file (restored/saved by the workflow cache)
35
+ # CANARY_SLACK_WEBHOOK optional; report is POSTed here when set
36
+ # CANARY_FP_SHA failproofai HEAD, the second version-gate dimension
37
+ # CANARY_VERSION_GATED "all" (default) | comma-sep list | "none" to force-probe
38
+ # CANARY_CLIS space-separated CLI subset (empty = all 12)
39
+ # CANARY_SKIP_BUILD set to 1 to reuse an existing dist/ (local iteration)
40
+ # ─────────────────────────────────────────────────────────────────────────────
41
+ set -u
42
+
43
+ HERE="$(cd "$(dirname "$0")" && pwd)"
44
+ REPO="${GITHUB_WORKSPACE:-$(dirname "$HERE")}"
45
+
46
+ VOL="${CANARY_VOL:-integration-suite}"
47
+ IMAGE="${CANARY_IMAGE:-failproofai-integration-suite:base}"
48
+ ENVFILE="${CANARY_ENVFILE:-$REPO/canary.env}"
49
+ TOKENS_DIR="${CANARY_TOKENS_DIR:-$REPO/tokens}"
50
+ STATE="${CANARY_STATE:-$REPO/integration-suite-state.json}"
51
+
52
+ step() { echo "── $* ──" >&2; }
53
+
54
+ cleanup() {
55
+ local rc=$?
56
+ step "cleanup"
57
+ rm -f "$ENVFILE"
58
+ rm -rf "$TOKENS_DIR"
59
+ docker volume rm "$VOL" -f >/dev/null 2>&1 || true
60
+ exit "$rc"
61
+ }
62
+ trap cleanup EXIT
63
+
64
+ # ── 0. adopt legacy version-gate state (rename transition) ──────────────────
65
+ # This harness was renamed from `cli-integration`, which took the Actions cache
66
+ # key with it. The workflow restores the legacy cache alongside the new one; if
67
+ # only the legacy file is present, adopt it so the version-gate keeps its
68
+ # baseline instead of re-probing all 12 CLIs and reporting every one as changed.
69
+ # Safe to delete (with the workflow's legacy restore step) once a run has saved
70
+ # state under the new key.
71
+ LEGACY_STATE="${CANARY_LEGACY_STATE:-$REPO/cli-integration-state.json}"
72
+ if [ ! -s "$STATE" ] && [ -s "$LEGACY_STATE" ]; then
73
+ step "adopting legacy version-gate state from $(basename "$LEGACY_STATE")"
74
+ cp "$LEGACY_STATE" "$STATE" || echo " WARN: copy failed — gate starts empty" >&2
75
+ fi
76
+
77
+ # ── 1. build failproofai under test ─────────────────────────────────────────
78
+ if [ "${CANARY_SKIP_BUILD:-0}" = 1 ]; then
79
+ step "skipping build (CANARY_SKIP_BUILD=1)"
80
+ else
81
+ step "building failproofai under test (dist/index.js + dist/cli.mjs — no dashboard)"
82
+ (
83
+ cd "$REPO" || exit 1
84
+ bun install --frozen-lockfile || exit 1
85
+ bun build --target=node --format=cjs --outfile=dist/index.js src/index.ts || exit 1
86
+ bun run build:cli || exit 1
87
+ ) || { echo "✗ build failed" >&2; exit 1; }
88
+ fi
89
+ if [ ! -s "$REPO/dist/index.js" ] || [ ! -s "$REPO/dist/cli.mjs" ]; then
90
+ echo "✗ dist/index.js and dist/cli.mjs must both be non-empty" >&2
91
+ exit 1
92
+ fi
93
+
94
+ # ── 2. decode OAuth token secrets ───────────────────────────────────────────
95
+ # Each is a base64 gzip-tar rooted at $HOME. A missing secret is NOT fatal: that
96
+ # CLI simply reports ERROR (can't auth) rather than taking the whole run down.
97
+ step "decoding OAuth token secrets"
98
+ mkdir -p "$TOKENS_DIR"
99
+ chmod 700 "$TOKENS_DIR"
100
+ decode_token() {
101
+ local name="$1" b64="$2"
102
+ if [ -z "$b64" ]; then
103
+ echo " ✗ $name: no secret set (that CLI will report ERROR)"
104
+ return 0
105
+ fi
106
+ if printf '%s' "$b64" | base64 -d > "$TOKENS_DIR/$name.tgz" 2>/dev/null; then
107
+ echo " ✓ $name: $(wc -c < "$TOKENS_DIR/$name.tgz") bytes"
108
+ else
109
+ echo " ✗ $name: base64 decode FAILED (that CLI will report ERROR)"
110
+ rm -f "$TOKENS_DIR/$name.tgz"
111
+ fi
112
+ }
113
+ decode_token cursor "${CURSOR_TOKEN_TGZ_B64:-}"
114
+ decode_token devin "${DEVIN_TOKEN_TGZ_B64:-}"
115
+ decode_token antigravity "${ANTIGRAVITY_TOKEN_TGZ_B64:-}"
116
+
117
+ # ── 3. sandbox image + per-run volume ───────────────────────────────────────
118
+ step "building sandbox image ($IMAGE)"
119
+ docker build -t "$IMAGE" "$HERE/" || { echo "✗ sandbox image build failed" >&2; exit 1; }
120
+
121
+ step "creating per-run HOME volume ($VOL)"
122
+ docker volume rm "$VOL" -f >/dev/null 2>&1 || true
123
+ docker volume create "$VOL" >/dev/null || { echo "✗ volume create failed" >&2; exit 1; }
124
+
125
+ # ── 4. install CLIs @latest, then inject tokens ─────────────────────────────
126
+ step "installing 12 CLIs @latest into the fresh volume"
127
+ docker run --rm -v "$VOL:/home/canary" -v "$HERE:/opt/canary:ro" \
128
+ "$IMAGE" bash /opt/canary/install-clis.sh \
129
+ || { echo "✗ CLI install failed" >&2; exit 1; }
130
+
131
+ step "injecting OAuth credential files"
132
+ docker run --rm -v "$VOL:/home/canary" \
133
+ -v "$HERE:/opt/canary:ro" -v "$TOKENS_DIR:/opt/tokens:ro" \
134
+ "$IMAGE" bash /opt/canary/inject-tokens.sh \
135
+ || { echo "✗ token injection failed" >&2; exit 1; }
136
+
137
+ # ── 5. gateway env-file ─────────────────────────────────────────────────────
138
+ # umask 077 so the file with the gateway key is never group/world readable.
139
+ step "assembling gateway env-file"
140
+ (
141
+ umask 077
142
+ {
143
+ echo "CANARY_LLM_API_KEY=${CANARY_LLM_API_KEY:-}"
144
+ echo "CANARY_LLM_BASE_URL=${CANARY_LLM_BASE_URL:-https://models.aikin.club}"
145
+ echo "CANARY_LLM_MODEL=${CANARY_LLM_MODEL:-deepseek-v4-pro}"
146
+ echo "CANARY_CLAUDE_MODEL=${CANARY_CLAUDE_MODEL:-claude-haiku-4-5}"
147
+ echo "CANARY_PI_MODEL=${CANARY_PI_MODEL:-claude-haiku-4-5}"
148
+ echo "COPILOT_GITHUB_TOKEN=${COPILOT_GITHUB_TOKEN:-}"
149
+ } > "$ENVFILE"
150
+ )
151
+
152
+ # ── 6. probe + report ───────────────────────────────────────────────────────
153
+ # run.sh word-splits CANARY_CLIS into args; unquoted on purpose. It owns the
154
+ # exit code (non-zero iff a hard FAIL verdict is present), which the trap
155
+ # preserves.
156
+ step "running integration probes + report"
157
+ # shellcheck disable=SC2086
158
+ CANARY_REPO="$REPO" \
159
+ CANARY_SANDBOX="$HERE" \
160
+ CANARY_VOL="$VOL" \
161
+ CANARY_IMAGE="$IMAGE" \
162
+ CANARY_STATE="$STATE" \
163
+ CANARY_ENVFILE="$ENVFILE" \
164
+ bash "$HERE/run.sh" ${CANARY_CLIS:-}
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env bash
2
2
  # ─────────────────────────────────────────────────────────────────────────────
3
- # failproofai CLI Integration Tests — orchestrator (runs on the GitHub Actions runner).
3
+ # failproofai integration suite — orchestrator (runs on the GitHub Actions runner).
4
4
  # ./run.sh [cli ...] default: all 12
5
5
  #
6
6
  # Mirrors the box's run-canary.sh, but every path is env-parameterised (the
7
7
  # runner is ephemeral) and the report is POSTed to a Slack Incoming Webhook
8
- # instead of delivered via Hermes. Assumes the workflow has already:
8
+ # instead of delivered via Hermes. Assumes ci-entrypoint.sh has already:
9
9
  # • checked out + built failproofai (dist/index.js + dist/cli.mjs) at $CANARY_REPO
10
10
  # • built the sandbox image ($CANARY_IMAGE)
11
11
  # • created the per-run volume ($CANARY_VOL) and INSTALLED the CLIs into it
@@ -23,8 +23,8 @@ HERE="$(cd "$(dirname "$0")" && pwd)"
23
23
 
24
24
  REPO="${CANARY_REPO:?CANARY_REPO (built failproofai checkout) required}"
25
25
  SANDBOX="${CANARY_SANDBOX:-$HERE}"
26
- VOL="${CANARY_VOL:-canary-ci}"
27
- IMAGE="${CANARY_IMAGE:-failproofai-canary:base}"
26
+ VOL="${CANARY_VOL:-integration-suite}"
27
+ IMAGE="${CANARY_IMAGE:-failproofai-integration-suite:base}"
28
28
  STATE="${CANARY_STATE:-$HERE/state.json}"
29
29
  ENVFILE="${CANARY_ENVFILE:?CANARY_ENVFILE (docker --env-file with gateway creds) required}"
30
30
  MODEL="${CANARY_LLM_MODEL:-deepseek-v4-pro}"
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Stable per-entry identity for the log viewer.
3
+ *
4
+ * Deliberately standalone and dependency-free: `lib/log-entries.ts` reaches
5
+ * fs/promises through its project-resolution imports, so a *value* import of
6
+ * it from a client component drags node:fs into the browser bundle and the
7
+ * session page 500s. Client code may only import types from there.
8
+ */
9
+ import type { LogEntry } from "./log-entries";
10
+
11
+ /**
12
+ * Assigns every entry a unique, render-stable identity.
13
+ *
14
+ * Not every CLI writes a per-record `uuid` — Codex, Copilot, Cursor and Pi
15
+ * transcripts have none, so `baseEntry` leaves it "". Keying rows off
16
+ * `uuid || timestamp` therefore collapses to the timestamp, which those CLIs
17
+ * reuse freely (one 771-record Codex session: 93 timestamps shared by 2-5
18
+ * records each). Duplicate React keys break reconciliation, and in the
19
+ * virtualized log list that strands orphaned DOM nodes stacked on top of live
20
+ * rows — they keep their old transform and never recover (#292 follow-up).
21
+ *
22
+ * Collisions are disambiguated by occurrence order, so keys stay stable for as
23
+ * long as the parsed entry list is — which is what React and the virtualizer's
24
+ * measurement cache both require.
25
+ */
26
+ export function buildEntryKeys(entries: LogEntry[]): Map<LogEntry, string> {
27
+ const keys = new Map<LogEntry, string>();
28
+ const seen = new Map<string, number>();
29
+ for (const entry of entries) {
30
+ const base = entry.uuid || `${entry._source}:${entry.timestamp}`;
31
+ const n = seen.get(base) ?? 0;
32
+ seen.set(base, n + 1);
33
+ keys.set(entry, n === 0 ? base : `${base}#${n}`);
34
+ }
35
+ return keys;
36
+ }
@@ -16,14 +16,28 @@ export function formatRelativeTime(ts: number): string {
16
16
 
17
17
  export function formatDuration(ms: number): string {
18
18
  if (ms < 1000) return `${ms}ms`;
19
- const seconds = ms / 1000;
20
- if (seconds < 60) return `${seconds.toFixed(1)}s`;
21
- const totalMinutes = Math.floor(seconds / 60);
22
- if (totalMinutes >= 60) {
23
- const hours = Math.floor(totalMinutes / 60);
24
- const remainingMinutes = totalMinutes % 60;
25
- return `${hours}h ${remainingMinutes}m`;
19
+
20
+ // Round to the precision the output will actually use, then bucket.
21
+ // Seconds are shown with one decimal place, so round to the nearest 0.1 s
22
+ // before deciding whether we have crossed into the minute range.
23
+ const deciseconds = Math.round(ms / 100);
24
+ if (deciseconds < 600) {
25
+ return `${(deciseconds / 10).toFixed(1)}s`;
26
+ }
27
+
28
+ // Minutes are shown with whole seconds, so round to the nearest second
29
+ // before splitting into minutes and seconds.
30
+ const totalSeconds = Math.round(ms / 1000);
31
+ if (totalSeconds < 3600) {
32
+ const minutes = Math.floor(totalSeconds / 60);
33
+ const seconds = totalSeconds % 60;
34
+ return `${minutes}m ${seconds}s`;
26
35
  }
27
- const remainingSeconds = (seconds % 60).toFixed(0);
28
- return `${totalMinutes}m ${remainingSeconds}s`;
36
+
37
+ // Hours are shown with whole minutes, so round to the nearest minute
38
+ // before splitting into hours and minutes.
39
+ const totalMinutes = Math.round(ms / 60000);
40
+ const hours = Math.floor(totalMinutes / 60);
41
+ const minutes = totalMinutes % 60;
42
+ return `${hours}h ${minutes}m`;
29
43
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "0.0.14-beta.1",
3
+ "version": "0.0.14",
4
4
  "description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs"
@@ -71,11 +71,11 @@
71
71
  "access": "public"
72
72
  },
73
73
  "devDependencies": {
74
- "@anthropic-ai/sdk": "^0.111.0",
74
+ "@anthropic-ai/sdk": "^0.112.4",
75
75
  "@mdx-js/mdx": "^3.1.1",
76
76
  "@tailwindcss/postcss": "^4.3.1",
77
77
  "@tanstack/react-virtual": "^3.14.3",
78
- "@testing-library/jest-dom": "^6.9.1",
78
+ "@testing-library/jest-dom": "^7.0.0",
79
79
  "@testing-library/react": "^16.3.2",
80
80
  "@testing-library/user-event": "^14.6.1",
81
81
  "@types/node": "26.1.1",
@@ -106,6 +106,7 @@
106
106
  "postcss": "8.5.14",
107
107
  "eslint-plugin-react-hooks": "7.0.1",
108
108
  "vite": "8.0.16",
109
- "undici": "7.28.0"
109
+ "undici": "7.28.0",
110
+ "brace-expansion": "5.0.7"
110
111
  }
111
112
  }
@@ -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":"0.0.14-beta.1"},"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"],"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","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":false,"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"cachedNavigations":false,"partialFallbacks":false,"dynamicOnHover":false,"varyParams":false,"prefetchInlining":false,"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"imgOptSkipMetadata":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"strictRouteTypes":false,"viewTransition":false,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":false,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":true,"serverComponentsHmrCache":true,"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":false,"turbopackInferModuleSideEffects":true,"turbopackPluginRuntimeStrategy":"childProcesses","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"],"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"},"distDirRoot":".next"}
12
+ const nextConfig = {"env":{"NEXT_PUBLIC_APP_VERSION":"0.0.14"},"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"],"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","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":false,"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"cachedNavigations":false,"partialFallbacks":false,"dynamicOnHover":false,"varyParams":false,"prefetchInlining":false,"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"imgOptSkipMetadata":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"strictRouteTypes":false,"viewTransition":false,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":false,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":true,"serverComponentsHmrCache":true,"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":false,"turbopackInferModuleSideEffects":true,"turbopackPluginRuntimeStrategy":"childProcesses","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"],"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"},"distDirRoot":".next"}
13
13
 
14
14
  process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig)
15
15