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,274 @@
|
|
|
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
|
+
"""Skills version drift detection for graph-agents-cli.
|
|
17
|
+
|
|
18
|
+
Owns the ``graph-agents-cli-`` skills prefix filter that ``info`` relies on.
|
|
19
|
+
The check is opt-out for disconnected installs: it is
|
|
20
|
+
skipped when ``GRAPH_AGENTS_CLI_NO_UPDATE_CHECK=1`` or a CI marker is set, and
|
|
21
|
+
it degrades silently (debug log only) when ``npx`` or the network is absent.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import logging
|
|
27
|
+
import os
|
|
28
|
+
import time
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
|
|
31
|
+
import click
|
|
32
|
+
import yaml
|
|
33
|
+
|
|
34
|
+
# Pin the skills npm package to avoid executing an unverified version.
|
|
35
|
+
SKILLS_NPX_PACKAGE = "skills@1.5.9"
|
|
36
|
+
|
|
37
|
+
SKILL_PREFIX = "graph-agents-cli-"
|
|
38
|
+
|
|
39
|
+
# Opt-out for the update and skills-version checks (disconnected installs). `main.py` reads the
|
|
40
|
+
# same variable before importing this module; the check re-reads it so that a
|
|
41
|
+
# direct call honours the opt-out too.
|
|
42
|
+
NO_UPDATE_CHECK_ENV = "GRAPH_AGENTS_CLI_NO_UPDATE_CHECK"
|
|
43
|
+
|
|
44
|
+
_SKILLS_CHECK_INTERVAL = 12 * 60 * 60 # 12 hours in seconds
|
|
45
|
+
_SKILLS_CHECK_STAMP = Path.home() / ".agents" / ".graph_agents_cli_skills_check"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _parse_skill_version(skill_md: Path) -> str | None:
|
|
49
|
+
"""Extract ``metadata.version`` from a SKILL.md YAML frontmatter.
|
|
50
|
+
|
|
51
|
+
Returns the version string, or ``None`` on any failure.
|
|
52
|
+
Logs at debug level when the file exists but cannot be parsed.
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
content = skill_md.read_text(encoding="utf-8")
|
|
56
|
+
except OSError:
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
if not content.startswith("---"):
|
|
60
|
+
logging.debug("Malformed skill file (no frontmatter): %s", skill_md)
|
|
61
|
+
return None
|
|
62
|
+
parts = content.split("---", 2)
|
|
63
|
+
if len(parts) < 3:
|
|
64
|
+
logging.debug("Malformed skill file (incomplete frontmatter): %s", skill_md)
|
|
65
|
+
return None
|
|
66
|
+
try:
|
|
67
|
+
frontmatter = yaml.safe_load(parts[1])
|
|
68
|
+
except yaml.YAMLError:
|
|
69
|
+
logging.debug("Malformed skill file (invalid YAML): %s", skill_md)
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
metadata = (frontmatter or {}).get("metadata") or {}
|
|
73
|
+
version = metadata.get("version") if isinstance(metadata, dict) else None
|
|
74
|
+
if not version:
|
|
75
|
+
logging.debug("Malformed skill file (missing metadata.version): %s", skill_md)
|
|
76
|
+
return str(version) if version else None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _find_installed_skills() -> dict[str, str]:
|
|
80
|
+
"""Return ``{skill_name: version}`` for every installed graph-agents-cli skill.
|
|
81
|
+
|
|
82
|
+
Fast path (~8 ms): scan ``~/.agents/skills/graph-agents-cli-*/SKILL.md``
|
|
83
|
+
directly. Falls back to ``npx skills list --json`` (~400 ms+) if
|
|
84
|
+
the well-known directory is empty or missing, which is more robust
|
|
85
|
+
when skills are installed to a non-default location.
|
|
86
|
+
"""
|
|
87
|
+
import json
|
|
88
|
+
|
|
89
|
+
result: dict[str, str] = {}
|
|
90
|
+
|
|
91
|
+
# Fast path: well-known global install location
|
|
92
|
+
skills_dir = Path.home() / ".agents" / "skills"
|
|
93
|
+
if skills_dir.is_dir():
|
|
94
|
+
for skill_dir in sorted(skills_dir.iterdir()):
|
|
95
|
+
if not skill_dir.name.startswith(SKILL_PREFIX):
|
|
96
|
+
continue
|
|
97
|
+
version = _parse_skill_version(skill_dir / "SKILL.md")
|
|
98
|
+
if version:
|
|
99
|
+
result[skill_dir.name] = version
|
|
100
|
+
|
|
101
|
+
if result:
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
# Slow path: ask npx skills for actual install locations
|
|
105
|
+
try:
|
|
106
|
+
from graph_agents_cli._runner import run_resolved
|
|
107
|
+
|
|
108
|
+
proc = run_resolved(
|
|
109
|
+
["npx", "-y", SKILLS_NPX_PACKAGE, "list", "--json"],
|
|
110
|
+
capture_output=True,
|
|
111
|
+
text=True,
|
|
112
|
+
encoding="utf-8",
|
|
113
|
+
errors="replace",
|
|
114
|
+
timeout=15,
|
|
115
|
+
)
|
|
116
|
+
if proc.returncode != 0:
|
|
117
|
+
return {}
|
|
118
|
+
entries = json.loads(proc.stdout)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
# Broad by design: this freshness check runs on every CLI invocation and
|
|
121
|
+
# must never abort a user's command, so any failure (no npx, no network)
|
|
122
|
+
# degrades silently to "no skills found" rather than propagating.
|
|
123
|
+
logging.debug("Could not query installed skills via npx: %s", e)
|
|
124
|
+
return {}
|
|
125
|
+
|
|
126
|
+
if not isinstance(entries, list):
|
|
127
|
+
return {}
|
|
128
|
+
|
|
129
|
+
for entry in entries:
|
|
130
|
+
if not isinstance(entry, dict):
|
|
131
|
+
logging.debug("Skipping malformed skills entry (expected an object): %r", entry)
|
|
132
|
+
continue
|
|
133
|
+
name = entry.get("name", "")
|
|
134
|
+
if not name.startswith(SKILL_PREFIX):
|
|
135
|
+
continue
|
|
136
|
+
path = entry.get("path")
|
|
137
|
+
if not path:
|
|
138
|
+
continue
|
|
139
|
+
version = _parse_skill_version(Path(path) / "SKILL.md")
|
|
140
|
+
if version:
|
|
141
|
+
result[name] = version
|
|
142
|
+
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def get_installed_skills() -> list[dict] | None:
|
|
147
|
+
"""Return installed graph-agents-cli skills as a list of dicts.
|
|
148
|
+
|
|
149
|
+
Returns None if the query fails (no ``npx``, no network, malformed output),
|
|
150
|
+
and without running it when :func:`skills_listing_skipped` names a reason:
|
|
151
|
+
``npx -y skills@... list`` may download the package, which a disconnected
|
|
152
|
+
install (``GRAPH_AGENTS_CLI_NO_UPDATE_CHECK=1``) and CI opt out of.
|
|
153
|
+
"""
|
|
154
|
+
import json
|
|
155
|
+
|
|
156
|
+
if skills_listing_skipped():
|
|
157
|
+
return None
|
|
158
|
+
try:
|
|
159
|
+
from graph_agents_cli._runner import run_resolved
|
|
160
|
+
|
|
161
|
+
result = run_resolved(
|
|
162
|
+
["npx", "-y", SKILLS_NPX_PACKAGE, "list", "--json"],
|
|
163
|
+
capture_output=True,
|
|
164
|
+
text=True,
|
|
165
|
+
encoding="utf-8",
|
|
166
|
+
errors="replace",
|
|
167
|
+
timeout=15,
|
|
168
|
+
)
|
|
169
|
+
if result.returncode != 0:
|
|
170
|
+
logging.debug(
|
|
171
|
+
"npx skills list failed (exit %d): %s",
|
|
172
|
+
result.returncode,
|
|
173
|
+
(result.stderr or "").strip(),
|
|
174
|
+
)
|
|
175
|
+
return None
|
|
176
|
+
skills = json.loads(result.stdout)
|
|
177
|
+
return [
|
|
178
|
+
s
|
|
179
|
+
for s in skills
|
|
180
|
+
if isinstance(s, dict) and str(s.get("name", "")).startswith(SKILL_PREFIX)
|
|
181
|
+
]
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logging.debug("Could not query installed skills: %s", e)
|
|
184
|
+
return None
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def skills_listing_skipped() -> str | None:
|
|
188
|
+
"""Why the skills listing (``npx skills list``) is not run, or None when it is.
|
|
189
|
+
|
|
190
|
+
The same opt-out as :func:`check_skills_version`: ``GRAPH_AGENTS_CLI_NO_UPDATE_CHECK=1``
|
|
191
|
+
(the disconnected profile) or a CI marker.
|
|
192
|
+
"""
|
|
193
|
+
if _is_opted_out():
|
|
194
|
+
return f"{NO_UPDATE_CHECK_ENV}=1"
|
|
195
|
+
marker = next((var for var in _CI_MARKERS if os.environ.get(var)), None)
|
|
196
|
+
return f"{marker} is set (CI)" if marker else None
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
_CI_MARKERS = ("CI", "BUILD_ID", "GITHUB_ACTIONS", "GITLAB_CI")
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _is_ci() -> bool:
|
|
203
|
+
"""Return True when running in a CI/automation environment.
|
|
204
|
+
|
|
205
|
+
The skills version-drift warning is a local developer-experience nicety: in
|
|
206
|
+
CI there is no human to act on it and skills are not installed, so the check
|
|
207
|
+
only adds noise (and may spawn a doomed ``npx`` subprocess). We skip it when
|
|
208
|
+
a well-known CI marker is present.
|
|
209
|
+
"""
|
|
210
|
+
return any(os.environ.get(var) for var in _CI_MARKERS)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _is_opted_out() -> bool:
|
|
214
|
+
"""Return True when the user disabled the check."""
|
|
215
|
+
return os.environ.get(NO_UPDATE_CHECK_ENV) == "1"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _skills_check_is_due() -> bool:
|
|
219
|
+
"""Return True if enough time has elapsed since the last check."""
|
|
220
|
+
try:
|
|
221
|
+
last = float(_SKILLS_CHECK_STAMP.read_text().strip())
|
|
222
|
+
return (time.time() - last) > _SKILLS_CHECK_INTERVAL
|
|
223
|
+
except (OSError, ValueError):
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _record_skills_check() -> None:
|
|
228
|
+
"""Write the current timestamp to the stamp file."""
|
|
229
|
+
try:
|
|
230
|
+
_SKILLS_CHECK_STAMP.parent.mkdir(parents=True, exist_ok=True)
|
|
231
|
+
_SKILLS_CHECK_STAMP.write_text(str(time.time()))
|
|
232
|
+
except OSError:
|
|
233
|
+
pass
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def check_skills_version() -> None:
|
|
237
|
+
"""Warn if any installed skill version doesn't match the running CLI version.
|
|
238
|
+
|
|
239
|
+
Rate-limited to once per 12 hours via a timestamp file so it can
|
|
240
|
+
run globally on every command without adding latency.
|
|
241
|
+
|
|
242
|
+
Scans all installed ``graph-agents-cli-*`` skills, compares each
|
|
243
|
+
``metadata.version`` with the running ``__version__``, and lists
|
|
244
|
+
the mismatched ones. Never blocks execution. Skipped entirely in CI
|
|
245
|
+
and when ``GRAPH_AGENTS_CLI_NO_UPDATE_CHECK=1``. Silent offline.
|
|
246
|
+
"""
|
|
247
|
+
if _is_opted_out() or _is_ci() or not _skills_check_is_due():
|
|
248
|
+
return
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
installed = _find_installed_skills()
|
|
252
|
+
except Exception as e: # never abort the user's command
|
|
253
|
+
logging.debug("Skills check skipped: %s", e)
|
|
254
|
+
return
|
|
255
|
+
if not installed:
|
|
256
|
+
return
|
|
257
|
+
|
|
258
|
+
from graph_agents_cli import __version__
|
|
259
|
+
|
|
260
|
+
_record_skills_check()
|
|
261
|
+
|
|
262
|
+
mismatched = {name: ver for name, ver in installed.items() if ver != __version__}
|
|
263
|
+
if not mismatched:
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
lines = [f" - {name} (v{ver})" for name, ver in mismatched.items()]
|
|
267
|
+
click.echo(
|
|
268
|
+
f"\n⚠️ Skills version mismatch — CLI is v{__version__}, "
|
|
269
|
+
f"but {len(mismatched)} skill(s) differ:\n"
|
|
270
|
+
+ "\n".join(lines)
|
|
271
|
+
+ "\n Run 'graph-agents-cli setup' to install the skills of this version (or "
|
|
272
|
+
"'graph-agents-cli update' to move the CLI and the skills to the latest release).\n",
|
|
273
|
+
err=True,
|
|
274
|
+
)
|
|
@@ -0,0 +1,189 @@
|
|
|
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
|
+
"""Tool resolution utilities.
|
|
17
|
+
|
|
18
|
+
Every external program the CLI shells out to (helm, kubectl, docker, gh, uv,
|
|
19
|
+
npx, ...) is resolved through :func:`require_tool`, which caches the resolved
|
|
20
|
+
path and, on Windows, retries with a cleaned PATH. Nothing here imports a
|
|
21
|
+
model SDK, LangChain, LangGraph, or a Kubernetes/Docker client.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import os
|
|
25
|
+
import re
|
|
26
|
+
import shlex
|
|
27
|
+
import shutil
|
|
28
|
+
from functools import cache
|
|
29
|
+
|
|
30
|
+
import click
|
|
31
|
+
|
|
32
|
+
_tool_paths: dict[str, str] = {}
|
|
33
|
+
|
|
34
|
+
# Matches ANSI escape sequences (CSI/SGR), used to scrub any residual color
|
|
35
|
+
# codes from captured subprocess output. We disable color at the source via
|
|
36
|
+
# NO_COLOR/FORCE_COLOR, but strip defensively in case the tool emits them anyway
|
|
37
|
+
# (e.g. on Windows PowerShell, where embedded escapes have caused error lines
|
|
38
|
+
# to render as the previous line's color).
|
|
39
|
+
_ANSI_RE = re.compile(r"\x1b\[[0-9;?]*[a-zA-Z]")
|
|
40
|
+
|
|
41
|
+
# Where `graph-agents-cli setup` fetches the skills from by default (pinned to the
|
|
42
|
+
# running release's tag, see `default_skills_source`).
|
|
43
|
+
# Keep in sync with extension._refs.FIRST_PARTY_REPO.
|
|
44
|
+
DEFAULT_SKILLS_SOURCE = "https://github.com/ss7172/graph-agents-cli"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def default_skills_source(version: str | None) -> str:
|
|
48
|
+
"""The first-party skills matching CLI ``version``: ``<repo>#v<version>``.
|
|
49
|
+
|
|
50
|
+
A release installs the skills of its own tag, so they never drift from the
|
|
51
|
+
CLI when the default branch moves on. A development build (a source
|
|
52
|
+
checkout, ``0.0.0-dev``, an unknown ``0.0.0``, a ``.devN`` or ``+local``
|
|
53
|
+
version) has no matching tag and uses the default branch.
|
|
54
|
+
"""
|
|
55
|
+
from packaging.version import InvalidVersion, Version
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
parsed = Version(version or "")
|
|
59
|
+
except InvalidVersion:
|
|
60
|
+
return DEFAULT_SKILLS_SOURCE
|
|
61
|
+
if parsed.is_devrelease or parsed.local or parsed == Version("0.0.0"):
|
|
62
|
+
return DEFAULT_SKILLS_SOURCE
|
|
63
|
+
return f"{DEFAULT_SKILLS_SOURCE}#v{version}"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
_UV_HINT = "Install uv (https://docs.astral.sh/uv/getting-started/installation/) and ensure it is in your PATH."
|
|
67
|
+
_NODE_HINT = "Install Node.js (https://nodejs.org/en/download) and ensure it is in your PATH."
|
|
68
|
+
|
|
69
|
+
# Default installation hints for the tools the CLI shells out to.
|
|
70
|
+
# These are used as fallbacks in require_tool when no specific hint is provided,
|
|
71
|
+
# ensuring helpful error messages even when tools are resolved implicitly (e.g. via run_resolved).
|
|
72
|
+
DEFAULT_INSTALL_HINTS = {
|
|
73
|
+
"helm": "Install Helm (https://helm.sh/docs/intro/install/) and ensure it is in your PATH.",
|
|
74
|
+
"kubectl": "Install kubectl (https://kubernetes.io/docs/tasks/tools/) and ensure it is in your PATH.",
|
|
75
|
+
"docker": "Install Docker (https://docs.docker.com/get-docker/) or a Docker-compatible CLI named 'docker' and ensure it is in your PATH.",
|
|
76
|
+
"argocd": "Install the Argo CD CLI (https://argo-cd.readthedocs.io/en/stable/cli_installation/) and ensure it is in your PATH.",
|
|
77
|
+
"gh": "Install the GitHub CLI (https://cli.github.com/) and ensure it is in your PATH.",
|
|
78
|
+
"uv": _UV_HINT,
|
|
79
|
+
# uvx is shipped by uv, so the install instructions are the same.
|
|
80
|
+
"uvx": f"uvx is part of uv. {_UV_HINT}",
|
|
81
|
+
"npx": f"npx is part of Node.js. {_NODE_HINT}",
|
|
82
|
+
"npm": f"npm is part of Node.js. {_NODE_HINT}",
|
|
83
|
+
"node": _NODE_HINT,
|
|
84
|
+
"git": "Install Git (https://git-scm.com/downloads) and ensure it is in your PATH.",
|
|
85
|
+
"langgraph": (
|
|
86
|
+
"langgraph is a project dependency (langgraph-cli), not a global tool: run "
|
|
87
|
+
"'graph-agents-cli install' inside the project, then invoke it as 'uv run langgraph'."
|
|
88
|
+
),
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class ToolNotFoundError(click.ClickException):
|
|
93
|
+
"""Raised when a required external tool is not found on PATH."""
|
|
94
|
+
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@cache
|
|
99
|
+
def _get_cleaned_path() -> str:
|
|
100
|
+
"""Returns a cleaned and expanded version of the PATH environment variable.
|
|
101
|
+
|
|
102
|
+
This function performs the following steps:
|
|
103
|
+
1. Splits the PATH environment variable using the OS-specific path separator.
|
|
104
|
+
2. Strips quotes from each path segment.
|
|
105
|
+
3. Filters out empty path segments.
|
|
106
|
+
4. Expands environment variables (like $HOME or %USERPROFILE%) within each segment.
|
|
107
|
+
5. Reconstructs the PATH string with the cleaned segments.
|
|
108
|
+
"""
|
|
109
|
+
raw_path = os.environ.get("PATH", "")
|
|
110
|
+
parts = raw_path.split(os.pathsep)
|
|
111
|
+
cleaned_parts = []
|
|
112
|
+
|
|
113
|
+
for part in parts:
|
|
114
|
+
# Strip quotes from each path segment.
|
|
115
|
+
part = part.strip('"').strip("'")
|
|
116
|
+
if not part:
|
|
117
|
+
continue
|
|
118
|
+
cleaned_parts.append(os.path.expandvars(part))
|
|
119
|
+
return os.pathsep.join(cleaned_parts)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def is_windows() -> bool:
|
|
123
|
+
"""Returns True if the current operating system is Windows."""
|
|
124
|
+
return os.name == "nt"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def install_hint(name: str) -> str:
|
|
128
|
+
"""Return the default installation hint for ``name`` (empty when unknown)."""
|
|
129
|
+
return DEFAULT_INSTALL_HINTS.get(name, "")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def require_tool(name: str, install_hint: str = "") -> str:
|
|
133
|
+
"""Finds a required external tool on the system PATH and returns its path.
|
|
134
|
+
|
|
135
|
+
This function performs the following steps:
|
|
136
|
+
1. Checks if the tool's path is already cached in `_tool_paths`.
|
|
137
|
+
2. If not cached, searches for the tool using `shutil.which` with the default PATH.
|
|
138
|
+
3. If not found and running on Windows, searches again using `shutil.which` with a cleaned PATH.
|
|
139
|
+
4. If still not found, raises a `ToolNotFoundError` with an install hint
|
|
140
|
+
(the explicit ``install_hint`` or the default for that tool).
|
|
141
|
+
5. If found, caches the path and returns it.
|
|
142
|
+
"""
|
|
143
|
+
if name in _tool_paths:
|
|
144
|
+
return _tool_paths[name]
|
|
145
|
+
|
|
146
|
+
path = shutil.which(name)
|
|
147
|
+
|
|
148
|
+
if path is None and is_windows():
|
|
149
|
+
path = shutil.which(name, path=_get_cleaned_path())
|
|
150
|
+
|
|
151
|
+
if path is None:
|
|
152
|
+
msg = f"'{name}' is not installed or not on PATH."
|
|
153
|
+
hint = install_hint or DEFAULT_INSTALL_HINTS.get(name, "")
|
|
154
|
+
if hint:
|
|
155
|
+
msg += f"\n {hint}"
|
|
156
|
+
raise ToolNotFoundError(msg)
|
|
157
|
+
|
|
158
|
+
_tool_paths[name] = path
|
|
159
|
+
return path
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def tool_available(name: str) -> bool:
|
|
163
|
+
"""Return True when ``name`` resolves on PATH, without raising."""
|
|
164
|
+
try:
|
|
165
|
+
require_tool(name)
|
|
166
|
+
except ToolNotFoundError:
|
|
167
|
+
return False
|
|
168
|
+
return True
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def run_npx_skills(args: list[str], spinner_msg: str):
|
|
172
|
+
"""Run an npx skills command, streaming output in real-time.
|
|
173
|
+
|
|
174
|
+
Always starts with ``["npx", "-y", SKILLS_NPX_PACKAGE]`` and appends
|
|
175
|
+
the additional ``args`` provided.
|
|
176
|
+
|
|
177
|
+
Raises:
|
|
178
|
+
click.ClickException: If the npx process exits non-zero.
|
|
179
|
+
"""
|
|
180
|
+
from graph_agents_cli._runner import run_resolved
|
|
181
|
+
from graph_agents_cli._skills_check import SKILLS_NPX_PACKAGE
|
|
182
|
+
|
|
183
|
+
full_args = ["npx", "-y", SKILLS_NPX_PACKAGE, *args]
|
|
184
|
+
click.secho(f" ▸ {shlex.join(full_args)}", fg="cyan", dim=True)
|
|
185
|
+
|
|
186
|
+
try:
|
|
187
|
+
run_resolved(full_args, check=True, encoding="utf-8", errors="replace")
|
|
188
|
+
except Exception as e:
|
|
189
|
+
raise click.ClickException("Error running npx skills") from e
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Copyright 2026 Google LLC
|
|
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
|
+
"""Trust tier decorators for agents CLI commands."""
|
|
16
|
+
|
|
17
|
+
import functools
|
|
18
|
+
|
|
19
|
+
import click
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def require_confirmation(message: str):
|
|
23
|
+
"""Decorator that adds --yes/-y and --interactive/-i options to destructive commands.
|
|
24
|
+
|
|
25
|
+
In interactive mode (-i), the user is prompted to confirm the action.
|
|
26
|
+
In auto-approve mode (-y) or strict programmatic mode (no flags), proceeds silently.
|
|
27
|
+
The decorated function receives an 'auto_approve' parameter.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
message: Confirmation prompt shown to the user.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def decorator(f):
|
|
34
|
+
@click.option(
|
|
35
|
+
"--interactive",
|
|
36
|
+
"-i",
|
|
37
|
+
is_flag=True,
|
|
38
|
+
default=False,
|
|
39
|
+
help="Enable interactive confirmation prompt.",
|
|
40
|
+
)
|
|
41
|
+
@click.option(
|
|
42
|
+
"-y",
|
|
43
|
+
"--yes",
|
|
44
|
+
"--auto-approve",
|
|
45
|
+
"auto_approve",
|
|
46
|
+
is_flag=True,
|
|
47
|
+
default=False,
|
|
48
|
+
help="Skip confirmation prompt.",
|
|
49
|
+
)
|
|
50
|
+
@functools.wraps(f)
|
|
51
|
+
def wrapper(*args, interactive=False, auto_approve=False, **kwargs):
|
|
52
|
+
if interactive and not auto_approve:
|
|
53
|
+
click.echo()
|
|
54
|
+
if not click.confirm(f" {message}", default=False):
|
|
55
|
+
click.echo()
|
|
56
|
+
click.secho(" Aborted.", fg="yellow")
|
|
57
|
+
return
|
|
58
|
+
click.echo()
|
|
59
|
+
# In strict programmatic mode (no -i, no -y): auto-proceed silently.
|
|
60
|
+
# `interactive` is not passed on: the prompt happens here, and no
|
|
61
|
+
# decorated command has ever read it.
|
|
62
|
+
return f(*args, auto_approve=auto_approve, **kwargs)
|
|
63
|
+
|
|
64
|
+
return wrapper
|
|
65
|
+
|
|
66
|
+
return decorator
|
|
@@ -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 api``: declare and change a project's outbound API access policy."""
|