failproofai 0.0.11-beta.9 → 0.0.11
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.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[externals]__1_g_b3t._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0dwpg-h._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0lnenda._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__13i_sva._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1_mqemn._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_0-tu4ot._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_1bnh1y0._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +3 -3
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0e446gb._.js → [root-of-the-server]__00uwqi6._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0808sha._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e4-6d8._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ehe24g._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g253ve._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0k65l27._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0wprfyc._.js → [root-of-the-server]__0kjb_s4._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0vxf0_g._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12mcauo._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mt35_w._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pcxxwg._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1uvfwgr._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +49 -21
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_0y4a-0q.js → node_modules_html-to-image_es_index_0ihmbv4.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +10 -10
- package/.next/standalone/.next/static/chunks/{3ty6dhcuogout.js → 02fywjt0by40a.js} +1 -1
- package/.next/standalone/.next/static/chunks/0xdx2ehtbdoeg.js +1 -0
- package/.next/standalone/.next/static/chunks/{07_d165p5h5ys.js → 1-a5rvq67k7ed.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3nj6g3xu9uy78.js → 15csyj1_rf0-w.js} +1 -1
- package/.next/standalone/.next/static/chunks/1o0xa47736gi9.css +2 -0
- package/.next/standalone/.next/static/chunks/24cv31x607n7k.js +1 -0
- package/.next/standalone/.next/static/chunks/2n_s8v1ae38_a.js +69 -0
- package/.next/standalone/.next/static/chunks/{277oc363p56n6.js → 2y-jmvrjxz60x.js} +2 -2
- package/.next/standalone/.next/static/chunks/{1kvadxkgnapyj.js → 3eik_d9qrvoft.js} +1 -1
- package/.next/standalone/.next/static/chunks/{168k-8z6k7e8z.css → 3i27c3hcriawq.css} +1 -1
- package/.next/standalone/.next/static/chunks/{2z42u62k-8-_q.js → 3v61675vr6jav.js} +1 -1
- package/.next/standalone/app/api/audit/invite/route.ts +10 -1
- package/.next/standalone/app/api/audit/run/route.ts +35 -0
- package/.next/standalone/app/api/auth/login-request/route.ts +2 -2
- package/.next/standalone/app/api/auth/login-verify/route.ts +10 -2
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +9 -1
- package/.next/standalone/app/audit/_components/audit-poster.tsx +11 -7
- package/.next/standalone/app/audit/_components/auth-dialog.tsx +6 -4
- package/.next/standalone/app/audit/_components/come-back-better-section.tsx +23 -3
- package/.next/standalone/app/audit/_components/invite-dialog.tsx +6 -3
- package/.next/standalone/app/audit/_components/share-templates.ts +58 -28
- package/.next/standalone/app/audit/audit-styles.css +17 -22
- package/.next/standalone/app/globals.css +27 -2
- package/.next/standalone/app/policies/hooks-client.tsx +33 -24
- package/.next/standalone/components/reach-developers.tsx +10 -25
- package/.next/standalone/lib/auth/api-server-client.ts +5 -2
- package/.next/standalone/lib/client-telemetry.ts +4 -0
- package/.next/standalone/package.json +6 -4
- package/.next/standalone/server.js +1 -1
- package/README.md +2 -2
- package/bin/failproofai.mjs +24 -5
- package/dist/cli.mjs +2328 -381
- package/lib/auth/api-server-client.ts +5 -2
- package/lib/client-telemetry.ts +4 -0
- package/package.json +6 -4
- package/scripts/launch.ts +30 -4
- package/scripts/postinstall.mjs +10 -1
- package/scripts/skew-log-filter.ts +46 -0
- package/scripts/validate-mdx.ts +139 -0
- package/src/audit/cli.ts +330 -0
- package/src/audit/open-browser.ts +69 -0
- package/src/auth/cli.ts +16 -13
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1r1h8v9._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1uatkiv._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1y6gxxb._.js +0 -3
- package/.next/standalone/.next/static/chunks/28mkxkl_d91-l.js +0 -1
- package/.next/standalone/.next/static/chunks/28x7jvo3kxd3u.js +0 -41
- package/.next/standalone/.next/static/chunks/29nrs5xs9c4hx.css +0 -2
- package/.next/standalone/.next/static/chunks/29tg7deqmq32l.js +0 -1
- /package/.next/standalone/.next/static/{NYPiJP6Rv_exQdSFVS8HP → P_MIRSeoE296wkbE-Icin}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{NYPiJP6Rv_exQdSFVS8HP → P_MIRSeoE296wkbE-Icin}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{NYPiJP6Rv_exQdSFVS8HP → P_MIRSeoE296wkbE-Icin}/_ssgManifest.js +0 -0
|
@@ -249,17 +249,20 @@ export interface InviteSendResult {
|
|
|
249
249
|
/**
|
|
250
250
|
* Send invite emails to a batch of friends. The api-server pulls the sender's
|
|
251
251
|
* email from the access-token claims and Cc's them on every outbound message
|
|
252
|
-
* so the recipient sees who invited them.
|
|
252
|
+
* so the recipient sees who invited them. `score` (the sender's audit score,
|
|
253
|
+
* 0–100) is forwarded so the invite body can show "mine came out at N/100";
|
|
254
|
+
* omit it and the api-server renders score-free copy.
|
|
253
255
|
*
|
|
254
256
|
* Contract is handed over to the platform team separately.
|
|
255
257
|
*/
|
|
256
258
|
export async function sendInvites(
|
|
257
259
|
accessToken: string,
|
|
258
260
|
to: string[],
|
|
261
|
+
score?: number,
|
|
259
262
|
): Promise<InviteSendResult> {
|
|
260
263
|
return postJson<InviteSendResult>(
|
|
261
264
|
"/v0/invite",
|
|
262
|
-
{ to },
|
|
265
|
+
score === undefined ? { to } : { to, score },
|
|
263
266
|
{ accessToken },
|
|
264
267
|
);
|
|
265
268
|
}
|
package/lib/client-telemetry.ts
CHANGED
|
@@ -34,6 +34,10 @@ export function captureClientEvent(
|
|
|
34
34
|
method: "POST",
|
|
35
35
|
headers: { "Content-Type": "application/json" },
|
|
36
36
|
body: payload,
|
|
37
|
+
// keepalive lets the request outlive a page navigation/unload — without it,
|
|
38
|
+
// events fired right before the page goes away (client_error / unhandled_*
|
|
39
|
+
// from an error boundary, or a share click that opens a new tab) are dropped.
|
|
40
|
+
keepalive: true,
|
|
37
41
|
signal: AbortSignal.timeout(5000),
|
|
38
42
|
}).catch(() => {});
|
|
39
43
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "failproofai",
|
|
3
|
-
"version": "0.0.11
|
|
3
|
+
"version": "0.0.11",
|
|
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"
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
"translate:readme": "bun scripts/translate-docs/cli.ts --readme-only",
|
|
39
39
|
"translate:docs": "bun scripts/translate-docs/cli.ts --docs-only",
|
|
40
40
|
"translate:dry-run": "bun scripts/translate-docs/cli.ts --dry-run",
|
|
41
|
-
"translate:validate": "bun scripts/translate-docs/cli.ts --validate"
|
|
41
|
+
"translate:validate": "bun scripts/translate-docs/cli.ts --validate",
|
|
42
|
+
"validate:mdx": "bun scripts/validate-mdx.ts"
|
|
42
43
|
},
|
|
43
44
|
"keywords": [
|
|
44
45
|
"claude",
|
|
@@ -71,13 +72,14 @@
|
|
|
71
72
|
"access": "public"
|
|
72
73
|
},
|
|
73
74
|
"devDependencies": {
|
|
74
|
-
"@anthropic-ai/sdk": "^0.
|
|
75
|
+
"@anthropic-ai/sdk": "^0.105.0",
|
|
76
|
+
"@mdx-js/mdx": "^3.1.1",
|
|
75
77
|
"@tailwindcss/postcss": "^4.3.1",
|
|
76
78
|
"@tanstack/react-virtual": "^3.14.3",
|
|
77
79
|
"@testing-library/jest-dom": "^6.9.1",
|
|
78
80
|
"@testing-library/react": "^16.3.2",
|
|
79
81
|
"@testing-library/user-event": "^14.6.1",
|
|
80
|
-
"@types/node": "
|
|
82
|
+
"@types/node": "26.0.0",
|
|
81
83
|
"@types/react": "19.2.17",
|
|
82
84
|
"@types/react-dom": "^19.2.3",
|
|
83
85
|
"@vitejs/plugin-react": "^6.0.1",
|
package/scripts/launch.ts
CHANGED
|
@@ -5,21 +5,23 @@ import { spawn } from "child_process";
|
|
|
5
5
|
import { realpathSync, existsSync } from "node:fs";
|
|
6
6
|
import { resolve, dirname } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { createInterface } from "node:readline";
|
|
8
9
|
import { parseScriptArgs } from "./parse-script-args";
|
|
9
10
|
import { diagnoseShadow } from "./install-diagnosis.mjs";
|
|
11
|
+
import { makeSkewLogFilter } from "./skew-log-filter";
|
|
10
12
|
import { version } from "../package.json";
|
|
11
13
|
|
|
12
14
|
export function launch(mode: "dev" | "start"): void {
|
|
13
15
|
const { loggingLevel, disableTelemetry, allowedDevOrigins, remainingArgs } = parseScriptArgs(process.argv.slice(2));
|
|
14
16
|
|
|
15
17
|
// Plain-text title + a labeled `Version` line that lines up with the
|
|
16
|
-
// `Star us` / `Docs` / `
|
|
18
|
+
// `Star us` / `Docs` / `Discord` lines below (all four labels pad to the
|
|
17
19
|
// same column so the values form a clean right-hand column).
|
|
18
20
|
console.log(`\n failproof ai\n`);
|
|
19
21
|
console.log(` 📦 Version: ${version}`);
|
|
20
22
|
console.log(` ⭐ Star us: https://github.com/failproofai/failproofai`);
|
|
21
|
-
console.log(` 📖 Docs: https://befailproof.ai`);
|
|
22
|
-
console.log(` 💬
|
|
23
|
+
console.log(` 📖 Docs: https://docs.befailproof.ai/introduction`);
|
|
24
|
+
console.log(` 💬 Discord: https://discord.gg/2zjBZP7yQJ\n`);
|
|
23
25
|
|
|
24
26
|
let cmd: string;
|
|
25
27
|
let cmdArgs: string[];
|
|
@@ -79,16 +81,40 @@ export function launch(mode: "dev" | "start"): void {
|
|
|
79
81
|
cmdArgs = ["--bun", "next", "dev", ...remainingArgs];
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
// In `start` (the shipped standalone server) we pipe + filter the child's
|
|
85
|
+
// output to drop the benign "Failed to find Server Action" deployment-skew
|
|
86
|
+
// block — a stale browser tab POSTing an old action ID after a rebuild, which
|
|
87
|
+
// the client recovers from via Next's graceful 404 (see skew-log-filter.ts).
|
|
88
|
+
// `dev` keeps "inherit" so Next's interactive compile output is untouched.
|
|
89
|
+
// FORCE_COLOR keeps the piped child's output colored despite the non-TTY pipe.
|
|
90
|
+
const filterLogs = mode === "start";
|
|
91
|
+
|
|
82
92
|
const nextProcess = spawn(cmd, cmdArgs, {
|
|
83
|
-
stdio: "inherit",
|
|
93
|
+
stdio: filterLogs ? ["inherit", "pipe", "pipe"] : "inherit",
|
|
84
94
|
env: {
|
|
85
95
|
...process.env,
|
|
96
|
+
...(filterLogs ? { FORCE_COLOR: process.env.FORCE_COLOR ?? "1" } : {}),
|
|
86
97
|
...(loggingLevel ? { FAILPROOFAI_LOG_LEVEL: loggingLevel } : {}),
|
|
87
98
|
...(disableTelemetry ? { FAILPROOFAI_TELEMETRY_DISABLED: "1" } : {}),
|
|
88
99
|
...(allowedDevOrigins ? { FAILPROOFAI_ALLOWED_DEV_ORIGINS: allowedDevOrigins.join(",") } : {}),
|
|
89
100
|
},
|
|
90
101
|
});
|
|
91
102
|
|
|
103
|
+
if (filterLogs) {
|
|
104
|
+
// One filter instance per stream — the skew block is multi-line and stateful.
|
|
105
|
+
for (const [src, dest] of [
|
|
106
|
+
[nextProcess.stdout, process.stdout],
|
|
107
|
+
[nextProcess.stderr, process.stderr],
|
|
108
|
+
] as const) {
|
|
109
|
+
if (!src) continue;
|
|
110
|
+
const filter = makeSkewLogFilter();
|
|
111
|
+
createInterface({ input: src }).on("line", (line) => {
|
|
112
|
+
const out = filter(line);
|
|
113
|
+
if (out !== null) dest.write(out + "\n");
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
92
118
|
nextProcess.on("error", (error) => {
|
|
93
119
|
console.error("Error starting Next.js:", error);
|
|
94
120
|
process.exit(1);
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -27,6 +27,14 @@ if (!existsSync(serverJsPath)) {
|
|
|
27
27
|
` The package may not have been built correctly.\n` +
|
|
28
28
|
` Try reinstalling: npm install -g failproofai@latest\n`
|
|
29
29
|
);
|
|
30
|
+
// Await so the event lands before process.exit(1) kills the in-flight fetch —
|
|
31
|
+
// a failed install is exactly the signal we most want and would otherwise lose.
|
|
32
|
+
await trackInstallEvent("package_install_failed", {
|
|
33
|
+
reason: "server_js_missing",
|
|
34
|
+
platform: platform(),
|
|
35
|
+
arch: arch(),
|
|
36
|
+
node_version: process.versions.node,
|
|
37
|
+
}).catch(() => {});
|
|
30
38
|
process.exit(1);
|
|
31
39
|
}
|
|
32
40
|
|
|
@@ -202,7 +210,7 @@ if (previousVersion === null) {
|
|
|
202
210
|
arch: arch(),
|
|
203
211
|
os_release: release(),
|
|
204
212
|
node_version: process.versions.node,
|
|
205
|
-
version
|
|
213
|
+
// `version` is carried automatically as `failproofai_version` — no explicit dup.
|
|
206
214
|
}).catch(() => {});
|
|
207
215
|
} else {
|
|
208
216
|
// Same version is a reinstall — still worth tracking; users hitting `npm install -g`
|
|
@@ -227,6 +235,7 @@ trackInstallEvent("package_installed", {
|
|
|
227
235
|
platform: platform(),
|
|
228
236
|
arch: arch(),
|
|
229
237
|
os_release: release(),
|
|
238
|
+
node_version: process.versions.node,
|
|
230
239
|
hostname_hash: hashToId(hostname()),
|
|
231
240
|
hooks_configured: hooksResult.configured,
|
|
232
241
|
hooks_registered: hooksResult.registered,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filters the benign Next.js "Failed to find Server Action" deployment-skew
|
|
3
|
+
* block out of the standalone server's forwarded output.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: the dashboard ships as a per-version Next standalone build,
|
|
6
|
+
* and Server Action IDs are hashed at build time. A browser tab left open
|
|
7
|
+
* across a rebuild/upgrade keeps POSTing a stale action ID the running build no
|
|
8
|
+
* longer has, so Next throws + logs (server-side, to stderr) a 3-line block:
|
|
9
|
+
*
|
|
10
|
+
* Error: Failed to find Server Action "<hash>". This request might be from an older or newer deployment.
|
|
11
|
+
* Read more: https://nextjs.org/docs/messages/failed-to-find-server-action
|
|
12
|
+
* at ignore-listed frames
|
|
13
|
+
*
|
|
14
|
+
* The client receives a graceful 404 (`x-nextjs-action-not-found: 1`) and
|
|
15
|
+
* recovers on its own, so the server-side log is pure noise. `launch.ts` pipes
|
|
16
|
+
* the spawned server's stdout/stderr through this filter (one instance per
|
|
17
|
+
* stream) to drop that block while passing everything else through verbatim.
|
|
18
|
+
*
|
|
19
|
+
* Stateful by design: the block spans multiple lines, so each stream needs its
|
|
20
|
+
* own filter instance. Call the returned function with each line; it returns
|
|
21
|
+
* the line to emit, or `null` to drop it.
|
|
22
|
+
*/
|
|
23
|
+
export function makeSkewLogFilter(): (line: string) => string | null {
|
|
24
|
+
let inSkewBlock = false;
|
|
25
|
+
return (line: string): string | null => {
|
|
26
|
+
if (line.includes("Failed to find Server Action")) {
|
|
27
|
+
inSkewBlock = true;
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
if (inSkewBlock) {
|
|
31
|
+
// Continuation lines of the same error block — drop them too.
|
|
32
|
+
const trimmed = line.trimStart();
|
|
33
|
+
if (
|
|
34
|
+
trimmed.startsWith("Read more:") ||
|
|
35
|
+
line.includes("failed-to-find-server-action") ||
|
|
36
|
+
line.includes("ignore-listed frames") ||
|
|
37
|
+
/^\s+at\s/.test(line)
|
|
38
|
+
) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
// First line that isn't part of the block — stop dropping and emit it.
|
|
42
|
+
inSkewBlock = false;
|
|
43
|
+
}
|
|
44
|
+
return line;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate that every docs MDX page parses with the same MDX engine Mintlify
|
|
3
|
+
* runs at deploy time.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: `mintlify validate` (the existing `docs` CI job) only checks
|
|
6
|
+
* `docs.json` structure and nav-link resolution — it does NOT parse page
|
|
7
|
+
* content. A page that is structurally referenced but contains an MDX syntax
|
|
8
|
+
* error (e.g. a `<slug>` that escaped its surrounding backticks because a
|
|
9
|
+
* translation dropped a closing `` ` ``) passes `mintlify validate` but fails
|
|
10
|
+
* the Mintlify deploy with:
|
|
11
|
+
*
|
|
12
|
+
* Failed to parse page content at path tr/cli/audit.mdx:
|
|
13
|
+
* Expected a closing tag for `<slug>` (61:127-61:133) before the end of `paragraph`
|
|
14
|
+
*
|
|
15
|
+
* That deploy runs post-merge, so the failure only surfaces on `main`. The
|
|
16
|
+
* auto-translation workflow regenerates these pages with an LLM, so this class
|
|
17
|
+
* of breakage recurs (see the `sanitizeJsxAttributes` / `stripStrayTrailingFence`
|
|
18
|
+
* heuristics in scripts/translate-docs/mdx-translator.ts — best-effort fixers
|
|
19
|
+
* that can't catch every case). This script is the deterministic safety net:
|
|
20
|
+
* run it on every PR so an unparseable page fails CI before it reaches `main`.
|
|
21
|
+
*
|
|
22
|
+
* The error string above is emitted by `@mdx-js/mdx`'s micromark MDX layer,
|
|
23
|
+
* which is the same engine Mintlify uses, so compiling here reproduces the
|
|
24
|
+
* deploy-time parse faithfully.
|
|
25
|
+
*/
|
|
26
|
+
import { readdirSync, statSync, readFileSync } from "node:fs";
|
|
27
|
+
import { dirname, join, relative } from "node:path";
|
|
28
|
+
import { fileURLToPath } from "node:url";
|
|
29
|
+
import { compile } from "@mdx-js/mdx";
|
|
30
|
+
|
|
31
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
const DOCS_DIR = join(__dirname, "..", "docs");
|
|
33
|
+
|
|
34
|
+
export interface MdxParseError {
|
|
35
|
+
message: string;
|
|
36
|
+
line?: number;
|
|
37
|
+
column?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Replace a leading YAML frontmatter block (`--- … ---`) with blank lines.
|
|
42
|
+
*
|
|
43
|
+
* Mintlify parses frontmatter as YAML, not MDX, so it never causes an MDX parse
|
|
44
|
+
* error. We blank it rather than delete it so the remaining content keeps its
|
|
45
|
+
* original line numbers — error positions then match the real file.
|
|
46
|
+
*/
|
|
47
|
+
export function stripFrontmatter(source: string): string {
|
|
48
|
+
const match = /^---\r?\n[\s\S]*?\r?\n---[ \t]*\r?\n?/.exec(source);
|
|
49
|
+
if (!match) return source;
|
|
50
|
+
// Keep newlines, drop every other character, so line numbers stay aligned.
|
|
51
|
+
const blanked = match[0].replace(/[^\n]/g, "");
|
|
52
|
+
return blanked + source.slice(match[0].length);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Compile one MDX source string with the deploy-time parser. Returns `null`
|
|
57
|
+
* when it parses cleanly, or the parse error (with position) otherwise.
|
|
58
|
+
*/
|
|
59
|
+
export async function findMdxParseError(
|
|
60
|
+
source: string,
|
|
61
|
+
): Promise<MdxParseError | null> {
|
|
62
|
+
try {
|
|
63
|
+
await compile(stripFrontmatter(source));
|
|
64
|
+
return null;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
const e = err as {
|
|
67
|
+
reason?: string;
|
|
68
|
+
message?: string;
|
|
69
|
+
line?: number;
|
|
70
|
+
column?: number;
|
|
71
|
+
place?: { start?: { line?: number; column?: number } };
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
message: e.reason ?? e.message ?? String(err),
|
|
75
|
+
line: e.line ?? e.place?.start?.line,
|
|
76
|
+
column: e.column ?? e.place?.start?.column,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Percent-encode a value for a GitHub Actions workflow command. Without this a
|
|
83
|
+
* multi-line MDX error message would be truncated at its first newline (and a
|
|
84
|
+
* literal `%` could mis-parse) when emitted as an `::error::` annotation.
|
|
85
|
+
* https://docs.github.com/actions/reference/workflow-commands-for-github-actions
|
|
86
|
+
*/
|
|
87
|
+
export function encodeAnnotation(value: string): string {
|
|
88
|
+
return value
|
|
89
|
+
.replace(/%/g, "%25")
|
|
90
|
+
.replace(/\r/g, "%0D")
|
|
91
|
+
.replace(/\n/g, "%0A");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function collectMdxFiles(dir: string): string[] {
|
|
95
|
+
const out: string[] = [];
|
|
96
|
+
for (const entry of readdirSync(dir)) {
|
|
97
|
+
const full = join(dir, entry);
|
|
98
|
+
if (statSync(full).isDirectory()) out.push(...collectMdxFiles(full));
|
|
99
|
+
else if (entry.endsWith(".mdx")) out.push(full);
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function main(): Promise<void> {
|
|
105
|
+
const files = collectMdxFiles(DOCS_DIR).sort();
|
|
106
|
+
const failures: Array<{ file: string; error: MdxParseError }> = [];
|
|
107
|
+
|
|
108
|
+
for (const file of files) {
|
|
109
|
+
const error = await findMdxParseError(readFileSync(file, "utf-8"));
|
|
110
|
+
if (error) failures.push({ file: relative(process.cwd(), file), error });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (failures.length === 0) {
|
|
114
|
+
console.log(`✓ ${files.length} MDX page(s) parsed cleanly`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.error(
|
|
119
|
+
`✗ ${failures.length} of ${files.length} MDX page(s) failed to parse:\n`,
|
|
120
|
+
);
|
|
121
|
+
for (const { file, error } of failures) {
|
|
122
|
+
const pos = error.line
|
|
123
|
+
? `:${error.line}${error.column ? `:${error.column}` : ""}`
|
|
124
|
+
: "";
|
|
125
|
+
console.error(` ${file}${pos}\n ${error.message}\n`);
|
|
126
|
+
// GitHub Actions inline annotation.
|
|
127
|
+
const loc =
|
|
128
|
+
(error.line ? `,line=${error.line}` : "") +
|
|
129
|
+
(error.column ? `,col=${error.column}` : "");
|
|
130
|
+
console.log(
|
|
131
|
+
`::error file=${encodeAnnotation(file)}${loc}::MDX parse error: ${encodeAnnotation(error.message)}`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
process.exitCode = 1;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (import.meta.main) {
|
|
138
|
+
void main();
|
|
139
|
+
}
|