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,407 @@
|
|
|
1
|
+
"""API key provisioning: keys list / show / create / update / disable / regenerate.
|
|
2
|
+
|
|
3
|
+
Keys are referenced by their **name** (unique within the org) across ``show`` / ``update`` /
|
|
4
|
+
``disable`` / ``regenerate``. The group mirrors the ``users`` visual + permission language: a
|
|
5
|
+
boxed list, a ``show`` identity card + grouped permissions panel, and the SAME
|
|
6
|
+
``--permission-set`` / ``--add`` / ``--remove`` flags as ``users create`` / ``users update``.
|
|
7
|
+
|
|
8
|
+
A KEY stores a **flat permission list** (no persisted role/set), so ``--permission-set`` is
|
|
9
|
+
expanded **client-side** to seed the grants (matching the dashboard's key SetPicker); the
|
|
10
|
+
human-only permissions (``keys:update`` / ``orgs:admin``) are stripped from a seed and rejected
|
|
11
|
+
from an explicit ``--add`` (the server's ``key_assignable`` would 422 them).
|
|
12
|
+
|
|
13
|
+
The CLI generates the secret for ``create`` (the server never echoes it back) and prints it
|
|
14
|
+
exactly once. ``regenerate`` rotates the secret and prints the new one once. Secrets go to
|
|
15
|
+
**stdout** (capturable); the "shown once" warning goes to stderr.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import secrets
|
|
21
|
+
import sys
|
|
22
|
+
from typing import List, Optional
|
|
23
|
+
|
|
24
|
+
import typer
|
|
25
|
+
|
|
26
|
+
from .. import _click_compat as click # the Click Typer is running; see _click_compat
|
|
27
|
+
from .. import client as api
|
|
28
|
+
from .. import output, permissions
|
|
29
|
+
from .._context import (
|
|
30
|
+
GLOBALS_EPILOG,
|
|
31
|
+
AppState,
|
|
32
|
+
deny_in_key_mode,
|
|
33
|
+
require_auth,
|
|
34
|
+
resolve_fields,
|
|
35
|
+
)
|
|
36
|
+
from ..errors import ForbiddenError, NotFoundError
|
|
37
|
+
from ..models import ApiKey
|
|
38
|
+
from ..permissions import PermissionTokenError
|
|
39
|
+
from ..permissions import parse_permission_tokens as _parse_permissions
|
|
40
|
+
from . import _write
|
|
41
|
+
|
|
42
|
+
# ``PermissionTokenError`` / ``_parse_permissions`` live in ``permissions.py`` (shared with
|
|
43
|
+
# ``users``); re-exported here under their historical names so the keys flow + its tests keep
|
|
44
|
+
# importing them from this module.
|
|
45
|
+
__all__ = ["PermissionTokenError", "_parse_permissions", "register"]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _resolve_key_or_exit(state: AppState, keys, name: str) -> ApiKey:
|
|
49
|
+
"""Resolve a unique key NAME (or id) to its key, raising a typed error the central
|
|
50
|
+
chokepoint renders (JSON envelope under ``--json``, red box otherwise): none → exit 6,
|
|
51
|
+
several → exit 2."""
|
|
52
|
+
return _write.resolve_one(keys, name, kind="key", list_cmd="keys list")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _parse_key_tokens_or_exit(state: AppState, tokens) -> List[str]:
|
|
56
|
+
"""Expand the compact ``slug:act1.act2`` ``--add`` / ``--remove`` tokens for a KEY (empty when
|
|
57
|
+
none given). A malformed/unknown token — or a human-only permission (``keys:update`` /
|
|
58
|
+
``orgs:admin``) — → a clean usage error (exit 2), before any create/update."""
|
|
59
|
+
if not tokens:
|
|
60
|
+
return []
|
|
61
|
+
try:
|
|
62
|
+
return permissions.parse_key_permission_tokens(tokens)
|
|
63
|
+
except PermissionTokenError as exc:
|
|
64
|
+
raise click.UsageError(str(exc))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _expand_key_set_or_exit(state: AppState, cctx, set_name: str) -> List[str]:
|
|
68
|
+
"""Expand a ``--permission-set`` NAME → its **key-assignable** permissions, to seed a key's
|
|
69
|
+
flat grants (keys have no persisted set). Resolves against the ORG's sets (fetched, so custom
|
|
70
|
+
sets work like the dashboard's SetPicker), falling back to the built-in presets if that read
|
|
71
|
+
is unavailable. Human-only perms are stripped. Unknown set → clean error + exit 2."""
|
|
72
|
+
try:
|
|
73
|
+
sets = api.list_permission_sets(cctx)
|
|
74
|
+
except (ForbiddenError, NotFoundError):
|
|
75
|
+
sets = {}
|
|
76
|
+
if set_name in sets:
|
|
77
|
+
return permissions.key_assignable_only(sets[set_name])
|
|
78
|
+
if set_name in permissions.PRESETS:
|
|
79
|
+
return permissions.key_assignable_only(permissions.PRESETS[set_name])
|
|
80
|
+
available = sorted(set(sets) | (set(permissions.PRESETS) - {"clear"}))
|
|
81
|
+
raise click.UsageError(
|
|
82
|
+
f'unknown permission set "{set_name}". available: {", ".join(available)}'
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def keys_list(
|
|
87
|
+
ctx: typer.Context,
|
|
88
|
+
show_id: bool = typer.Option(False, "--show-id", help="Prepend a short key-id column (the full id is always in --json)."),
|
|
89
|
+
fields: Optional[str] = typer.Option(None, "--fields", help="Comma-separated subset of fields. e.g. `id,name,permissions`."),
|
|
90
|
+
) -> None:
|
|
91
|
+
"""List the org's API keys (metadata only — secrets are never returned), active keys first.
|
|
92
|
+
|
|
93
|
+
Shows `created · name · permissions · status` in a boxed table — **active keys sort to the
|
|
94
|
+
top** (then revoked), each group newest-first. The raw key id is hidden by default (use
|
|
95
|
+
`--show-id` for a short id, or `--json` for the full id). Needs `keys:read`. With `--json`:
|
|
96
|
+
`{"keys": [{id, name, permissions, created_at, revoked_at}]}`.
|
|
97
|
+
|
|
98
|
+
Example:
|
|
99
|
+
|
|
100
|
+
* `fp keys list`
|
|
101
|
+
* `fp --json keys list`
|
|
102
|
+
"""
|
|
103
|
+
state: AppState = ctx.obj
|
|
104
|
+
cctx = require_auth(state)
|
|
105
|
+
keys = api.list_keys(cctx)
|
|
106
|
+
cols = resolve_fields(fields, ApiKey)
|
|
107
|
+
if state.json:
|
|
108
|
+
payload = output.project_dicts(keys, cols) if cols else keys
|
|
109
|
+
output.emit_json({"keys": payload})
|
|
110
|
+
return
|
|
111
|
+
if cols:
|
|
112
|
+
# `--fields` asks for specific raw columns → the generic table (no bespoke styling).
|
|
113
|
+
output.print_table(list(cols), output.project_rows(keys, cols), title=f"API keys ({len(keys)})")
|
|
114
|
+
return
|
|
115
|
+
output.render_keys(keys, show_id=show_id)
|
|
116
|
+
output.keys_footer(keys)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def keys_show(
|
|
120
|
+
ctx: typer.Context,
|
|
121
|
+
name: str = typer.Argument(..., help="Key name (unique within the org)."),
|
|
122
|
+
) -> None:
|
|
123
|
+
"""Show one key's identity and full permissions — like `users show`, for an API key.
|
|
124
|
+
|
|
125
|
+
Renders an identity card (name, created date, permission count, status) then the shared
|
|
126
|
+
grouped permissions panel with **all** the key's grants. Referenced by **name**. Needs
|
|
127
|
+
`keys:read`. With `--json`: the full key object `{id, name, permissions, created_at, revoked_at}`.
|
|
128
|
+
|
|
129
|
+
Example:
|
|
130
|
+
|
|
131
|
+
* `fp keys show ci-bot`
|
|
132
|
+
"""
|
|
133
|
+
state: AppState = ctx.obj
|
|
134
|
+
cctx = require_auth(state)
|
|
135
|
+
key = _resolve_key_or_exit(state, api.list_keys(cctx), name)
|
|
136
|
+
if state.json:
|
|
137
|
+
output.emit_json(key)
|
|
138
|
+
return
|
|
139
|
+
output.render_key_show(key)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def keys_create(
|
|
143
|
+
ctx: typer.Context,
|
|
144
|
+
name: str = typer.Argument(..., help="Human-readable key name (unique within the org)."),
|
|
145
|
+
permission_set: Optional[str] = typer.Option(None, "--permission-set", help="Role to seed the key from — a permission set: `read-only`, `standard`, `admin`, or a custom set your org defines in the dashboard. The set is expanded into the key's grants (human-only perms are dropped). Omit for no base role."),
|
|
146
|
+
add: Optional[List[str]] = typer.Option(None, "--add", help="Grant extra permissions on top of the set, as `slug:action.action` tokens (dotted actions expand: `events:read.add` → `events:read`, `events:add`). Several via comma, repeated flag, or a quoted group: `--add events:read,keys:read` · `--add a --add b` · `--add \"a b\"`."),
|
|
147
|
+
remove: Optional[List[str]] = typer.Option(None, "--remove", help="Drop permissions from the set, same `slug:action.action` token format as --add (comma / repeated / quoted)."),
|
|
148
|
+
) -> None:
|
|
149
|
+
"""Create an API key and reveal its secret **once**.
|
|
150
|
+
|
|
151
|
+
Grant permissions exactly like `users create`: start from a role with `--permission-set`
|
|
152
|
+
(a built-in `read-only` / `standard` / `admin`, or a custom org set — expanded into the key's
|
|
153
|
+
grants), then fine-tune with `--add` / `--remove`. The effective grants are
|
|
154
|
+
`(set ∪ added) − removed`. `--add` / `--remove` take the compact `slug:action.action` token
|
|
155
|
+
format (dotted actions expand). Human-only permissions (`keys:update`) can't be granted to a
|
|
156
|
+
key.
|
|
157
|
+
|
|
158
|
+
The secret is generated locally; the server stores only a hash. Needs `keys:create`. With
|
|
159
|
+
`--json`: `{id, name, permissions, created_at, key}` — `key` is the only place the secret
|
|
160
|
+
appears, and `permissions` is the expanded flat list.
|
|
161
|
+
|
|
162
|
+
Examples:
|
|
163
|
+
|
|
164
|
+
* `fp keys create ci-bot --permission-set read-only` — a read-only key
|
|
165
|
+
* `fp keys create deployer --add events:read.add,keys:read` — just the listed grants
|
|
166
|
+
* `fp keys create ops --permission-set standard --add keys:create --remove agent:use` — a role, tuned
|
|
167
|
+
* `fp keys create ci-bot --permission-set read-only | pbcopy` — pipe captures just the secret
|
|
168
|
+
"""
|
|
169
|
+
state: AppState = ctx.obj
|
|
170
|
+
if not name.strip():
|
|
171
|
+
raise typer.BadParameter("key name must not be empty.", param_hint="NAME")
|
|
172
|
+
parsed_add = _parse_key_tokens_or_exit(state, add)
|
|
173
|
+
parsed_remove = _parse_key_tokens_or_exit(state, remove)
|
|
174
|
+
both = sorted(set(parsed_add) & set(parsed_remove))
|
|
175
|
+
if both:
|
|
176
|
+
raise typer.BadParameter(f"{', '.join(both)} given to both --add and --remove.")
|
|
177
|
+
cctx = require_auth(state)
|
|
178
|
+
if any(k.name == name for k in api.list_keys(cctx)): # names are unique
|
|
179
|
+
raise click.UsageError(f'a key named "{name}" already exists')
|
|
180
|
+
base = _expand_key_set_or_exit(state, cctx, permission_set) if permission_set else []
|
|
181
|
+
flat = sorted((set(base) | set(parsed_add)) - set(parsed_remove))
|
|
182
|
+
secret = secrets.token_hex(32) # 64 hex chars, mirrors the dashboard's generateToken()
|
|
183
|
+
result = api.create_key(cctx, name=name, key=secret, permissions=flat)
|
|
184
|
+
_write.record_action("api_key_created", resource="key", success=True, permission_count=len(flat))
|
|
185
|
+
perms = result.permissions or flat
|
|
186
|
+
if state.json:
|
|
187
|
+
output.emit_json({"id": result.id, "name": result.name, "permissions": perms,
|
|
188
|
+
"created_at": result.created_at, "key": secret})
|
|
189
|
+
return
|
|
190
|
+
if not sys.stdout.isatty():
|
|
191
|
+
print(secret) # piped/redirected → just the secret (capturable)
|
|
192
|
+
return
|
|
193
|
+
output.render_key_created(result) # green identity card
|
|
194
|
+
output.render_created_secret_box(secret) # the secret, shown once
|
|
195
|
+
output.permissions_box(perms) # the grouped grants
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def keys_update(
|
|
199
|
+
ctx: typer.Context,
|
|
200
|
+
name: str = typer.Argument(..., help="Key name to update (unique within the org)."),
|
|
201
|
+
permission_set: Optional[str] = typer.Option(None, "--permission-set", help="Reseed the key from a permission set: `read-only`, `standard`, `admin`, or a custom org set. REPLACES the key's grants with the set (then applies any --add/--remove). Human-only perms are dropped."),
|
|
202
|
+
add: Optional[List[str]] = typer.Option(None, "--add", help="Grant permissions, same `slug:action.action` token format as `keys create` (dotted actions expand; comma / repeated flag / quoted compose). Incremental — merged into the key's CURRENT grants, unless --permission-set is also given."),
|
|
203
|
+
remove: Optional[List[str]] = typer.Option(None, "--remove", help="Revoke permissions, same `slug:action.action` token format as --add. Incremental — applied to the key's CURRENT grants, unless --permission-set is also given."),
|
|
204
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirmation prompt. The prompt only appears on an interactive terminal: under --json, or with stdin redirected, this command proceeds without asking."),
|
|
205
|
+
) -> None:
|
|
206
|
+
"""Change an API key's permissions and show the diff — like `users update`, by key name.
|
|
207
|
+
|
|
208
|
+
Two ways to use it (combine if you like):
|
|
209
|
+
|
|
210
|
+
* **Reseed from a role** — `--permission-set <set>` replaces the key's grants with that set
|
|
211
|
+
(a built-in `read-only` / `standard` / `admin`, or a custom org set); add `--add`/`--remove`
|
|
212
|
+
to tune it.
|
|
213
|
+
* **Tweak incrementally** — `--add` / `--remove` **alone** adjust the key's *current* grants.
|
|
214
|
+
Both take the compact `slug:action.action` token format (dotted actions expand). Human-only
|
|
215
|
+
permissions (`keys:update`) can't be granted to a key.
|
|
216
|
+
|
|
217
|
+
The CLI reads the current grants, computes the result `(set ∪ added) − removed`, and shows a
|
|
218
|
+
git-style diff — added green, removed struck-through, unchanged dim. It confirms first (default
|
|
219
|
+
no; `--yes` skips); a no-op exits without calling the server. The key keeps working — only its
|
|
220
|
+
permissions change. Needs `keys:update`. With `--json`:
|
|
221
|
+
`{id, name, permissions, created_at, revoked_at, added, removed}`.
|
|
222
|
+
|
|
223
|
+
Examples:
|
|
224
|
+
|
|
225
|
+
* `fp keys update ci-bot --add keys:read` — grant on top of current grants
|
|
226
|
+
* `fp keys update ci-bot --remove events:add,agent:use` — revoke a couple of grants
|
|
227
|
+
* `fp keys update ci-bot --permission-set standard --yes` — reseed from the standard role
|
|
228
|
+
"""
|
|
229
|
+
state: AppState = ctx.obj
|
|
230
|
+
# The only `keys` subcommand an API key can never run: it needs `keys:update`, and
|
|
231
|
+
# `Permission::key_assignable` forbids that grant on ANY key (a bearer key may
|
|
232
|
+
# create keys, never edit one). So this is unreachable by construction, not just
|
|
233
|
+
# unlikely — say so here instead of letting the server's 403 imply "ask for the
|
|
234
|
+
# permission", which nobody can grant.
|
|
235
|
+
deny_in_key_mode(
|
|
236
|
+
state,
|
|
237
|
+
"keys update",
|
|
238
|
+
"it needs the keys:update permission, which cannot be granted to any API key",
|
|
239
|
+
)
|
|
240
|
+
if permission_set is None and not add and not remove:
|
|
241
|
+
raise typer.BadParameter("nothing to update — pass --permission-set, --add, and/or --remove.")
|
|
242
|
+
parsed_add = _parse_key_tokens_or_exit(state, add)
|
|
243
|
+
parsed_remove = _parse_key_tokens_or_exit(state, remove)
|
|
244
|
+
both = sorted(set(parsed_add) & set(parsed_remove))
|
|
245
|
+
if both:
|
|
246
|
+
raise typer.BadParameter(f"{', '.join(both)} given to both --add and --remove.")
|
|
247
|
+
cctx = require_auth(state)
|
|
248
|
+
key = _resolve_key_or_exit(state, api.list_keys(cctx), name)
|
|
249
|
+
before = set(key.permissions)
|
|
250
|
+
add_set, remove_set = set(parsed_add), set(parsed_remove)
|
|
251
|
+
|
|
252
|
+
if permission_set is not None:
|
|
253
|
+
# Reseed: the set is the base; --add/--remove are fresh tweaks on top.
|
|
254
|
+
base = _expand_key_set_or_exit(state, cctx, permission_set)
|
|
255
|
+
after = sorted((set(base) | add_set) - remove_set)
|
|
256
|
+
else:
|
|
257
|
+
# Incremental: apply the deltas to the key's CURRENT flat grants.
|
|
258
|
+
after = sorted((before | add_set) - remove_set)
|
|
259
|
+
after_set = set(after)
|
|
260
|
+
added, removed = sorted(after_set - before), sorted(before - after_set)
|
|
261
|
+
|
|
262
|
+
if not added and not removed: # no-op: don't call the server, don't prompt
|
|
263
|
+
if state.json:
|
|
264
|
+
output.emit_json({"id": key.id, "name": key.name, "permissions": sorted(before),
|
|
265
|
+
"created_at": key.created_at, "revoked_at": key.revoked_at,
|
|
266
|
+
"added": [], "removed": []})
|
|
267
|
+
else:
|
|
268
|
+
output.key_no_change()
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
proceed = (not _write.should_prompt(state, yes)) or output.confirm_key_update(
|
|
272
|
+
key.name, len(added), len(removed))
|
|
273
|
+
if not proceed:
|
|
274
|
+
if state.json:
|
|
275
|
+
output.emit_json({"cancelled": True})
|
|
276
|
+
else:
|
|
277
|
+
output.print_cancelled("permissions unchanged")
|
|
278
|
+
return
|
|
279
|
+
|
|
280
|
+
result = api.update_key(cctx, key.id, permissions=after)
|
|
281
|
+
final = set(result.permissions)
|
|
282
|
+
added, removed = sorted(final - before), sorted(before - final)
|
|
283
|
+
_write.record_action("api_key_updated", resource="key", success=True, permission_count=len(final))
|
|
284
|
+
if state.json:
|
|
285
|
+
output.emit_json({"id": result.id, "name": result.name, "permissions": sorted(final),
|
|
286
|
+
"created_at": result.created_at, "revoked_at": result.revoked_at,
|
|
287
|
+
"added": added, "removed": removed})
|
|
288
|
+
return
|
|
289
|
+
output.render_key_updated(result, added=added, removed=removed, union=sorted(before | final))
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def keys_disable(
|
|
293
|
+
ctx: typer.Context,
|
|
294
|
+
name: str = typer.Argument(..., help="Key name to disable (unique within the org)."),
|
|
295
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirmation prompt. The prompt only appears on an interactive terminal: under --json, or with stdin redirected, this command proceeds without asking."),
|
|
296
|
+
) -> None:
|
|
297
|
+
"""Disable (revoke) an API key by name. This cannot be undone.
|
|
298
|
+
|
|
299
|
+
Resolves the unique key name, confirms (amber, default no), then revokes it — anything
|
|
300
|
+
using the key stops working immediately. Needs `keys:disable`. With `--json`:
|
|
301
|
+
`{"name", "status": "disabled"}` (or `{"cancelled": true}` on a declined prompt).
|
|
302
|
+
|
|
303
|
+
Example:
|
|
304
|
+
|
|
305
|
+
* `fp keys disable "ci-bot" --yes`
|
|
306
|
+
"""
|
|
307
|
+
state: AppState = ctx.obj
|
|
308
|
+
cctx = require_auth(state)
|
|
309
|
+
key = _resolve_key_or_exit(state, api.list_keys(cctx), name)
|
|
310
|
+
if key.revoked_at: # already disabled — a no-op, not an error
|
|
311
|
+
if state.json:
|
|
312
|
+
output.emit_json({"name": key.name, "status": "disabled"})
|
|
313
|
+
else:
|
|
314
|
+
output.key_already_disabled(key.name)
|
|
315
|
+
return
|
|
316
|
+
if not _write.confirm_destructive(
|
|
317
|
+
state, "disable the key", key.name,
|
|
318
|
+
consequence="this revokes the key immediately — anything using it will stop working",
|
|
319
|
+
assume_yes=yes,
|
|
320
|
+
):
|
|
321
|
+
if state.json:
|
|
322
|
+
output.emit_json({"cancelled": True})
|
|
323
|
+
else:
|
|
324
|
+
output.print_cancelled()
|
|
325
|
+
return
|
|
326
|
+
api.disable_key(cctx, key.id)
|
|
327
|
+
_write.record_action("api_key_disabled", resource="key", success=True, destructive=True)
|
|
328
|
+
if state.json:
|
|
329
|
+
output.emit_json({"name": key.name, "status": "disabled"})
|
|
330
|
+
else:
|
|
331
|
+
output.key_disabled(key.name)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def keys_regenerate(
|
|
335
|
+
ctx: typer.Context,
|
|
336
|
+
name: str = typer.Argument(..., help="Key name to rotate the secret for (unique within the org)."),
|
|
337
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip the confirmation prompt. The prompt only appears on an interactive terminal: under --json, or with stdin redirected, this command proceeds without asking."),
|
|
338
|
+
) -> None:
|
|
339
|
+
"""Rotate an API key's secret by name and reveal the new secret **once**.
|
|
340
|
+
|
|
341
|
+
Resolves the unique key name, confirms (amber, default no), then rotates — the old secret
|
|
342
|
+
stops working immediately. The new secret is shown once: piping captures just the raw
|
|
343
|
+
secret (`fp keys regenerate admin -y | pbcopy`); interactively it's shown in a box.
|
|
344
|
+
Needs `keys:regenerate`. With `--json`: `{"name", "key"}` — the new secret under `key`, the
|
|
345
|
+
same field `keys create` uses (or `{"cancelled": true}` on a declined prompt).
|
|
346
|
+
|
|
347
|
+
Example:
|
|
348
|
+
|
|
349
|
+
* `fp keys regenerate "ci-bot" -y`
|
|
350
|
+
"""
|
|
351
|
+
state: AppState = ctx.obj
|
|
352
|
+
cctx = require_auth(state)
|
|
353
|
+
key = _resolve_key_or_exit(state, api.list_keys(cctx), name)
|
|
354
|
+
if not _write.confirm_destructive(
|
|
355
|
+
state, "regenerate the secret for key", key.name,
|
|
356
|
+
consequence="this revokes the current secret immediately and can't be undone",
|
|
357
|
+
assume_yes=yes,
|
|
358
|
+
):
|
|
359
|
+
if state.json:
|
|
360
|
+
output.emit_json({"cancelled": True})
|
|
361
|
+
else:
|
|
362
|
+
output.print_cancelled()
|
|
363
|
+
return
|
|
364
|
+
secret = api.regenerate_key(cctx, key.id)
|
|
365
|
+
_write.record_action("api_key_regenerated", resource="key", success=True)
|
|
366
|
+
if state.json:
|
|
367
|
+
output.emit_json({"name": key.name, "key": secret})
|
|
368
|
+
return
|
|
369
|
+
if sys.stdout.isatty():
|
|
370
|
+
output.render_secret_box(key.name, secret) # pretty box (with secret) → stderr
|
|
371
|
+
else:
|
|
372
|
+
print(secret) # piped/redirected → bare secret to stdout, capturable
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
_KEYS_GROUP_HELP = """Provision and manage API keys — list, inspect, create, and adjust their permissions.
|
|
376
|
+
|
|
377
|
+
Keys are referenced by **name** (unique in the org). A key carries a flat set of permissions;
|
|
378
|
+
grant them from a role (`--permission-set`) plus `--add` / `--remove` overrides — the same
|
|
379
|
+
language as `users`. The secret is shown **once**, at create / regenerate.
|
|
380
|
+
|
|
381
|
+
**Subcommands:** `list` · `show` · `create` · `update` · `disable` · `regenerate`
|
|
382
|
+
|
|
383
|
+
**Examples:**
|
|
384
|
+
|
|
385
|
+
* `fp keys list` — all keys, active first
|
|
386
|
+
* `fp keys show ci-bot` — one key's identity + full grants
|
|
387
|
+
* `fp keys create ci-bot --permission-set read-only` — invite a key with a role
|
|
388
|
+
* `fp keys update ci-bot --add keys:read --remove agent:use` — tweak grants
|
|
389
|
+
* `fp keys regenerate ci-bot -y` — rotate the secret (shown once)
|
|
390
|
+
* `fp keys disable ci-bot` — revoke it
|
|
391
|
+
"""
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def register(app: typer.Typer) -> None:
|
|
395
|
+
keys_app = typer.Typer(
|
|
396
|
+
no_args_is_help=True,
|
|
397
|
+
rich_markup_mode="markdown",
|
|
398
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
399
|
+
help=_KEYS_GROUP_HELP,
|
|
400
|
+
)
|
|
401
|
+
keys_app.command("list", epilog=GLOBALS_EPILOG)(keys_list)
|
|
402
|
+
keys_app.command("show", epilog=GLOBALS_EPILOG)(keys_show)
|
|
403
|
+
keys_app.command("create", epilog=GLOBALS_EPILOG)(keys_create)
|
|
404
|
+
keys_app.command("update", epilog=GLOBALS_EPILOG)(keys_update)
|
|
405
|
+
keys_app.command("disable", epilog=GLOBALS_EPILOG)(keys_disable)
|
|
406
|
+
keys_app.command("regenerate", epilog=GLOBALS_EPILOG)(keys_regenerate)
|
|
407
|
+
app.add_typer(keys_app, name="keys")
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""fp list — discover the distinct values behind the dashboard's filter dropdowns.
|
|
2
|
+
|
|
3
|
+
Each subcommand returns a flat list of strings for one facet (the same data that powers the
|
|
4
|
+
dashboard's dropdowns), handy for finding valid filter values to pass to `sessions` / `events`.
|
|
5
|
+
Every value comes from a per-org cached endpoint, so it stays cheap to call.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
|
|
12
|
+
from .. import client as api
|
|
13
|
+
from .. import output
|
|
14
|
+
from .._context import GLOBALS_EPILOG, AppState, require_auth
|
|
15
|
+
|
|
16
|
+
# Friendly `list <name>` -> (facet key in client._FACET_PATHS, required permission, one-liner,
|
|
17
|
+
# title description). The order here is the order shown in `fp list --help`.
|
|
18
|
+
_LIST_KINDS = {
|
|
19
|
+
"envs": ("environments", "events:read or evaluations:read", "Environments seen across events.", "seen across events"),
|
|
20
|
+
"agents": ("agent_ids", "events:read or evaluations:read", "Agent ids seen across events.", "seen across events"),
|
|
21
|
+
"event_types": ("event_types", "events:read or evaluations:read", "Event types (agent_start, tool_use, error, …).", "seen across events"),
|
|
22
|
+
"score_filters": ("score_filters", "evaluations:read", "Evaluation score keys / metrics (for `--score KEY:MIN..MAX`).", "evaluation score keys"),
|
|
23
|
+
"models": ("models", "events:read", "Model names seen across events.", "seen across events"),
|
|
24
|
+
"hooks": ("hook_names", "events:read", "Hook names seen across events.", "seen across events"),
|
|
25
|
+
"tools": ("tool_names", "events:read", "Tool names seen across events.", "seen across events"),
|
|
26
|
+
"error_types": ("error_types", "events:read", "Error types seen across events.", "seen across events"),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
def _make_list_cmd(name: str, facet_key: str, perm: str, summary: str, description: str):
|
|
30
|
+
"""Build the handler for one `list <name>` subcommand (a thin wrapper over the shared
|
|
31
|
+
facet fetch, so all nine share one code path)."""
|
|
32
|
+
|
|
33
|
+
def _cmd(ctx: typer.Context) -> None:
|
|
34
|
+
state: AppState = ctx.obj
|
|
35
|
+
cctx = require_auth(state)
|
|
36
|
+
values = api.list_facet(cctx, facet_key)
|
|
37
|
+
if state.json:
|
|
38
|
+
output.emit_json({"kind": name, "values": values})
|
|
39
|
+
return
|
|
40
|
+
output.render_value_list(name, values, description=description)
|
|
41
|
+
|
|
42
|
+
_cmd.__name__ = f"list_{name}"
|
|
43
|
+
_cmd.__doc__ = (
|
|
44
|
+
f"{summary}\n\n"
|
|
45
|
+
f" Needs `{perm}`. With `--json`: `{{\"kind\": \"{name}\", \"values\": [...]}}`.\n\n"
|
|
46
|
+
f" Example:\n\n"
|
|
47
|
+
f" * `fp --json list {name} | jq '.values'`"
|
|
48
|
+
)
|
|
49
|
+
return _cmd
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def register(app: typer.Typer) -> None:
|
|
53
|
+
list_app = typer.Typer(
|
|
54
|
+
no_args_is_help=True,
|
|
55
|
+
rich_markup_mode="markdown",
|
|
56
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
57
|
+
help="List the distinct values behind the dashboard's filter dropdowns. Subcommands: "
|
|
58
|
+
"**envs**, **agents**, **event_types**, **score_filters**, **models**, **hooks**, "
|
|
59
|
+
"**tools**, **error_types**.",
|
|
60
|
+
)
|
|
61
|
+
for name, (facet_key, perm, summary, description) in _LIST_KINDS.items():
|
|
62
|
+
list_app.command(name, epilog=GLOBALS_EPILOG)(_make_list_cmd(name, facet_key, perm, summary, description))
|
|
63
|
+
app.add_typer(list_app, name="list")
|