graph-agents-cli 0.3.1__py3-none-any.whl
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.
- graph_agents_cli/__init__.py +26 -0
- graph_agents_cli/_api_policy.py +2145 -0
- graph_agents_cli/_approvals.py +400 -0
- graph_agents_cli/_build.py +186 -0
- graph_agents_cli/_build_info.json +7 -0
- graph_agents_cli/_chat_client.py +462 -0
- graph_agents_cli/_click.py +157 -0
- graph_agents_cli/_defaults.py +139 -0
- graph_agents_cli/_experiments.py +64 -0
- graph_agents_cli/_http.py +192 -0
- graph_agents_cli/_output.py +83 -0
- graph_agents_cli/_project.py +462 -0
- graph_agents_cli/_remote.py +220 -0
- graph_agents_cli/_response_schema.py +264 -0
- graph_agents_cli/_runner.py +319 -0
- graph_agents_cli/_skills_check.py +274 -0
- graph_agents_cli/_tools.py +189 -0
- graph_agents_cli/_trust.py +66 -0
- graph_agents_cli/api/__init__.py +15 -0
- graph_agents_cli/api/_changes.py +506 -0
- graph_agents_cli/api/_files.py +658 -0
- graph_agents_cli/api/cmd_api.py +2480 -0
- graph_agents_cli/deploy/__init__.py +15 -0
- graph_agents_cli/deploy/_config.py +171 -0
- graph_agents_cli/deploy/_image.py +128 -0
- graph_agents_cli/deploy/_kube.py +286 -0
- graph_agents_cli/deploy/_modes.py +234 -0
- graph_agents_cli/deploy/_preflight.py +370 -0
- graph_agents_cli/deploy/_values.py +168 -0
- graph_agents_cli/deploy/cmd_deploy.py +1866 -0
- graph_agents_cli/deploy/gitops.py +562 -0
- graph_agents_cli/deploy/local_load.py +273 -0
- graph_agents_cli/dev/__init__.py +13 -0
- graph_agents_cli/dev/cmd_build.py +131 -0
- graph_agents_cli/dev/cmd_install.py +78 -0
- graph_agents_cli/dev/cmd_lint.py +119 -0
- graph_agents_cli/dev/cmd_playground.py +297 -0
- graph_agents_cli/dev/policy_check.py +1287 -0
- graph_agents_cli/eval/__init__.py +22 -0
- graph_agents_cli/eval/_client.py +670 -0
- graph_agents_cli/eval/_common.py +177 -0
- graph_agents_cli/eval/_judge.py +168 -0
- graph_agents_cli/eval/_judge_runner.py +238 -0
- graph_agents_cli/eval/_paths.py +212 -0
- graph_agents_cli/eval/checks.py +581 -0
- graph_agents_cli/eval/cmd_analyze.py +278 -0
- graph_agents_cli/eval/cmd_compare.py +284 -0
- graph_agents_cli/eval/cmd_eval_group.py +80 -0
- graph_agents_cli/eval/cmd_generate.py +558 -0
- graph_agents_cli/eval/cmd_grade.py +466 -0
- graph_agents_cli/eval/cmd_metric.py +156 -0
- graph_agents_cli/eval/cmd_run.py +370 -0
- graph_agents_cli/eval/cmd_submit.py +400 -0
- graph_agents_cli/eval/config.py +435 -0
- graph_agents_cli/eval/dataset.py +350 -0
- graph_agents_cli/eval/gate.py +420 -0
- graph_agents_cli/eval/transcript.py +192 -0
- graph_agents_cli/extension/__init__.py +13 -0
- graph_agents_cli/extension/_compat.py +86 -0
- graph_agents_cli/extension/_loader.py +293 -0
- graph_agents_cli/extension/_manifest.py +135 -0
- graph_agents_cli/extension/_overrides.py +195 -0
- graph_agents_cli/extension/_paths.py +91 -0
- graph_agents_cli/extension/_refs.py +193 -0
- graph_agents_cli/extension/_resolver.py +453 -0
- graph_agents_cli/extension/_schema.py +106 -0
- graph_agents_cli/extension/_spec.py +253 -0
- graph_agents_cli/extension/_sync.py +102 -0
- graph_agents_cli/extension/_trust.py +58 -0
- graph_agents_cli/extension/cmd_extension_add.py +259 -0
- graph_agents_cli/extension/cmd_extension_group.py +57 -0
- graph_agents_cli/extension/cmd_extension_list.py +56 -0
- graph_agents_cli/extension/cmd_extension_remove.py +61 -0
- graph_agents_cli/extension/cmd_extension_update.py +195 -0
- graph_agents_cli/info/__init__.py +13 -0
- graph_agents_cli/info/cmd_info.py +222 -0
- graph_agents_cli/infra/__init__.py +15 -0
- graph_agents_cli/infra/checks.py +1169 -0
- graph_agents_cli/infra/cmd_infra.py +103 -0
- graph_agents_cli/main.py +591 -0
- graph_agents_cli/peer/__init__.py +15 -0
- graph_agents_cli/peer/_generate.py +254 -0
- graph_agents_cli/peer/cmd_peer.py +1151 -0
- graph_agents_cli/run/__init__.py +13 -0
- graph_agents_cli/run/_local_server.py +1157 -0
- graph_agents_cli/run/_signals.py +141 -0
- graph_agents_cli/run/cmd_approvals.py +530 -0
- graph_agents_cli/run/cmd_run.py +1421 -0
- graph_agents_cli/scaffold/__init__.py +19 -0
- graph_agents_cli/scaffold/agents/README.md +24 -0
- graph_agents_cli/scaffold/agents/empty_py/.template/templateconfig.yaml +22 -0
- graph_agents_cli/scaffold/agents/langgraph/.env.example +292 -0
- graph_agents_cli/scaffold/agents/langgraph/.template/templateconfig.yaml +28 -0
- graph_agents_cli/scaffold/agents/langgraph/Dockerfile +59 -0
- graph_agents_cli/scaffold/agents/langgraph/Dockerfile.langgraph-server +59 -0
- graph_agents_cli/scaffold/agents/langgraph/README.md +571 -0
- graph_agents_cli/scaffold/agents/langgraph/api-policy.yaml +60 -0
- graph_agents_cli/scaffold/agents/langgraph/app/__init__.py +20 -0
- graph_agents_cli/scaffold/agents/langgraph/app/agent.py +174 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/__init__.py +15 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/a2a.py +2162 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/a2a_client.py +1167 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/api_client.py +4220 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/approvals.py +1349 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/auth.py +1986 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/chat.py +2962 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/checkpointer.py +432 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/content.py +569 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/db.py +580 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/limits.py +203 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/metrics.py +231 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/middleware.py +361 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/model.py +611 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/playground.py +230 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/run_locks.py +459 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/structured.py +755 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/telemetry.py +681 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/threads.py +493 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/token_exchange.py +959 -0
- graph_agents_cli/scaffold/agents/langgraph/app/fast_api_app.py +770 -0
- graph_agents_cli/scaffold/agents/langgraph/app/policies/__init__.py +55 -0
- graph_agents_cli/scaffold/agents/langgraph/app/policies/custom.py +97 -0
- graph_agents_cli/scaffold/agents/langgraph/app/tools/__init__.py +46 -0
- graph_agents_cli/scaffold/agents/langgraph/app/tools/example_api.py +92 -0
- graph_agents_cli/scaffold/agents/langgraph/app/tools/weather.py +33 -0
- graph_agents_cli/scaffold/agents/langgraph/langgraph.json +14 -0
- graph_agents_cli/scaffold/agents/langgraph/pyproject.toml +78 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/conftest.py +376 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/eval/datasets/basic-dataset.json +53 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/eval/eval_config.yaml +32 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/approval_graph.py +137 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/fake_issuer.py +216 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/fake_openai.py +357 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_a2a_outcomes.py +569 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_a2a_relay.py +479 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_api_surface.py +812 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_approvals.py +1367 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_approvals_server.py +794 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_cross_actor.py +497 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_cross_actor_server.py +247 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_history_repair.py +278 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_model_apis.py +242 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_postgres.py +637 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_resilience_postgres.py +770 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_runtime_guardrails.py +854 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_server_e2e.py +340 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_server_runtime.py +989 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_structured_answers.py +584 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_structured_server.py +222 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_token_exchange_issuer.py +650 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/load_test/.results/.placeholder +0 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/load_test/README.md +22 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/load_test/conftest.py +21 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/load_test/load_test.py +81 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_a2a_client.py +824 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_a2a_scoping.py +724 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_client.py +1214 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_client_hardening.py +716 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_policy_rpc.py +767 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_approval_ledger.py +1536 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_fake_model.py +115 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_jwt_policy.py +991 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_limits.py +310 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_logging.py +148 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_logging_hardening.py +271 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_policy.py +378 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_resilience.py +610 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_server_auth.py +702 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_structured.py +673 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_telemetry.py +404 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_thread_listing.py +255 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_threads.py +268 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_token_exchange.py +1320 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_untrusted_content.py +393 -0
- graph_agents_cli/scaffold/agents/langgraph/uv-fastapi.lock +2084 -0
- graph_agents_cli/scaffold/agents/langgraph/uv-langgraph-server.lock +2106 -0
- graph_agents_cli/scaffold/agents/langgraph/{{cookiecutter.agent_guidance_filename}} +129 -0
- graph_agents_cli/scaffold/base_templates/_shared/graph-agents-cli-manifest.yaml +36 -0
- graph_agents_cli/scaffold/base_templates/python/.dockerignore +32 -0
- graph_agents_cli/scaffold/base_templates/python/.github/CODEOWNERS +30 -0
- graph_agents_cli/scaffold/base_templates/python/.github/agent.env +7 -0
- graph_agents_cli/scaffold/base_templates/python/.github/workflows/pr_checks.yaml +214 -0
- graph_agents_cli/scaffold/base_templates/python/.gitignore +209 -0
- graph_agents_cli/scaffold/base_templates/python/tests/unit/test_dummy.py +23 -0
- graph_agents_cli/scaffold/base_templates/python/{{cookiecutter.agent_guidance_filename}} +35 -0
- graph_agents_cli/scaffold/cmd_scaffold_group.py +49 -0
- graph_agents_cli/scaffold/commands/__init__.py +13 -0
- graph_agents_cli/scaffold/commands/create.py +1424 -0
- graph_agents_cli/scaffold/commands/enhance.py +1652 -0
- graph_agents_cli/scaffold/commands/upgrade.py +570 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/agent.env +12 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/workflows/promote-to-prod.yaml +371 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/workflows/staging.yaml +450 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-dev.yaml +43 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-prod.yaml +41 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-staging.yaml +43 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/.helmignore +14 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/Chart.yaml +21 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/examples/networkpolicy.yaml +103 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/NOTES.txt +48 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/_helpers.tpl +189 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/certificate.yaml +15 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/configmap.yaml +10 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/deployment.yaml +199 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/hpa.yaml +22 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/httproute.yaml +30 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/ingress.yaml +39 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/networkpolicy.yaml +48 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/pdb.yaml +13 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/postgresql-secret.yaml +37 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/service.yaml +15 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/serviceaccount.yaml +13 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/servicemonitor.yaml +42 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-dev.yaml +22 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-prod.yaml +45 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-staging.yaml +29 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values.yaml +396 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/tests/integration/test_chart.py +269 -0
- graph_agents_cli/scaffold/deployment_targets/none/README.md +5 -0
- graph_agents_cli/scaffold/deployment_targets/none/python/README.md +6 -0
- graph_agents_cli/scaffold/utils/__init__.py +13 -0
- graph_agents_cli/scaffold/utils/backup.py +212 -0
- graph_agents_cli/scaffold/utils/build_record.py +257 -0
- graph_agents_cli/scaffold/utils/cli_options.py +184 -0
- graph_agents_cli/scaffold/utils/fs.py +83 -0
- graph_agents_cli/scaffold/utils/generate_locks.py +214 -0
- graph_agents_cli/scaffold/utils/generation_metadata.py +88 -0
- graph_agents_cli/scaffold/utils/keyedit.py +768 -0
- graph_agents_cli/scaffold/utils/keymerge.py +537 -0
- graph_agents_cli/scaffold/utils/language.py +138 -0
- graph_agents_cli/scaffold/utils/lock_utils.py +94 -0
- graph_agents_cli/scaffold/utils/logging.py +77 -0
- graph_agents_cli/scaffold/utils/manifest.py +292 -0
- graph_agents_cli/scaffold/utils/merge.py +970 -0
- graph_agents_cli/scaffold/utils/merge3.py +216 -0
- graph_agents_cli/scaffold/utils/openapi_seed.py +199 -0
- graph_agents_cli/scaffold/utils/remote_template.py +376 -0
- graph_agents_cli/scaffold/utils/template.py +1352 -0
- graph_agents_cli/scaffold/utils/upgrade.py +894 -0
- graph_agents_cli/scaffold/utils/version.py +438 -0
- graph_agents_cli/secrets/__init__.py +15 -0
- graph_agents_cli/secrets/_apply.py +954 -0
- graph_agents_cli/secrets/_required.py +188 -0
- graph_agents_cli/secrets/cmd_secrets.py +211 -0
- graph_agents_cli/setup/__init__.py +13 -0
- graph_agents_cli/setup/_antigravity.py +221 -0
- graph_agents_cli/setup/cmd_auth.py +1030 -0
- graph_agents_cli/setup/cmd_dev_token.py +513 -0
- graph_agents_cli/setup/cmd_setup.py +428 -0
- graph_agents_cli/setup/cmd_update.py +140 -0
- graph_agents_cli/skills/__init__.py +13 -0
- graph_agents_cli/skills/_bundle.py +65 -0
- graph_agents_cli/skills/data/README.md +19 -0
- graph_agents_cli/skills/data/graph-agents-cli-deploy/SKILL.md +357 -0
- graph_agents_cli/skills/data/graph-agents-cli-deploy/references/github-settings.md +113 -0
- graph_agents_cli/skills/data/graph-agents-cli-deploy/references/gitops.md +137 -0
- graph_agents_cli/skills/data/graph-agents-cli-deploy/references/kubernetes.md +315 -0
- graph_agents_cli/skills/data/graph-agents-cli-deploy/references/secrets.md +160 -0
- graph_agents_cli/skills/data/graph-agents-cli-eval/SKILL.md +303 -0
- graph_agents_cli/skills/data/graph-agents-cli-eval/references/dataset_schema.md +282 -0
- graph_agents_cli/skills/data/graph-agents-cli-eval/references/metrics-guide.md +143 -0
- graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/SKILL.md +659 -0
- graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/langchain-models.md +124 -0
- graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/langgraph.md +235 -0
- graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/template-contract.md +477 -0
- graph_agents_cli/skills/data/graph-agents-cli-observability/SKILL.md +231 -0
- graph_agents_cli/skills/data/graph-agents-cli-observability/references/langsmith.md +46 -0
- graph_agents_cli/skills/data/graph-agents-cli-observability/references/otel.md +59 -0
- graph_agents_cli/skills/data/graph-agents-cli-scaffold/SKILL.md +414 -0
- graph_agents_cli/skills/data/graph-agents-cli-scaffold/references/flags.md +134 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/SKILL.md +478 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/references/brainstorming.md +118 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/references/commands.md +419 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/references/extension.md +156 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/references/internals.md +67 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/references/spec-template.md +56 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/references/terminology.md +119 -0
- graph_agents_cli/system/__init__.py +15 -0
- graph_agents_cli/system/_apply.py +519 -0
- graph_agents_cli/system/_checks.py +1023 -0
- graph_agents_cli/system/_deploy.py +215 -0
- graph_agents_cli/system/_model.py +363 -0
- graph_agents_cli/system/_system.py +664 -0
- graph_agents_cli/system/_views.py +208 -0
- graph_agents_cli/system/cmd_system.py +423 -0
- graph_agents_cli-0.3.1.dist-info/METADATA +162 -0
- graph_agents_cli-0.3.1.dist-info/RECORD +291 -0
- graph_agents_cli-0.3.1.dist-info/WHEEL +4 -0
- graph_agents_cli-0.3.1.dist-info/entry_points.txt +2 -0
- graph_agents_cli-0.3.1.dist-info/licenses/LICENSE +201 -0
- graph_agents_cli-0.3.1.dist-info/licenses/NOTICE +19 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Copyright 2026 graph-agents-cli contributors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Shared defaults: model providers, runtimes, targets, CD modes.
|
|
16
|
+
|
|
17
|
+
Model names are placeholders; verify
|
|
18
|
+
them at release time. Nothing here imports a model SDK.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import sys
|
|
24
|
+
from collections.abc import Sequence
|
|
25
|
+
|
|
26
|
+
MODEL_PROVIDERS: tuple[str, ...] = ("openai", "anthropic", "gemini", "openai-compatible")
|
|
27
|
+
|
|
28
|
+
# Test-only provider (LangChain GenericFakeChatModel); never offered by `create`.
|
|
29
|
+
FAKE_PROVIDER = "fake"
|
|
30
|
+
|
|
31
|
+
DEFAULT_MODEL_PROVIDER = "openai"
|
|
32
|
+
|
|
33
|
+
DEFAULT_MODELS: dict[str, str] = {
|
|
34
|
+
"openai": "gpt-5-mini",
|
|
35
|
+
"anthropic": "claude-sonnet-5",
|
|
36
|
+
"gemini": "gemini-3.8-flash",
|
|
37
|
+
"openai-compatible": "qwen2.5:14b",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
PROVIDER_KEY_VARS: dict[str, str] = {
|
|
41
|
+
"openai": "OPENAI_API_KEY",
|
|
42
|
+
"anthropic": "ANTHROPIC_API_KEY",
|
|
43
|
+
"gemini": "GOOGLE_API_KEY",
|
|
44
|
+
"openai-compatible": "MODEL_API_KEY",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
RUNTIMES: tuple[str, ...] = ("fastapi", "langgraph-server")
|
|
48
|
+
DEFAULT_RUNTIME = "fastapi"
|
|
49
|
+
|
|
50
|
+
CHECKPOINTERS: tuple[str, ...] = ("memory", "postgres")
|
|
51
|
+
|
|
52
|
+
DEPLOYMENT_TARGETS: tuple[str, ...] = ("kubernetes", "none")
|
|
53
|
+
DEFAULT_DEPLOYMENT_TARGET = "kubernetes"
|
|
54
|
+
|
|
55
|
+
CD_MODES: tuple[str, ...] = ("argocd", "helm-push", "skip")
|
|
56
|
+
DEFAULT_CD = "skip"
|
|
57
|
+
|
|
58
|
+
AUTH_POLICIES: tuple[str, ...] = ("shared-bearer", "jwt", "custom")
|
|
59
|
+
DEFAULT_AUTH_POLICY = "shared-bearer"
|
|
60
|
+
# Policies scaffolded as a stub the project implements: the manifest records
|
|
61
|
+
# auth_policy_implemented: false for them, and deploy to staging/prod refuses
|
|
62
|
+
# until the developer flips it.
|
|
63
|
+
STUB_AUTH_POLICIES: frozenset[str] = frozenset({"custom"})
|
|
64
|
+
# Retired policy names still accepted from an existing manifest or AUTH_POLICY.
|
|
65
|
+
LEGACY_AUTH_POLICY_ALIASES: dict[str, str] = {"product-session": "custom"}
|
|
66
|
+
|
|
67
|
+
_warned_aliases: set[str] = set()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def normalize_auth_policy(value: str | None, *, warn: bool = True) -> str:
|
|
71
|
+
"""The current name of an auth policy read from a manifest or the environment.
|
|
72
|
+
|
|
73
|
+
``product-session`` is the retired name of ``custom``: it maps to ``custom``
|
|
74
|
+
with a one-line deprecation warning (once per process). ``None`` or an
|
|
75
|
+
empty value is the default policy.
|
|
76
|
+
"""
|
|
77
|
+
name = (value or DEFAULT_AUTH_POLICY).strip().lower()
|
|
78
|
+
alias = LEGACY_AUTH_POLICY_ALIASES.get(name)
|
|
79
|
+
if alias is None:
|
|
80
|
+
return name
|
|
81
|
+
if warn and name not in _warned_aliases:
|
|
82
|
+
_warned_aliases.add(name)
|
|
83
|
+
print(
|
|
84
|
+
f"Warning: auth_policy '{name}' is deprecated and read as '{alias}'; "
|
|
85
|
+
f"record auth_policy: {alias} in graph-agents-cli-manifest.yaml.",
|
|
86
|
+
file=sys.stderr,
|
|
87
|
+
)
|
|
88
|
+
return alias
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def auth_policy_implemented_default(auth_policy: str) -> bool:
|
|
92
|
+
"""False for a policy scaffolded as a stub (``custom``), True otherwise."""
|
|
93
|
+
return normalize_auth_policy(auth_policy, warn=False) not in STUB_AUTH_POLICIES
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
ENVIRONMENTS: tuple[str, ...] = ("dev", "staging", "prod")
|
|
97
|
+
|
|
98
|
+
DEFAULT_REGISTRY_HOST = "ghcr.io"
|
|
99
|
+
DEFAULT_REGISTRY_PLACEHOLDER = "ghcr.io/CHANGE-ME"
|
|
100
|
+
# The one command that replaces the placeholder everywhere `create` wrote it:
|
|
101
|
+
# `build` and `deploy` read the manifest, Argo CD and the chart read the values,
|
|
102
|
+
# and the CI workflows read .github/agent.env.
|
|
103
|
+
REGISTRY_FIX_COMMAND = "graph-agents-cli scaffold enhance --registry <host>/<org>"
|
|
104
|
+
REGISTRY_FIX_EFFECT = (
|
|
105
|
+
"it sets create_params.registry in graph-agents-cli-manifest.yaml, image.repository "
|
|
106
|
+
"in the chart's values.yaml and IMAGE_REPOSITORY in .github/agent.env"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
DEFAULT_AGENT_GUIDANCE_FILENAME = "AGENTS.md"
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def default_secret_keys(
|
|
113
|
+
model_provider: str,
|
|
114
|
+
runtime: str,
|
|
115
|
+
api_token_envs: Sequence[str] = (),
|
|
116
|
+
*,
|
|
117
|
+
auth_policy: str = "shared-bearer",
|
|
118
|
+
) -> list[str]:
|
|
119
|
+
"""Allow-listed Secret keys for a new project.
|
|
120
|
+
|
|
121
|
+
The list follows the model provider (its key variable), the runtime (the
|
|
122
|
+
connection strings it reads) and the auth policy (``API_KEY`` only under
|
|
123
|
+
``shared-bearer``, the one policy that reads it). ``api_token_envs`` are
|
|
124
|
+
the ``token_env`` variables of the ``auth: bearer`` APIs in
|
|
125
|
+
``api-policy.yaml``; they are appended so each token reaches the
|
|
126
|
+
environment's Secret.
|
|
127
|
+
"""
|
|
128
|
+
keys = [PROVIDER_KEY_VARS.get(model_provider, "MODEL_API_KEY"), "JUDGE_API_KEY"]
|
|
129
|
+
if runtime == "langgraph-server":
|
|
130
|
+
keys += ["DATABASE_URI", "REDIS_URI"]
|
|
131
|
+
else:
|
|
132
|
+
keys.append("POSTGRES_DSN")
|
|
133
|
+
if normalize_auth_policy(auth_policy, warn=False) == "shared-bearer":
|
|
134
|
+
keys.append("API_KEY")
|
|
135
|
+
keys.append("LANGSMITH_API_KEY")
|
|
136
|
+
for token_env in api_token_envs:
|
|
137
|
+
if token_env and token_env not in keys:
|
|
138
|
+
keys.append(token_env)
|
|
139
|
+
return keys
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Copyright 2026 Google LLC
|
|
2
|
+
# Modifications Copyright 2026 graph-agents-cli contributors
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""Experiment flags.
|
|
17
|
+
|
|
18
|
+
The registry is intentionally empty (no feature is experimental today): the
|
|
19
|
+
mechanism is kept so a future flag can be gated behind
|
|
20
|
+
``GRAPH_AGENTS_CLI_EXPERIMENTS='{"label": value}'`` without re-inventing it.
|
|
21
|
+
The former ``build_command`` gate is gone; ``build`` is always available.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
import logging
|
|
26
|
+
import os
|
|
27
|
+
from typing import Any, NamedTuple
|
|
28
|
+
|
|
29
|
+
EXPERIMENTS_ENV = "GRAPH_AGENTS_CLI_EXPERIMENTS"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Experiment(NamedTuple):
|
|
33
|
+
label: str
|
|
34
|
+
value_type: type
|
|
35
|
+
default_value: Any
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Central registry of experiments: new experiment flags get added here.
|
|
39
|
+
_REGISTRY: dict[str, Experiment] = {}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def resolve_experiment(label: str) -> Any:
|
|
43
|
+
"""Returns the value for the given experiment label."""
|
|
44
|
+
if label not in _REGISTRY:
|
|
45
|
+
raise ValueError(f"Unknown experiment: {label}")
|
|
46
|
+
|
|
47
|
+
exp = _REGISTRY[label]
|
|
48
|
+
|
|
49
|
+
# Check for environment variable override
|
|
50
|
+
env_val = os.environ.get(EXPERIMENTS_ENV)
|
|
51
|
+
if env_val:
|
|
52
|
+
try:
|
|
53
|
+
overrides = json.loads(env_val)
|
|
54
|
+
if label in overrides:
|
|
55
|
+
val = overrides[label]
|
|
56
|
+
# Cast to correct type if necessary
|
|
57
|
+
return exp.value_type(val)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
logging.warning(
|
|
60
|
+
f"Failed to apply {EXPERIMENTS_ENV} override for '{label}', "
|
|
61
|
+
f"using default ({exp.default_value}). Error: {e}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return exp.default_value
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Copyright 2026 graph-agents-cli contributors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""HTTP clients for the CLI's own requests, and how they treat proxies.
|
|
16
|
+
|
|
17
|
+
httpx takes ``HTTP_PROXY``, ``HTTPS_PROXY``, ``ALL_PROXY`` and ``NO_PROXY`` from
|
|
18
|
+
the environment (and, on macOS, the system proxy settings), and it builds a
|
|
19
|
+
transport for every proxy it finds when a client is created, whatever host the
|
|
20
|
+
request is for. So a proxy it cannot use (an unknown scheme, or a SOCKS proxy
|
|
21
|
+
without the ``socksio`` package) breaks every request, and ``NO_PROXY`` does
|
|
22
|
+
not help. Coding-agent sandboxes set exactly such variables (Codex's network
|
|
23
|
+
proxy: an HTTP proxy in ``HTTP(S)_PROXY`` and a ``socks5h://`` one in
|
|
24
|
+
``ALL_PROXY``).
|
|
25
|
+
|
|
26
|
+
The rules here:
|
|
27
|
+
|
|
28
|
+
* A request to this machine (``localhost``, ``*.localhost``, a loopback or
|
|
29
|
+
unspecified address) never goes through a proxy: its client ignores the
|
|
30
|
+
environment (``trust_env=False``). That is the local server of ``run``,
|
|
31
|
+
``eval`` and ``approvals``, and the playground.
|
|
32
|
+
* Any other request honours the environment's proxies, SOCKS included
|
|
33
|
+
(graph-agents-cli depends on ``httpx[socks]``). A proxy setting httpx cannot
|
|
34
|
+
use is a :class:`ProxyConfigError`: one line that names the variable, exit 3.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
import importlib.util
|
|
40
|
+
import ipaddress
|
|
41
|
+
import os
|
|
42
|
+
import urllib.request
|
|
43
|
+
from typing import Any
|
|
44
|
+
from urllib.parse import urlsplit, urlunsplit
|
|
45
|
+
|
|
46
|
+
import click
|
|
47
|
+
import httpx
|
|
48
|
+
|
|
49
|
+
EXIT_CONFIG_ERROR = 3
|
|
50
|
+
# The proxy schemes httpx can use; socks5 and socks5h need the socksio package.
|
|
51
|
+
SUPPORTED_PROXY_SCHEMES = ("http", "https", "socks5", "socks5h")
|
|
52
|
+
SOCKS_SCHEMES = ("socks5", "socks5h")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ProxyConfigError(click.ClickException):
|
|
56
|
+
"""A proxy setting in the environment that the CLI's HTTP client cannot use (exit 3)."""
|
|
57
|
+
|
|
58
|
+
exit_code = EXIT_CONFIG_ERROR
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def is_local(url: str | httpx.URL) -> bool:
|
|
62
|
+
"""Whether ``url`` names this machine: localhost, a loopback or an unspecified address."""
|
|
63
|
+
try:
|
|
64
|
+
host = httpx.URL(url).host
|
|
65
|
+
except (httpx.InvalidURL, TypeError, ValueError):
|
|
66
|
+
return False
|
|
67
|
+
host = host.rstrip(".").lower()
|
|
68
|
+
if host == "localhost" or host.endswith(".localhost"):
|
|
69
|
+
return True
|
|
70
|
+
try:
|
|
71
|
+
address = ipaddress.ip_address(host)
|
|
72
|
+
except ValueError:
|
|
73
|
+
return False
|
|
74
|
+
return address.is_loopback or address.is_unspecified
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _redacted(value: str) -> str:
|
|
78
|
+
"""``value`` without the ``user:password@`` of a proxy URL."""
|
|
79
|
+
try:
|
|
80
|
+
parts = urlsplit(value)
|
|
81
|
+
except ValueError:
|
|
82
|
+
return "<unparseable>"
|
|
83
|
+
if "@" not in parts.netloc:
|
|
84
|
+
return value
|
|
85
|
+
return urlunsplit(parts._replace(netloc="***@" + parts.netloc.rsplit("@", 1)[1]))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _variable(kind: str) -> str:
|
|
89
|
+
"""The environment variable httpx read the ``kind`` (http, https, all) proxy from."""
|
|
90
|
+
for name in (f"{kind}_proxy", f"{kind.upper()}_PROXY"):
|
|
91
|
+
if os.environ.get(name):
|
|
92
|
+
return name
|
|
93
|
+
return f"the system {kind.upper()} proxy setting"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def env_proxy_problem() -> str | None:
|
|
97
|
+
"""Why the environment's proxy settings would break an httpx client, or None when usable.
|
|
98
|
+
|
|
99
|
+
It checks what httpx itself reads (``urllib.request.getproxies``): every proxy
|
|
100
|
+
it would build a transport for, not only the one a request would use.
|
|
101
|
+
"""
|
|
102
|
+
try:
|
|
103
|
+
proxies = urllib.request.getproxies()
|
|
104
|
+
except Exception: # an unreadable system setting: httpx fails on it too, but say nothing
|
|
105
|
+
return None
|
|
106
|
+
for kind in ("all", "http", "https"):
|
|
107
|
+
value = (proxies.get(kind) or "").strip()
|
|
108
|
+
if not value:
|
|
109
|
+
continue
|
|
110
|
+
where = f"{_variable(kind)}={_redacted(value)}"
|
|
111
|
+
scheme = value.split("://", 1)[0].lower() if "://" in value else "http"
|
|
112
|
+
if scheme not in SUPPORTED_PROXY_SCHEMES:
|
|
113
|
+
return (
|
|
114
|
+
f"the proxy in {where} cannot be used: its scheme {scheme!r} is not one of "
|
|
115
|
+
f"{', '.join(SUPPORTED_PROXY_SCHEMES)}. Fix or unset {_variable(kind)} "
|
|
116
|
+
"(requests to this machine never use a proxy)."
|
|
117
|
+
)
|
|
118
|
+
if scheme in SOCKS_SCHEMES and importlib.util.find_spec("socksio") is None:
|
|
119
|
+
return (
|
|
120
|
+
f"the SOCKS proxy in {where} needs the socksio package, which this "
|
|
121
|
+
"installation lacks: reinstall graph-agents-cli (it depends on httpx[socks]) "
|
|
122
|
+
f"or unset {_variable(kind)} (requests to this machine never use a proxy)."
|
|
123
|
+
)
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def check_env_proxies(url: str | httpx.URL) -> None:
|
|
128
|
+
"""Raise :class:`ProxyConfigError` when a request to ``url`` would fail on a proxy setting.
|
|
129
|
+
|
|
130
|
+
Nothing to check for a local ``url``: its requests ignore the environment.
|
|
131
|
+
"""
|
|
132
|
+
if is_local(url):
|
|
133
|
+
return
|
|
134
|
+
problem = env_proxy_problem()
|
|
135
|
+
if problem:
|
|
136
|
+
raise ProxyConfigError(f"Cannot send requests to {_origin(url)}: {problem}")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _origin(url: str | httpx.URL) -> str:
|
|
140
|
+
try:
|
|
141
|
+
parsed = httpx.URL(url)
|
|
142
|
+
except (httpx.InvalidURL, TypeError, ValueError):
|
|
143
|
+
return "the agent"
|
|
144
|
+
port = f":{parsed.port}" if parsed.port else ""
|
|
145
|
+
return f"{parsed.scheme}://{parsed.host}{port}"
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _options(url: str | httpx.URL, kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
149
|
+
if is_local(url):
|
|
150
|
+
return {**kwargs, "trust_env": False}
|
|
151
|
+
check_env_proxies(url)
|
|
152
|
+
return kwargs
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _built(factory: Any, url: str | httpx.URL, kwargs: dict[str, Any]) -> Any:
|
|
156
|
+
options = _options(url, kwargs)
|
|
157
|
+
try:
|
|
158
|
+
return factory(**options)
|
|
159
|
+
except ImportError as exc: # a proxy transport whose package is missing
|
|
160
|
+
raise ProxyConfigError(f"Cannot send requests to {_origin(url)}: {exc}") from exc
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def client(url: str | httpx.URL, **kwargs: Any) -> httpx.Client:
|
|
164
|
+
"""An ``httpx.Client`` for requests to ``url``'s origin (see the module's proxy rules)."""
|
|
165
|
+
return _built(httpx.Client, url, kwargs)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def async_client(url: str | httpx.URL, **kwargs: Any) -> httpx.AsyncClient:
|
|
169
|
+
"""An ``httpx.AsyncClient`` for requests to ``url``'s origin (see the module's proxy rules)."""
|
|
170
|
+
return _built(httpx.AsyncClient, url, kwargs)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def request(
|
|
174
|
+
method: str,
|
|
175
|
+
url: str | httpx.URL,
|
|
176
|
+
*,
|
|
177
|
+
timeout: float | httpx.Timeout | None = 5.0,
|
|
178
|
+
**kwargs: Any,
|
|
179
|
+
) -> httpx.Response:
|
|
180
|
+
"""One request, like ``httpx.request``, with the module's proxy rules."""
|
|
181
|
+
with client(url, timeout=timeout) as http:
|
|
182
|
+
return http.request(method, url, **kwargs)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def get(url: str | httpx.URL, **kwargs: Any) -> httpx.Response:
|
|
186
|
+
"""``GET url``, like ``httpx.get``, with the module's proxy rules."""
|
|
187
|
+
return request("GET", url, **kwargs)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def delete(url: str | httpx.URL, **kwargs: Any) -> httpx.Response:
|
|
191
|
+
"""``DELETE url``, like ``httpx.delete``, with the module's proxy rules."""
|
|
192
|
+
return request("DELETE", url, **kwargs)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Copyright 2026 Google LLC
|
|
2
|
+
# Modifications Copyright 2026 graph-agents-cli contributors
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""Output helpers for agents CLI."""
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
from enum import IntEnum
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from rich.console import Console as _RichConsole
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ExitCode(IntEnum):
|
|
28
|
+
OK = 0
|
|
29
|
+
ERROR = 1
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# The widest a table grows off a terminal (CI logs, a pipe, a file) when COLUMNS is
|
|
33
|
+
# unset: Rich would use 80 columns there and cut long cells with "…".
|
|
34
|
+
NON_TERMINAL_TABLE_WIDTH = 250
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Console(_RichConsole):
|
|
38
|
+
"""Rich Console configured with project-wide defaults (soft_wrap=True)."""
|
|
39
|
+
|
|
40
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
41
|
+
kwargs.setdefault("soft_wrap", True)
|
|
42
|
+
super().__init__(*args, **kwargs)
|
|
43
|
+
self.explicit_width = kwargs.get("width") is not None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def print_table(console: _RichConsole, table: Any) -> None:
|
|
47
|
+
"""Print ``table`` whole: off a terminal it may grow past 80 columns to fit its cells.
|
|
48
|
+
|
|
49
|
+
Only when the width was not chosen (no ``width``, no ``COLUMNS``); up to
|
|
50
|
+
:data:`NON_TERMINAL_TABLE_WIDTH`, beyond which cells fold onto more lines.
|
|
51
|
+
Rules and panels printed by the same console keep the normal width.
|
|
52
|
+
"""
|
|
53
|
+
chosen = (
|
|
54
|
+
getattr(console, "explicit_width", False) or (os.environ.get("COLUMNS") or "").isdigit()
|
|
55
|
+
)
|
|
56
|
+
if console.is_terminal or chosen:
|
|
57
|
+
console.print(table)
|
|
58
|
+
return
|
|
59
|
+
from rich.measure import Measurement
|
|
60
|
+
|
|
61
|
+
# Measured against the cap: the console's own options would clamp it to 80.
|
|
62
|
+
options = console.options.update_width(NON_TERMINAL_TABLE_WIDTH)
|
|
63
|
+
needed = Measurement.get(console, options, table).maximum
|
|
64
|
+
if needed <= console.width:
|
|
65
|
+
console.print(table)
|
|
66
|
+
return
|
|
67
|
+
original = console.width
|
|
68
|
+
console.width = min(needed, NON_TERMINAL_TABLE_WIDTH)
|
|
69
|
+
try:
|
|
70
|
+
console.print(table)
|
|
71
|
+
finally:
|
|
72
|
+
console.width = original
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def emit(data: dict) -> None:
|
|
76
|
+
"""Write structured data to stdout."""
|
|
77
|
+
print(json.dumps(data), file=sys.stdout)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def emit_error(msg: str, code: int = ExitCode.ERROR) -> None:
|
|
81
|
+
"""Write error message to stderr and exit."""
|
|
82
|
+
print(json.dumps({"error": msg}), file=sys.stderr)
|
|
83
|
+
raise SystemExit(code)
|