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.
Files changed (291) hide show
  1. graph_agents_cli/__init__.py +26 -0
  2. graph_agents_cli/_api_policy.py +2145 -0
  3. graph_agents_cli/_approvals.py +400 -0
  4. graph_agents_cli/_build.py +186 -0
  5. graph_agents_cli/_build_info.json +7 -0
  6. graph_agents_cli/_chat_client.py +462 -0
  7. graph_agents_cli/_click.py +157 -0
  8. graph_agents_cli/_defaults.py +139 -0
  9. graph_agents_cli/_experiments.py +64 -0
  10. graph_agents_cli/_http.py +192 -0
  11. graph_agents_cli/_output.py +83 -0
  12. graph_agents_cli/_project.py +462 -0
  13. graph_agents_cli/_remote.py +220 -0
  14. graph_agents_cli/_response_schema.py +264 -0
  15. graph_agents_cli/_runner.py +319 -0
  16. graph_agents_cli/_skills_check.py +274 -0
  17. graph_agents_cli/_tools.py +189 -0
  18. graph_agents_cli/_trust.py +66 -0
  19. graph_agents_cli/api/__init__.py +15 -0
  20. graph_agents_cli/api/_changes.py +506 -0
  21. graph_agents_cli/api/_files.py +658 -0
  22. graph_agents_cli/api/cmd_api.py +2480 -0
  23. graph_agents_cli/deploy/__init__.py +15 -0
  24. graph_agents_cli/deploy/_config.py +171 -0
  25. graph_agents_cli/deploy/_image.py +128 -0
  26. graph_agents_cli/deploy/_kube.py +286 -0
  27. graph_agents_cli/deploy/_modes.py +234 -0
  28. graph_agents_cli/deploy/_preflight.py +370 -0
  29. graph_agents_cli/deploy/_values.py +168 -0
  30. graph_agents_cli/deploy/cmd_deploy.py +1866 -0
  31. graph_agents_cli/deploy/gitops.py +562 -0
  32. graph_agents_cli/deploy/local_load.py +273 -0
  33. graph_agents_cli/dev/__init__.py +13 -0
  34. graph_agents_cli/dev/cmd_build.py +131 -0
  35. graph_agents_cli/dev/cmd_install.py +78 -0
  36. graph_agents_cli/dev/cmd_lint.py +119 -0
  37. graph_agents_cli/dev/cmd_playground.py +297 -0
  38. graph_agents_cli/dev/policy_check.py +1287 -0
  39. graph_agents_cli/eval/__init__.py +22 -0
  40. graph_agents_cli/eval/_client.py +670 -0
  41. graph_agents_cli/eval/_common.py +177 -0
  42. graph_agents_cli/eval/_judge.py +168 -0
  43. graph_agents_cli/eval/_judge_runner.py +238 -0
  44. graph_agents_cli/eval/_paths.py +212 -0
  45. graph_agents_cli/eval/checks.py +581 -0
  46. graph_agents_cli/eval/cmd_analyze.py +278 -0
  47. graph_agents_cli/eval/cmd_compare.py +284 -0
  48. graph_agents_cli/eval/cmd_eval_group.py +80 -0
  49. graph_agents_cli/eval/cmd_generate.py +558 -0
  50. graph_agents_cli/eval/cmd_grade.py +466 -0
  51. graph_agents_cli/eval/cmd_metric.py +156 -0
  52. graph_agents_cli/eval/cmd_run.py +370 -0
  53. graph_agents_cli/eval/cmd_submit.py +400 -0
  54. graph_agents_cli/eval/config.py +435 -0
  55. graph_agents_cli/eval/dataset.py +350 -0
  56. graph_agents_cli/eval/gate.py +420 -0
  57. graph_agents_cli/eval/transcript.py +192 -0
  58. graph_agents_cli/extension/__init__.py +13 -0
  59. graph_agents_cli/extension/_compat.py +86 -0
  60. graph_agents_cli/extension/_loader.py +293 -0
  61. graph_agents_cli/extension/_manifest.py +135 -0
  62. graph_agents_cli/extension/_overrides.py +195 -0
  63. graph_agents_cli/extension/_paths.py +91 -0
  64. graph_agents_cli/extension/_refs.py +193 -0
  65. graph_agents_cli/extension/_resolver.py +453 -0
  66. graph_agents_cli/extension/_schema.py +106 -0
  67. graph_agents_cli/extension/_spec.py +253 -0
  68. graph_agents_cli/extension/_sync.py +102 -0
  69. graph_agents_cli/extension/_trust.py +58 -0
  70. graph_agents_cli/extension/cmd_extension_add.py +259 -0
  71. graph_agents_cli/extension/cmd_extension_group.py +57 -0
  72. graph_agents_cli/extension/cmd_extension_list.py +56 -0
  73. graph_agents_cli/extension/cmd_extension_remove.py +61 -0
  74. graph_agents_cli/extension/cmd_extension_update.py +195 -0
  75. graph_agents_cli/info/__init__.py +13 -0
  76. graph_agents_cli/info/cmd_info.py +222 -0
  77. graph_agents_cli/infra/__init__.py +15 -0
  78. graph_agents_cli/infra/checks.py +1169 -0
  79. graph_agents_cli/infra/cmd_infra.py +103 -0
  80. graph_agents_cli/main.py +591 -0
  81. graph_agents_cli/peer/__init__.py +15 -0
  82. graph_agents_cli/peer/_generate.py +254 -0
  83. graph_agents_cli/peer/cmd_peer.py +1151 -0
  84. graph_agents_cli/run/__init__.py +13 -0
  85. graph_agents_cli/run/_local_server.py +1157 -0
  86. graph_agents_cli/run/_signals.py +141 -0
  87. graph_agents_cli/run/cmd_approvals.py +530 -0
  88. graph_agents_cli/run/cmd_run.py +1421 -0
  89. graph_agents_cli/scaffold/__init__.py +19 -0
  90. graph_agents_cli/scaffold/agents/README.md +24 -0
  91. graph_agents_cli/scaffold/agents/empty_py/.template/templateconfig.yaml +22 -0
  92. graph_agents_cli/scaffold/agents/langgraph/.env.example +292 -0
  93. graph_agents_cli/scaffold/agents/langgraph/.template/templateconfig.yaml +28 -0
  94. graph_agents_cli/scaffold/agents/langgraph/Dockerfile +59 -0
  95. graph_agents_cli/scaffold/agents/langgraph/Dockerfile.langgraph-server +59 -0
  96. graph_agents_cli/scaffold/agents/langgraph/README.md +571 -0
  97. graph_agents_cli/scaffold/agents/langgraph/api-policy.yaml +60 -0
  98. graph_agents_cli/scaffold/agents/langgraph/app/__init__.py +20 -0
  99. graph_agents_cli/scaffold/agents/langgraph/app/agent.py +174 -0
  100. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/__init__.py +15 -0
  101. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/a2a.py +2162 -0
  102. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/a2a_client.py +1167 -0
  103. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/api_client.py +4220 -0
  104. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/approvals.py +1349 -0
  105. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/auth.py +1986 -0
  106. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/chat.py +2962 -0
  107. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/checkpointer.py +432 -0
  108. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/content.py +569 -0
  109. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/db.py +580 -0
  110. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/limits.py +203 -0
  111. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/metrics.py +231 -0
  112. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/middleware.py +361 -0
  113. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/model.py +611 -0
  114. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/playground.py +230 -0
  115. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/run_locks.py +459 -0
  116. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/structured.py +755 -0
  117. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/telemetry.py +681 -0
  118. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/threads.py +493 -0
  119. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/token_exchange.py +959 -0
  120. graph_agents_cli/scaffold/agents/langgraph/app/fast_api_app.py +770 -0
  121. graph_agents_cli/scaffold/agents/langgraph/app/policies/__init__.py +55 -0
  122. graph_agents_cli/scaffold/agents/langgraph/app/policies/custom.py +97 -0
  123. graph_agents_cli/scaffold/agents/langgraph/app/tools/__init__.py +46 -0
  124. graph_agents_cli/scaffold/agents/langgraph/app/tools/example_api.py +92 -0
  125. graph_agents_cli/scaffold/agents/langgraph/app/tools/weather.py +33 -0
  126. graph_agents_cli/scaffold/agents/langgraph/langgraph.json +14 -0
  127. graph_agents_cli/scaffold/agents/langgraph/pyproject.toml +78 -0
  128. graph_agents_cli/scaffold/agents/langgraph/tests/conftest.py +376 -0
  129. graph_agents_cli/scaffold/agents/langgraph/tests/eval/datasets/basic-dataset.json +53 -0
  130. graph_agents_cli/scaffold/agents/langgraph/tests/eval/eval_config.yaml +32 -0
  131. graph_agents_cli/scaffold/agents/langgraph/tests/integration/approval_graph.py +137 -0
  132. graph_agents_cli/scaffold/agents/langgraph/tests/integration/fake_issuer.py +216 -0
  133. graph_agents_cli/scaffold/agents/langgraph/tests/integration/fake_openai.py +357 -0
  134. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_a2a_outcomes.py +569 -0
  135. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_a2a_relay.py +479 -0
  136. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_api_surface.py +812 -0
  137. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_approvals.py +1367 -0
  138. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_approvals_server.py +794 -0
  139. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_cross_actor.py +497 -0
  140. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_cross_actor_server.py +247 -0
  141. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_history_repair.py +278 -0
  142. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_model_apis.py +242 -0
  143. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_postgres.py +637 -0
  144. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_resilience_postgres.py +770 -0
  145. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_runtime_guardrails.py +854 -0
  146. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_server_e2e.py +340 -0
  147. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_server_runtime.py +989 -0
  148. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_structured_answers.py +584 -0
  149. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_structured_server.py +222 -0
  150. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_token_exchange_issuer.py +650 -0
  151. graph_agents_cli/scaffold/agents/langgraph/tests/load_test/.results/.placeholder +0 -0
  152. graph_agents_cli/scaffold/agents/langgraph/tests/load_test/README.md +22 -0
  153. graph_agents_cli/scaffold/agents/langgraph/tests/load_test/conftest.py +21 -0
  154. graph_agents_cli/scaffold/agents/langgraph/tests/load_test/load_test.py +81 -0
  155. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_a2a_client.py +824 -0
  156. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_a2a_scoping.py +724 -0
  157. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_client.py +1214 -0
  158. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_client_hardening.py +716 -0
  159. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_policy_rpc.py +767 -0
  160. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_approval_ledger.py +1536 -0
  161. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_fake_model.py +115 -0
  162. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_jwt_policy.py +991 -0
  163. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_limits.py +310 -0
  164. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_logging.py +148 -0
  165. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_logging_hardening.py +271 -0
  166. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_policy.py +378 -0
  167. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_resilience.py +610 -0
  168. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_server_auth.py +702 -0
  169. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_structured.py +673 -0
  170. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_telemetry.py +404 -0
  171. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_thread_listing.py +255 -0
  172. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_threads.py +268 -0
  173. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_token_exchange.py +1320 -0
  174. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_untrusted_content.py +393 -0
  175. graph_agents_cli/scaffold/agents/langgraph/uv-fastapi.lock +2084 -0
  176. graph_agents_cli/scaffold/agents/langgraph/uv-langgraph-server.lock +2106 -0
  177. graph_agents_cli/scaffold/agents/langgraph/{{cookiecutter.agent_guidance_filename}} +129 -0
  178. graph_agents_cli/scaffold/base_templates/_shared/graph-agents-cli-manifest.yaml +36 -0
  179. graph_agents_cli/scaffold/base_templates/python/.dockerignore +32 -0
  180. graph_agents_cli/scaffold/base_templates/python/.github/CODEOWNERS +30 -0
  181. graph_agents_cli/scaffold/base_templates/python/.github/agent.env +7 -0
  182. graph_agents_cli/scaffold/base_templates/python/.github/workflows/pr_checks.yaml +214 -0
  183. graph_agents_cli/scaffold/base_templates/python/.gitignore +209 -0
  184. graph_agents_cli/scaffold/base_templates/python/tests/unit/test_dummy.py +23 -0
  185. graph_agents_cli/scaffold/base_templates/python/{{cookiecutter.agent_guidance_filename}} +35 -0
  186. graph_agents_cli/scaffold/cmd_scaffold_group.py +49 -0
  187. graph_agents_cli/scaffold/commands/__init__.py +13 -0
  188. graph_agents_cli/scaffold/commands/create.py +1424 -0
  189. graph_agents_cli/scaffold/commands/enhance.py +1652 -0
  190. graph_agents_cli/scaffold/commands/upgrade.py +570 -0
  191. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/agent.env +12 -0
  192. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/workflows/promote-to-prod.yaml +371 -0
  193. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/workflows/staging.yaml +450 -0
  194. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-dev.yaml +43 -0
  195. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-prod.yaml +41 -0
  196. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-staging.yaml +43 -0
  197. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/.helmignore +14 -0
  198. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/Chart.yaml +21 -0
  199. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/examples/networkpolicy.yaml +103 -0
  200. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/NOTES.txt +48 -0
  201. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/_helpers.tpl +189 -0
  202. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/certificate.yaml +15 -0
  203. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/configmap.yaml +10 -0
  204. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/deployment.yaml +199 -0
  205. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/hpa.yaml +22 -0
  206. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/httproute.yaml +30 -0
  207. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/ingress.yaml +39 -0
  208. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/networkpolicy.yaml +48 -0
  209. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/pdb.yaml +13 -0
  210. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/postgresql-secret.yaml +37 -0
  211. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/service.yaml +15 -0
  212. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/serviceaccount.yaml +13 -0
  213. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/servicemonitor.yaml +42 -0
  214. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-dev.yaml +22 -0
  215. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-prod.yaml +45 -0
  216. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-staging.yaml +29 -0
  217. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values.yaml +396 -0
  218. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/tests/integration/test_chart.py +269 -0
  219. graph_agents_cli/scaffold/deployment_targets/none/README.md +5 -0
  220. graph_agents_cli/scaffold/deployment_targets/none/python/README.md +6 -0
  221. graph_agents_cli/scaffold/utils/__init__.py +13 -0
  222. graph_agents_cli/scaffold/utils/backup.py +212 -0
  223. graph_agents_cli/scaffold/utils/build_record.py +257 -0
  224. graph_agents_cli/scaffold/utils/cli_options.py +184 -0
  225. graph_agents_cli/scaffold/utils/fs.py +83 -0
  226. graph_agents_cli/scaffold/utils/generate_locks.py +214 -0
  227. graph_agents_cli/scaffold/utils/generation_metadata.py +88 -0
  228. graph_agents_cli/scaffold/utils/keyedit.py +768 -0
  229. graph_agents_cli/scaffold/utils/keymerge.py +537 -0
  230. graph_agents_cli/scaffold/utils/language.py +138 -0
  231. graph_agents_cli/scaffold/utils/lock_utils.py +94 -0
  232. graph_agents_cli/scaffold/utils/logging.py +77 -0
  233. graph_agents_cli/scaffold/utils/manifest.py +292 -0
  234. graph_agents_cli/scaffold/utils/merge.py +970 -0
  235. graph_agents_cli/scaffold/utils/merge3.py +216 -0
  236. graph_agents_cli/scaffold/utils/openapi_seed.py +199 -0
  237. graph_agents_cli/scaffold/utils/remote_template.py +376 -0
  238. graph_agents_cli/scaffold/utils/template.py +1352 -0
  239. graph_agents_cli/scaffold/utils/upgrade.py +894 -0
  240. graph_agents_cli/scaffold/utils/version.py +438 -0
  241. graph_agents_cli/secrets/__init__.py +15 -0
  242. graph_agents_cli/secrets/_apply.py +954 -0
  243. graph_agents_cli/secrets/_required.py +188 -0
  244. graph_agents_cli/secrets/cmd_secrets.py +211 -0
  245. graph_agents_cli/setup/__init__.py +13 -0
  246. graph_agents_cli/setup/_antigravity.py +221 -0
  247. graph_agents_cli/setup/cmd_auth.py +1030 -0
  248. graph_agents_cli/setup/cmd_dev_token.py +513 -0
  249. graph_agents_cli/setup/cmd_setup.py +428 -0
  250. graph_agents_cli/setup/cmd_update.py +140 -0
  251. graph_agents_cli/skills/__init__.py +13 -0
  252. graph_agents_cli/skills/_bundle.py +65 -0
  253. graph_agents_cli/skills/data/README.md +19 -0
  254. graph_agents_cli/skills/data/graph-agents-cli-deploy/SKILL.md +357 -0
  255. graph_agents_cli/skills/data/graph-agents-cli-deploy/references/github-settings.md +113 -0
  256. graph_agents_cli/skills/data/graph-agents-cli-deploy/references/gitops.md +137 -0
  257. graph_agents_cli/skills/data/graph-agents-cli-deploy/references/kubernetes.md +315 -0
  258. graph_agents_cli/skills/data/graph-agents-cli-deploy/references/secrets.md +160 -0
  259. graph_agents_cli/skills/data/graph-agents-cli-eval/SKILL.md +303 -0
  260. graph_agents_cli/skills/data/graph-agents-cli-eval/references/dataset_schema.md +282 -0
  261. graph_agents_cli/skills/data/graph-agents-cli-eval/references/metrics-guide.md +143 -0
  262. graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/SKILL.md +659 -0
  263. graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/langchain-models.md +124 -0
  264. graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/langgraph.md +235 -0
  265. graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/template-contract.md +477 -0
  266. graph_agents_cli/skills/data/graph-agents-cli-observability/SKILL.md +231 -0
  267. graph_agents_cli/skills/data/graph-agents-cli-observability/references/langsmith.md +46 -0
  268. graph_agents_cli/skills/data/graph-agents-cli-observability/references/otel.md +59 -0
  269. graph_agents_cli/skills/data/graph-agents-cli-scaffold/SKILL.md +414 -0
  270. graph_agents_cli/skills/data/graph-agents-cli-scaffold/references/flags.md +134 -0
  271. graph_agents_cli/skills/data/graph-agents-cli-workflow/SKILL.md +478 -0
  272. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/brainstorming.md +118 -0
  273. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/commands.md +419 -0
  274. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/extension.md +156 -0
  275. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/internals.md +67 -0
  276. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/spec-template.md +56 -0
  277. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/terminology.md +119 -0
  278. graph_agents_cli/system/__init__.py +15 -0
  279. graph_agents_cli/system/_apply.py +519 -0
  280. graph_agents_cli/system/_checks.py +1023 -0
  281. graph_agents_cli/system/_deploy.py +215 -0
  282. graph_agents_cli/system/_model.py +363 -0
  283. graph_agents_cli/system/_system.py +664 -0
  284. graph_agents_cli/system/_views.py +208 -0
  285. graph_agents_cli/system/cmd_system.py +423 -0
  286. graph_agents_cli-0.3.1.dist-info/METADATA +162 -0
  287. graph_agents_cli-0.3.1.dist-info/RECORD +291 -0
  288. graph_agents_cli-0.3.1.dist-info/WHEEL +4 -0
  289. graph_agents_cli-0.3.1.dist-info/entry_points.txt +2 -0
  290. graph_agents_cli-0.3.1.dist-info/licenses/LICENSE +201 -0
  291. graph_agents_cli-0.3.1.dist-info/licenses/NOTICE +19 -0
@@ -0,0 +1,506 @@
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
+ """What an ``api`` command changes in the policy, as data (no files, no output).
16
+
17
+ Access is always an explicit choice: ``read-only`` and ``read-write`` are
18
+ shorthands for a list of methods that is written into the file in full (the
19
+ file never stores a preset name), and ``custom`` takes the list itself.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import copy
25
+ from collections.abc import Mapping, Sequence
26
+ from dataclasses import dataclass
27
+ from pathlib import Path
28
+ from typing import Any
29
+
30
+ import click
31
+ import yaml
32
+
33
+ from graph_agents_cli._api_policy import (
34
+ _RPC_METHOD_RE,
35
+ A2A_OPERATION_KEY,
36
+ A2A_OPERATIONS,
37
+ ANY_METHOD,
38
+ HTTP_METHODS,
39
+ RPC_METHOD_KEY,
40
+ normalize_path,
41
+ path_matches,
42
+ path_template_problem,
43
+ )
44
+
45
+ READ_ONLY = "read-only"
46
+ READ_WRITE = "read-write"
47
+ CUSTOM = "custom"
48
+ ACCESS_CHOICES = (READ_ONLY, READ_WRITE, CUSTOM)
49
+ PRESETS: dict[str, tuple[str, ...]] = {
50
+ READ_ONLY: ("GET", "HEAD"),
51
+ READ_WRITE: ("GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"),
52
+ }
53
+ ALLOWED = "allowed_operations"
54
+ DENIED = "denied_operations"
55
+
56
+
57
+ class ApiCommandError(click.ClickException):
58
+ """The change cannot be made: an invalid result or a configuration problem (exit 3)."""
59
+
60
+ exit_code = 3
61
+
62
+
63
+ # ---------------------------------------------------------------------------
64
+ # Methods
65
+ # ---------------------------------------------------------------------------
66
+
67
+
68
+ def parse_methods(value: str | None) -> list[str]:
69
+ """``get, Post`` -> ``["GET", "POST"]`` (canonical order); ``*`` alone -> ``["*"]``."""
70
+ if value is None:
71
+ return []
72
+ items = [item.strip().upper() for item in value.split(",") if item.strip()]
73
+ if not items:
74
+ raise click.UsageError("--methods needs at least one HTTP method")
75
+ if ANY_METHOD in items:
76
+ if len(items) != 1:
77
+ raise click.UsageError('--methods "*" must stand alone (every method)')
78
+ return [ANY_METHOD]
79
+ unknown = [item for item in items if item not in HTTP_METHODS]
80
+ if unknown:
81
+ raise click.UsageError(
82
+ f"unknown HTTP method(s) {', '.join(unknown)} (allowed: {', '.join(HTTP_METHODS)}, "
83
+ 'or "*" alone)'
84
+ )
85
+ return [m for m in HTTP_METHODS if m in items]
86
+
87
+
88
+ def access_methods(access: str, methods: list[str]) -> list[str]:
89
+ """The ``allowed_methods`` an access choice writes into the file."""
90
+ if access == CUSTOM:
91
+ if not methods:
92
+ raise click.UsageError("custom access needs --methods M,... (the methods to allow)")
93
+ return list(methods)
94
+ if methods:
95
+ raise click.UsageError(f"--methods goes with custom access only (not {access})")
96
+ return list(PRESETS[access])
97
+
98
+
99
+ def preset_name(methods: Sequence[str]) -> str | None:
100
+ """``read-only`` / ``read-write`` when ``methods`` is exactly that preset, else None."""
101
+ normalized = [str(m).upper() for m in methods]
102
+ for name, preset in PRESETS.items():
103
+ if sorted(normalized) == sorted(preset):
104
+ return name
105
+ return None
106
+
107
+
108
+ def effective_methods(methods: Sequence[str]) -> list[str]:
109
+ normalized = [str(m).upper() for m in methods]
110
+ return list(HTTP_METHODS) if ANY_METHOD in normalized else normalized
111
+
112
+
113
+ def describe_methods(methods: Sequence[str]) -> str:
114
+ normalized = [str(m).upper() for m in methods]
115
+ if ANY_METHOD in normalized:
116
+ return '"*" (every method)'
117
+ text = ", ".join(normalized)
118
+ preset = preset_name(normalized)
119
+ return f"{text} (the {preset} preset)" if preset else text
120
+
121
+
122
+ # ---------------------------------------------------------------------------
123
+ # Operation entries
124
+ # ---------------------------------------------------------------------------
125
+
126
+
127
+ @dataclass(frozen=True)
128
+ class OperationRef:
129
+ """How a command names an operation: an operationId, a method and a path, or all three;
130
+ on a JSON-RPC API also the JSON-RPC method (``rpc_method``) and what an A2A message
131
+ decides (``a2a_operation``)."""
132
+
133
+ operation_id: str | None = None
134
+ method: str | None = None
135
+ path: str | None = None
136
+ rpc_method: str | None = None
137
+ a2a_operation: str | None = None
138
+
139
+ @property
140
+ def rpc(self) -> bool:
141
+ """Whether it names a JSON-RPC request (``rpc_method`` or ``a2a_operation``)."""
142
+ return self.rpc_method is not None or self.a2a_operation is not None
143
+
144
+ @classmethod
145
+ def from_options(
146
+ cls,
147
+ operation_id: str | None,
148
+ method: str | None,
149
+ path: str | None,
150
+ *,
151
+ combine: bool = False,
152
+ rpc_method: str | None = None,
153
+ a2a_operation: str | None = None,
154
+ endpoint: bool = False,
155
+ ) -> OperationRef:
156
+ """Read OPERATION_ID / ``--method`` / ``--path`` (and ``--rpc-method`` /
157
+ ``--a2a-operation``).
158
+
159
+ With ``combine`` (``allow``, ``deny``) OPERATION_ID may come with
160
+ ``--method M --path P``, the endpoint it names: the entry then pins all
161
+ three. With ``--rpc-method`` or ``--a2a-operation`` the other options are
162
+ optional (``--method`` and ``--path`` together, and ``--method`` POST);
163
+ ``endpoint`` (``allow``) requires them, so an allow names its endpoint.
164
+ """
165
+ if rpc_method is not None or a2a_operation is not None:
166
+ return cls._rpc(operation_id, method, path, rpc_method, a2a_operation, endpoint)
167
+ if operation_id and (method or path) and not combine:
168
+ raise click.UsageError(
169
+ "name the operation by OPERATION_ID or by --method/--path, not both"
170
+ )
171
+ if operation_id and any(c.isspace() for c in operation_id):
172
+ raise click.UsageError("OPERATION_ID must not contain whitespace")
173
+ if operation_id and not (method or path):
174
+ return cls(operation_id=operation_id)
175
+ if not (method and path):
176
+ if operation_id:
177
+ raise click.UsageError(
178
+ "with OPERATION_ID, give both --method and --path (the endpoint it names), "
179
+ "or neither"
180
+ )
181
+ raise click.UsageError("name the operation: OPERATION_ID, or --method M --path P")
182
+ method = method.strip().upper()
183
+ if method not in HTTP_METHODS:
184
+ raise click.UsageError(
185
+ f"unknown HTTP method {method} (allowed: {', '.join(HTTP_METHODS)})"
186
+ )
187
+ problem = path_template_problem(path)
188
+ if problem:
189
+ raise click.UsageError(f"--path {path}: {problem}")
190
+ return cls(operation_id=operation_id or None, method=method, path=path)
191
+
192
+ @classmethod
193
+ def _rpc(
194
+ cls,
195
+ operation_id: str | None,
196
+ method: str | None,
197
+ path: str | None,
198
+ rpc_method: str | None,
199
+ a2a_operation: str | None,
200
+ endpoint: bool,
201
+ ) -> OperationRef:
202
+ if operation_id and any(c.isspace() for c in operation_id):
203
+ raise click.UsageError("OPERATION_ID must not contain whitespace")
204
+ if rpc_method is not None and not _RPC_METHOD_RE.fullmatch(rpc_method):
205
+ raise click.UsageError(
206
+ f"--rpc-method {rpc_method!r}: a JSON-RPC method name is a letter, then up to 63 "
207
+ "letters, digits, '_', '/' or '.'"
208
+ )
209
+ if a2a_operation is not None and a2a_operation not in A2A_OPERATIONS:
210
+ raise click.UsageError("--a2a-operation takes approve or reject")
211
+ if bool(method) != bool(path) or (endpoint and not (method and path)):
212
+ raise click.UsageError(
213
+ "give both --method POST and --path P (the JSON-RPC endpoint)"
214
+ + ("" if endpoint else ", or neither")
215
+ )
216
+ if method is not None:
217
+ method = method.strip().upper()
218
+ if method != "POST":
219
+ raise click.UsageError(
220
+ f"--method {method}: a JSON-RPC request is a POST (GET and HEAD carry no "
221
+ "rpc_method)"
222
+ )
223
+ problem = path_template_problem(path)
224
+ if problem:
225
+ raise click.UsageError(f"--path {path}: {problem}")
226
+ return cls(
227
+ operation_id=operation_id or None,
228
+ method=method,
229
+ path=path,
230
+ rpc_method=rpc_method,
231
+ a2a_operation=a2a_operation,
232
+ )
233
+
234
+ def describe(self) -> str:
235
+ endpoint = f"{self.method} {self.path}" if self.path else ""
236
+ rpc = [f"{k} {v}" for k, v in self._rpc_fields() if v]
237
+ return " ".join(part for part in (self.operation_id, endpoint, *rpc) if part)
238
+
239
+ def args(self) -> str:
240
+ """The command-line form (for suggestions)."""
241
+ endpoint = f"--method {self.method} --path {self.path}" if self.path else ""
242
+ rpc = [f"--{k.replace('_', '-')} {v}" for k, v in self._rpc_fields() if v]
243
+ return " ".join(part for part in (self.operation_id, endpoint, *rpc) if part)
244
+
245
+ def _rpc_fields(self) -> tuple[tuple[str, str | None], ...]:
246
+ return ((RPC_METHOD_KEY, self.rpc_method), (A2A_OPERATION_KEY, self.a2a_operation))
247
+
248
+
249
+ def spec_operations(spec: Mapping[str, Any]) -> list[tuple[str, str, str | None]]:
250
+ """``(path, METHOD, operationId)`` of every operation of an OpenAPI spec."""
251
+ from graph_agents_cli.dev.policy_check import _spec_operations
252
+
253
+ return _spec_operations(dict(spec))
254
+
255
+
256
+ def build_entry(
257
+ ref: OperationRef,
258
+ methods: list[str],
259
+ spec: Mapping[str, Any] | None,
260
+ spec_name: str,
261
+ ) -> tuple[dict[str, Any], list[str]]:
262
+ """The ``allowed_operations`` / ``denied_operations`` entry for ``ref``, and warnings.
263
+
264
+ With an OpenAPI spec, an operationId must exist in it, and its method and
265
+ path are filled in (all three must then match: AND semantics), so the entry
266
+ is exact; a ``--method``/``--path`` given with it must agree with the
267
+ spec. Without a spec, an operationId given with ``--method M --path P``
268
+ pins all three.
269
+ """
270
+ warnings: list[str] = []
271
+ if ANY_METHOD in methods:
272
+ raise click.UsageError('an operation entry lists its methods; "*" is not allowed there')
273
+ wanted = [m for m in HTTP_METHODS if m in {*methods, *([ref.method] if ref.method else [])}]
274
+ if ref.rpc:
275
+ # A JSON-RPC request (0.3): judged by what its body is, not by an OpenAPI spec.
276
+ entry = {
277
+ key: value
278
+ for key, value in (
279
+ ("operationId", ref.operation_id),
280
+ ("path", ref.path),
281
+ ("methods", wanted or None),
282
+ (RPC_METHOD_KEY, ref.rpc_method),
283
+ (A2A_OPERATION_KEY, ref.a2a_operation),
284
+ )
285
+ if value is not None
286
+ }
287
+ return entry, warnings
288
+ if ref.operation_id:
289
+ entry: dict[str, Any] = {"operationId": ref.operation_id}
290
+ if spec is not None:
291
+ found = [(p, m) for p, m, op_id in spec_operations(spec) if op_id == ref.operation_id]
292
+ if not found:
293
+ raise ApiCommandError(
294
+ f"operationId {ref.operation_id} is not in {spec_name} (the API's openapi "
295
+ "spec); check the id, or update the spec"
296
+ )
297
+ spec_path, spec_method = found[0]
298
+ if wanted and wanted != [spec_method]:
299
+ raise ApiCommandError(
300
+ f"{ref.operation_id} is {spec_method} {spec_path} in {spec_name}, not "
301
+ f"{', '.join(wanted)}"
302
+ )
303
+ if ref.path is not None and not path_matches(spec_path, ref.path):
304
+ raise ApiCommandError(
305
+ f"{ref.operation_id} is {spec_method} {spec_path} in {spec_name}, not "
306
+ f"{ref.path}"
307
+ )
308
+ entry["path"] = spec_path
309
+ entry["methods"] = [spec_method]
310
+ return entry, warnings
311
+ if ref.path is not None:
312
+ entry["path"] = ref.path
313
+ if wanted:
314
+ entry["methods"] = wanted
315
+ return entry, warnings
316
+ assert ref.method is not None and ref.path is not None
317
+ entry = {"path": ref.path, "methods": wanted}
318
+ if spec is not None:
319
+ known = {(p, m) for p, m, _ in spec_operations(spec)}
320
+ for method in wanted:
321
+ if not any(m == method and path_matches(p, ref.path) for p, m in known):
322
+ warnings.append(f"{method} {ref.path} is not in {spec_name}")
323
+ return entry, warnings
324
+
325
+
326
+ def _normalized(entry: Mapping[str, Any]) -> tuple[Any, ...]:
327
+ path = entry.get("path")
328
+ return (
329
+ entry.get("operationId"),
330
+ normalize_path(str(path)) if path is not None else None,
331
+ tuple(sorted(str(m).upper() for m in entry.get("methods") or [])),
332
+ entry.get(RPC_METHOD_KEY),
333
+ entry.get(A2A_OPERATION_KEY),
334
+ )
335
+
336
+
337
+ def same_entry(a: Mapping[str, Any], b: Mapping[str, Any]) -> bool:
338
+ return _normalized(a) == _normalized(b)
339
+
340
+
341
+ def describe_entry(entry: Mapping[str, Any]) -> str:
342
+ parts = []
343
+ if entry.get("operationId") is not None:
344
+ parts.append(str(entry["operationId"]))
345
+ methods = entry.get("methods")
346
+ parts.append(",".join(str(m).upper() for m in methods) if methods else "any method")
347
+ if entry.get("path") is not None:
348
+ parts.append(str(entry["path"]))
349
+ for key in (RPC_METHOD_KEY, A2A_OPERATION_KEY):
350
+ if entry.get(key) is not None:
351
+ parts.append(f"{key}={entry[key]}")
352
+ return " ".join(parts)
353
+
354
+
355
+ def matching_indexes(entries: Sequence[Mapping[str, Any]], ref: OperationRef) -> list[int]:
356
+ """The entries ``ref`` names: by operationId, or by path covering the method; with
357
+ ``rpc_method`` or ``a2a_operation``, the entries pinning those (and its operationId)."""
358
+ found = []
359
+ for index, entry in enumerate(entries):
360
+ if ref.rpc:
361
+ if all(
362
+ value is None or entry.get(key) == value
363
+ for key, value in ((*ref._rpc_fields(), ("operationId", ref.operation_id)))
364
+ ):
365
+ found.append(index)
366
+ continue
367
+ if ref.operation_id is not None:
368
+ if entry.get("operationId") == ref.operation_id:
369
+ found.append(index)
370
+ continue
371
+ path = entry.get("path")
372
+ if path is None or normalize_path(str(path)) != normalize_path(str(ref.path)):
373
+ continue
374
+ methods = [str(m).upper() for m in entry.get("methods") or []]
375
+ if not methods or ref.method in methods:
376
+ found.append(index)
377
+ return found
378
+
379
+
380
+ @dataclass
381
+ class Revocation:
382
+ """One entry ``api revoke`` changes: removed, or only one of its methods removed."""
383
+
384
+ index: int
385
+ entry: dict[str, Any]
386
+ remaining_methods: list[str] | None # None: the whole entry goes
387
+
388
+ def describe(self) -> str:
389
+ if self.remaining_methods is None:
390
+ return f"remove {describe_entry(self.entry)}"
391
+ named = " ".join(
392
+ str(self.entry[key]) for key in ("operationId", "path") if self.entry.get(key)
393
+ )
394
+ return f"keep {named} for {', '.join(self.remaining_methods)} only"
395
+
396
+
397
+ def plan_revocations(
398
+ entries: Sequence[Mapping[str, Any]], ref: OperationRef, every_method: Sequence[str]
399
+ ) -> list[Revocation]:
400
+ """What revoking ``ref`` does to each matching entry, last entry first.
401
+
402
+ ``--method M --path P`` takes M out of an entry and nothing else. An entry
403
+ without ``methods`` covers every method, ``every_method`` (the API's
404
+ allowed methods for an allowed entry, every HTTP method for a denial): it
405
+ keeps the others, listed explicitly, so revoking GET from a denial of every
406
+ method never lifts it for DELETE.
407
+ """
408
+ plans = []
409
+ for index in reversed(matching_indexes(entries, ref)):
410
+ entry = dict(entries[index])
411
+ if ref.method is None:
412
+ plans.append(Revocation(index, entry, None))
413
+ continue
414
+ methods = [str(m).upper() for m in entry.get("methods") or []] or list(every_method)
415
+ remaining = [m for m in methods if m != ref.method]
416
+ plans.append(Revocation(index, entry, remaining or None))
417
+ return plans
418
+
419
+
420
+ # ---------------------------------------------------------------------------
421
+ # Limits
422
+ # ---------------------------------------------------------------------------
423
+
424
+
425
+ def parse_limit(value: str | None, option: str) -> int | str | None:
426
+ """``--max-calls-per-run 20`` -> 20, ``none`` -> None; not given -> ``"keep"``."""
427
+ if value is None:
428
+ return "keep"
429
+ if value.strip().lower() == "none":
430
+ return None
431
+ try:
432
+ number = int(value)
433
+ except ValueError:
434
+ raise click.UsageError(f"{option} takes an integer >= 1, or none") from None
435
+ if number < 1:
436
+ raise click.UsageError(f"{option} takes an integer >= 1, or none")
437
+ return number
438
+
439
+
440
+ LIMIT_KEYS = ("max_calls_per_run", "rate_per_minute", "max_response_bytes")
441
+
442
+
443
+ def new_limits(
444
+ current: Mapping[str, Any] | None,
445
+ max_calls: int | str | None,
446
+ rate: int | str | None,
447
+ max_bytes: int | str | None = "keep",
448
+ ) -> dict[str, int] | None:
449
+ """The ``limits`` mapping after the change; None when no limit is left.
450
+
451
+ ``"keep"`` leaves a limit as it is, None removes it; the other keys stay.
452
+ """
453
+ limits = dict(current or {})
454
+ for key, value in zip(LIMIT_KEYS, (max_calls, rate, max_bytes), strict=True):
455
+ if value == "keep":
456
+ continue
457
+ if value is None:
458
+ limits.pop(key, None)
459
+ else:
460
+ limits[key] = value
461
+ ordered = {k: limits[k] for k in LIMIT_KEYS if k in limits}
462
+ return ordered or None
463
+
464
+
465
+ def describe_limits(limits: Mapping[str, Any] | None) -> str:
466
+ if not limits:
467
+ return "none"
468
+ parts = []
469
+ if "max_calls_per_run" in limits:
470
+ parts.append(f"{limits['max_calls_per_run']} call(s) per run")
471
+ if "rate_per_minute" in limits:
472
+ parts.append(f"{limits['rate_per_minute']} per minute per replica")
473
+ if "max_response_bytes" in limits:
474
+ parts.append(f"answers of at most {limits['max_response_bytes']} bytes")
475
+ return ", ".join(parts)
476
+
477
+
478
+ def limits_widen(before: Mapping[str, Any] | None, after: Mapping[str, Any] | None) -> bool:
479
+ """True when a limit is removed or raised."""
480
+ before, after = before or {}, after or {}
481
+ return any(key not in after or after[key] > before[key] for key in before)
482
+
483
+
484
+ # ---------------------------------------------------------------------------
485
+ # Specs
486
+ # ---------------------------------------------------------------------------
487
+
488
+
489
+ def load_spec(root: Path, reference: str) -> dict[str, Any]:
490
+ """The OpenAPI spec an API names (relative to the project root); exit 3 when unreadable."""
491
+ from graph_agents_cli.dev.policy_check import load_openapi
492
+
493
+ path = Path(reference)
494
+ if not path.is_absolute():
495
+ path = root / path
496
+ try:
497
+ return load_openapi(path)
498
+ except (OSError, ValueError, yaml.YAMLError) as exc:
499
+ raise ApiCommandError(f"cannot read the OpenAPI spec {reference}: {exc}") from exc
500
+
501
+
502
+ def with_api(document: Mapping[str, Any] | None, name: str, api: Mapping[str, Any]) -> dict:
503
+ new = copy.deepcopy(dict(document)) if document else {"apis": {}}
504
+ new["apis"] = dict(new.get("apis") or {})
505
+ new["apis"][name] = copy.deepcopy(dict(api))
506
+ return new