failproofai 1.0.7-beta.1 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +4 -4
- 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 +4 -4
- 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 +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- 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 +4 -4
- 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/_not-found/__PAGE__.segment.rsc +14 -14
- 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.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +7 -8
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +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.nft.json +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/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 +5 -7
- 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 +14 -14
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- 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 +6 -6
- 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 +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +11 -13
- 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 +7 -8
- 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 +7 -7
- 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 +6 -7
- 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/app/settings/page/server-reference-manifest.json +8 -41
- package/.next/standalone/.next/server/app/settings/page.js +9 -12
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1j-zsg5._.js → [externals]__1_bftcl._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__19_pzeq._.js → [externals]__1msfs-h._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1bf34x4._.js → [root-of-the-server]__1_r2rbg._.js} +7 -5
- package/.next/standalone/.next/server/chunks/_09dz7xv._.js +21 -21
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_1q5i8mb._.js → _1c3k-8x._.js} +2 -2
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +16 -16
- 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/src_hooks_0iu54mz._.js +3 -0
- package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3._.js +2 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-_ki57._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0yxwl6j._.js → [root-of-the-server]__0bd3mje._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cg-bgc._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cpu_mj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1mf3zp6._.js → [root-of-the-server]__0cxe_2_._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u3w0ll._.js +22 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17d_ffl._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19d9tgz._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1jiwfsj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1phc187._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_06imw3p._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1w_5l7t._.js → _0h_douw._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_214wgrp._.js → _1-i_gzc._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_0bn2oo8._.js → _166t73i._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1q46vxx._.js → _1_qswah._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_1gb0ifp._.js → _1es2j7i._.js} +5 -5
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- 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 +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_18_d8l1._.js → node_modules_next_dist_0drixxt._.js} +4 -4
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4._.js +10 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me65._.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 +23 -56
- package/.next/standalone/.next/static/chunks/094xgi4owxaqf.js +1 -0
- package/.next/standalone/.next/static/chunks/0__8a7m868fvf.js +1 -0
- package/.next/standalone/.next/static/chunks/{12tvm75t5ffui.js → 0o6qlkgubtoex.js} +1 -1
- package/.next/standalone/.next/static/chunks/13i7-9is-vhys.js +1 -0
- package/.next/standalone/.next/static/chunks/1rz20_pz828f3.js +6 -0
- package/.next/standalone/.next/static/chunks/2k9f4tyv04809.css +1 -0
- package/.next/standalone/.next/static/chunks/{0vmd180qfntfb.js → 2klitrtzpaoe0.js} +1 -1
- package/.next/standalone/.next/static/chunks/{258668t68du6b.js → 2mdh397ghgnvv.js} +1 -1
- package/.next/standalone/.next/static/chunks/2rshywgeqsyzk.css +2 -0
- package/.next/standalone/.next/static/chunks/3pzx4chkhko9k.js +1 -0
- package/.next/standalone/.next/static/chunks/{0qrbdkv9qmvli.js → 3rh5o7e16irrm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{29fql3nbnfc9q.js → 43ufqrz8qo3h-.js} +1 -1
- package/.next/standalone/SECURITY.md +53 -0
- package/.next/standalone/app/actions/pack-actions.ts +0 -12
- package/.next/standalone/app/policies/hooks-client.tsx +0 -9
- package/.next/standalone/app/settings/page.tsx +1 -20
- package/.next/standalone/app/settings/settings-client.tsx +1 -27
- package/.next/standalone/app/settings/settings.css +0 -79
- package/.next/standalone/package.json +9 -9
- package/.next/standalone/sdk/python/skill/SKILL.md +60 -14
- package/.next/standalone/sdk/python/skill/agents/openai.yaml +2 -1
- package/.next/standalone/sdk/python/skill/references/evaluator.md +255 -0
- package/.next/standalone/sdk/python/skill/references/events.md +17 -8
- package/.next/standalone/sdk/python/skill/references/frameworks.md +3 -0
- package/.next/standalone/sdk/python/skill/references/install.md +3 -0
- package/.next/standalone/sdk/python/skill/references/integration.md +6 -2
- package/.next/standalone/sdk/python/skill/references/typescript.md +568 -0
- package/.next/standalone/sdk/typescript/CHANGELOG.md +133 -0
- package/.next/standalone/sdk/typescript/LICENSE +42 -0
- package/.next/standalone/sdk/typescript/README.md +552 -0
- package/.next/standalone/sdk/typescript/eslint.config.mjs +59 -0
- package/.next/standalone/sdk/typescript/examples/research-agent.ts +197 -0
- package/.next/standalone/sdk/typescript/integration/ai.test.ts +920 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/agent.ts +337 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/package-lock.json +261 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/package.json +16 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/surfaces.ts +605 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/tsconfig.json +12 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/tsconfig.surfaces.json +4 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/agent.ts +342 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/package-lock.json +156 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/package.json +16 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/surfaces.ts +628 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/tsconfig.json +12 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/tsconfig.surfaces.json +4 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-6/agent.ts +346 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-6/package-lock.json +156 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-6/package.json +13 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-6/surfaces.ts +651 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-6/tsconfig.json +12 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-6/tsconfig.surfaces.json +4 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-7/agent.ts +350 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-7/package-lock.json +153 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-7/package.json +13 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-7/surfaces.ts +651 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-7/tsconfig.json +12 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-7/tsconfig.surfaces.json +4 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/agent.ts +623 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/package-lock.json +344 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/package.json +19 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/tsconfig.json +12 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-1/agent-v1.ts +99 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-1/agent.ts +623 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-1/package-lock.json +336 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-1/package.json +15 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-1/tsconfig.json +12 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/agent.ts +96 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/package-lock.json +441 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/package.json +19 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/tsconfig.json +12 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/vendor/lc-weather-provider/index.cjs +53 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/vendor/lc-weather-provider/index.mjs +55 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/vendor/lc-weather-provider/package.json +18 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.11/agent.ts +659 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.11/package-lock.json +635 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.11/package.json +15 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.11/tsconfig.json +12 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.12/agent.ts +659 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.12/package-lock.json +553 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.12/package.json +15 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.12/tsconfig.json +12 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/agent.ts +877 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/mcp-server.mjs +66 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/package-lock.json +6797 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/package.json +24 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/tsconfig.json +12 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/agent.ts +872 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/mcp-server.mjs +66 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/package-lock.json +2540 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/package.json +15 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/tsconfig.json +12 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/app/actions.ts +18 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/app/api/ai/route.ts +42 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/app/api/edge/route.ts +29 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/app/api/langgraph/route.ts +21 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/app/api/llamaindex/route.ts +11 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/app/api/mastra/route.ts +19 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/app/api/status/route.ts +7 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/app/layout.tsx +9 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/app/page.tsx +15 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/instrumentation.ts +15 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/lib/ai.ts +61 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/lib/langgraph.ts +68 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/lib/llamaindex.ts +98 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/lib/mastra.ts +90 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/next.config.ts +52 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package-lock.json +4343 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package.json +28 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/runtimes/agent.ts +51 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/runtimes/deno-npm.ts +76 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/runtimes/package-lock.json +484 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/runtimes/package.json +15 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/types/agent.ts +79 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/types/package-lock.json +740 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/types/package.json +11 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/vanilla/agent.ts +197 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/vanilla/package-lock.json +70 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/vanilla/package.json +12 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/vanilla/tsconfig.json +12 -0
- package/.next/standalone/sdk/typescript/integration/global-setup.ts +29 -0
- package/.next/standalone/sdk/typescript/integration/harness.ts +451 -0
- package/.next/standalone/sdk/typescript/integration/langchain.test.ts +682 -0
- package/.next/standalone/sdk/typescript/integration/llamaindex.test.ts +709 -0
- package/.next/standalone/sdk/typescript/integration/mastra-coverage.test.ts +386 -0
- package/.next/standalone/sdk/typescript/integration/mastra.test.ts +311 -0
- package/.next/standalone/sdk/typescript/integration/nextjs.test.ts +341 -0
- package/.next/standalone/sdk/typescript/integration/runtime-parity.ts +180 -0
- package/.next/standalone/sdk/typescript/integration/runtimes.bun.test.ts +15 -0
- package/.next/standalone/sdk/typescript/integration/runtimes.core.test.ts +113 -0
- package/.next/standalone/sdk/typescript/integration/runtimes.deno.test.ts +19 -0
- package/.next/standalone/sdk/typescript/integration/types.test.ts +141 -0
- package/.next/standalone/sdk/typescript/integration/vanilla.test.ts +255 -0
- package/.next/standalone/sdk/typescript/package-lock.json +2640 -0
- package/.next/standalone/sdk/typescript/package.json +401 -0
- package/.next/standalone/sdk/typescript/scripts/finalize-build.mjs +123 -0
- package/.next/standalone/sdk/typescript/scripts/release.mjs +177 -0
- package/.next/standalone/sdk/typescript/src/clock.ts +58 -0
- package/.next/standalone/sdk/typescript/src/context.ts +214 -0
- package/.next/standalone/sdk/typescript/src/edge/adapter.ts +18 -0
- package/.next/standalone/sdk/typescript/src/edge/ai.ts +116 -0
- package/.next/standalone/sdk/typescript/src/edge/index.ts +238 -0
- package/.next/standalone/sdk/typescript/src/edge/langchain.ts +18 -0
- package/.next/standalone/sdk/typescript/src/edge/llamaindex.ts +12 -0
- package/.next/standalone/sdk/typescript/src/edge/mastra.ts +17 -0
- package/.next/standalone/sdk/typescript/src/edge/notice.ts +33 -0
- package/.next/standalone/sdk/typescript/src/environment.ts +75 -0
- package/.next/standalone/sdk/typescript/src/evaluator/authoring.ts +480 -0
- package/.next/standalone/sdk/typescript/src/evaluator/cli.ts +96 -0
- package/.next/standalone/sdk/typescript/src/evaluator/client.ts +421 -0
- package/.next/standalone/sdk/typescript/src/evaluator/expression.ts +1292 -0
- package/.next/standalone/sdk/typescript/src/evaluator/index.ts +144 -0
- package/.next/standalone/sdk/typescript/src/evaluator/protocol.ts +747 -0
- package/.next/standalone/sdk/typescript/src/evaluator/runtime.ts +930 -0
- package/.next/standalone/sdk/typescript/src/evaluator/sandbox-worker.ts +171 -0
- package/.next/standalone/sdk/typescript/src/evaluator/source-limits.ts +27 -0
- package/.next/standalone/sdk/typescript/src/evaluator/source.ts +509 -0
- package/.next/standalone/sdk/typescript/src/events.ts +879 -0
- package/.next/standalone/sdk/typescript/src/exit.ts +117 -0
- package/.next/standalone/sdk/typescript/src/index.ts +186 -0
- package/.next/standalone/sdk/typescript/src/integrations/ai.ts +1566 -0
- package/.next/standalone/sdk/typescript/src/integrations/compat.ts +322 -0
- package/.next/standalone/sdk/typescript/src/integrations/core.ts +1321 -0
- package/.next/standalone/sdk/typescript/src/integrations/index.ts +355 -0
- package/.next/standalone/sdk/typescript/src/integrations/langchain.ts +2340 -0
- package/.next/standalone/sdk/typescript/src/integrations/llamaindex.ts +2111 -0
- package/.next/standalone/sdk/typescript/src/integrations/mastra.ts +1802 -0
- package/.next/standalone/sdk/typescript/src/logger.ts +98 -0
- package/.next/standalone/sdk/typescript/src/next.ts +115 -0
- package/.next/standalone/sdk/typescript/src/node-require.ts +446 -0
- package/.next/standalone/sdk/typescript/src/redact.ts +305 -0
- package/.next/standalone/sdk/typescript/src/resolver.ts +120 -0
- package/.next/standalone/sdk/typescript/src/runtime.ts +29 -0
- package/.next/standalone/sdk/typescript/src/schema.ts +410 -0
- package/.next/standalone/sdk/typescript/src/scopes.ts +701 -0
- package/.next/standalone/sdk/typescript/src/shared.ts +33 -0
- package/.next/standalone/sdk/typescript/src/version.ts +5 -0
- package/.next/standalone/sdk/typescript/src/writer.ts +934 -0
- package/.next/standalone/sdk/typescript/test/adapters.test.ts +397 -0
- package/.next/standalone/sdk/typescript/test/ai.test.ts +1076 -0
- package/.next/standalone/sdk/typescript/test/copies.test.ts +204 -0
- package/.next/standalone/sdk/typescript/test/edge.test.ts +183 -0
- package/.next/standalone/sdk/typescript/test/evaluator-client.test.ts +234 -0
- package/.next/standalone/sdk/typescript/test/evaluator-protocol.test.ts +225 -0
- package/.next/standalone/sdk/typescript/test/events.test.ts +193 -0
- package/.next/standalone/sdk/typescript/test/expression.test.ts +181 -0
- package/.next/standalone/sdk/typescript/test/global-setup.ts +26 -0
- package/.next/standalone/sdk/typescript/test/helpers.ts +130 -0
- package/.next/standalone/sdk/typescript/test/integrations.test.ts +369 -0
- package/.next/standalone/sdk/typescript/test/langchain-copies.test.ts +204 -0
- package/.next/standalone/sdk/typescript/test/langchain.test.ts +999 -0
- package/.next/standalone/sdk/typescript/test/llamaindex.test.ts +1760 -0
- package/.next/standalone/sdk/typescript/test/mastra-coverage.test.ts +501 -0
- package/.next/standalone/sdk/typescript/test/mastra-lifecycle.test.ts +479 -0
- package/.next/standalone/sdk/typescript/test/mastra.test.ts +285 -0
- package/.next/standalone/sdk/typescript/test/next.test.ts +109 -0
- package/.next/standalone/sdk/typescript/test/packaging.test.ts +312 -0
- package/.next/standalone/sdk/typescript/test/redaction.test.ts +171 -0
- package/.next/standalone/sdk/typescript/test/runtimes.test.ts +101 -0
- package/.next/standalone/sdk/typescript/test/sandbox.test.ts +189 -0
- package/.next/standalone/sdk/typescript/test/scopes.test.ts +271 -0
- package/.next/standalone/sdk/typescript/test/setup.ts +19 -0
- package/.next/standalone/sdk/typescript/test/skill-snippets.test.ts +73 -0
- package/.next/standalone/sdk/typescript/test/spool-contract.test.ts +124 -0
- package/.next/standalone/sdk/typescript/test/tracker-bounds.test.ts +191 -0
- package/.next/standalone/sdk/typescript/test/wire-format.test.ts +214 -0
- package/.next/standalone/sdk/typescript/test/writer.test.ts +407 -0
- package/.next/standalone/sdk/typescript/tsconfig.build.json +15 -0
- package/.next/standalone/sdk/typescript/tsconfig.cjs.json +19 -0
- package/.next/standalone/sdk/typescript/tsconfig.json +28 -0
- package/.next/standalone/sdk/typescript/vitest.config.ts +33 -0
- package/.next/standalone/sdk/typescript/vitest.integration.config.ts +23 -0
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +2 -115
- package/dist/cli.mjs +6538 -13686
- package/dist/index.js +1 -19
- package/dist/worker.mjs +2085 -8012
- package/package.json +9 -9
- package/pi-extension/index.ts +0 -11
- package/scripts/build-policy-pack.mjs +2 -39
- package/src/hooks/builtin-policies.ts +3 -21
- package/src/hooks/cloud-enrollment-cli.ts +1 -1
- package/src/hooks/cloud-managed-policies.ts +0 -22
- package/src/hooks/custom-hooks-loader.ts +7 -45
- package/src/hooks/custom-hooks-registry.ts +1 -45
- package/src/hooks/first-run-gate.ts +0 -5
- package/src/hooks/fp-home.ts +0 -23
- package/src/hooks/handler.ts +6 -265
- package/src/hooks/hook-activity-store.ts +1 -105
- package/src/hooks/hook-telemetry.ts +0 -41
- package/src/hooks/loader-utils.ts +0 -6
- package/src/hooks/pack-cli.ts +17 -278
- package/src/hooks/pack-manifest.ts +7 -479
- package/src/hooks/pack-store.ts +10 -156
- package/src/hooks/policy-catalog.ts +0 -65
- package/src/hooks/policy-evaluator.ts +796 -940
- package/src/hooks/policy-registry.ts +0 -25
- package/src/hooks/policy-types.ts +0 -126
- package/src/hooks/worker-server.ts +26 -119
- package/src/index.ts +0 -6
- package/.next/standalone/.next/server/chunks/src_hooks_01frwmb._.js +0 -5
- package/.next/standalone/.next/server/chunks/src_hooks_18qtd42._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01bmjsj._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04usis8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__056wjo4._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__059yza8._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0eip4_k._.js +0 -22
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n0xg95._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qcb0mg._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qxnccm._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rwtwpm._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s_yomn._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0soxz2z._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0yrsbd_._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11mayhe._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__13d-wb6._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1dinjii._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pprgri._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1q4p5b8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qiz0e4._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_042cgl1._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0uyu3jf._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1feuvhb._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9sni._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h4._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_0w6mzq5._.js +0 -151
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_095a_79._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_15t8kqj._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_18k8rl0._.js +0 -12
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1fm2w5z._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1j0zy3v._.js +0 -3
- package/.next/standalone/.next/static/chunks/0cd-_8-c-m1ea.js +0 -6
- package/.next/standalone/.next/static/chunks/0uldbut9y2-e8.js +0 -1
- package/.next/standalone/.next/static/chunks/1u5zsejmgrir_.js +0 -1
- package/.next/standalone/.next/static/chunks/285spx855h_3r.css +0 -2
- package/.next/standalone/.next/static/chunks/2qv4hshejedtx.css +0 -1
- package/.next/standalone/.next/static/chunks/2vkvu9-opa_1z.js +0 -1
- package/.next/standalone/.next/static/chunks/37lhv7wa3ywt6.js +0 -1
- package/.next/standalone/app/actions/get-jev-config.ts +0 -409
- package/.next/standalone/app/actions/update-jev-config.ts +0 -420
- package/.next/standalone/app/components/jev-notices.tsx +0 -96
- package/.next/standalone/app/settings/jev-panel.tsx +0 -469
- package/src/hooks/effective-reviewers.ts +0 -79
- package/src/hooks/jev-activity.ts +0 -385
- package/src/hooks/jev-cli.ts +0 -1193
- package/src/hooks/policy-authority.ts +0 -333
- package/src/hooks/policy-reviewability.ts +0 -229
- package/src/hooks/semantic/combine.ts +0 -541
- package/src/hooks/semantic/compile.ts +0 -176
- package/src/hooks/semantic/decide.ts +0 -392
- package/src/hooks/semantic/envelope.ts +0 -1296
- package/src/hooks/semantic/evaluator.ts +0 -547
- package/src/hooks/semantic/facts.ts +0 -292
- package/src/hooks/semantic/intent.ts +0 -1190
- package/src/hooks/semantic/jev-client.ts +0 -643
- package/src/hooks/semantic/jev-config.ts +0 -594
- package/src/hooks/semantic/jev-review.ts +0 -374
- package/src/hooks/semantic/jev-stats.ts +0 -289
- package/src/hooks/semantic/jev-throttle.ts +0 -421
- package/src/hooks/semantic/pack-policies.ts +0 -251
- package/src/hooks/semantic/policies.ts +0 -596
- package/src/hooks/semantic/precondition-names.ts +0 -60
- package/src/hooks/semantic/preconditions.ts +0 -58
- package/src/hooks/semantic/redact.ts +0 -2910
- package/src/hooks/semantic/types.ts +0 -145
- package/src/hooks/semver-precedence.ts +0 -128
- /package/.next/standalone/.next/static/{T8YAYM9h_64fKv705vug7 → PgeWCHmyVbjRznv2VO7KF}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{T8YAYM9h_64fKv705vug7 → PgeWCHmyVbjRznv2VO7KF}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{T8YAYM9h_64fKv705vug7 → PgeWCHmyVbjRznv2VO7KF}/_ssgManifest.js +0 -0
|
@@ -1,594 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The customer's own Jev configuration (BYOK) — the single opt-in to the
|
|
3
|
-
* two-tier evaluator.
|
|
4
|
-
*
|
|
5
|
-
* `loadJevConfig()` returning null means Jev is off and the regex engine runs
|
|
6
|
-
* exactly as it does without this module.
|
|
7
|
-
*
|
|
8
|
-
* # Global only
|
|
9
|
-
*
|
|
10
|
-
* The file is `~/.failproofai/jev.json` and nothing else. A repository's config
|
|
11
|
-
* can never set or override it, because a cloned repo choosing the endpoint
|
|
12
|
-
* that judges its own tool calls would be choosing its own verdict. So:
|
|
13
|
-
*
|
|
14
|
-
* - no project-scope file is ever read (`<cwd>/.failproofai/jev.json` is inert);
|
|
15
|
-
* - `provider`, `baseUrl`, `model` and `accountId` come from the file at
|
|
16
|
-
* `failproofaiHome()/jev.json` only, never from the environment — a repo's
|
|
17
|
-
* `.claude/settings.json` can set env vars for a session. (`FAILPROOFAI_HOME`
|
|
18
|
-
* is not an exception to that: it relocates the ENTIRE failproofai layout,
|
|
19
|
-
* policies and all, rather than redirecting Jev in particular, and a layout
|
|
20
|
-
* pointed somewhere new has no policies to evaluate and no 0600 jev.json a
|
|
21
|
-
* checkout could have produced.)
|
|
22
|
-
* - `FAILPROOFAI_JEV_API_KEY` may supply the KEY, and only when the file has
|
|
23
|
-
* none. It exists for single-session use and for people who keep keys off
|
|
24
|
-
* disk. It can never switch Jev on by itself (no file → null), never replace
|
|
25
|
-
* a key the file carries, and never change where requests go. Note the daemon
|
|
26
|
-
* forwards a hook's stdin and cwd, not its environment, so on a
|
|
27
|
-
* daemon-configured machine the file is the only channel that works.
|
|
28
|
-
*
|
|
29
|
-
* # Owner-only
|
|
30
|
-
*
|
|
31
|
-
* The file holds a credential, so it is written at 0600 and a copy that is
|
|
32
|
-
* group- or world-accessible is REFUSED rather than read: returning null there
|
|
33
|
-
* falls back to the regex engine, which is the safe direction (see §4 of the
|
|
34
|
-
* design — without a config the path is today's, byte for byte). Any group or
|
|
35
|
-
* other bit counts, not just read: a group-writable file lets someone else
|
|
36
|
-
* choose the endpoint.
|
|
37
|
-
*
|
|
38
|
-
* Its DIRECTORY is checked too, for write bits only. A directory another user
|
|
39
|
-
* can write into gives them the file's power by another route — unlink it and
|
|
40
|
-
* create their own 0600 one, which every check above would then read as the
|
|
41
|
-
* owner's. `jev setup` takes those bits off, so a home that some older code
|
|
42
|
-
* path created at the umask is fixed the first time Jev is configured. Read
|
|
43
|
-
* bits are left alone at both ends: they give nobody that power, and
|
|
44
|
-
* `config.json` beside it is world-readable by design.
|
|
45
|
-
*
|
|
46
|
-
* # Read on every hook event, on purpose
|
|
47
|
-
*
|
|
48
|
-
* Deliberately uncached: one open + fstat + read of a file capped at 64 KiB is
|
|
49
|
-
* microseconds, and it means `failproofai jev setup` / `remove` take effect on
|
|
50
|
-
* the very next tool call — including inside the long-lived daemon worker —
|
|
51
|
-
* with no restart and no stale state to reason about.
|
|
52
|
-
*/
|
|
53
|
-
import { closeSync, constants as fsConstants, fstatSync, openSync, readSync, statSync } from "node:fs";
|
|
54
|
-
import { dirname } from "node:path";
|
|
55
|
-
import { jevConfigFile } from "../fp-home";
|
|
56
|
-
|
|
57
|
-
export type JevProviderKind = "typesafe" | "openrouter" | "vercel" | "cloudflare" | "custom";
|
|
58
|
-
|
|
59
|
-
export interface JevConfig {
|
|
60
|
-
provider: JevProviderKind;
|
|
61
|
-
apiKey: string;
|
|
62
|
-
/** Required for `custom`; an optional override otherwise. */
|
|
63
|
-
baseUrl?: string;
|
|
64
|
-
/** `cloudflare` only. */
|
|
65
|
-
accountId?: string;
|
|
66
|
-
/** Provider-specific model id; each provider has a default. */
|
|
67
|
-
model?: string;
|
|
68
|
-
/** Default 3000; the reasoning is on `DEFAULT_JEV_TIMEOUT_MS` in `evaluator.ts`. */
|
|
69
|
-
timeoutMs?: number;
|
|
70
|
-
/** `shadow` logs Jev and enforces regex; `enforce` applies the combine rules. Default `enforce`. */
|
|
71
|
-
mode?: "shadow" | "enforce";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export const DEFAULT_JEV_MODE: NonNullable<JevConfig["mode"]> = "enforce";
|
|
75
|
-
|
|
76
|
-
export const JEV_PROVIDER_KINDS: readonly JevProviderKind[] = ["typesafe", "openrouter", "vercel", "cloudflare", "custom"];
|
|
77
|
-
|
|
78
|
-
/** The env var that may supply the key (and nothing else) when the file carries none. */
|
|
79
|
-
export const JEV_API_KEY_ENV = "FAILPROOFAI_JEV_API_KEY";
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* The same 3000 ms as `DEFAULT_JEV_TIMEOUT_MS` in `evaluator.ts`, which is
|
|
83
|
-
* where the measurements and the tradeoff behind the number are written out.
|
|
84
|
-
* In short: pooled p95 over 1,449 answered calls is 1692 ms and the tail runs
|
|
85
|
-
* to 3624 ms, so 1500 ms aborted 8.4% of answers (43% of cold-process calls in
|
|
86
|
-
* the worst session) and silently fell back to regex; 3000 ms aborts 0.28%.
|
|
87
|
-
* Kept separate from the evaluator's copy so the config layer does not import
|
|
88
|
-
* the evaluator; the two are pinned equal by a test.
|
|
89
|
-
*/
|
|
90
|
-
export const JEV_CONFIG_DEFAULT_TIMEOUT_MS = 3_000;
|
|
91
|
-
/** Bounds on `timeoutMs`. Every millisecond of it can be added to a tool call. */
|
|
92
|
-
export const MIN_JEV_TIMEOUT_MS = 100;
|
|
93
|
-
export const MAX_JEV_TIMEOUT_MS = 10_000;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* The Jev family the decision thresholds were calibrated against. A reported
|
|
97
|
-
* version with a different major.minor is treated as degraded (the caller falls
|
|
98
|
-
* back to regex), and a configured model id naming another family is refused
|
|
99
|
-
* outright: every call it made would fall back anyway.
|
|
100
|
-
*/
|
|
101
|
-
export const JEV_CALIBRATED_FAMILY = { major: 1, minor: 13 } as const;
|
|
102
|
-
|
|
103
|
-
/** No legitimate config is anywhere near this; the hook path never reads more. */
|
|
104
|
-
const MAX_CONFIG_BYTES = 64 * 1024;
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Non-blocking open: a FIFO in the file's place would otherwise hang every
|
|
108
|
-
* hook in `open()` until something wrote to it. For a regular file the flag
|
|
109
|
-
* changes nothing, and the fstat below refuses anything that is not one.
|
|
110
|
-
*/
|
|
111
|
-
const OPEN_FLAGS = fsConstants.O_RDONLY | (fsConstants.O_NONBLOCK ?? 0);
|
|
112
|
-
|
|
113
|
-
export function jevConfigPath(): string {
|
|
114
|
-
return jevConfigFile();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// ── Model ids and versions ───────────────────────────────────────────────────
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* `jev-1.13.0`, `jev-1.13`, `typesafe/jev-1.13`, `typesafe/jev-1.13-20260917`,
|
|
121
|
-
* `typesafe-ai/jev-1.13.0`. Aliases (`typesafe/jev`, `typesafe-ai/jev`,
|
|
122
|
-
* `~typesafe/jev-latest`) carry no version and do not match.
|
|
123
|
-
*/
|
|
124
|
-
const VERSIONED_MODEL_RE = /^(?:(?:typesafe|typesafe-ai)\/)?jev-(\d{1,4})\.(\d{1,4})(?:\.(\d{1,6}))?(?:-(\d{8}))?$/;
|
|
125
|
-
|
|
126
|
-
export interface JevModelVersion {
|
|
127
|
-
major: number;
|
|
128
|
-
minor: number;
|
|
129
|
-
patch: number | null;
|
|
130
|
-
/** OpenRouter's snapshot date, `YYYYMMDD`. */
|
|
131
|
-
date: string | null;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** The Jev version a model id names, or null for an alias / anything else. */
|
|
135
|
-
export function jevModelVersion(model: string): JevModelVersion | null {
|
|
136
|
-
const m = VERSIONED_MODEL_RE.exec(model);
|
|
137
|
-
if (!m) return null;
|
|
138
|
-
return { major: Number(m[1]), minor: Number(m[2]), patch: m[3] === undefined ? null : Number(m[3]), date: m[4] ?? null };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** True when the id names the calibrated family (any patch, any snapshot date). */
|
|
142
|
-
export function isCalibratedJevModel(model: string): boolean {
|
|
143
|
-
const v = jevModelVersion(model);
|
|
144
|
-
return v !== null && v.major === JEV_CALIBRATED_FAMILY.major && v.minor === JEV_CALIBRATED_FAMILY.minor;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ── Validation ───────────────────────────────────────────────────────────────
|
|
148
|
-
|
|
149
|
-
/** A Cloudflare account id. It is interpolated into a URL path, so anything else is refused. */
|
|
150
|
-
export const CLOUDFLARE_ACCOUNT_ID_RE = /^[0-9a-f]{32}$/;
|
|
151
|
-
/** Visible ASCII only: a key goes into an HTTP header, so whitespace or a CR/LF is refused, not trimmed into shape. */
|
|
152
|
-
const API_KEY_RE = /^[\x21-\x7e]{1,4096}$/;
|
|
153
|
-
const MODEL_RE = /^[A-Za-z0-9._:/@~+-]{1,200}$/;
|
|
154
|
-
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
155
|
-
|
|
156
|
-
export type ValidationResult<T> =
|
|
157
|
-
| { ok: true; value: T }
|
|
158
|
-
/**
|
|
159
|
-
* `missingKey` marks the one failure that is not a fault in the file: it
|
|
160
|
-
* carries no `apiKey` and `FAILPROOFAI_JEV_API_KEY` is unset here. Callers
|
|
161
|
-
* that can tell those apart (`inspectJevConfig`, and `jev status` through it)
|
|
162
|
-
* use the flag rather than matching on `problem`.
|
|
163
|
-
*/
|
|
164
|
-
| { ok: false; problem: string; missingKey?: true };
|
|
165
|
-
|
|
166
|
-
/** A key is visible ASCII, one line, at most 4 KiB. The message never includes the key. */
|
|
167
|
-
export function validateApiKey(key: unknown): string | null {
|
|
168
|
-
if (typeof key !== "string" || key.length === 0) return "the API key is empty";
|
|
169
|
-
if (!API_KEY_RE.test(key)) return "the API key must be one line of visible ASCII with no spaces (at most 4096 characters)";
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* An endpoint base URL: https, or http to a loopback host only (a local proxy).
|
|
175
|
-
* No credentials in the URL and no fragment — a key belongs in the key field,
|
|
176
|
-
* where it is sent as a bearer and never printed. `validateJevConfig` further
|
|
177
|
-
* accepts the loopback http form only in shadow mode.
|
|
178
|
-
*/
|
|
179
|
-
export function validateBaseUrl(raw: unknown): ValidationResult<string> {
|
|
180
|
-
if (typeof raw !== "string" || raw.trim() === "") return { ok: false, problem: "baseUrl must be a non-empty string" };
|
|
181
|
-
let url: URL;
|
|
182
|
-
try {
|
|
183
|
-
url = new URL(raw.trim());
|
|
184
|
-
} catch {
|
|
185
|
-
return { ok: false, problem: "baseUrl is not a valid URL" };
|
|
186
|
-
}
|
|
187
|
-
if (url.protocol !== "https:" && !(url.protocol === "http:" && LOOPBACK_HOSTS.has(url.hostname))) {
|
|
188
|
-
return { ok: false, problem: "baseUrl must use https (plain http is accepted only for localhost)" };
|
|
189
|
-
}
|
|
190
|
-
if (url.username || url.password) return { ok: false, problem: "baseUrl must not carry credentials; put the key in the key field" };
|
|
191
|
-
if (url.hash) return { ok: false, problem: "baseUrl must not have a #fragment" };
|
|
192
|
-
// Trailing slashes come off the PATH, never the string: a query string is legal
|
|
193
|
-
// (some proxies want `?api-version=`), and the endpoint path is appended to
|
|
194
|
-
// the pathname, so it must never end up inside the query.
|
|
195
|
-
url.pathname = url.pathname.replace(/\/+$/, "") || "/";
|
|
196
|
-
return { ok: true, value: url.toString() };
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function isPlainHttp(url: string): boolean {
|
|
200
|
-
try {
|
|
201
|
-
return new URL(url).protocol === "http:";
|
|
202
|
-
} catch {
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Prefixes real credentials carry: OpenAI / OpenRouter / Anthropic / Stripe
|
|
209
|
-
* (`sk-`, `sk_`, `rk_`), GitHub, GitLab, Slack, AWS, Google, Hugging Face and
|
|
210
|
-
* Vercel AI Gateway (`vck_`). No Jev model id starts with any of them.
|
|
211
|
-
*/
|
|
212
|
-
const CREDENTIAL_PREFIX_RE = /^(?:(?:sk|rk)[-_]|gh[pousr]_|github_pat_|glpat-|xox[abprs]-|AKIA[0-9A-Z]{12}|AIza[0-9A-Za-z_-]{20}|hf_[A-Za-z0-9]{16}|vck_)/;
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Whether a would-be model id is shaped like a credential: a known key prefix,
|
|
216
|
-
* or 32+ characters mixing letters and digits with no `/` and no "jev" — which
|
|
217
|
-
* describes a pasted token, and no model id any Jev route knows. The check
|
|
218
|
-
* exists so a key pasted into `--model` is refused rather than written to the
|
|
219
|
-
* file, printed by `jev status`, and sent to the provider as the model.
|
|
220
|
-
*/
|
|
221
|
-
export function looksLikeCredential(s: string): boolean {
|
|
222
|
-
if (CREDENTIAL_PREFIX_RE.test(s)) return true;
|
|
223
|
-
return s.length >= 32 && !s.includes("/") && !/jev/i.test(s) && /[A-Za-z]/.test(s) && /[0-9]/.test(s);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function validateModel(raw: unknown): ValidationResult<string> {
|
|
227
|
-
if (typeof raw !== "string" || !MODEL_RE.test(raw)) {
|
|
228
|
-
return { ok: false, problem: "model must be 1–200 characters of letters, digits and . _ : / @ ~ + -" };
|
|
229
|
-
}
|
|
230
|
-
if (looksLikeCredential(raw)) {
|
|
231
|
-
// Never quoted: it may well be a key.
|
|
232
|
-
return {
|
|
233
|
-
ok: false,
|
|
234
|
-
problem: "model looks like an API key, not a model id (not repeated here). The key goes on stdin: failproofai jev setup --key-stdin",
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
const v = jevModelVersion(raw);
|
|
238
|
-
if (v && (v.major !== JEV_CALIBRATED_FAMILY.major || v.minor !== JEV_CALIBRATED_FAMILY.minor)) {
|
|
239
|
-
return {
|
|
240
|
-
ok: false,
|
|
241
|
-
problem:
|
|
242
|
-
`model ${raw} names Jev ${v.major}.${v.minor}, but the thresholds are calibrated for Jev ` +
|
|
243
|
-
`${JEV_CALIBRATED_FAMILY.major}.${JEV_CALIBRATED_FAMILY.minor} — every call would fall back to regex`,
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
return { ok: true, value: raw };
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Validate a parsed `jev.json` (or a config assembled by `jev setup`) into a
|
|
251
|
-
* normalized `JevConfig`: `mode` and `timeoutMs` filled with their defaults,
|
|
252
|
-
* `baseUrl` normalized, and only the fields this provider uses kept.
|
|
253
|
-
*
|
|
254
|
-
* `envKey` is the value of `FAILPROOFAI_JEV_API_KEY`, used only when the object
|
|
255
|
-
* has no `apiKey`. Unknown top-level keys are ignored so a newer failproofai's
|
|
256
|
-
* file does not switch Jev off on an older one. Problems never quote the key.
|
|
257
|
-
*/
|
|
258
|
-
export function validateJevConfig(raw: unknown, envKey?: string | null): ValidationResult<JevConfig> {
|
|
259
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { ok: false, problem: "the file must hold a JSON object" };
|
|
260
|
-
const o = raw as Record<string, unknown>;
|
|
261
|
-
|
|
262
|
-
const provider = o.provider;
|
|
263
|
-
if (typeof provider !== "string" || !(JEV_PROVIDER_KINDS as readonly string[]).includes(provider)) {
|
|
264
|
-
return { ok: false, problem: `provider must be one of ${JEV_PROVIDER_KINDS.join(", ")}` };
|
|
265
|
-
}
|
|
266
|
-
const kind = provider as JevProviderKind;
|
|
267
|
-
|
|
268
|
-
let apiKey: string;
|
|
269
|
-
if (o.apiKey !== undefined) {
|
|
270
|
-
const bad = validateApiKey(o.apiKey);
|
|
271
|
-
if (bad) return { ok: false, problem: bad };
|
|
272
|
-
apiKey = o.apiKey as string;
|
|
273
|
-
} else if (envKey !== undefined && envKey !== null && envKey !== "") {
|
|
274
|
-
const bad = validateApiKey(envKey);
|
|
275
|
-
if (bad) return { ok: false, problem: `${JEV_API_KEY_ENV} is set but invalid: ${bad}` };
|
|
276
|
-
apiKey = envKey;
|
|
277
|
-
} else {
|
|
278
|
-
return {
|
|
279
|
-
ok: false,
|
|
280
|
-
problem: `no API key: set apiKey in the file (failproofai jev setup), or ${JEV_API_KEY_ENV} for this session`,
|
|
281
|
-
missingKey: true,
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const cfg: JevConfig = { provider: kind, apiKey, mode: DEFAULT_JEV_MODE, timeoutMs: JEV_CONFIG_DEFAULT_TIMEOUT_MS };
|
|
286
|
-
|
|
287
|
-
if (o.baseUrl !== undefined) {
|
|
288
|
-
const r = validateBaseUrl(o.baseUrl);
|
|
289
|
-
if (!r.ok) return r;
|
|
290
|
-
cfg.baseUrl = r.value;
|
|
291
|
-
} else if (kind === "custom") {
|
|
292
|
-
return { ok: false, problem: "provider custom needs a baseUrl" };
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (kind === "cloudflare") {
|
|
296
|
-
if (typeof o.accountId !== "string" || !CLOUDFLARE_ACCOUNT_ID_RE.test(o.accountId)) {
|
|
297
|
-
// It is interpolated into a URL path, so it is refused rather than escaped.
|
|
298
|
-
return { ok: false, problem: "provider cloudflare needs accountId: 32 lowercase hex characters" };
|
|
299
|
-
}
|
|
300
|
-
cfg.accountId = o.accountId;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (o.model !== undefined) {
|
|
304
|
-
const r = validateModel(o.model);
|
|
305
|
-
if (!r.ok) return r;
|
|
306
|
-
// The key pasted twice — once on stdin, once as --model — whatever its shape.
|
|
307
|
-
if (r.value === apiKey) return { ok: false, problem: "model is the API key (not repeated here); give the model id, or leave --model out for the provider's default" };
|
|
308
|
-
cfg.model = r.value;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
if (o.timeoutMs !== undefined) {
|
|
312
|
-
const t = o.timeoutMs;
|
|
313
|
-
if (typeof t !== "number" || !Number.isInteger(t) || t < MIN_JEV_TIMEOUT_MS || t > MAX_JEV_TIMEOUT_MS) {
|
|
314
|
-
return { ok: false, problem: `timeoutMs must be a whole number of milliseconds from ${MIN_JEV_TIMEOUT_MS} to ${MAX_JEV_TIMEOUT_MS}` };
|
|
315
|
-
}
|
|
316
|
-
cfg.timeoutMs = t;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
if (o.mode !== undefined) {
|
|
320
|
-
if (o.mode !== "shadow" && o.mode !== "enforce") return { ok: false, problem: 'mode must be "shadow" or "enforce"' };
|
|
321
|
-
cfg.mode = o.mode;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Plain http reaches only a loopback host (`validateBaseUrl`), and nothing
|
|
325
|
-
// authenticates the server there: while the local proxy is down, any process
|
|
326
|
-
// of this user — the agent being judged included — can bind its port and
|
|
327
|
-
// answer "none" to every question. In enforce mode that answer clears
|
|
328
|
-
// reviewable denies; in shadow mode it changes nothing, so that is the only
|
|
329
|
-
// mode it is accepted in.
|
|
330
|
-
if (cfg.baseUrl !== undefined && isPlainHttp(cfg.baseUrl) && cfg.mode !== "shadow") {
|
|
331
|
-
return {
|
|
332
|
-
ok: false,
|
|
333
|
-
problem:
|
|
334
|
-
"plain http (to localhost) is accepted only with mode shadow: in enforce mode Jev's answers can clear a deny, " +
|
|
335
|
-
"and while the local proxy is down any process on this machine could take its port and answer. Use https, or mode shadow",
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
return { ok: true, value: cfg };
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// ── Loading ──────────────────────────────────────────────────────────────────
|
|
343
|
-
|
|
344
|
-
export type JevConfigInspection =
|
|
345
|
-
| { status: "absent"; path: string }
|
|
346
|
-
| { status: "ok"; path: string; mode: number | null; keySource: "file" | "env"; config: JevConfig }
|
|
347
|
-
| {
|
|
348
|
-
/**
|
|
349
|
-
* The file is sound and names `FAILPROOFAI_JEV_API_KEY` as the key's
|
|
350
|
-
* source (`jev setup --key-from-env` writes exactly this), but the
|
|
351
|
-
* variable is not set in THIS process. Jev is off here — `loadJevConfig`
|
|
352
|
-
* returns null, as for every status but `ok` — and on wherever the
|
|
353
|
-
* variable is set. Kept apart from `refused` because nothing is wrong
|
|
354
|
-
* with the file: telling the owner to write a valid one would have them
|
|
355
|
-
* overwrite the config they chose, and a provisioning check reading
|
|
356
|
-
* `status --json` should see a configured machine, not a broken one.
|
|
357
|
-
*/
|
|
358
|
-
status: "key-missing";
|
|
359
|
-
path: string;
|
|
360
|
-
mode: number | null;
|
|
361
|
-
/** Everything the file says except the key, so `jev status` can show the route. */
|
|
362
|
-
routing: Omit<JevConfig, "apiKey">;
|
|
363
|
-
problem: string;
|
|
364
|
-
}
|
|
365
|
-
| {
|
|
366
|
-
status: "refused";
|
|
367
|
-
path: string;
|
|
368
|
-
/** The file's permission bits, when they are the reason. */
|
|
369
|
-
mode: number | null;
|
|
370
|
-
reason: "too-open" | "unreadable" | "too-large" | "not-json" | "invalid";
|
|
371
|
-
problem: string;
|
|
372
|
-
/** The command that fixes it, when one does. */
|
|
373
|
-
fix?: string;
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
function readEnvKey(): string | null {
|
|
377
|
-
const v = process.env[JEV_API_KEY_ENV];
|
|
378
|
-
return v === undefined || v === "" ? null : v;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* A placeholder key, used only to answer "is this file sound apart from the
|
|
383
|
-
* key?" when the variable that would supply it is unset in this process.
|
|
384
|
-
* Validation stops at the missing key, so the fields after it — baseUrl,
|
|
385
|
-
* accountId, model, timeoutMs, mode — are otherwise never checked, and a file
|
|
386
|
-
* that is also malformed further down must still be reported as malformed. It
|
|
387
|
-
* is never written, never sent, and never returned: `inspectJevConfig` copies
|
|
388
|
-
* the routing fields out of the config it validates and drops the rest.
|
|
389
|
-
*/
|
|
390
|
-
const KEY_STAND_IN = "supplied-by-the-environment";
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* Group- or world-WRITE bits on the directory holding the config. Anyone who
|
|
394
|
-
* can write there can unlink the owner's file and leave their own 0600 one,
|
|
395
|
-
* which every check on the file itself then passes. Read bits are deliberately
|
|
396
|
-
* not included: a 0755 home is ordinary and gives nobody that power, while the
|
|
397
|
-
* file inside it is checked for read bits too.
|
|
398
|
-
*/
|
|
399
|
-
const DIR_WRITABLE_BY_OTHERS = 0o022;
|
|
400
|
-
|
|
401
|
-
/** The config directory's mode when others can write to it, else null. */
|
|
402
|
-
function looseConfigDirMode(path: string): number | null {
|
|
403
|
-
if (!modesAreMeaningful()) return null;
|
|
404
|
-
try {
|
|
405
|
-
const mode = statSync(dirname(path)).mode & 0o777;
|
|
406
|
-
return (mode & DIR_WRITABLE_BY_OTHERS) !== 0 ? mode : null;
|
|
407
|
-
} catch {
|
|
408
|
-
// No directory, or no permission to stat it: the file read reports that.
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/** Whether permission bits can be trusted to mean anything here. */
|
|
414
|
-
function modesAreMeaningful(): boolean {
|
|
415
|
-
// On Windows `stat().mode` is synthesized (0o666 for any writable file), so
|
|
416
|
-
// the check would refuse every file without protecting anything. Setup is
|
|
417
|
-
// refused on Windows (see `isDaemonSupportedPlatform`), so this is the
|
|
418
|
-
// in-process dev path only.
|
|
419
|
-
return process.platform !== "win32";
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* Everything `failproofai jev status` needs to say about the file, without the
|
|
424
|
-
* key. `loadJevConfig()` is this with the reasons thrown away.
|
|
425
|
-
*
|
|
426
|
-
* The file is opened once and every check runs against that descriptor, so the
|
|
427
|
-
* permissions checked are the permissions of the bytes read.
|
|
428
|
-
*/
|
|
429
|
-
export function inspectJevConfig(): JevConfigInspection {
|
|
430
|
-
const path = jevConfigPath();
|
|
431
|
-
let fd: number;
|
|
432
|
-
try {
|
|
433
|
-
fd = openSync(path, OPEN_FLAGS);
|
|
434
|
-
} catch (err) {
|
|
435
|
-
const code = (err as NodeJS.ErrnoException).code;
|
|
436
|
-
if (code === "ENOENT" || code === "ENOTDIR") return { status: "absent", path };
|
|
437
|
-
return { status: "refused", path, mode: null, reason: "unreadable", problem: `cannot open it (${code ?? "error"})` };
|
|
438
|
-
}
|
|
439
|
-
let text: string;
|
|
440
|
-
let mode: number | null = null;
|
|
441
|
-
try {
|
|
442
|
-
const st = fstatSync(fd);
|
|
443
|
-
if (!st.isFile()) return { status: "refused", path, mode: null, reason: "unreadable", problem: "it is not a regular file" };
|
|
444
|
-
mode = st.mode & 0o777;
|
|
445
|
-
if (modesAreMeaningful() && (mode & 0o077) !== 0) {
|
|
446
|
-
return {
|
|
447
|
-
status: "refused",
|
|
448
|
-
path,
|
|
449
|
-
mode,
|
|
450
|
-
reason: "too-open",
|
|
451
|
-
problem: `its permissions are ${mode.toString(8).padStart(4, "0")}; it holds a key, so it must be owner-only (chmod 600 ${path})`,
|
|
452
|
-
fix: `chmod 600 ${path}`,
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
const dirMode = looseConfigDirMode(path);
|
|
456
|
-
if (dirMode !== null) {
|
|
457
|
-
// 0600 on the file means nothing while another user can replace the file.
|
|
458
|
-
return {
|
|
459
|
-
status: "refused",
|
|
460
|
-
path,
|
|
461
|
-
mode,
|
|
462
|
-
reason: "too-open",
|
|
463
|
-
problem:
|
|
464
|
-
`its directory ${dirname(path)} is ${dirMode.toString(8).padStart(4, "0")} — other users can write there, ` +
|
|
465
|
-
`so they can put their own config in its place; it must be owner-only (chmod 700 ${dirname(path)})`,
|
|
466
|
-
fix: `chmod 700 ${dirname(path)}`,
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
if (st.size > MAX_CONFIG_BYTES) return { status: "refused", path, mode, reason: "too-large", problem: `it is larger than ${MAX_CONFIG_BYTES} bytes` };
|
|
470
|
-
const buf = Buffer.alloc(st.size);
|
|
471
|
-
let off = 0;
|
|
472
|
-
while (off < buf.length) {
|
|
473
|
-
const n = readSync(fd, buf, off, buf.length - off, off);
|
|
474
|
-
if (n === 0) break;
|
|
475
|
-
off += n;
|
|
476
|
-
}
|
|
477
|
-
text = buf.subarray(0, off).toString("utf8");
|
|
478
|
-
} catch (err) {
|
|
479
|
-
return { status: "refused", path, mode, reason: "unreadable", problem: `cannot read it (${(err as NodeJS.ErrnoException).code ?? "error"})` };
|
|
480
|
-
} finally {
|
|
481
|
-
try {
|
|
482
|
-
closeSync(fd);
|
|
483
|
-
} catch {
|
|
484
|
-
// Nothing useful to do.
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
let parsed: unknown;
|
|
489
|
-
try {
|
|
490
|
-
parsed = JSON.parse(text);
|
|
491
|
-
} catch {
|
|
492
|
-
return { status: "refused", path, mode, reason: "not-json", problem: "it is not valid JSON" };
|
|
493
|
-
}
|
|
494
|
-
const envKey = readEnvKey();
|
|
495
|
-
const r = validateJevConfig(parsed, envKey);
|
|
496
|
-
if (!r.ok) {
|
|
497
|
-
if (r.missingKey === true) {
|
|
498
|
-
// Nothing is wrong with the file unless the REST of it is wrong too, and
|
|
499
|
-
// validation never got that far, so ask again with a stand-in key.
|
|
500
|
-
const rest = validateJevConfig(parsed, KEY_STAND_IN);
|
|
501
|
-
if (rest.ok) {
|
|
502
|
-
const routing: Omit<JevConfig, "apiKey"> = { provider: rest.value.provider, mode: rest.value.mode, timeoutMs: rest.value.timeoutMs };
|
|
503
|
-
if (rest.value.baseUrl !== undefined) routing.baseUrl = rest.value.baseUrl;
|
|
504
|
-
if (rest.value.accountId !== undefined) routing.accountId = rest.value.accountId;
|
|
505
|
-
if (rest.value.model !== undefined) routing.model = rest.value.model;
|
|
506
|
-
return {
|
|
507
|
-
status: "key-missing",
|
|
508
|
-
path,
|
|
509
|
-
mode,
|
|
510
|
-
routing,
|
|
511
|
-
problem: `it carries no apiKey, so the key comes from ${JEV_API_KEY_ENV} — which is not set in this environment`,
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
return { status: "refused", path, mode, reason: "invalid", problem: rest.problem };
|
|
515
|
-
}
|
|
516
|
-
return { status: "refused", path, mode, reason: "invalid", problem: r.problem };
|
|
517
|
-
}
|
|
518
|
-
const keySource = (parsed as Record<string, unknown>).apiKey !== undefined ? "file" : "env";
|
|
519
|
-
return { status: "ok", path, mode, keySource, config: r.value };
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* The validated global config, or null — absent, refused, invalid, or a
|
|
524
|
-
* key-from-the-environment file in an environment that does not set it all
|
|
525
|
-
* mean Jev is off and the regex path runs unchanged. Never throws.
|
|
526
|
-
*/
|
|
527
|
-
export function loadJevConfig(): JevConfig | null {
|
|
528
|
-
try {
|
|
529
|
-
const r = inspectJevConfig();
|
|
530
|
-
return r.status === "ok" ? r.config : null;
|
|
531
|
-
} catch {
|
|
532
|
-
return null;
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
export interface JevConfigFileForUpdate {
|
|
537
|
-
raw: Record<string, unknown>;
|
|
538
|
-
/** The file's permission bits (null where they mean nothing). */
|
|
539
|
-
mode: number | null;
|
|
540
|
-
/**
|
|
541
|
-
* Group or other bits were set on the file, or write bits on its directory:
|
|
542
|
-
* the loader refuses this file, and someone other than its owner may have
|
|
543
|
-
* written it — so the endpoint it names is not trusted with the key it holds
|
|
544
|
-
* (see `jev setup`). A directory others can write into means the same thing,
|
|
545
|
-
* because the file in it can simply be replaced.
|
|
546
|
-
*/
|
|
547
|
-
tooOpen: boolean;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* The file's raw JSON object regardless of its permissions, with those
|
|
552
|
-
* permissions, for `jev setup` to update in place (carry the key over a mode
|
|
553
|
-
* switch, or re-save a file that is too open at 0600) and for `jev status` to
|
|
554
|
-
* show what a refused file names. Never used on the hook path.
|
|
555
|
-
*/
|
|
556
|
-
export function readJevConfigFileForUpdate(): JevConfigFileForUpdate | null {
|
|
557
|
-
const path = jevConfigPath();
|
|
558
|
-
let fd: number;
|
|
559
|
-
try {
|
|
560
|
-
fd = openSync(path, OPEN_FLAGS);
|
|
561
|
-
} catch {
|
|
562
|
-
return null;
|
|
563
|
-
}
|
|
564
|
-
try {
|
|
565
|
-
const st = fstatSync(fd);
|
|
566
|
-
if (!st.isFile() || st.size > MAX_CONFIG_BYTES) return null;
|
|
567
|
-
const buf = Buffer.alloc(st.size);
|
|
568
|
-
let off = 0;
|
|
569
|
-
while (off < buf.length) {
|
|
570
|
-
const n = readSync(fd, buf, off, buf.length - off, off);
|
|
571
|
-
if (n === 0) break;
|
|
572
|
-
off += n;
|
|
573
|
-
}
|
|
574
|
-
const parsed: unknown = JSON.parse(buf.subarray(0, off).toString("utf8"));
|
|
575
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
576
|
-
const meaningful = modesAreMeaningful();
|
|
577
|
-
const mode = meaningful ? st.mode & 0o777 : null;
|
|
578
|
-
const tooOpen = mode !== null && ((mode & 0o077) !== 0 || looseConfigDirMode(path) !== null);
|
|
579
|
-
return { raw: parsed as Record<string, unknown>, mode, tooOpen };
|
|
580
|
-
} catch {
|
|
581
|
-
return null;
|
|
582
|
-
} finally {
|
|
583
|
-
try {
|
|
584
|
-
closeSync(fd);
|
|
585
|
-
} catch {
|
|
586
|
-
// Nothing useful to do.
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
/** `readJevConfigFileForUpdate()` without the permissions. */
|
|
592
|
-
export function readJevConfigForUpdate(): Record<string, unknown> | null {
|
|
593
|
-
return readJevConfigFileForUpdate()?.raw ?? null;
|
|
594
|
-
}
|