failproofai 1.0.1 → 1.0.2-beta.0
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 +6 -6
- 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 +2 -2
- 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 +2 -2
- 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 +3 -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 +5 -4
- 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 +2 -2
- 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 +4 -3
- 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 +4 -4
- 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 +4 -4
- 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 +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 +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 +2 -2
- 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 +66 -22
- package/.next/standalone/.next/server/app/policies/page.js +5 -4
- 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 +2 -2
- 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 +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 +2 -2
- 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 +4 -4
- package/.next/standalone/.next/server/app/settings/page.js +1 -1
- 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]__1qzqcio._.js → [externals]__0e32-7c._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[externals]__1s462yl._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p8poxd._.js → [root-of-the-server]__0igxt32._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0neqlu-._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t9oqnn._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +3 -0
- package/.next/standalone/.next/server/chunks/_185gyk9._.js +3 -0
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +5 -6
- package/.next/standalone/.next/server/chunks/lib_telemetry-id_ts_1k8gj9o._.js +3 -0
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_09d5c79._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_04op_ou._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_1d2g73o._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0dr20uz._.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_0xbw3bu._.js +12 -0
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
- 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]__0spkm68._.js → [root-of-the-server]__07gm3zl._.js} +21 -20
- 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]__0oa1lav._.js +1 -1
- 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]__0s740oi._.js +6 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__07tbbf3._.js → [root-of-the-server]__0zig5sv._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__192nhvh._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0lvyuv4._.js → _00e9tnf._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0u5zauv._.js → _12l4a_k._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +13 -13
- 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 +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_08o7bqo._.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/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware.js +1 -1
- 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 +71 -27
- package/.next/standalone/.next/static/chunks/{37fcpc36x9gpe.js → 0eqky-7n7dgdm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1ide1wl2f7zi1.js → 0sa9v5mhc8861.js} +13 -13
- package/.next/standalone/.next/static/chunks/1mfzsja6ftim5.css +2 -0
- package/.next/standalone/.next/static/chunks/{3k7mxbp9hiom0.js → 1nuqytbta44af.js} +1 -1
- package/.next/standalone/.next/static/chunks/1sl2xmn9inx3y.js +6 -0
- package/.next/standalone/.next/static/chunks/{2_2lhuojq2xop.js → 2aawsjld9ied_.js} +1 -1
- package/.next/standalone/.next/static/chunks/2gn7ut4svofm2.js +1 -0
- package/.next/standalone/.next/static/chunks/{31s1smp-g7gks.js → 2hs7ycvsav7v2.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0k5050ezjvv49.js → 2i2yt3hzjredm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3c808qrfcq0im.js → 35xg6retpe6rp.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2zpp1we2jv6tb.js → 3y54nw0i7dsjl.js} +1 -1
- package/.next/standalone/SECURITY.md +5 -4
- package/.next/standalone/app/actions/get-hooks-config.ts +108 -17
- package/.next/standalone/app/actions/pack-actions.ts +153 -0
- package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
- package/.next/standalone/app/audit/_components/share-templates.ts +13 -13
- package/.next/standalone/app/policies/hooks-client.tsx +320 -14
- package/.next/standalone/components/navbar.tsx +2 -28
- package/.next/standalone/components/reach-developers.tsx +8 -1
- package/.next/standalone/docs-old/agenteye/python-sdk-skill.mdx +1 -1
- package/.next/standalone/docs-old/agenteye/python-sdk.mdx +59 -35
- package/.next/standalone/docs-old/i18n/README.ar.md +1 -0
- package/.next/standalone/docs-old/i18n/README.de.md +1 -0
- package/.next/standalone/docs-old/i18n/README.es.md +1 -0
- package/.next/standalone/docs-old/i18n/README.fr.md +1 -0
- package/.next/standalone/docs-old/i18n/README.he.md +1 -0
- package/.next/standalone/docs-old/i18n/README.hi.md +1 -0
- package/.next/standalone/docs-old/i18n/README.it.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ja.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ko.md +1 -0
- package/.next/standalone/docs-old/i18n/README.pt-br.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ru.md +1 -0
- package/.next/standalone/docs-old/i18n/README.tr.md +1 -0
- package/.next/standalone/docs-old/i18n/README.vi.md +1 -0
- package/.next/standalone/docs-old/i18n/README.zh.md +1 -0
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +431 -0
- package/.next/standalone/fp-cloud-cli/LICENSE +42 -0
- package/.next/standalone/fp-cloud-cli/MANIFEST.in +10 -0
- package/.next/standalone/fp-cloud-cli/README.md +259 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/__init__.py +10 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/__main__.py +4 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +64 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_context.py +332 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_version.py +1 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics.py +432 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics_config.py +77 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics_registry.py +83 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/app.py +492 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/auth.py +160 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +1694 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/__init__.py +0 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/_write.py +214 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/agent_cmds.py +407 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/alerts_cmds.py +445 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/audits_cmds.py +1054 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/auth_cmds.py +512 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/errors_cmds.py +190 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/evals_cmds.py +161 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/events_cmds.py +159 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +416 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/guardrails_cmds.py +148 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +472 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/keys_cmds.py +407 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/list_cmds.py +63 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/orgs_cmds.py +319 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +499 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/queries_cmds.py +378 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/sessions_cmds.py +151 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/settings_cmds.py +150 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/usage_cmds.py +35 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/users_cmds.py +404 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/config.py +330 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/dates.py +78 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +345 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/errors.py +98 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/models.py +891 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/orgs.py +30 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +6593 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +208 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +290 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/py.typed +0 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/select.py +322 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/theme.py +53 -0
- package/.next/standalone/fp-cloud-cli/pyproject.toml +106 -0
- package/.next/standalone/fp-cloud-cli/skill/SKILL.md +302 -0
- package/.next/standalone/fp-cloud-cli/skill/agents/openai.yaml +8 -0
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +254 -0
- package/.next/standalone/fp-cloud-cli/tests/__init__.py +0 -0
- package/.next/standalone/fp-cloud-cli/tests/conftest.py +92 -0
- package/.next/standalone/fp-cloud-cli/tests/test_alerting.py +644 -0
- package/.next/standalone/fp-cloud-cli/tests/test_analytics.py +385 -0
- package/.next/standalone/fp-cloud-cli/tests/test_audits.py +950 -0
- package/.next/standalone/fp-cloud-cli/tests/test_auth.py +98 -0
- package/.next/standalone/fp-cloud-cli/tests/test_auth_mode.py +272 -0
- package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +118 -0
- package/.next/standalone/fp-cloud-cli/tests/test_client.py +299 -0
- package/.next/standalone/fp-cloud-cli/tests/test_commands.py +814 -0
- package/.next/standalone/fp-cloud-cli/tests/test_config.py +99 -0
- package/.next/standalone/fp-cloud-cli/tests/test_dashboards_agent.py +340 -0
- package/.next/standalone/fp-cloud-cli/tests/test_dates.py +76 -0
- package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +481 -0
- package/.next/standalone/fp-cloud-cli/tests/test_facets.py +170 -0
- package/.next/standalone/fp-cloud-cli/tests/test_failproofai_home.py +722 -0
- package/.next/standalone/fp-cloud-cli/tests/test_fp_home_contract.py +198 -0
- package/.next/standalone/fp-cloud-cli/tests/test_hardening.py +186 -0
- package/.next/standalone/fp-cloud-cli/tests/test_help_table_coverage.py +127 -0
- package/.next/standalone/fp-cloud-cli/tests/test_keys_queries.py +496 -0
- package/.next/standalone/fp-cloud-cli/tests/test_list.py +59 -0
- package/.next/standalone/fp-cloud-cli/tests/test_multivalue.py +183 -0
- package/.next/standalone/fp-cloud-cli/tests/test_no_customer_identifiers.py +168 -0
- package/.next/standalone/fp-cloud-cli/tests/test_operator.py +492 -0
- package/.next/standalone/fp-cloud-cli/tests/test_orgs.py +692 -0
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +1732 -0
- package/.next/standalone/fp-cloud-cli/tests/test_policy_check.py +191 -0
- package/.next/standalone/fp-cloud-cli/tests/test_readme_matches_reality.py +125 -0
- package/.next/standalone/fp-cloud-cli/tests/test_review_fixes.py +161 -0
- package/.next/standalone/fp-cloud-cli/tests/test_telemetry_completeness.py +90 -0
- package/.next/standalone/fp-cloud-cli/tests/test_usage.py +115 -0
- package/.next/standalone/fp-cloud-cli/tests/test_v1_origin_diagnostic.py +70 -0
- package/.next/standalone/fp-cloud-cli/tests/test_v1_routing.py +214 -0
- package/.next/standalone/fp-cloud-cli/tests/test_whoami.py +104 -0
- package/.next/standalone/fp-cloud-cli/uv.lock +552 -0
- package/.next/standalone/package.json +29 -22
- package/.next/standalone/sdk/python/CHANGELOG.md +70 -0
- package/.next/standalone/sdk/python/LICENSE +42 -0
- package/.next/standalone/sdk/python/MANIFEST.in +21 -0
- package/.next/standalone/sdk/python/README.md +466 -0
- package/.next/standalone/sdk/python/docs/README.md +249 -0
- package/.next/standalone/sdk/python/docs/_shared/README.md +43 -0
- package/.next/standalone/sdk/python/docs/_shared/__init__.py +195 -0
- package/.next/standalone/sdk/python/docs/crewai/README.md +328 -0
- package/.next/standalone/sdk/python/docs/crewai/examples/quickstart.py +61 -0
- package/.next/standalone/sdk/python/docs/crewai/examples/research_crew.py +92 -0
- package/.next/standalone/sdk/python/docs/langgraph/README.md +481 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/quickstart.py +51 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/research_agent.py +86 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/supervisor_handoff.py +83 -0
- package/.next/standalone/sdk/python/docs/llama_index/README.md +332 -0
- package/.next/standalone/sdk/python/docs/llama_index/examples/quickstart.py +62 -0
- package/.next/standalone/sdk/python/docs/llama_index/examples/research_agent.py +93 -0
- package/.next/standalone/sdk/python/docs/manual/README.md +491 -0
- package/.next/standalone/sdk/python/docs/manual/examples/quickstart.py +57 -0
- package/.next/standalone/sdk/python/docs/manual/examples/research_agent.py +133 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/README.md +323 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/examples/quickstart.py +48 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/examples/research_agent.py +94 -0
- package/.next/standalone/sdk/python/failproofai_sdk/__init__.py +131 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_context.py +218 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_environment.py +78 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_events.py +752 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_resolver.py +118 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_runtime.py +21 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_schema.py +317 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_scopes.py +422 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_writer.py +750 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/__init__.py +274 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/_compat.py +247 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/_core.py +1076 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/crewai.py +1669 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/langchain.py +2151 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/llama_index.py +1588 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/pydantic_ai.py +977 -0
- package/.next/standalone/sdk/python/failproofai_sdk/py.typed +0 -0
- package/.next/standalone/sdk/python/pyproject.toml +92 -0
- package/.next/standalone/sdk/python/skill/SKILL.md +399 -0
- package/.next/standalone/sdk/python/skill/agents/openai.yaml +8 -0
- package/.next/standalone/sdk/python/skill/references/events.md +234 -0
- package/.next/standalone/sdk/python/skill/references/frameworks.md +314 -0
- package/.next/standalone/sdk/python/skill/references/install.md +100 -0
- package/.next/standalone/sdk/python/skill/references/integration.md +352 -0
- package/.next/standalone/sdk/python/tests/__init__.py +0 -0
- package/.next/standalone/sdk/python/tests/conftest.py +169 -0
- package/.next/standalone/sdk/python/tests/integrations/__init__.py +6 -0
- package/.next/standalone/sdk/python/tests/integrations/test_crewai.py +1299 -0
- package/.next/standalone/sdk/python/tests/integrations/test_langchain.py +2338 -0
- package/.next/standalone/sdk/python/tests/integrations/test_llama_index.py +1576 -0
- package/.next/standalone/sdk/python/tests/integrations/test_pydantic_ai.py +1102 -0
- package/.next/standalone/sdk/python/tests/test_context.py +410 -0
- package/.next/standalone/sdk/python/tests/test_docs.py +411 -0
- package/.next/standalone/sdk/python/tests/test_durability.py +1352 -0
- package/.next/standalone/sdk/python/tests/test_encoding.py +571 -0
- package/.next/standalone/sdk/python/tests/test_integrations.py +1127 -0
- package/.next/standalone/sdk/python/tests/test_no_customer_identifiers.py +214 -0
- package/.next/standalone/sdk/python/tests/test_packaging.py +185 -0
- package/.next/standalone/sdk/python/tests/test_resolver_umbrella.py +178 -0
- package/.next/standalone/sdk/python/tests/test_scopes.py +487 -0
- package/.next/standalone/sdk/python/tests/test_sdk.py +923 -0
- package/.next/standalone/sdk/python/tests/test_server_contract.py +650 -0
- package/.next/standalone/sdk/python/tests/test_site_docs.py +371 -0
- package/.next/standalone/sdk/python/tests/test_skill_snippets.py +165 -0
- package/.next/standalone/sdk/python/tests/test_spool_contract.py +287 -0
- package/.next/standalone/sdk/python/tests/test_spool_creation.py +360 -0
- package/.next/standalone/sdk/python/tests/test_wire_format.py +279 -0
- package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +378 -0
- package/.next/standalone/sdk/python/uv.lock +6005 -0
- package/.next/standalone/server.js +1 -1
- package/README.md +77 -21
- package/bin/failproofai.mjs +145 -11
- package/dist/cli.mjs +4482 -2205
- package/dist/index.js +5 -5
- package/dist/worker.mjs +2399 -1978
- package/package.json +29 -22
- package/policy-pack/SHA256SUMS +2 -0
- package/policy-pack/failproofai-pack.json +732 -0
- package/policy-pack/failproofai-pack.mjs +2017 -0
- package/scripts/build-policy-pack.mjs +121 -0
- package/scripts/changelog-section.py +77 -0
- package/scripts/prune-standalone.mjs +3 -0
- package/scripts/python-version.py +232 -0
- package/scripts/translate-docs/cli.ts +21 -0
- package/scripts/translate-docs/mintlify-nav.ts +85 -13
- package/scripts/translate-docs/translator.ts +66 -11
- package/src/audit/cache.ts +40 -3
- package/src/audit/cli.ts +1 -1
- package/src/audit/index.ts +1 -1
- package/src/audit/replay.ts +89 -3
- package/src/audit/report.ts +5 -5
- package/src/audit/schedule-cli.ts +49 -53
- package/src/hooks/builtin-policies.ts +217 -579
- package/src/hooks/cloud-enrollment-cli.ts +66 -27
- package/src/hooks/cloud-managed-policies.ts +6 -1
- package/src/hooks/configure-wizard.ts +1 -1
- package/src/hooks/custom-hooks-loader.ts +89 -6
- package/src/hooks/fp-home.ts +92 -4
- package/src/hooks/fp-reset.ts +26 -2
- package/src/hooks/handler.ts +181 -11
- package/src/hooks/harness-cli.ts +79 -25
- package/src/hooks/hook-activity-store.ts +13 -3
- package/src/hooks/install-prompt.ts +25 -3
- package/src/hooks/manager.ts +418 -172
- package/src/hooks/migrations.ts +2 -2
- package/src/hooks/pack-cli.ts +505 -0
- package/src/hooks/pack-failclosed.ts +189 -0
- package/src/hooks/pack-manifest.ts +344 -0
- package/src/hooks/pack-store.ts +930 -0
- package/src/hooks/policy-catalog.ts +535 -0
- package/src/hooks/policy-evaluator.ts +24 -16
- package/src/hooks/policy-presets.ts +4 -2
- package/src/hooks/policy-registry.ts +8 -2
- package/src/hooks/policy-types.ts +26 -0
- package/src/hooks/session-pause-cli.ts +33 -9
- package/src/hooks/tui.ts +570 -0
- package/src/hooks/types.ts +15 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_usx0j._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_zk340._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0agdw2a._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d1u_na._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1ger0a2._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +0 -3
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1iiwm5e._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kc8_v5._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kgy9da._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mhqk5a._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pltpn8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qkcyf-._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w0x_ok._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_dfocn._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bfex62._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kzapg5._.js +0 -6
- package/.next/standalone/.next/static/chunks/36fw6yk1s6x_f.js +0 -1
- package/.next/standalone/.next/static/chunks/3tzq0260n17ae.js +0 -6
- package/.next/standalone/.next/static/chunks/3wq9x0c-7ld5d.css +0 -2
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Emit the builtin policies AS a policy pack.
|
|
4
|
+
*
|
|
5
|
+
* This is the dress rehearsal for builtins leaving the npm package. It produces
|
|
6
|
+
* exactly what a third-party publisher would upload to a GitHub release — a
|
|
7
|
+
* manifest, ONE bundled entry artifact, and a SHA256SUMS — so the same lane that
|
|
8
|
+
* loads a stranger's pack can be pointed at ours and proved to produce identical
|
|
9
|
+
* enforcement.
|
|
10
|
+
*
|
|
11
|
+
* Nothing on the hook path reads the output. `builtin-pack-conformance.test.ts`
|
|
12
|
+
* loads it into a scratch registry and compares. That is the point: it turns the
|
|
13
|
+
* eventual switch-over into a comparison someone already ran, rather than a leap.
|
|
14
|
+
*
|
|
15
|
+
* ## Two things it deliberately does
|
|
16
|
+
*
|
|
17
|
+
* **One file.** Transitive local imports are rewritten by the loader with no
|
|
18
|
+
* integrity check of their own — only the ENTRY is content-addressed — so a
|
|
19
|
+
* multi-file pack could not honestly claim to be digest-pinned. Bundling is what
|
|
20
|
+
* makes the digest mean what it says.
|
|
21
|
+
*
|
|
22
|
+
* ## Runs under bun, not node
|
|
23
|
+
*
|
|
24
|
+
* It imports `policy-catalog.ts` directly for the manifest. Node cannot resolve
|
|
25
|
+
* an extensionless TypeScript specifier at all before 22, and this repo targets
|
|
26
|
+
* node >= 20.9 — on 22 it "works" while printing a reparse warning, which is the
|
|
27
|
+
* worst of both: green here, broken on a supported version. bun reads TS
|
|
28
|
+
* natively, and every other build step in this repo is already bun.
|
|
29
|
+
*
|
|
30
|
+
* **It omits the `alwaysOn` policy.** `block-failproofai-commands` is the guard
|
|
31
|
+
* that stops an agent disabling failproofai, it ships compiled in, and
|
|
32
|
+
* `pack-manifest.ts` REFUSES any pack that declares `alwaysOn`. A pack containing
|
|
33
|
+
* it would be rejected by our own loader — correctly.
|
|
34
|
+
*/
|
|
35
|
+
import { execFileSync } from "node:child_process";
|
|
36
|
+
import { createHash } from "node:crypto";
|
|
37
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
38
|
+
import { dirname, join, resolve } from "node:path";
|
|
39
|
+
import { fileURLToPath } from "node:url";
|
|
40
|
+
|
|
41
|
+
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
42
|
+
// `--out <dir>` lets the conformance test generate into a temp directory rather
|
|
43
|
+
// than depending on a build step having run first — the unit suite and the build
|
|
44
|
+
// are separate CI jobs, so a test that assumed `policy-pack/` existed would be
|
|
45
|
+
// green locally and skipped-or-broken in CI.
|
|
46
|
+
const outArg = process.argv.indexOf("--out");
|
|
47
|
+
const OUT_DIR = outArg !== -1 && process.argv[outArg + 1]
|
|
48
|
+
? resolve(process.argv[outArg + 1])
|
|
49
|
+
: join(ROOT, "policy-pack");
|
|
50
|
+
// Not `failproofai/builtins`. These stop being builtins the moment they ship as
|
|
51
|
+
// a pack, and an id is the most durable place a retired word can hide — it ends
|
|
52
|
+
// up in every machine's installed.json and in the listing every user reads.
|
|
53
|
+
const PACK_ID = "failproofai/core";
|
|
54
|
+
const ENTRY_ASSET = "failproofai-pack.mjs";
|
|
55
|
+
const MANIFEST_ASSET = "failproofai-pack.json";
|
|
56
|
+
const CHECKSUMS_ASSET = "SHA256SUMS";
|
|
57
|
+
|
|
58
|
+
const version = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8")).version;
|
|
59
|
+
|
|
60
|
+
// A generated entry that registers every non-alwaysOn builtin through the public
|
|
61
|
+
// policy API — the same API a third-party pack uses. `failproofai` stays external
|
|
62
|
+
// so the loader's shim resolves it, exactly as it does for any other pack.
|
|
63
|
+
const entrySource = `
|
|
64
|
+
import { customPolicies } from "failproofai";
|
|
65
|
+
import { BUILTIN_POLICIES } from ${JSON.stringify(join(ROOT, "src/hooks/builtin-policies.ts"))};
|
|
66
|
+
|
|
67
|
+
for (const policy of BUILTIN_POLICIES) {
|
|
68
|
+
if (policy.alwaysOn) continue;
|
|
69
|
+
customPolicies.add({
|
|
70
|
+
name: policy.name,
|
|
71
|
+
description: policy.description,
|
|
72
|
+
match: policy.match,
|
|
73
|
+
fn: policy.fn,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
rmSync(OUT_DIR, { recursive: true, force: true });
|
|
79
|
+
mkdirSync(OUT_DIR, { recursive: true });
|
|
80
|
+
|
|
81
|
+
const tempEntry = join(OUT_DIR, ".entry.generated.ts");
|
|
82
|
+
writeFileSync(tempEntry, entrySource, "utf8");
|
|
83
|
+
try {
|
|
84
|
+
execFileSync(
|
|
85
|
+
"bun",
|
|
86
|
+
["build", "--target=node", "--format=esm", "--external", "failproofai",
|
|
87
|
+
"--outfile", join(OUT_DIR, ENTRY_ASSET), tempEntry],
|
|
88
|
+
{ cwd: ROOT, stdio: ["pipe", "pipe", "inherit"] },
|
|
89
|
+
);
|
|
90
|
+
} finally {
|
|
91
|
+
rmSync(tempEntry, { force: true });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// The manifest's policy list comes from the CATALOG, never from the bundle —
|
|
95
|
+
// which is the whole reason the catalog is pure literal data. `pack add`
|
|
96
|
+
// validates these with the loader's own rules, so a shape the loader would
|
|
97
|
+
// refuse fails this build rather than shipping.
|
|
98
|
+
const { POLICY_CATALOG } = await import(join(ROOT, "src/hooks/policy-catalog.ts"));
|
|
99
|
+
const policies = POLICY_CATALOG.filter((p) => !p.alwaysOn).map((p) => ({ ...p }));
|
|
100
|
+
|
|
101
|
+
const manifest = JSON.stringify(
|
|
102
|
+
{ id: PACK_ID, version, effect: "enforce", policies },
|
|
103
|
+
null,
|
|
104
|
+
2,
|
|
105
|
+
) + "\n";
|
|
106
|
+
writeFileSync(join(OUT_DIR, MANIFEST_ASSET), manifest, "utf8");
|
|
107
|
+
|
|
108
|
+
const sha = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
109
|
+
const entryBytes = readFileSync(join(OUT_DIR, ENTRY_ASSET));
|
|
110
|
+
writeFileSync(
|
|
111
|
+
join(OUT_DIR, CHECKSUMS_ASSET),
|
|
112
|
+
`${sha(Buffer.from(manifest))} ${MANIFEST_ASSET}\n${sha(entryBytes)} ${ENTRY_ASSET}\n`,
|
|
113
|
+
"utf8",
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const omitted = POLICY_CATALOG.filter((p) => p.alwaysOn).map((p) => p.name);
|
|
117
|
+
console.log(
|
|
118
|
+
`[policy-pack] ${PACK_ID}@${version} — ${policies.length} policies, ` +
|
|
119
|
+
`${(entryBytes.length / 1024).toFixed(1)} KB entry` +
|
|
120
|
+
(omitted.length ? `; omitted alwaysOn: ${omitted.join(", ")}` : ""),
|
|
121
|
+
);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Print one version's section out of a CHANGELOG, for a GitHub Release body.
|
|
3
|
+
|
|
4
|
+
changelog-section.py <changelog> <version>
|
|
5
|
+
|
|
6
|
+
Both PyPI publish workflows call this from `preflight` — BEFORE anything is built
|
|
7
|
+
and long before anything is uploaded — so a release with no notes fails at the one
|
|
8
|
+
point in the pipeline where failing is free. It cannot be fixed afterwards: PyPI
|
|
9
|
+
never releases a version for reuse, so "publish it again, with notes this time" is
|
|
10
|
+
not an available remedy, and the GitHub Release would stand empty against a
|
|
11
|
+
version that is permanently on the index.
|
|
12
|
+
|
|
13
|
+
The heading grammar is the one CLAUDE.md fixes for the root changelog and which
|
|
14
|
+
the per-package files follow:
|
|
15
|
+
|
|
16
|
+
## 0.0.1b1 — 2026-08-24
|
|
17
|
+
|
|
18
|
+
The date is not parsed; only the version is matched, and it is matched with a
|
|
19
|
+
trailing word boundary so a request for `0.0.1b1` cannot be answered by
|
|
20
|
+
`0.0.1b10`'s section — which is a real hazard once a beta counter passes nine,
|
|
21
|
+
and one that would put the WRONG release's notes on a tag with no error anywhere.
|
|
22
|
+
|
|
23
|
+
Stdlib only, same reason as `python-version.py`: it runs in the job that holds no
|
|
24
|
+
publishing identity precisely because it installs nothing.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import re
|
|
30
|
+
import sys
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class SectionError(Exception):
|
|
34
|
+
"""A message a maintainer can act on, printed without a traceback."""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def extract(text: str, version: str) -> str:
|
|
38
|
+
"""The body under `## <version>`, up to the next `## ` heading."""
|
|
39
|
+
heading = re.compile(rf"^##[ \t]+{re.escape(version)}\b.*$", re.MULTILINE)
|
|
40
|
+
match = heading.search(text)
|
|
41
|
+
if not match:
|
|
42
|
+
raise SectionError(
|
|
43
|
+
f"no '## {version}' section — add one before releasing, or the tag "
|
|
44
|
+
f"gets a release with nothing in it"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
rest = text[match.end():]
|
|
48
|
+
nxt = re.search(r"^##[ \t]", rest, re.MULTILINE)
|
|
49
|
+
body = (rest[: nxt.start()] if nxt else rest).strip()
|
|
50
|
+
|
|
51
|
+
if not body:
|
|
52
|
+
# A heading with nothing under it is the same failure as no heading at
|
|
53
|
+
# all, and is what you get from stamping a version onto an empty section.
|
|
54
|
+
raise SectionError(f"the '## {version}' section is empty")
|
|
55
|
+
return body
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def main(argv: list[str]) -> int:
|
|
59
|
+
if len(argv) < 3:
|
|
60
|
+
print(__doc__ or "", file=sys.stderr)
|
|
61
|
+
return 2
|
|
62
|
+
path, version = argv[1], argv[2]
|
|
63
|
+
try:
|
|
64
|
+
with open(path, encoding="utf-8") as handle:
|
|
65
|
+
text = handle.read()
|
|
66
|
+
except OSError as exc:
|
|
67
|
+
raise SectionError(f"cannot read {path}: {exc}") from exc
|
|
68
|
+
print(extract(text, version))
|
|
69
|
+
return 0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
try:
|
|
74
|
+
raise SystemExit(main(sys.argv))
|
|
75
|
+
except SectionError as error:
|
|
76
|
+
print(f"error: {error}", file=sys.stderr)
|
|
77
|
+
raise SystemExit(1) from None
|
|
@@ -119,6 +119,9 @@ const STANDALONE_ROOT_PRUNE = [
|
|
|
119
119
|
// plugin/extension dirs are shipped from the package root by `files`, not
|
|
120
120
|
// from inside the standalone bundle.
|
|
121
121
|
"integration-suite", "pi-extension", "openclaw-plugin", "docker-hook-sync",
|
|
122
|
+
// Generated by build:pack and shipped from the root by `files`, same as the
|
|
123
|
+
// two above — without this it lands in the tarball twice.
|
|
124
|
+
"policy-pack",
|
|
122
125
|
];
|
|
123
126
|
const STANDALONE_ROOT_PRUNE_FILES = [
|
|
124
127
|
// Top-level markdown / licenses / docs
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Version arithmetic for the two Python packages, `fp-cloud-cli` and `failproofai-sdk`.
|
|
3
|
+
|
|
4
|
+
Both publish workflows call this; it is the ONE place the release scheme is written
|
|
5
|
+
down, so the two pipelines cannot drift into disagreeing about what comes next.
|
|
6
|
+
|
|
7
|
+
resolve <version-file> [<dist>] -> key=value lines for $GITHUB_OUTPUT
|
|
8
|
+
write <version-file> <version> -> rewrite __version__ in place
|
|
9
|
+
|
|
10
|
+
Given a dist name, `resolve` also emits the release tag: `<dist>-v<version>`, e.g.
|
|
11
|
+
`fp-cloud-cli-v0.0.1b1`. Namespaced rather than the bare `vX.Y.Z` the npm package uses,
|
|
12
|
+
because three release lines share one repository and one tag namespace — a bare
|
|
13
|
+
`v0.0.1b1` would read as a release of `failproofai` itself, and `gh release list`
|
|
14
|
+
would interleave all three with no way to tell them apart.
|
|
15
|
+
|
|
16
|
+
THE SCHEME. It mirrors `publish.yml`'s npm rule, spelled in PEP 440 rather than
|
|
17
|
+
semver, because PyPI has no dist-tags: there is no movable `beta` pointer to
|
|
18
|
+
publish behind, so the pre-release marker in the version string is the entire
|
|
19
|
+
channel mechanism.
|
|
20
|
+
|
|
21
|
+
beta X.Y.ZbN -> X.Y.Zb(N+1) another beta off the same base
|
|
22
|
+
stable X.Y.Z -> X.Y.(Z+1)b0 open the next patch's beta line
|
|
23
|
+
|
|
24
|
+
`b` and not `-beta.`: `0.1.0-beta.1` is not a PEP 440 version at all, and pip
|
|
25
|
+
would reject the sdist name. `b0` and not `b1` after a stable, because that is
|
|
26
|
+
what npm's rule does (`X.Y.(Z+1)-beta.0`) and a release scheme that differs
|
|
27
|
+
between two of this repo's registries for no reason is a scheme nobody can
|
|
28
|
+
recite from memory.
|
|
29
|
+
|
|
30
|
+
WHY NON-CANONICAL SPELLINGS ARE REJECTED RATHER THAN ACCEPTED. `0.0.01b1`,
|
|
31
|
+
`0.0.1b01`, `1.2.3-beta.1` and `v1.2.3` are all legal PEP 440 and all normalise to
|
|
32
|
+
something else on the way to PyPI — so the file would say one thing, the wheel
|
|
33
|
+
another, and preflight's "is this already published" check would ask PyPI about a
|
|
34
|
+
third. Every consumer here compares version STRINGS, so this module normalises and
|
|
35
|
+
refuses anything that is not already in its final form, naming what PyPI would have
|
|
36
|
+
turned it into.
|
|
37
|
+
|
|
38
|
+
Stdlib only, and deliberately: this runs in the preflight job, which is the one
|
|
39
|
+
job in those workflows that has no publishing identity precisely BECAUSE it runs
|
|
40
|
+
before anything is installed. A dependency here would put third-party code back
|
|
41
|
+
into it.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from __future__ import annotations
|
|
45
|
+
|
|
46
|
+
import re
|
|
47
|
+
import sys
|
|
48
|
+
|
|
49
|
+
# `__version__ = "..."` — matched, never exec'd. Importing the module would run
|
|
50
|
+
# whatever else the file grew, in a job that is about to be handed a token.
|
|
51
|
+
_ASSIGNMENT = re.compile(r"""(__version__\s*=\s*)(["'])([^"']+)(\2)""")
|
|
52
|
+
|
|
53
|
+
# The two shapes the scheme knows how to advance.
|
|
54
|
+
_STABLE = re.compile(r"^(\d+)\.(\d+)\.(\d+)$")
|
|
55
|
+
_BETA = re.compile(r"^(\d+)\.(\d+)\.(\d+)b(\d+)$")
|
|
56
|
+
|
|
57
|
+
# PEP 440's own pre-release spellings, and the canonical form each collapses to.
|
|
58
|
+
_PRE_LABELS = {
|
|
59
|
+
"alpha": "a",
|
|
60
|
+
"a": "a",
|
|
61
|
+
"beta": "b",
|
|
62
|
+
"b": "b",
|
|
63
|
+
"c": "rc",
|
|
64
|
+
"pre": "rc",
|
|
65
|
+
"preview": "rc",
|
|
66
|
+
"rc": "rc",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# PEP 440, verbatim from the spec's appendix. Used to normalise, and to tell "a
|
|
70
|
+
# version this scheme will not advance" (an rc, a .post, a .dev — publishable, no
|
|
71
|
+
# auto-bump) apart from "not a version at all" (a typo — refuse before a build).
|
|
72
|
+
_PEP440 = re.compile(
|
|
73
|
+
r"""^\s*v?
|
|
74
|
+
(?:(?P<epoch>[0-9]+)!)?
|
|
75
|
+
(?P<release>[0-9]+(?:\.[0-9]+)*)
|
|
76
|
+
(?P<pre>[-_.]?(?P<pre_l>alpha|a|beta|b|preview|pre|c|rc)[-_.]?(?P<pre_n>[0-9]+)?)?
|
|
77
|
+
(?P<post>(?:-(?P<post_n1>[0-9]+))|(?:[-_.]?(?P<post_l>post|rev|r)[-_.]?(?P<post_n2>[0-9]+)?))?
|
|
78
|
+
(?P<dev>[-_.]?dev[-_.]?(?P<dev_n>[0-9]+)?)?
|
|
79
|
+
(?:\+(?P<local>[a-z0-9]+(?:[-_.][a-z0-9]+)*))?
|
|
80
|
+
\s*$""",
|
|
81
|
+
re.VERBOSE | re.IGNORECASE,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class VersionError(Exception):
|
|
86
|
+
"""A message a maintainer can act on, printed without a traceback."""
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def read_version(path: str) -> str:
|
|
90
|
+
"""The `__version__` literal in a `_version.py`, exactly as written."""
|
|
91
|
+
try:
|
|
92
|
+
with open(path, encoding="utf-8") as handle:
|
|
93
|
+
text = handle.read()
|
|
94
|
+
except OSError as exc:
|
|
95
|
+
raise VersionError(f"cannot read {path}: {exc}") from exc
|
|
96
|
+
|
|
97
|
+
found = _ASSIGNMENT.findall(text)
|
|
98
|
+
if not found:
|
|
99
|
+
raise VersionError(f"no __version__ assignment in {path}")
|
|
100
|
+
if len(found) > 1:
|
|
101
|
+
# Two assignments means the last one wins at import time and the first
|
|
102
|
+
# one is what a careless regex edit would rewrite. Refuse rather than
|
|
103
|
+
# guess which is live.
|
|
104
|
+
raise VersionError(f"{len(found)} __version__ assignments in {path}; expected exactly 1")
|
|
105
|
+
return found[0][2]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def normalize(version: str) -> str:
|
|
109
|
+
"""The form PyPI will store, per PEP 440's normalisation rules."""
|
|
110
|
+
match = _PEP440.match(version)
|
|
111
|
+
if not match:
|
|
112
|
+
raise VersionError(f"'{version}' is not a valid PEP 440 version")
|
|
113
|
+
|
|
114
|
+
out = ""
|
|
115
|
+
epoch = match.group("epoch")
|
|
116
|
+
if epoch and int(epoch):
|
|
117
|
+
out += f"{int(epoch)}!"
|
|
118
|
+
out += ".".join(str(int(part)) for part in match.group("release").split("."))
|
|
119
|
+
if match.group("pre"):
|
|
120
|
+
out += _PRE_LABELS[match.group("pre_l").lower()] + str(int(match.group("pre_n") or 0))
|
|
121
|
+
if match.group("post"):
|
|
122
|
+
out += f".post{int(match.group('post_n1') or match.group('post_n2') or 0)}"
|
|
123
|
+
if match.group("dev"):
|
|
124
|
+
out += f".dev{int(match.group('dev_n') or 0)}"
|
|
125
|
+
local = match.group("local")
|
|
126
|
+
if local:
|
|
127
|
+
out += "+" + local.lower().replace("_", ".").replace("-", ".")
|
|
128
|
+
return out
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def classify(version: str) -> tuple[str, str]:
|
|
132
|
+
"""`(scheme, next_version)`. `next_version` is empty when the scheme cannot advance."""
|
|
133
|
+
canonical = normalize(version)
|
|
134
|
+
if canonical != version:
|
|
135
|
+
# Checked before the scheme, not inside it, so it also catches the shapes
|
|
136
|
+
# the scheme does not advance — `1.2.3-beta.1` publishes as `1.2.3b1`, and
|
|
137
|
+
# a preflight that asked PyPI about the former learned nothing about the
|
|
138
|
+
# version actually going up.
|
|
139
|
+
raise VersionError(
|
|
140
|
+
f"'{version}' is not canonical — PyPI would normalise it to '{canonical}', "
|
|
141
|
+
"so the file, the wheel and the published version would disagree"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
match = _BETA.match(version)
|
|
145
|
+
if match:
|
|
146
|
+
major, minor, patch, beta = (int(g) for g in match.groups())
|
|
147
|
+
return "beta", f"{major}.{minor}.{patch}b{beta + 1}"
|
|
148
|
+
|
|
149
|
+
match = _STABLE.match(version)
|
|
150
|
+
if match:
|
|
151
|
+
major, minor, patch = (int(g) for g in match.groups())
|
|
152
|
+
return "stable", f"{major}.{minor}.{patch + 1}b0"
|
|
153
|
+
|
|
154
|
+
# Canonical, valid, and not one of the two shapes: an rc, a .post, a .dev, an
|
|
155
|
+
# epoch. PyPI takes it, so publishing is not blocked — but nothing here knows
|
|
156
|
+
# what should follow it, and inventing a successor is how a release line
|
|
157
|
+
# silently forks. The workflow warns and leaves main's version for a human.
|
|
158
|
+
return "other", ""
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def is_prerelease(version: str) -> bool:
|
|
162
|
+
match = _PEP440.match(version)
|
|
163
|
+
if not match:
|
|
164
|
+
raise VersionError(f"'{version}' is not a valid PEP 440 version")
|
|
165
|
+
return bool(match.group("pre") or match.group("dev"))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
#: Tag prefixes this repository already uses, and must not be confused with. The
|
|
169
|
+
#: npm package tags bare `vX.Y.Z`; a Python release tag must never be mistakable
|
|
170
|
+
#: for one, in `gh release list`, in the repo's release feed, or by the CLI, which
|
|
171
|
+
#: builds its daemon download URLs out of `v<version>` tags.
|
|
172
|
+
_RESERVED_TAG_PREFIX = "v"
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def release_tag(dist: str, version: str) -> str:
|
|
176
|
+
tag = f"{dist}-v{version}"
|
|
177
|
+
if not dist or dist.startswith(_RESERVED_TAG_PREFIX) and dist[1:2].isdigit():
|
|
178
|
+
raise VersionError(f"dist name '{dist}' would produce a tag mistakable for an npm release tag")
|
|
179
|
+
return tag
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def resolve(path: str, dist: str | None = None) -> str:
|
|
183
|
+
version = read_version(path)
|
|
184
|
+
scheme, following = classify(version)
|
|
185
|
+
lines = [
|
|
186
|
+
f"version={version}",
|
|
187
|
+
f"scheme={scheme}",
|
|
188
|
+
f"is_prerelease={'true' if is_prerelease(version) else 'false'}",
|
|
189
|
+
f"next_version={following}",
|
|
190
|
+
]
|
|
191
|
+
if dist:
|
|
192
|
+
lines.append(f"tag={release_tag(dist, version)}")
|
|
193
|
+
return "\n".join(lines)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def write(path: str, version: str) -> str:
|
|
197
|
+
"""Rewrite the literal in place, leaving every other byte of the file alone."""
|
|
198
|
+
scheme, _ = classify(version) # refuses a typo before it reaches a commit
|
|
199
|
+
if scheme == "other":
|
|
200
|
+
# `write` is only ever called with a version this module computed, so
|
|
201
|
+
# reaching here means someone wired it to an arbitrary string.
|
|
202
|
+
raise VersionError(f"'{version}' is outside the release scheme; set it by hand")
|
|
203
|
+
|
|
204
|
+
with open(path, encoding="utf-8") as handle:
|
|
205
|
+
text = handle.read()
|
|
206
|
+
replaced, count = _ASSIGNMENT.subn(
|
|
207
|
+
lambda m: f"{m.group(1)}{m.group(2)}{version}{m.group(4)}", text
|
|
208
|
+
)
|
|
209
|
+
if count != 1:
|
|
210
|
+
raise VersionError(f"expected exactly 1 __version__ assignment in {path}, found {count}")
|
|
211
|
+
with open(path, "w", encoding="utf-8") as handle:
|
|
212
|
+
handle.write(replaced)
|
|
213
|
+
return version
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def main(argv: list[str]) -> int:
|
|
217
|
+
if len(argv) >= 3 and argv[1] == "resolve":
|
|
218
|
+
print(resolve(argv[2], argv[3] if len(argv) >= 4 else None))
|
|
219
|
+
return 0
|
|
220
|
+
if len(argv) >= 4 and argv[1] == "write":
|
|
221
|
+
print(write(argv[2], argv[3]))
|
|
222
|
+
return 0
|
|
223
|
+
print(__doc__ or "", file=sys.stderr)
|
|
224
|
+
return 2
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
if __name__ == "__main__":
|
|
228
|
+
try:
|
|
229
|
+
raise SystemExit(main(sys.argv))
|
|
230
|
+
except VersionError as error:
|
|
231
|
+
print(f"error: {error}", file=sys.stderr)
|
|
232
|
+
raise SystemExit(1) from None
|
|
@@ -39,6 +39,11 @@ const { values: args } = parseArgs({
|
|
|
39
39
|
validate: { type: "boolean", default: false },
|
|
40
40
|
prune: { type: "boolean", default: false },
|
|
41
41
|
"no-prune": { type: "boolean", default: false },
|
|
42
|
+
// Publish what succeeded instead of discarding it. One page failing used to
|
|
43
|
+
// exit 1, which made the box job die before its push — on 2026-08-18 that
|
|
44
|
+
// threw away 782 good translations (2.7M tokens) because ONE page overran
|
|
45
|
+
// the output limit. Opt-in, so CI and hand runs keep failing loudly.
|
|
46
|
+
"allow-partial": { type: "boolean", default: false },
|
|
42
47
|
model: { type: "string", short: "m" },
|
|
43
48
|
help: { type: "boolean", short: "h", default: false },
|
|
44
49
|
},
|
|
@@ -56,6 +61,8 @@ Options:
|
|
|
56
61
|
--readme-only Only translate the README
|
|
57
62
|
--docs-only Only translate Mintlify docs
|
|
58
63
|
--dry-run Show what would be translated without calling the API
|
|
64
|
+
--allow-partial Exit 0 when SOME pages translated, listing the failures
|
|
65
|
+
(default: any failure exits 1 and publishes nothing)
|
|
59
66
|
-f, --force Ignore cache, re-translate everything
|
|
60
67
|
--update-nav Regenerate docs.json navigation after translation
|
|
61
68
|
--validate Check all nav references resolve to files
|
|
@@ -441,6 +448,20 @@ async function main() {
|
|
|
441
448
|
}
|
|
442
449
|
|
|
443
450
|
if (errors.length > 0) {
|
|
451
|
+
// Printed either way — a partial run must never read as a clean one.
|
|
452
|
+
console.log(`\nFAILED PAGES (${errors.length}):`);
|
|
453
|
+
for (const e of errors) {
|
|
454
|
+
console.log(` ${e.source} [${e.lang}]: ${e.error}`);
|
|
455
|
+
}
|
|
456
|
+
if (args["allow-partial"] && translated.length > 0) {
|
|
457
|
+
// A stable marker the box job greps for, so the failure reaches Slack and
|
|
458
|
+
// the PR body instead of being buried in a 700-line log.
|
|
459
|
+
console.log(
|
|
460
|
+
`\nPARTIAL RUN — ${translated.length} page(s) translated, ${errors.length} failed. ` +
|
|
461
|
+
`Publishing what succeeded.`,
|
|
462
|
+
);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
444
465
|
process.exit(1);
|
|
445
466
|
}
|
|
446
467
|
}
|
|
@@ -1,19 +1,29 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { getLanguageByCode, NAV_TRANSLATIONS } from "./config";
|
|
5
5
|
|
|
6
6
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
const
|
|
7
|
+
const DOCS_DIR = join(__dirname, "..", "..", "docs");
|
|
8
|
+
const DOCS_JSON_PATH = join(DOCS_DIR, "docs.json");
|
|
8
9
|
|
|
9
10
|
interface NavGroup {
|
|
10
11
|
group: string;
|
|
11
|
-
|
|
12
|
+
// OPTIONAL, and both of these are why: a Mintlify group may carry `pages`, or
|
|
13
|
+
// nested `groups`, or neither — the docs rebuild introduced a group whose
|
|
14
|
+
// content is an `openapi` spec and has no pages at all. `pages` entries are
|
|
15
|
+
// likewise strings OR nested groups. Typing these as required is what let
|
|
16
|
+
// `group.pages.map` crash the nightly run.
|
|
17
|
+
pages?: (string | NavGroup)[];
|
|
18
|
+
groups?: NavGroup[];
|
|
19
|
+
// Everything else (expanded, icon, openapi, …) rides along untouched.
|
|
20
|
+
[key: string]: unknown;
|
|
12
21
|
}
|
|
13
22
|
|
|
14
23
|
interface NavTab {
|
|
15
24
|
tab: string;
|
|
16
25
|
groups: NavGroup[];
|
|
26
|
+
[key: string]: unknown;
|
|
17
27
|
}
|
|
18
28
|
|
|
19
29
|
interface LanguageNav {
|
|
@@ -76,9 +86,33 @@ export function getNavigationPageReferences(
|
|
|
76
86
|
* Build a navigation entry for a specific language by transforming the
|
|
77
87
|
* English navigation structure.
|
|
78
88
|
*/
|
|
89
|
+
/**
|
|
90
|
+
* Does the localized file for a nav entry exist on disk?
|
|
91
|
+
*
|
|
92
|
+
* Defaults to "yes" so the pure transform stays testable with fixtures, and the
|
|
93
|
+
* REAL check is injected by updateDocsJson / localizeProductsNavigation, which
|
|
94
|
+
* are the two paths that write docs.json.
|
|
95
|
+
*/
|
|
96
|
+
export type PageExists = (relativePath: string) => boolean;
|
|
97
|
+
|
|
98
|
+
const fileOnDisk: PageExists = (rel) => existsSync(join(DOCS_DIR, rel));
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Keep a group only if it still carries something to render. Dropping every
|
|
102
|
+
* missing page can empty a group, and an empty group is its own validation
|
|
103
|
+
* error — but a group whose content is an `openapi` spec has no pages by
|
|
104
|
+
* design and must survive.
|
|
105
|
+
*/
|
|
106
|
+
function hasContent(group: NavGroup): boolean {
|
|
107
|
+
if (Array.isArray(group.pages) && group.pages.length > 0) return true;
|
|
108
|
+
if (Array.isArray(group.groups) && group.groups.length > 0) return true;
|
|
109
|
+
return !Array.isArray(group.pages) && !Array.isArray(group.groups);
|
|
110
|
+
}
|
|
111
|
+
|
|
79
112
|
export function buildLanguageNav(
|
|
80
113
|
englishTabs: NavTab[],
|
|
81
114
|
lang: string,
|
|
115
|
+
exists: PageExists = () => true,
|
|
82
116
|
): LanguageNav {
|
|
83
117
|
const t = NAV_TRANSLATIONS[lang];
|
|
84
118
|
if (!t) throw new Error(`No nav translations for language: ${lang}`);
|
|
@@ -97,13 +131,47 @@ export function buildLanguageNav(
|
|
|
97
131
|
Examples: t.examples,
|
|
98
132
|
};
|
|
99
133
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
134
|
+
// Rebuilt by SPREADING the English group, not by picking two fields off it.
|
|
135
|
+
// The old form silently dropped every other key — `expanded`, `icon`,
|
|
136
|
+
// `openapi` — so a localized nav quietly lost them, and it crashed outright
|
|
137
|
+
// on the first group that had no `pages` at all.
|
|
138
|
+
const localizeGroup = (group: NavGroup): NavGroup => {
|
|
139
|
+
const out: NavGroup = { ...group, group: groupNameMap[group.group] || group.group };
|
|
140
|
+
if (Array.isArray(group.pages)) {
|
|
141
|
+
// A page entry is a path to prefix, or a nested group to recurse into.
|
|
142
|
+
//
|
|
143
|
+
// A path is DROPPED when its localized file is not on disk. The English
|
|
144
|
+
// tree is the source of the nav, so without this every English page gets
|
|
145
|
+
// an entry in all fourteen languages whether or not it was translated —
|
|
146
|
+
// and `mintlify validate` rejects the run. That took down a nightly job
|
|
147
|
+
// that had already translated 784 pages: one page failed, the nav still
|
|
148
|
+
// referenced it in that language, and every successful page was discarded
|
|
149
|
+
// with it. Omitting the entry is the honest state: the page does not
|
|
150
|
+
// exist in that language yet.
|
|
151
|
+
out.pages = group.pages
|
|
152
|
+
.map((page) =>
|
|
153
|
+
typeof page === "string" ? `${lang}/${page}` : localizeGroup(page),
|
|
154
|
+
)
|
|
155
|
+
.filter((page) =>
|
|
156
|
+
typeof page === "string" ? exists(`${page}.mdx`) : hasContent(page),
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (Array.isArray(group.groups)) {
|
|
160
|
+
out.groups = group.groups.map(localizeGroup).filter(hasContent);
|
|
161
|
+
}
|
|
162
|
+
// No pages and no groups (an `openapi` group): carried through as-is. The
|
|
163
|
+
// spec is not translated, and dropping the group would remove the API
|
|
164
|
+
// reference from every non-English nav.
|
|
165
|
+
return out;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const tabs: NavTab[] = englishTabs
|
|
169
|
+
.map((tab) => ({
|
|
170
|
+
...tab,
|
|
171
|
+
tab: tabNameMap[tab.tab] || tab.tab,
|
|
172
|
+
groups: (tab.groups ?? []).map(localizeGroup).filter(hasContent),
|
|
173
|
+
}))
|
|
174
|
+
.filter((tab) => tab.groups.length > 0);
|
|
107
175
|
|
|
108
176
|
return {
|
|
109
177
|
language: getLanguageByCode(lang)?.mintlifyCode ?? lang,
|
|
@@ -125,10 +193,11 @@ export function readDocsConfig(): Record<string, unknown> {
|
|
|
125
193
|
export function generateLanguagesArray(
|
|
126
194
|
englishTabs: NavTab[],
|
|
127
195
|
langCodes: string[],
|
|
196
|
+
exists: PageExists = () => true,
|
|
128
197
|
): LanguageNav[] {
|
|
129
198
|
// English first (default)
|
|
130
199
|
const english: LanguageNav = { language: "en", tabs: englishTabs };
|
|
131
|
-
const others = langCodes.map((code) => buildLanguageNav(englishTabs, code));
|
|
200
|
+
const others = langCodes.map((code) => buildLanguageNav(englishTabs, code, exists));
|
|
132
201
|
return [english, ...others];
|
|
133
202
|
}
|
|
134
203
|
|
|
@@ -136,6 +205,9 @@ export function generateLanguagesArray(
|
|
|
136
205
|
export function localizeProductsNavigation(
|
|
137
206
|
products: unknown[],
|
|
138
207
|
langCodes: string[],
|
|
208
|
+
// Defaults to the real disk check, like updateDocsJson — injectable so the
|
|
209
|
+
// pure transform can be exercised against fixtures that are not on disk.
|
|
210
|
+
exists: PageExists = fileOnDisk,
|
|
139
211
|
): Record<string, unknown>[] {
|
|
140
212
|
return products.map((value) => {
|
|
141
213
|
const product = value as Record<string, unknown>;
|
|
@@ -149,7 +221,7 @@ export function localizeProductsNavigation(
|
|
|
149
221
|
const { tabs: _tabs, ...rest } = product;
|
|
150
222
|
return {
|
|
151
223
|
...rest,
|
|
152
|
-
languages: generateLanguagesArray(englishTabs, langCodes),
|
|
224
|
+
languages: generateLanguagesArray(englishTabs, langCodes, exists),
|
|
153
225
|
};
|
|
154
226
|
});
|
|
155
227
|
}
|
|
@@ -177,7 +249,7 @@ export function updateDocsJson(langCodes: string[]): void {
|
|
|
177
249
|
}
|
|
178
250
|
|
|
179
251
|
const newNav: Record<string, unknown> = {
|
|
180
|
-
languages: generateLanguagesArray(englishTabs, langCodes),
|
|
252
|
+
languages: generateLanguagesArray(englishTabs, langCodes, fileOnDisk),
|
|
181
253
|
};
|
|
182
254
|
if (nav.global) {
|
|
183
255
|
newNav.global = nav.global;
|