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,438 @@
|
|
|
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
|
+
"""Version, install-spec and update-check utilities for the CLI.
|
|
17
|
+
|
|
18
|
+
``install_spec()`` is the single place that says where the CLI is installed
|
|
19
|
+
from: ``setup``, ``update``, the ``scaffold upgrade`` baseline and the CI
|
|
20
|
+
workflows of every generated project use it. It is a pinned git reference to
|
|
21
|
+
the GitHub repository today and can be flipped to a package-index requirement
|
|
22
|
+
later without touching the callers; ``GRAPH_AGENTS_CLI_INSTALL_SPEC`` overrides
|
|
23
|
+
it (a private mirror, a local checkout, a wheel). A ``{version}`` in the override
|
|
24
|
+
is replaced by the version asked for, so one mirror setting serves every
|
|
25
|
+
release. Replaying an older release (the ``scaffold upgrade`` baseline, a
|
|
26
|
+
version-locked ``scaffold enhance``) uses ``pinned_install_spec``, which refuses
|
|
27
|
+
an override without ``{version}``: that override installs one fixed build, so it
|
|
28
|
+
cannot stand for the older version. ``{version}`` is a release number (the
|
|
29
|
+
``cli_version`` a manifest records), so the override selects releases only; a
|
|
30
|
+
build between two releases (same version, another commit) is named with
|
|
31
|
+
``scaffold upgrade --baseline-ref`` (``resolve_baseline_ref``).
|
|
32
|
+
|
|
33
|
+
The update check compares the running version with the latest GitHub release.
|
|
34
|
+
It is opt-out through ``GRAPH_AGENTS_CLI_NO_UPDATE_CHECK=1`` (disconnected
|
|
35
|
+
installs) and fails silently when GitHub is unreachable.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
import logging
|
|
39
|
+
import os
|
|
40
|
+
import re
|
|
41
|
+
import time
|
|
42
|
+
import unicodedata
|
|
43
|
+
from dataclasses import dataclass
|
|
44
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
45
|
+
from pathlib import Path
|
|
46
|
+
|
|
47
|
+
import click
|
|
48
|
+
from packaging import version as pkg_version
|
|
49
|
+
|
|
50
|
+
from graph_agents_cli._output import Console
|
|
51
|
+
|
|
52
|
+
console = Console(stderr=True)
|
|
53
|
+
|
|
54
|
+
PACKAGE_NAME = "graph-agents-cli"
|
|
55
|
+
REPO_URL = "https://github.com/ss7172/graph-agents-cli"
|
|
56
|
+
INSTALL_SPEC_ENV = "GRAPH_AGENTS_CLI_INSTALL_SPEC"
|
|
57
|
+
VERSION_PLACEHOLDER = "{version}"
|
|
58
|
+
LATEST_RELEASE_URL = "https://api.github.com/repos/ss7172/graph-agents-cli/releases/latest"
|
|
59
|
+
NO_UPDATE_CHECK_ENV = "GRAPH_AGENTS_CLI_NO_UPDATE_CHECK"
|
|
60
|
+
# The 0.0.0 sentinel used when a real version can't be determined: an
|
|
61
|
+
# uninstalled/dev checkout (get_current_version) or an unreachable release
|
|
62
|
+
# feed (get_latest_version). Not a real release, so it can't be fetched.
|
|
63
|
+
UNKNOWN_VERSION = "0.0.0"
|
|
64
|
+
_UPDATE_CHECK_INTERVAL = 12 * 60 * 60 # 12 hours in seconds
|
|
65
|
+
_UPDATE_CHECK_STAMP = Path.home() / ".config" / "graph-agents-cli" / "update_check"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class InstallSpecError(click.ClickException):
|
|
69
|
+
"""``GRAPH_AGENTS_CLI_INSTALL_SPEC`` cannot give the spec asked for (exit 3)."""
|
|
70
|
+
|
|
71
|
+
exit_code = 3
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class InvalidInstallSpecError(InstallSpecError):
|
|
75
|
+
"""``GRAPH_AGENTS_CLI_INSTALL_SPEC`` is malformed: whitespace or control characters (exit 3)."""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# A PEP 508 direct reference, `graph-agents-cli[extras] @ <url>`: the one form
|
|
79
|
+
# whose spec holds whitespace, exactly the single spaces around its `@`.
|
|
80
|
+
_DIRECT_REFERENCE = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]*(\[[A-Za-z0-9._,-]*\])? @ \S+")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _invalid_character(value: str) -> str | None:
|
|
84
|
+
"""Describe the first character an install spec may not contain, or None.
|
|
85
|
+
|
|
86
|
+
Control and format characters (newline, CR, NUL, escape, bidi overrides...)
|
|
87
|
+
and every whitespace character are refused, except the two spaces of a
|
|
88
|
+
PEP 508 direct reference (``name @ url``).
|
|
89
|
+
"""
|
|
90
|
+
names = {
|
|
91
|
+
"\n": "a newline",
|
|
92
|
+
"\r": "a carriage return",
|
|
93
|
+
"\0": "a NUL byte",
|
|
94
|
+
"\t": "a tab",
|
|
95
|
+
" ": "a space",
|
|
96
|
+
}
|
|
97
|
+
for position, char in enumerate(value):
|
|
98
|
+
category = unicodedata.category(char)
|
|
99
|
+
if char == " ":
|
|
100
|
+
continue # judged below, once nothing worse was found
|
|
101
|
+
if char in names:
|
|
102
|
+
what = names[char]
|
|
103
|
+
elif char.isspace() or category in ("Zl", "Zp"):
|
|
104
|
+
what = f"whitespace (U+{ord(char):04X})"
|
|
105
|
+
elif category in ("Cc", "Cf"):
|
|
106
|
+
what = f"a control character (U+{ord(char):04X})"
|
|
107
|
+
else:
|
|
108
|
+
continue
|
|
109
|
+
return f"{what} at position {position + 1}"
|
|
110
|
+
if " " in value and _DIRECT_REFERENCE.fullmatch(value) is None:
|
|
111
|
+
return f"{names[' ']} at position {value.index(' ') + 1}"
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _install_spec_override() -> str:
|
|
116
|
+
"""The override, stripped; :class:`InvalidInstallSpecError` when it cannot be a spec.
|
|
117
|
+
|
|
118
|
+
The spec is written into every generated project's ``.github/agent.env``
|
|
119
|
+
(one NAME=VALUE per line, loaded into ``$GITHUB_ENV``) and passed to ``uv``
|
|
120
|
+
as one argument: a newline would add lines of its own to the workflows'
|
|
121
|
+
environment, and stray whitespace or an invisible control character would
|
|
122
|
+
make a different command than the one shown. It is refused instead.
|
|
123
|
+
"""
|
|
124
|
+
value = os.environ.get(INSTALL_SPEC_ENV, "").strip()
|
|
125
|
+
problem = _invalid_character(value)
|
|
126
|
+
if problem is not None:
|
|
127
|
+
shown = repr(value) if len(value) <= 120 else repr(value[:117]) + "..."
|
|
128
|
+
raise InvalidInstallSpecError(
|
|
129
|
+
f"{INSTALL_SPEC_ENV} contains {problem}: {shown}.\n"
|
|
130
|
+
" It must be a single install spec (a git URL such as "
|
|
131
|
+
f"git+https://git.example.com/{PACKAGE_NAME}@v{VERSION_PLACEHOLDER}, a local "
|
|
132
|
+
f"path, a wheel, {PACKAGE_NAME}==<version>, or {PACKAGE_NAME} @ <url>) with no "
|
|
133
|
+
"control characters and no whitespace (except the spaces around the @ of "
|
|
134
|
+
f"'{PACKAGE_NAME} @ <url>'). Fix it or unset it."
|
|
135
|
+
)
|
|
136
|
+
return value
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def install_spec(version: str | None = None) -> str:
|
|
140
|
+
"""Where to install graph-agents-cli from, for ``uv tool install`` / ``uvx --from``.
|
|
141
|
+
|
|
142
|
+
``GRAPH_AGENTS_CLI_INSTALL_SPEC`` wins when set, with ``{version}`` replaced
|
|
143
|
+
by ``version`` (``InstallSpecError`` when the override needs a version and
|
|
144
|
+
none is known). Otherwise a released ``version`` pins the git tag
|
|
145
|
+
``v<version>``, and no version (or the ``0.0.0`` unknown-version sentinel)
|
|
146
|
+
names the repository's default branch.
|
|
147
|
+
"""
|
|
148
|
+
known = version if version and version != UNKNOWN_VERSION else None
|
|
149
|
+
override = _install_spec_override()
|
|
150
|
+
if override:
|
|
151
|
+
if VERSION_PLACEHOLDER not in override:
|
|
152
|
+
return override
|
|
153
|
+
if known is None:
|
|
154
|
+
raise InstallSpecError(
|
|
155
|
+
f"{INSTALL_SPEC_ENV} contains {VERSION_PLACEHOLDER}, but no "
|
|
156
|
+
f"{PACKAGE_NAME} version is known here (an uninstalled checkout, or no "
|
|
157
|
+
f"release found); set it to a spec without {VERSION_PLACEHOLDER}."
|
|
158
|
+
)
|
|
159
|
+
return override.replace(VERSION_PLACEHOLDER, known)
|
|
160
|
+
return _default_install_spec(known)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _default_install_spec(version: str | None) -> str:
|
|
164
|
+
"""The repository's spec: the ``v<version>`` tag, or the default branch."""
|
|
165
|
+
if version and version != UNKNOWN_VERSION:
|
|
166
|
+
return f"git+{REPO_URL}@v{version}"
|
|
167
|
+
return f"git+{REPO_URL}"
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def pinned_install_spec(version: str) -> str | None:
|
|
171
|
+
"""The spec that installs exactly ``version``, or None when none can be trusted to.
|
|
172
|
+
|
|
173
|
+
For replaying an older release: the ``scaffold upgrade`` baseline and a
|
|
174
|
+
version-locked ``scaffold enhance``. An override without ``{version}``
|
|
175
|
+
installs one fixed build whatever version is asked, so a snapshot rendered
|
|
176
|
+
by it would be taken for ``version``'s and misclassify files; None then
|
|
177
|
+
(``pinned_spec_unavailable`` says why and what to do).
|
|
178
|
+
"""
|
|
179
|
+
override = _install_spec_override()
|
|
180
|
+
if override and VERSION_PLACEHOLDER not in override:
|
|
181
|
+
return None
|
|
182
|
+
return install_spec(version)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def pinned_spec_unavailable(version: str) -> str:
|
|
186
|
+
"""Why ``pinned_install_spec(version)`` is None, and the ways out."""
|
|
187
|
+
return (
|
|
188
|
+
f"{INSTALL_SPEC_ENV} is set without {VERSION_PLACEHOLDER}, so it cannot install "
|
|
189
|
+
f"{PACKAGE_NAME} {version} specifically. Put {VERSION_PLACEHOLDER} where the "
|
|
190
|
+
f"version goes (for example git+https://git.example.com/{PACKAGE_NAME}@v{VERSION_PLACEHOLDER}), "
|
|
191
|
+
f"or unset it"
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@dataclass(frozen=True)
|
|
196
|
+
class BaselineSource:
|
|
197
|
+
"""The CLI build that renders an upgrade's old snapshot, as a ``uvx --from`` spec.
|
|
198
|
+
|
|
199
|
+
``refresh`` rebuilds a local directory whatever uv cached for it: a checkout
|
|
200
|
+
at an older commit may key its build cache on ``pyproject.toml`` alone.
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
spec: str
|
|
204
|
+
label: str
|
|
205
|
+
refresh: bool = False
|
|
206
|
+
commit: str | None = None
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# A git ref as `--baseline-ref` accepts it: a tag, a branch or a commit.
|
|
210
|
+
_GIT_REF_RE = re.compile(r"[A-Za-z0-9][A-Za-z0-9._/+-]*")
|
|
211
|
+
_FULL_COMMIT_RE = re.compile(r"[0-9a-f]{40}([0-9a-f]{24})?")
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _check_git_ref(ref: str, flag: str) -> None:
|
|
215
|
+
if not _GIT_REF_RE.fullmatch(ref) or ".." in ref or ref.endswith((".lock", "/", ".")):
|
|
216
|
+
raise InstallSpecError(
|
|
217
|
+
f"{flag} {ref!r} is not a git ref (a tag, a branch or a commit such as 1a2b3c4)."
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _resolve_local_commit(clone: Path, ref: str, flag: str) -> str:
|
|
222
|
+
"""``ref``'s full commit in the clone at ``clone`` (InstallSpecError when it has none)."""
|
|
223
|
+
from graph_agents_cli._runner import run_resolved
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
result = run_resolved(
|
|
227
|
+
["git", "-C", str(clone), "rev-parse", "--verify", "--quiet", f"{ref}^{{commit}}"],
|
|
228
|
+
capture_output=True,
|
|
229
|
+
text=True,
|
|
230
|
+
check=False,
|
|
231
|
+
)
|
|
232
|
+
except OSError as e:
|
|
233
|
+
raise InstallSpecError(
|
|
234
|
+
f"{flag}: git could not run to look up {ref!r} in {clone}: {e}"
|
|
235
|
+
) from e
|
|
236
|
+
commit = (result.stdout or "").strip()
|
|
237
|
+
if result.returncode != 0 or not _FULL_COMMIT_RE.fullmatch(commit):
|
|
238
|
+
raise InstallSpecError(
|
|
239
|
+
f"{flag}: {clone} has no commit named {ref!r} (is it a clone of {PACKAGE_NAME} "
|
|
240
|
+
"that holds that commit? `git fetch` it first)."
|
|
241
|
+
)
|
|
242
|
+
return commit
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def commit_install_spec(commit: str) -> str:
|
|
246
|
+
"""The spec of one commit of the default repository."""
|
|
247
|
+
return f"git+{REPO_URL}@{commit}"
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def resolve_baseline_ref(ref: str, *, flag: str = "--baseline-ref") -> BaselineSource:
|
|
251
|
+
"""The build ``scaffold upgrade --baseline-ref`` names.
|
|
252
|
+
|
|
253
|
+
* a full install spec (``git+https://...@<ref>``, ``git+file://...``,
|
|
254
|
+
``graph-agents-cli==X``, ``graph-agents-cli @ <url>``): used as it is;
|
|
255
|
+
* an existing path (a checkout, an unpacked sdist, a wheel): that build;
|
|
256
|
+
* ``<clone>@<ref>`` where ``<clone>`` is a local git clone: ``ref``'s commit
|
|
257
|
+
in it (looked up now, so a typo fails before anything is built);
|
|
258
|
+
* anything else is a tag, branch or commit of the default repository.
|
|
259
|
+
|
|
260
|
+
Raises :class:`InstallSpecError` (exit 3) when ``ref`` cannot name a build.
|
|
261
|
+
"""
|
|
262
|
+
value = ref.strip()
|
|
263
|
+
# Plain spaces can be part of a path; every other whitespace or control
|
|
264
|
+
# character is refused, and a spec follows the install-spec rules.
|
|
265
|
+
problem = _invalid_character(value.replace(" ", "_"))
|
|
266
|
+
if not value or problem is not None:
|
|
267
|
+
raise InstallSpecError(
|
|
268
|
+
f"{flag} must name a {PACKAGE_NAME} build"
|
|
269
|
+
+ (f"; it contains {problem}." if problem else ".")
|
|
270
|
+
)
|
|
271
|
+
if "://" in value or value.startswith(("git+", PACKAGE_NAME)):
|
|
272
|
+
problem = _invalid_character(value)
|
|
273
|
+
if problem is not None:
|
|
274
|
+
raise InstallSpecError(
|
|
275
|
+
f"{flag} {value!r} is not an install spec: it contains {problem}."
|
|
276
|
+
)
|
|
277
|
+
return BaselineSource(spec=value, label=value)
|
|
278
|
+
path = Path(value).expanduser()
|
|
279
|
+
# A path is what is written as one, or a checkout or distribution that exists:
|
|
280
|
+
# a branch named like some directory here (`main`, `release/0.2`) stays a ref.
|
|
281
|
+
written_as_path = value.startswith(("/", ".", "~"))
|
|
282
|
+
build_source = (path.is_dir() and (path / "pyproject.toml").is_file()) or (
|
|
283
|
+
path.is_file() and value.endswith((".whl", ".tar.gz", ".zip"))
|
|
284
|
+
)
|
|
285
|
+
if path.exists() and (written_as_path or build_source):
|
|
286
|
+
resolved = path.resolve()
|
|
287
|
+
return BaselineSource(spec=str(resolved), label=f"the build at {resolved}", refresh=True)
|
|
288
|
+
if "@" in value:
|
|
289
|
+
clone_text, _, git_ref = value.rpartition("@")
|
|
290
|
+
clone = Path(clone_text).expanduser()
|
|
291
|
+
if clone_text and clone.is_dir():
|
|
292
|
+
_check_git_ref(git_ref, flag)
|
|
293
|
+
commit = _resolve_local_commit(clone.resolve(), git_ref, flag)
|
|
294
|
+
return BaselineSource(
|
|
295
|
+
spec=f"git+{clone.resolve().as_uri()}@{commit}",
|
|
296
|
+
label=f"commit {commit[:12]} of {clone.resolve()}",
|
|
297
|
+
commit=commit,
|
|
298
|
+
)
|
|
299
|
+
if clone_text and ("/" in clone_text or clone_text.startswith((".", "~"))):
|
|
300
|
+
raise InstallSpecError(f"{flag}: {clone_text} is not a directory (a local clone).")
|
|
301
|
+
_check_git_ref(value, flag)
|
|
302
|
+
commit = value if _FULL_COMMIT_RE.fullmatch(value) else None
|
|
303
|
+
return BaselineSource(
|
|
304
|
+
spec=f"git+{REPO_URL}@{value}", label=f"{value} of {REPO_URL}", commit=commit
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def requirement(spec: str | None = None, extras: str | None = None) -> str:
|
|
309
|
+
"""A requirement string for ``spec`` (default ``install_spec()``), with optional extras.
|
|
310
|
+
|
|
311
|
+
A URL or path spec becomes ``graph-agents-cli[extras] @ <spec>``; a
|
|
312
|
+
name-based spec (``graph-agents-cli==1.2.3``) gets the extras after the name.
|
|
313
|
+
"""
|
|
314
|
+
spec = spec or install_spec()
|
|
315
|
+
if not extras:
|
|
316
|
+
return spec
|
|
317
|
+
if spec.startswith(PACKAGE_NAME):
|
|
318
|
+
return f"{PACKAGE_NAME}[{extras}]{spec[len(PACKAGE_NAME) :]}"
|
|
319
|
+
return f"{PACKAGE_NAME}[{extras}] @ {spec}"
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def install_command(extras: str | None = None, *, version: str | None = None) -> str:
|
|
323
|
+
"""The ``uv tool install`` command line users can copy (quoted for a POSIX shell)."""
|
|
324
|
+
import shlex
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
spec = install_spec(version)
|
|
328
|
+
except InvalidInstallSpecError:
|
|
329
|
+
# A hint must not fail (or print the malformed override): the command that
|
|
330
|
+
# needs the spec reports the error itself.
|
|
331
|
+
spec = _default_install_spec(version)
|
|
332
|
+
except InstallSpecError:
|
|
333
|
+
spec = _install_spec_override() # a hint: the user fills in {version}
|
|
334
|
+
return shlex.join(["uv", "tool", "install", "--force", requirement(spec, extras)])
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def update_check_disabled() -> bool:
|
|
338
|
+
"""True when the user opted out of the update check."""
|
|
339
|
+
return os.environ.get(NO_UPDATE_CHECK_ENV) == "1"
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def _update_check_is_due() -> bool:
|
|
343
|
+
"""Return True if enough time has elapsed since the last check."""
|
|
344
|
+
try:
|
|
345
|
+
last = float(_UPDATE_CHECK_STAMP.read_text().strip())
|
|
346
|
+
return (time.time() - last) > _UPDATE_CHECK_INTERVAL
|
|
347
|
+
except (OSError, ValueError):
|
|
348
|
+
return True
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _record_update_check() -> None:
|
|
352
|
+
"""Write the current timestamp to the stamp file."""
|
|
353
|
+
try:
|
|
354
|
+
_UPDATE_CHECK_STAMP.parent.mkdir(parents=True, exist_ok=True)
|
|
355
|
+
_UPDATE_CHECK_STAMP.write_text(str(time.time()))
|
|
356
|
+
except OSError:
|
|
357
|
+
pass
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def get_current_version() -> str:
|
|
361
|
+
"""Get the current installed version of the package."""
|
|
362
|
+
try:
|
|
363
|
+
return version(PACKAGE_NAME)
|
|
364
|
+
except PackageNotFoundError:
|
|
365
|
+
# Package isn't installed (editable / dev checkout).
|
|
366
|
+
return UNKNOWN_VERSION
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def cli_install_spec() -> str:
|
|
370
|
+
"""The install spec a generated project pins: the creating CLI's own version."""
|
|
371
|
+
return install_spec(get_current_version())
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def get_latest_version() -> str:
|
|
375
|
+
"""The latest GitHub release of the CLI (tag ``v<version>``); UNKNOWN_VERSION when unknown.
|
|
376
|
+
|
|
377
|
+
Unknown covers offline, rate-limited, no release yet and a tag that is not
|
|
378
|
+
a version.
|
|
379
|
+
"""
|
|
380
|
+
try:
|
|
381
|
+
import requests
|
|
382
|
+
|
|
383
|
+
response = requests.get(
|
|
384
|
+
LATEST_RELEASE_URL,
|
|
385
|
+
timeout=2,
|
|
386
|
+
headers={"Accept": "application/vnd.github+json"},
|
|
387
|
+
)
|
|
388
|
+
if response.status_code != 200:
|
|
389
|
+
return UNKNOWN_VERSION
|
|
390
|
+
tag = str(response.json().get("tag_name") or "")
|
|
391
|
+
latest = tag[1:] if tag.startswith("v") else tag
|
|
392
|
+
pkg_version.Version(latest)
|
|
393
|
+
return latest
|
|
394
|
+
except Exception:
|
|
395
|
+
return UNKNOWN_VERSION # GitHub couldn't be reached or answered unexpectedly
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def check_for_updates() -> tuple[bool, str, str]:
|
|
399
|
+
"""Check if a newer version of the package is available.
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
Tuple of (needs_update, current_version, latest_version)
|
|
403
|
+
"""
|
|
404
|
+
current = get_current_version()
|
|
405
|
+
latest = get_latest_version()
|
|
406
|
+
|
|
407
|
+
needs_update = pkg_version.parse(latest) > pkg_version.parse(current)
|
|
408
|
+
|
|
409
|
+
return needs_update, current, latest
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def display_update_message() -> None:
|
|
413
|
+
"""Check for updates and display a message if an update is available.
|
|
414
|
+
|
|
415
|
+
Skipped when ``GRAPH_AGENTS_CLI_NO_UPDATE_CHECK=1`` or when a check ran
|
|
416
|
+
recently; any failure (offline, index down) is logged at debug level only.
|
|
417
|
+
"""
|
|
418
|
+
if update_check_disabled() or not _update_check_is_due():
|
|
419
|
+
return
|
|
420
|
+
|
|
421
|
+
try:
|
|
422
|
+
needs_update, current, latest = check_for_updates()
|
|
423
|
+
|
|
424
|
+
# We only record the check if we successfully queried it
|
|
425
|
+
_record_update_check()
|
|
426
|
+
|
|
427
|
+
if needs_update:
|
|
428
|
+
console.print(
|
|
429
|
+
f"\n[yellow]⚠️ Update available: {current} → {latest}[/]",
|
|
430
|
+
highlight=False,
|
|
431
|
+
)
|
|
432
|
+
console.print(
|
|
433
|
+
f"[yellow]Run `{PACKAGE_NAME} update` or `{install_command(version=latest)}`.[/]",
|
|
434
|
+
highlight=False,
|
|
435
|
+
)
|
|
436
|
+
except Exception as e:
|
|
437
|
+
# Don't let version checking errors affect the CLI
|
|
438
|
+
logging.debug("Error checking for updates: %s", e)
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
"""graph-agents-cli secrets commands."""
|