keystone-cli 0.4.0__tar.gz → 0.6.0__tar.gz

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 (244) hide show
  1. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/PKG-INFO +68 -2
  2. keystone_cli-0.6.0/README.md +140 -0
  3. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/pyproject.toml +11 -4
  4. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/__main__.py +4 -1
  5. keystone_cli-0.6.0/src/keystone_cli/auth/api_key.py +48 -0
  6. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/auth/device_flow.py +19 -5
  7. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/auth/token_store.py +31 -0
  8. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/commands/agent.py +43 -36
  9. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/commands/login.py +14 -3
  10. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/commands/tool_credentials.py +100 -12
  11. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/commands/whoami.py +34 -0
  12. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/commands/workspace.py +16 -4
  13. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/runner.py +90 -7
  14. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/scaffold.py +6 -0
  15. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/data-space/pkg/graph.py.tmpl +7 -3
  16. keystone_cli-0.6.0/src/keystone_cli/templates/file-tools/README.md.tmpl +57 -0
  17. keystone_cli-0.6.0/src/keystone_cli/templates/file-tools/agent.yaml.tmpl +27 -0
  18. keystone_cli-0.6.0/src/keystone_cli/templates/file-tools/pkg/graph.py.tmpl +99 -0
  19. keystone_cli-0.6.0/src/keystone_cli/templates/file-tools/pyproject.toml.tmpl +24 -0
  20. keystone_cli-0.6.0/src/keystone_cli/templates/file-tools/tests/test_graph.py.tmpl +141 -0
  21. keystone_cli-0.6.0/src/keystone_cli/templates/generative-ui/README.md.tmpl +49 -0
  22. keystone_cli-0.6.0/src/keystone_cli/templates/generative-ui/agent.yaml.tmpl +25 -0
  23. keystone_cli-0.6.0/src/keystone_cli/templates/generative-ui/pkg/graph.py.tmpl +61 -0
  24. keystone_cli-0.6.0/src/keystone_cli/templates/generative-ui/pyproject.toml.tmpl +24 -0
  25. keystone_cli-0.6.0/src/keystone_cli/templates/generative-ui/tests/test_graph.py.tmpl +84 -0
  26. keystone_cli-0.6.0/src/keystone_cli/templates/ui-card/env.tmpl +10 -0
  27. keystone_cli-0.6.0/src/keystone_cli/templates/ui-card/gitignore.tmpl +7 -0
  28. keystone_cli-0.6.0/src/keystone_cli/templates/ui-card/pkg/__init__.py.tmpl +0 -0
  29. keystone_cli-0.6.0/src/keystone_cli/templates/ui-card/tests/conftest.py.tmpl +25 -0
  30. keystone_cli-0.6.0/src/keystone_cli/templates/user-tools/env.tmpl +10 -0
  31. keystone_cli-0.6.0/src/keystone_cli/templates/user-tools/gitignore.tmpl +7 -0
  32. keystone_cli-0.6.0/src/keystone_cli/templates/user-tools/pkg/__init__.py.tmpl +0 -0
  33. keystone_cli-0.6.0/src/keystone_cli/templates/user-tools/tests/conftest.py.tmpl +25 -0
  34. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli.egg-info/PKG-INFO +68 -2
  35. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli.egg-info/SOURCES.txt +20 -0
  36. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli.egg-info/requires.txt +1 -1
  37. keystone_cli-0.6.0/tests/test_api_key.py +284 -0
  38. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_deploy.py +32 -0
  39. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_hitl_approver_cli.py +8 -0
  40. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_init.py +5 -0
  41. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_offline.py +103 -0
  42. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_tool_credentials.py +62 -0
  43. keystone_cli-0.4.0/README.md +0 -74
  44. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/setup.cfg +0 -0
  45. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/__init__.py +0 -0
  46. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/_build_env.py +0 -0
  47. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/_jsonout.py +0 -0
  48. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/_stdio.py +0 -0
  49. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/_tls.py +0 -0
  50. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/_version.py +0 -0
  51. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/agent_logs.py +0 -0
  52. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/agent_skills.py +0 -0
  53. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/auth/__init__.py +0 -0
  54. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/commands/__init__.py +0 -0
  55. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/commands/dev.py +0 -0
  56. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/commands/kb.py +0 -0
  57. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/commands/kb_metadata.py +0 -0
  58. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/commands/prompt.py +0 -0
  59. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/commands/skills.py +0 -0
  60. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/devloop.py +0 -0
  61. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/fetch.py +0 -0
  62. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/manifest_ref.py +0 -0
  63. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/platform_client.py +0 -0
  64. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/run_debug.py +0 -0
  65. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/skills/keystone-debug/SKILL.md +0 -0
  66. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/blank/README.md.tmpl +0 -0
  67. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/blank/agent.yaml.tmpl +0 -0
  68. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/blank/env.tmpl +0 -0
  69. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/blank/gitignore.tmpl +0 -0
  70. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/blank/pkg/__init__.py.tmpl +0 -0
  71. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/blank/pkg/graph.py.tmpl +0 -0
  72. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/blank/pyproject.toml.tmpl +0 -0
  73. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/blank/tests/conftest.py.tmpl +0 -0
  74. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/blank/tests/test_graph.py.tmpl +0 -0
  75. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/data-space/README.md.tmpl +0 -0
  76. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/data-space/agent.yaml.tmpl +0 -0
  77. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/data-space/env.tmpl +0 -0
  78. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/data-space/gitignore.tmpl +0 -0
  79. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/data-space/pkg/__init__.py.tmpl +0 -0
  80. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/data-space/pyproject.toml.tmpl +0 -0
  81. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/data-space/tests/conftest.py.tmpl +0 -0
  82. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/data-space/tests/test_graph.py.tmpl +0 -0
  83. {keystone_cli-0.4.0/src/keystone_cli/templates/guardrails → keystone_cli-0.6.0/src/keystone_cli/templates/file-tools}/env.tmpl +0 -0
  84. {keystone_cli-0.4.0/src/keystone_cli/templates/guardrails → keystone_cli-0.6.0/src/keystone_cli/templates/file-tools}/gitignore.tmpl +0 -0
  85. {keystone_cli-0.4.0/src/keystone_cli/templates/guardrails → keystone_cli-0.6.0/src/keystone_cli/templates/file-tools}/pkg/__init__.py.tmpl +0 -0
  86. {keystone_cli-0.4.0/src/keystone_cli/templates/guardrails → keystone_cli-0.6.0/src/keystone_cli/templates/file-tools}/tests/conftest.py.tmpl +0 -0
  87. {keystone_cli-0.4.0/src/keystone_cli/templates/hitl → keystone_cli-0.6.0/src/keystone_cli/templates/generative-ui}/env.tmpl +0 -0
  88. {keystone_cli-0.4.0/src/keystone_cli/templates/hitl → keystone_cli-0.6.0/src/keystone_cli/templates/generative-ui}/gitignore.tmpl +0 -0
  89. {keystone_cli-0.4.0/src/keystone_cli/templates/hitl → keystone_cli-0.6.0/src/keystone_cli/templates/generative-ui}/pkg/__init__.py.tmpl +0 -0
  90. {keystone_cli-0.4.0/src/keystone_cli/templates/hitl → keystone_cli-0.6.0/src/keystone_cli/templates/generative-ui}/tests/conftest.py.tmpl +0 -0
  91. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/guardrails/README.md.tmpl +0 -0
  92. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/guardrails/agent.yaml.tmpl +0 -0
  93. {keystone_cli-0.4.0/src/keystone_cli/templates/kb-builder → keystone_cli-0.6.0/src/keystone_cli/templates/guardrails}/env.tmpl +0 -0
  94. {keystone_cli-0.4.0/src/keystone_cli/templates/kb-builder → keystone_cli-0.6.0/src/keystone_cli/templates/guardrails}/gitignore.tmpl +0 -0
  95. {keystone_cli-0.4.0/src/keystone_cli/templates/kb-builder → keystone_cli-0.6.0/src/keystone_cli/templates/guardrails}/pkg/__init__.py.tmpl +0 -0
  96. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/guardrails/pkg/graph.py.tmpl +0 -0
  97. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/guardrails/pyproject.toml.tmpl +0 -0
  98. {keystone_cli-0.4.0/src/keystone_cli/templates/kb-builder → keystone_cli-0.6.0/src/keystone_cli/templates/guardrails}/tests/conftest.py.tmpl +0 -0
  99. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/guardrails/tests/test_graph.py.tmpl +0 -0
  100. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/hitl/README.md.tmpl +0 -0
  101. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/hitl/agent.yaml.tmpl +0 -0
  102. {keystone_cli-0.4.0/src/keystone_cli/templates/llm-streaming → keystone_cli-0.6.0/src/keystone_cli/templates/hitl}/env.tmpl +0 -0
  103. {keystone_cli-0.4.0/src/keystone_cli/templates/llm-streaming → keystone_cli-0.6.0/src/keystone_cli/templates/hitl}/gitignore.tmpl +0 -0
  104. {keystone_cli-0.4.0/src/keystone_cli/templates/llm-streaming → keystone_cli-0.6.0/src/keystone_cli/templates/hitl}/pkg/__init__.py.tmpl +0 -0
  105. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/hitl/pkg/graph.py.tmpl +0 -0
  106. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/hitl/pyproject.toml.tmpl +0 -0
  107. {keystone_cli-0.4.0/src/keystone_cli/templates/llm-streaming → keystone_cli-0.6.0/src/keystone_cli/templates/hitl}/tests/conftest.py.tmpl +0 -0
  108. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/hitl/tests/test_graph.py.tmpl +0 -0
  109. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/kb-builder/README.md.tmpl +0 -0
  110. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/kb-builder/agent.yaml.tmpl +0 -0
  111. {keystone_cli-0.4.0/src/keystone_cli/templates/llm → keystone_cli-0.6.0/src/keystone_cli/templates/kb-builder}/env.tmpl +0 -0
  112. {keystone_cli-0.4.0/src/keystone_cli/templates/llm → keystone_cli-0.6.0/src/keystone_cli/templates/kb-builder}/gitignore.tmpl +0 -0
  113. {keystone_cli-0.4.0/src/keystone_cli/templates/llm → keystone_cli-0.6.0/src/keystone_cli/templates/kb-builder}/pkg/__init__.py.tmpl +0 -0
  114. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/kb-builder/pkg/graph.py.tmpl +0 -0
  115. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/kb-builder/pyproject.toml.tmpl +0 -0
  116. {keystone_cli-0.4.0/src/keystone_cli/templates/llm → keystone_cli-0.6.0/src/keystone_cli/templates/kb-builder}/tests/conftest.py.tmpl +0 -0
  117. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/kb-builder/tests/test_graph.py.tmpl +0 -0
  118. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/llm/README.md.tmpl +0 -0
  119. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/llm/agent.yaml.tmpl +0 -0
  120. {keystone_cli-0.4.0/src/keystone_cli/templates/memory → keystone_cli-0.6.0/src/keystone_cli/templates/llm}/env.tmpl +0 -0
  121. {keystone_cli-0.4.0/src/keystone_cli/templates/memory → keystone_cli-0.6.0/src/keystone_cli/templates/llm}/gitignore.tmpl +0 -0
  122. {keystone_cli-0.4.0/src/keystone_cli/templates/memory → keystone_cli-0.6.0/src/keystone_cli/templates/llm}/pkg/__init__.py.tmpl +0 -0
  123. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/llm/pkg/graph.py.tmpl +0 -0
  124. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/llm/pyproject.toml.tmpl +0 -0
  125. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/llm/system-prompt.txt.tmpl +0 -0
  126. {keystone_cli-0.4.0/src/keystone_cli/templates/memory → keystone_cli-0.6.0/src/keystone_cli/templates/llm}/tests/conftest.py.tmpl +0 -0
  127. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/llm/tests/test_graph.py.tmpl +0 -0
  128. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/llm-streaming/README.md.tmpl +0 -0
  129. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/llm-streaming/agent.yaml.tmpl +0 -0
  130. {keystone_cli-0.4.0/src/keystone_cli/templates/memory-service → keystone_cli-0.6.0/src/keystone_cli/templates/llm-streaming}/env.tmpl +0 -0
  131. {keystone_cli-0.4.0/src/keystone_cli/templates/memory-service → keystone_cli-0.6.0/src/keystone_cli/templates/llm-streaming}/gitignore.tmpl +0 -0
  132. {keystone_cli-0.4.0/src/keystone_cli/templates/memory-service → keystone_cli-0.6.0/src/keystone_cli/templates/llm-streaming}/pkg/__init__.py.tmpl +0 -0
  133. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/llm-streaming/pkg/graph.py.tmpl +0 -0
  134. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/llm-streaming/pyproject.toml.tmpl +0 -0
  135. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/llm-streaming/system-prompt.txt.tmpl +0 -0
  136. {keystone_cli-0.4.0/src/keystone_cli/templates/memory-service → keystone_cli-0.6.0/src/keystone_cli/templates/llm-streaming}/tests/conftest.py.tmpl +0 -0
  137. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/llm-streaming/tests/test_graph.py.tmpl +0 -0
  138. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/memory/README.md.tmpl +0 -0
  139. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/memory/agent.yaml.tmpl +0 -0
  140. {keystone_cli-0.4.0/src/keystone_cli/templates/rag-qa → keystone_cli-0.6.0/src/keystone_cli/templates/memory}/env.tmpl +0 -0
  141. {keystone_cli-0.4.0/src/keystone_cli/templates/rag-qa → keystone_cli-0.6.0/src/keystone_cli/templates/memory}/gitignore.tmpl +0 -0
  142. {keystone_cli-0.4.0/src/keystone_cli/templates/rag-qa → keystone_cli-0.6.0/src/keystone_cli/templates/memory}/pkg/__init__.py.tmpl +0 -0
  143. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/memory/pkg/graph.py.tmpl +0 -0
  144. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/memory/pyproject.toml.tmpl +0 -0
  145. {keystone_cli-0.4.0/src/keystone_cli/templates/rag-qa → keystone_cli-0.6.0/src/keystone_cli/templates/memory}/tests/conftest.py.tmpl +0 -0
  146. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/memory/tests/test_graph.py.tmpl +0 -0
  147. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/memory-service/README.md.tmpl +0 -0
  148. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/memory-service/agent.yaml.tmpl +0 -0
  149. {keystone_cli-0.4.0/src/keystone_cli/templates/ocr → keystone_cli-0.6.0/src/keystone_cli/templates/memory-service}/env.tmpl +0 -0
  150. {keystone_cli-0.4.0/src/keystone_cli/templates/ocr → keystone_cli-0.6.0/src/keystone_cli/templates/memory-service}/gitignore.tmpl +0 -0
  151. {keystone_cli-0.4.0/src/keystone_cli/templates/ocr → keystone_cli-0.6.0/src/keystone_cli/templates/memory-service}/pkg/__init__.py.tmpl +0 -0
  152. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/memory-service/pkg/graph.py.tmpl +0 -0
  153. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/memory-service/pyproject.toml.tmpl +0 -0
  154. {keystone_cli-0.4.0/src/keystone_cli/templates/ocr → keystone_cli-0.6.0/src/keystone_cli/templates/memory-service}/tests/conftest.py.tmpl +0 -0
  155. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/memory-service/tests/test_graph.py.tmpl +0 -0
  156. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/ocr/README.md.tmpl +0 -0
  157. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/ocr/agent.yaml.tmpl +0 -0
  158. {keystone_cli-0.4.0/src/keystone_cli/templates/skill → keystone_cli-0.6.0/src/keystone_cli/templates/ocr}/env.tmpl +0 -0
  159. {keystone_cli-0.4.0/src/keystone_cli/templates/skill → keystone_cli-0.6.0/src/keystone_cli/templates/ocr}/gitignore.tmpl +0 -0
  160. {keystone_cli-0.4.0/src/keystone_cli/templates/skill → keystone_cli-0.6.0/src/keystone_cli/templates/ocr}/pkg/__init__.py.tmpl +0 -0
  161. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/ocr/pkg/graph.py.tmpl +0 -0
  162. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/ocr/pyproject.toml.tmpl +0 -0
  163. {keystone_cli-0.4.0/src/keystone_cli/templates/skill → keystone_cli-0.6.0/src/keystone_cli/templates/ocr}/tests/conftest.py.tmpl +0 -0
  164. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/ocr/tests/test_graph.py.tmpl +0 -0
  165. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/rag-qa/README.md.tmpl +0 -0
  166. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/rag-qa/agent.yaml.tmpl +0 -0
  167. {keystone_cli-0.4.0/src/keystone_cli/templates/tool-agent → keystone_cli-0.6.0/src/keystone_cli/templates/rag-qa}/env.tmpl +0 -0
  168. {keystone_cli-0.4.0/src/keystone_cli/templates/tool-agent → keystone_cli-0.6.0/src/keystone_cli/templates/rag-qa}/gitignore.tmpl +0 -0
  169. {keystone_cli-0.4.0/src/keystone_cli/templates/tool-agent → keystone_cli-0.6.0/src/keystone_cli/templates/rag-qa}/pkg/__init__.py.tmpl +0 -0
  170. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/rag-qa/pkg/graph.py.tmpl +0 -0
  171. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/rag-qa/pyproject.toml.tmpl +0 -0
  172. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/rag-qa/system-prompt.txt.tmpl +0 -0
  173. {keystone_cli-0.4.0/src/keystone_cli/templates/tool-agent → keystone_cli-0.6.0/src/keystone_cli/templates/rag-qa}/tests/conftest.py.tmpl +0 -0
  174. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/rag-qa/tests/test_graph.py.tmpl +0 -0
  175. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/skill/README.md.tmpl +0 -0
  176. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/skill/agent.yaml.tmpl +0 -0
  177. {keystone_cli-0.4.0/src/keystone_cli/templates/ui-card → keystone_cli-0.6.0/src/keystone_cli/templates/skill}/env.tmpl +0 -0
  178. {keystone_cli-0.4.0/src/keystone_cli/templates/ui-card → keystone_cli-0.6.0/src/keystone_cli/templates/skill}/gitignore.tmpl +0 -0
  179. {keystone_cli-0.4.0/src/keystone_cli/templates/ui-card → keystone_cli-0.6.0/src/keystone_cli/templates/skill}/pkg/__init__.py.tmpl +0 -0
  180. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/skill/pkg/graph.py.tmpl +0 -0
  181. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/skill/pyproject.toml.tmpl +0 -0
  182. {keystone_cli-0.4.0/src/keystone_cli/templates/ui-card → keystone_cli-0.6.0/src/keystone_cli/templates/skill}/tests/conftest.py.tmpl +0 -0
  183. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/skill/tests/test_graph.py.tmpl +0 -0
  184. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/tool-agent/README.md.tmpl +0 -0
  185. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/tool-agent/agent.yaml.tmpl +0 -0
  186. {keystone_cli-0.4.0/src/keystone_cli/templates/user-tools → keystone_cli-0.6.0/src/keystone_cli/templates/tool-agent}/env.tmpl +0 -0
  187. {keystone_cli-0.4.0/src/keystone_cli/templates/user-tools → keystone_cli-0.6.0/src/keystone_cli/templates/tool-agent}/gitignore.tmpl +0 -0
  188. {keystone_cli-0.4.0/src/keystone_cli/templates/user-tools → keystone_cli-0.6.0/src/keystone_cli/templates/tool-agent}/pkg/__init__.py.tmpl +0 -0
  189. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/tool-agent/pkg/graph.py.tmpl +0 -0
  190. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/tool-agent/pyproject.toml.tmpl +0 -0
  191. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/tool-agent/system-prompt.txt.tmpl +0 -0
  192. {keystone_cli-0.4.0/src/keystone_cli/templates/user-tools → keystone_cli-0.6.0/src/keystone_cli/templates/tool-agent}/tests/conftest.py.tmpl +0 -0
  193. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/tool-agent/tests/test_graph.py.tmpl +0 -0
  194. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/ui-card/README.md.tmpl +0 -0
  195. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/ui-card/agent.yaml.tmpl +0 -0
  196. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/ui-card/pkg/graph.py.tmpl +0 -0
  197. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/ui-card/pyproject.toml.tmpl +0 -0
  198. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/ui-card/tests/test_graph.py.tmpl +0 -0
  199. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/user-tools/README.md.tmpl +0 -0
  200. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/user-tools/agent.yaml.tmpl +0 -0
  201. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/user-tools/pkg/graph.py.tmpl +0 -0
  202. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/user-tools/pyproject.toml.tmpl +0 -0
  203. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli/templates/user-tools/tests/test_graph.py.tmpl +0 -0
  204. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli.egg-info/dependency_links.txt +0 -0
  205. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli.egg-info/entry_points.txt +0 -0
  206. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/src/keystone_cli.egg-info/top_level.txt +0 -0
  207. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_agent.py +0 -0
  208. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_agent_manifest_cmd.py +0 -0
  209. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_agent_test_cmd.py +0 -0
  210. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_assistant_provenance_and_flags.py +0 -0
  211. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_auth.py +0 -0
  212. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_build_progress.py +0 -0
  213. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_ci_token.py +0 -0
  214. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_config_env_mismatch.py +0 -0
  215. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_dev.py +0 -0
  216. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_doctor.py +0 -0
  217. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_encoding.py +0 -0
  218. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_fetch.py +0 -0
  219. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_help_quality.py +0 -0
  220. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_hitl_decisions_cli.py +0 -0
  221. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_invoke.py +0 -0
  222. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_invoke_stream.py +0 -0
  223. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_json_output.py +0 -0
  224. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_jsonout.py +0 -0
  225. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_kb_commands.py +0 -0
  226. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_kb_metadata_commands.py +0 -0
  227. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_login_command.py +0 -0
  228. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_logs.py +0 -0
  229. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_platform_client.py +0 -0
  230. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_prompt_commands.py +0 -0
  231. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_publish.py +0 -0
  232. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_retire_delete.py +0 -0
  233. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_run_debug.py +0 -0
  234. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_runs.py +0 -0
  235. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_skills.py +0 -0
  236. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_status.py +0 -0
  237. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_stdio.py +0 -0
  238. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_template_prompt_guard.py +0 -0
  239. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_template_unit_tests.py +0 -0
  240. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_threads.py +0 -0
  241. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_tls.py +0 -0
  242. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_traces.py +0 -0
  243. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_version.py +0 -0
  244. {keystone_cli-0.4.0 → keystone_cli-0.6.0}/tests/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: keystone-cli
3
- Version: 0.4.0
3
+ Version: 0.6.0
4
4
  Summary: Keystone CLI — build, test, deploy and operate pro-code agents, and manage knowledge bases, prompts, tool credentials and skills on the Keystone platform.
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -8,7 +8,7 @@ Classifier: Environment :: Console
8
8
  Classifier: Operating System :: OS Independent
9
9
  Requires-Python: >=3.12
10
10
  Description-Content-Type: text/markdown
11
- Requires-Dist: keystone-agent-sdk<0.14,>=0.13.20
11
+ Requires-Dist: keystone-agent-sdk<0.14,>=0.13.28
12
12
  Requires-Dist: keystone-request-context<0.4,>=0.3
13
13
  Requires-Dist: typer<1,>=0.12
14
14
  Requires-Dist: httpx<1,>=0.27
@@ -72,6 +72,72 @@ keystone --version
72
72
 
73
73
  To see the fields of `agent.yaml`, run `keystone agent manifest`. Every command has `--help`.
74
74
 
75
+ ## CI with a service account
76
+
77
+ Use a platform service account in CI. The CLI sends its API key as `Authorization: Bearer <key>`.
78
+ A CI job does not run `keystone login`.
79
+
80
+ Before you start, an admin gives the service account three permissions on its workspace, one time:
81
+
82
+ | Permission | Resource | Why |
83
+ | --- | --- | --- |
84
+ | `agent:register` | the workspace of the service account | Register a new agent |
85
+ | `agent-vb:version:create` | the workspace of the service account | Upload a bundle |
86
+ | `agent:read` | the workspace of the service account | Find an agent by name (`delete`, `versions`, `status` and others) |
87
+
88
+ In the portal, open **Settings → Service Accounts**, open the access dialog of the account, and
89
+ select **This workspace** as the resource.
90
+
91
+ `deploy --publish`, `invoke`, `runs` and `threads` work without `agent:read` on the workspace. A
92
+ service account can not read agent logs: `keystone agent logs` needs `trace:read`, which only a user
93
+ can hold.
94
+
95
+ When the service account deploys a new agent, it gets all other permissions on that agent
96
+ automatically: read, invoke, and version create, deploy, publish and retire. To invoke an agent that
97
+ another user created, an admin must give `agent:invoke` on that agent.
98
+
99
+ Set these variables in the CI job:
100
+
101
+ | Variable | Value |
102
+ | --- | --- |
103
+ | `KEYSTONE_API_KEY` | The API key of the service account. Keep it in a masked CI secret. |
104
+ | `KEYSTONE_WORKSPACE_ID` | The UUID of the target workspace. |
105
+ | `KEYSTONE_API_BASE` | Optional. The platform API URL. The default is the URL of your CLI package. |
106
+ | `KEYSTONE_AGENT_API_BASE` | Optional. The agent API URL for `invoke`, `runs` and `threads`. |
107
+
108
+ Then do these steps:
109
+
110
+ 1. Make sure that the CLI uses the service account.
111
+
112
+ ```bash
113
+ export KEYSTONE_API_KEY=<sa-api-key>
114
+ export KEYSTONE_WORKSPACE_ID=<workspace-uuid>
115
+ keystone whoami
116
+ ```
117
+
118
+ 2. Deploy the agent.
119
+
120
+ ```bash
121
+ keystone agent deploy my-agent --publish --wait
122
+ ```
123
+
124
+ 3. Call the agent.
125
+
126
+ ```bash
127
+ keystone agent invoke my-agent --input '{"prompt": "ping"}'
128
+ ```
129
+
130
+ Do not put the API key on the command line. The shell history and the CI log can show it.
131
+
132
+ These rules apply when `KEYSTONE_API_KEY` is set:
133
+
134
+ - The key has priority over `KEYSTONE_TOKEN` and over a saved login.
135
+ - The CLI does not save the key to a file, and it does not show the key.
136
+ `keystone whoami` shows only the key ID.
137
+ - The CLI does not select a workspace for you. `--workspace-id` has priority over `KEYSTONE_WORKSPACE_ID`.
138
+ `KEYSTONE_WORKSPACE_ID` has priority over the saved workspace.
139
+ - `keystone workspace list` and `keystone workspace use` do not operate with a key.
140
+
75
141
  ## Commands
76
142
 
77
143
  | Command | What it does |
@@ -0,0 +1,140 @@
1
+ # keystone-cli
2
+
3
+ `keystone` is the command-line tool of the Keystone platform. Use it to build, test, deploy and operate
4
+ pro-code agents. You can also manage knowledge bases, prompts, tool credentials and skills with it.
5
+
6
+ The CLI includes the Keystone agent SDK (`keystone-agent-sdk`). An agent project that you create with
7
+ the CLI uses that SDK.
8
+
9
+ ## Install
10
+
11
+ The CLI needs Python 3.12 or later.
12
+
13
+ ```bash
14
+ pip install keystone-cli
15
+ keystone --version
16
+ ```
17
+
18
+ ## Get started
19
+
20
+ 1. Log in. The command opens your browser and keeps your session for later commands.
21
+
22
+ ```bash
23
+ keystone login
24
+ ```
25
+
26
+ 2. Select the workspace that your commands use.
27
+
28
+ ```bash
29
+ keystone workspace use <name>
30
+ ```
31
+
32
+ 3. Create an agent project from a template, then check it.
33
+
34
+ ```bash
35
+ keystone agent init my-agent --template llm
36
+ keystone agent validate my-agent
37
+ ```
38
+
39
+ 4. Run the unit tests of the agent on your computer.
40
+
41
+ ```bash
42
+ keystone agent test my-agent
43
+ ```
44
+
45
+ 5. Deploy the agent, make the new version Live, and call it.
46
+
47
+ ```bash
48
+ keystone agent deploy my-agent --publish --wait
49
+ keystone agent invoke my-agent --input '{"prompt": "hi"}'
50
+ ```
51
+
52
+ To see the fields of `agent.yaml`, run `keystone agent manifest`. Every command has `--help`.
53
+
54
+ ## CI with a service account
55
+
56
+ Use a platform service account in CI. The CLI sends its API key as `Authorization: Bearer <key>`.
57
+ A CI job does not run `keystone login`.
58
+
59
+ Before you start, an admin gives the service account three permissions on its workspace, one time:
60
+
61
+ | Permission | Resource | Why |
62
+ | --- | --- | --- |
63
+ | `agent:register` | the workspace of the service account | Register a new agent |
64
+ | `agent-vb:version:create` | the workspace of the service account | Upload a bundle |
65
+ | `agent:read` | the workspace of the service account | Find an agent by name (`delete`, `versions`, `status` and others) |
66
+
67
+ In the portal, open **Settings → Service Accounts**, open the access dialog of the account, and
68
+ select **This workspace** as the resource.
69
+
70
+ `deploy --publish`, `invoke`, `runs` and `threads` work without `agent:read` on the workspace. A
71
+ service account can not read agent logs: `keystone agent logs` needs `trace:read`, which only a user
72
+ can hold.
73
+
74
+ When the service account deploys a new agent, it gets all other permissions on that agent
75
+ automatically: read, invoke, and version create, deploy, publish and retire. To invoke an agent that
76
+ another user created, an admin must give `agent:invoke` on that agent.
77
+
78
+ Set these variables in the CI job:
79
+
80
+ | Variable | Value |
81
+ | --- | --- |
82
+ | `KEYSTONE_API_KEY` | The API key of the service account. Keep it in a masked CI secret. |
83
+ | `KEYSTONE_WORKSPACE_ID` | The UUID of the target workspace. |
84
+ | `KEYSTONE_API_BASE` | Optional. The platform API URL. The default is the URL of your CLI package. |
85
+ | `KEYSTONE_AGENT_API_BASE` | Optional. The agent API URL for `invoke`, `runs` and `threads`. |
86
+
87
+ Then do these steps:
88
+
89
+ 1. Make sure that the CLI uses the service account.
90
+
91
+ ```bash
92
+ export KEYSTONE_API_KEY=<sa-api-key>
93
+ export KEYSTONE_WORKSPACE_ID=<workspace-uuid>
94
+ keystone whoami
95
+ ```
96
+
97
+ 2. Deploy the agent.
98
+
99
+ ```bash
100
+ keystone agent deploy my-agent --publish --wait
101
+ ```
102
+
103
+ 3. Call the agent.
104
+
105
+ ```bash
106
+ keystone agent invoke my-agent --input '{"prompt": "ping"}'
107
+ ```
108
+
109
+ Do not put the API key on the command line. The shell history and the CI log can show it.
110
+
111
+ These rules apply when `KEYSTONE_API_KEY` is set:
112
+
113
+ - The key has priority over `KEYSTONE_TOKEN` and over a saved login.
114
+ - The CLI does not save the key to a file, and it does not show the key.
115
+ `keystone whoami` shows only the key ID.
116
+ - The CLI does not select a workspace for you. `--workspace-id` has priority over `KEYSTONE_WORKSPACE_ID`.
117
+ `KEYSTONE_WORKSPACE_ID` has priority over the saved workspace.
118
+ - `keystone workspace list` and `keystone workspace use` do not operate with a key.
119
+
120
+ ## Commands
121
+
122
+ | Command | What it does |
123
+ | --- | --- |
124
+ | `keystone login`, `keystone whoami` | Log in, and show your user. |
125
+ | `keystone workspace` | List and select the workspace that your commands use. |
126
+ | `keystone agent` | Develop and test an agent locally (`init`, `validate`, `run`, `test`, `manifest`), ship it (`deploy`, `publish`, `status`, `retire`, `delete`), and operate and debug it (`invoke`, `logs`, `fetch`, `doctor`, `runs`, `hitl`, `traces`, `threads`). |
127
+ | `keystone dev` | Run the graph of the agent locally again each time you save a file. |
128
+ | `keystone kb` | Create and manage knowledge bases. |
129
+ | `keystone prompt` | Manage the prompts of a workspace. |
130
+ | `keystone tool-credentials` | Manage the credentials that the catalogue tools of your agents use. |
131
+ | `keystone skills` | Install Claude Code skills for work on agents. |
132
+ | `keystone version` | Show the CLI version, the target environment and the version of the bundled agent SDK. |
133
+
134
+ ## When a run fails
135
+
136
+ Start with this command. It shows the steps, the errors and the logs of one run.
137
+
138
+ ```bash
139
+ keystone agent runs debug <agent> <run_id>
140
+ ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "keystone-cli"
7
- version = "0.4.0"
7
+ version = "0.6.0"
8
8
  description = "Keystone CLI — build, test, deploy and operate pro-code agents, and manage knowledge bases, prompts, tool credentials and skills on the Keystone platform."
9
9
  # The PyPI project page shows this file (it had no description before 0.4.0). It is public: no internal
10
10
  # hosts, entity ids or workspace ids in it.
@@ -19,8 +19,11 @@ classifiers = [
19
19
  dependencies = [
20
20
  # Pinned to a compatible range: on an index (Artifact Registry) an unpinned name resolves to
21
21
  # ANY published version — the wheel's metadata is the only pin that travels with it.
22
- # 0.4.0 needs 0.13.20: the file client of FDP-6964 and the A2UI chat blocks of FDP-7060.
23
- "keystone-agent-sdk>=0.13.20,<0.14",
22
+ # 0.4.2 needs 0.13.22: `ui.generative_ui_tools` for the generative-ui template (FDP-6967).
23
+ # 0.4.3 needs 0.13.23: the file-tools template's nine tools, `attachments_as_content()` and
24
+ # `testing.FakeFileArtifact` (FDP-6967).
25
+ # 0.4.5 needs 0.13.26: `clients.kb_tools()` gives two KB tools, kb_search + kb_read_around (FDP-7133).
26
+ "keystone-agent-sdk>=0.13.28,<0.14",
24
27
  # Transitive (the SDK's building-block clients) + used directly by `run` to set a local
25
28
  # caller-identity ContextVar and to patch the clients for --offline. uv sources are NOT
26
29
  # transitive, so declare it directly (cf. services/llm-gateway).
@@ -52,6 +55,10 @@ keystone = "keystone_cli.__main__:app"
52
55
  [tool.uv.sources]
53
56
  keystone-agent-sdk = { path = "../agent_sdk" }
54
57
  keystone-request-context = { path = "../request_context" }
58
+ # The SDK's dependency since 0.13.22 (FDP-6967); uv sources are NOT transitive.
59
+ keystone-agui = { path = "../agui" }
60
+ # The SDK's dependency since 0.13.23 (FDP-6967); uv sources are NOT transitive.
61
+ keystone-agent-tools = { path = "../agent_tools" }
55
62
 
56
63
  [tool.setuptools.packages.find]
57
64
  where = ["src"]
@@ -61,7 +68,7 @@ where = ["src"]
61
68
  keystone_cli = ["templates/*/*.tmpl", "templates/*/pkg/*.tmpl", "templates/*/tests/*.tmpl", "skills/*/SKILL.md"]
62
69
 
63
70
  [tool.ruff]
64
- target-version = "0.4.0"
71
+ target-version = "0.6.0"
65
72
  line-length = 120
66
73
 
67
74
  [tool.ruff.lint]
@@ -41,8 +41,11 @@ Environment:
41
41
  use one file per environment
42
42
  KEYSTONE_API_BASE platform API URL (overrides the saved one)
43
43
  KEYSTONE_AGENT_API_BASE agent API URL: invoke, runs, hitl, threads
44
+ KEYSTONE_API_KEY service-account API key for CI; used as given,
45
+ never saved, wins over every other credential
46
+ KEYSTONE_WORKSPACE_ID workspace UUID (overrides the saved one;
47
+ required with KEYSTONE_API_KEY)
44
48
  KEYSTONE_TOKEN access token, used as given and never renewed
45
- (for CI)
46
49
  KEYSTONE_ISSUER login: Keycloak realm URL
47
50
  KEYSTONE_CLIENT_ID login: client id
48
51
  KEYSTONE_CLIENT_SECRET login --client-credentials: client secret
@@ -0,0 +1,48 @@
1
+ """Platform service-account API key from the environment — the CI credential (FDP-7177 G6).
2
+
3
+ A platform SA API key (``ak_<env>_<id>.<secret>``, minted in the portal) goes on the wire as
4
+ ``Authorization: Bearer <key>``. Kong validates it and stamps ``X-Service-Account-ID``; the CLI never
5
+ decodes it, renews it, or talks to Keycloak for it.
6
+
7
+ Rules this module exists to keep in ONE place:
8
+
9
+ * It is read ONLY from ``KEYSTONE_API_KEY`` — never a flag (argv shows up in ``ps``, shell history
10
+ and CI logs) and never the config file (a persisted key outlives the job that owns it).
11
+ * It is never printed. :func:`describe` returns the key id (the part before the dot, which the portal
12
+ and every audit event already show) or a short masked prefix — never the secret.
13
+ * An API key has no ``/users/me`` (platform refuses it), so the workspace cannot be picked
14
+ interactively: it must come from ``--workspace-id`` / ``KEYSTONE_WORKSPACE_ID`` / the config.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import os
20
+ from collections.abc import Mapping
21
+
22
+ # An env var NAME, not a value (same suppressions as device_flow.ENV_TOKEN, for the same reason).
23
+ ENV_API_KEY = "KEYSTONE_API_KEY" # noqa: S105 # pragma: allowlist secret # nosec B105
24
+
25
+ _MASKED_CHARS = 6
26
+
27
+
28
+ def from_env(env: Mapping[str, str] | None = None) -> str:
29
+ """The API key from ``KEYSTONE_API_KEY``, stripped; ``""`` when unset or blank."""
30
+ env = os.environ if env is None else env
31
+ return (env.get(ENV_API_KEY) or "").strip()
32
+
33
+
34
+ def in_use(env: Mapping[str, str] | None = None) -> bool:
35
+ """True when this process authenticates with a service-account API key."""
36
+ return bool(from_env(env))
37
+
38
+
39
+ def describe(key: str) -> str:
40
+ """A printable handle for ``key`` that never contains the secret.
41
+
42
+ Platform keys are ``<key id>.<secret>`` and the key id (``ak_<env>_<random>``) is public: the
43
+ portal lists it and audit events carry it. Anything else is shown as a short masked prefix.
44
+ """
45
+ key_id, sep, secret = key.partition(".")
46
+ if sep and key_id.startswith("ak_") and secret:
47
+ return key_id
48
+ return f"{key[:_MASKED_CHARS]}…" if len(key) > _MASKED_CHARS * 2 else "…"
@@ -2,8 +2,10 @@
2
2
 
3
3
  Pure-ish transport logic (takes an ``httpx.Client`` + injectable ``sleep``/``now``/``env`` for tests).
4
4
  The Typer command in ``commands/login`` handles printing + storing. :func:`get_access_token` is the
5
- read path every command uses, and it knows three ways to produce a bearer:
5
+ read path every command uses, and it knows four ways to produce a bearer:
6
6
 
7
+ 0. ``KEYSTONE_API_KEY`` — a platform service-account API key (FDP-7177). Highest precedence, used
8
+ verbatim, never refreshed, never written to disk. The recommended CI path (see ``auth/api_key``).
7
9
  1. ``KEYSTONE_TOKEN`` in the environment — used verbatim, never refreshed, never written to disk.
8
10
  The CI escape hatch for when something else already minted a token.
9
11
  2. the cached access token, while it is still fresh;
@@ -20,6 +22,7 @@ from typing import Any
20
22
 
21
23
  import httpx
22
24
 
25
+ from keystone_cli.auth import api_key
23
26
  from keystone_cli.auth.token_store import AuthConfig
24
27
 
25
28
  _DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code"
@@ -35,6 +38,8 @@ ENV_TOKEN = "KEYSTONE_TOKEN" # noqa: S105 # nosec B105
35
38
  # The client-credentials secret is read ONLY from the environment, never from a flag: a value on
36
39
  # argv is visible in `ps`, lands in shell history, and CI runners echo command lines into build logs.
37
40
  ENV_CLIENT_SECRET = "KEYSTONE_CLIENT_SECRET" # noqa: S105 # pragma: allowlist secret # nosec B105
41
+ # Platform service-account API key (FDP-7177) — re-exported so callers have one auth namespace.
42
+ ENV_API_KEY = api_key.ENV_API_KEY
38
43
 
39
44
  MODE_DEVICE = "device"
40
45
  MODE_CLIENT_CREDENTIALS = "client_credentials"
@@ -155,12 +160,20 @@ def get_access_token(
155
160
  ) -> str:
156
161
  """Return a valid bearer. Every command goes through here.
157
162
 
158
- Order, and why: an explicitly injected ``KEYSTONE_TOKEN`` wins over the cache, because in CI a
159
- stale ``~/.keystone/config.json`` left in the image would otherwise silently deploy as the wrong
160
- identity. Then the cache while fresh, then renewal per :attr:`AuthConfig.auth_mode`.
163
+ Order, and why: a service-account ``KEYSTONE_API_KEY`` first (FDP-7177 — the CI identity is the
164
+ one the job was given, never a person's), then an explicitly injected ``KEYSTONE_TOKEN``. Both win
165
+ over the cache, because in CI a stale ``~/.keystone/config.json`` left in the image would
166
+ otherwise silently deploy as the wrong identity. Then the cache while fresh, then renewal per
167
+ :attr:`AuthConfig.auth_mode`.
161
168
  """
162
169
  env = os.environ if env is None else env
163
170
 
171
+ key = api_key.from_env(env)
172
+ if key:
173
+ # Same contract as KEYSTONE_TOKEN below: verbatim, no request, nothing saved — not even onto
174
+ # the in-memory config, so a later save() for another reason cannot carry it to disk.
175
+ return key
176
+
164
177
  injected = (env.get(ENV_TOKEN) or "").strip()
165
178
  if injected:
166
179
  # Used verbatim: we hold no refresh token for it and it is not ours to renew. Deliberately
@@ -185,7 +198,8 @@ def get_access_token(
185
198
  else:
186
199
  if not cfg.refresh_token:
187
200
  raise DeviceFlowError(
188
- f"not logged in — run `keystone login`, or set {ENV_TOKEN} / use --client-credentials for CI"
201
+ f"not logged in — run `keystone login`; for CI set {ENV_API_KEY} (service account, "
202
+ f"recommended), or set {ENV_TOKEN} / use --client-credentials"
189
203
  )
190
204
  token = refresh_access_token(client, cfg.issuer, cfg.client_id, cfg.refresh_token)
191
205
  finally:
@@ -14,6 +14,7 @@ from dataclasses import asdict, dataclass, fields
14
14
  from pathlib import Path
15
15
 
16
16
  from keystone_cli._build_env import BUILD_ENV
17
+ from keystone_cli.auth import api_key
17
18
 
18
19
  # Baked platform defaults so `keystone login` works with NO flags (like `aws sso login`), PER ENV.
19
20
  # NOT secrets — client id is a public identifier, issuer/api are public URLs. The env is stamped into
@@ -155,6 +156,36 @@ def platform_base(cfg: AuthConfig, override: str | None = None) -> str:
155
156
  return (override or os.environ.get("KEYSTONE_API_BASE") or cfg.api_base or DEFAULT_API_BASE).rstrip("/")
156
157
 
157
158
 
159
+ def agent_platform_base(cfg: AuthConfig, control_base: str) -> str:
160
+ """Agent data-plane URL (invoke/runs/hitl/threads): ``KEYSTONE_AGENT_API_BASE``, then the saved
161
+ one, then ``control_base`` (FDP-3072 §11.4b)."""
162
+ return (os.environ.get("KEYSTONE_AGENT_API_BASE") or cfg.agent_api_base or control_base).rstrip("/")
163
+
164
+
165
+ # FDP-7177 — workspace from the environment, for CI. A service-account API key cannot reach
166
+ # `/users/me`, so it has no picker: this (or --workspace-id, or a saved one) is how it names its workspace.
167
+ ENV_WORKSPACE_ID = "KEYSTONE_WORKSPACE_ID"
168
+
169
+
170
+ def resolve_workspace(cfg: AuthConfig, override: str | None = None) -> str:
171
+ """Workspace a command targets: ``override`` (``--workspace-id``), then ``KEYSTONE_WORKSPACE_ID``,
172
+ then the one saved by ``keystone workspace use``. ``""`` when none. The one place this order is written."""
173
+ return (override or os.environ.get(ENV_WORKSPACE_ID, "").strip() or cfg.workspace_id).strip()
174
+
175
+
176
+ def no_workspace_message() -> str:
177
+ """The error for "no workspace" — with an API key there is no picker, so say what to set instead."""
178
+ if api_key.in_use():
179
+ return (
180
+ f"✗ no workspace — {api_key.ENV_API_KEY} is set and a service account cannot pick one; "
181
+ f"set {ENV_WORKSPACE_ID}=<workspace-uuid> (or pass --workspace-id)."
182
+ )
183
+ return (
184
+ f"✗ no workspace selected — run `keystone workspace use <name|id>` "
185
+ f"(or pass --workspace-id, or set {ENV_WORKSPACE_ID})."
186
+ )
187
+
188
+
158
189
  def load() -> AuthConfig:
159
190
  path = config_path()
160
191
  if not path.exists():
@@ -429,12 +429,9 @@ def deploy(
429
429
  # Workspace is caller-supplied (not in the JWT — the user picks it): --workspace-id wins, else the
430
430
  # one saved by `keystone workspace use`. entity/user are edge-injected from the token, so they are
431
431
  # NOT sent here (a client-set X-Entity-ID would be spoofable; the edge overrides it anyway).
432
- workspace = workspace_id or cfg.workspace_id
432
+ workspace = ts.resolve_workspace(cfg, workspace_id)
433
433
  if not workspace:
434
- typer.secho(
435
- "✗ no workspace selected — run `keystone workspace use <name|id>` (or pass --workspace-id).",
436
- fg=typer.colors.RED,
437
- )
434
+ typer.secho(ts.no_workspace_message(), fg=typer.colors.RED)
438
435
  raise typer.Exit(1)
439
436
  try:
440
437
  token = df.get_access_token(cfg, save=ts.save) # cached, or refreshed via the stored refresh token
@@ -504,7 +501,11 @@ def deploy(
504
501
  pub_headers = _header_source(cfg, {"X-Workspace-ID": workspace})
505
502
  try:
506
503
  with httpx.Client(timeout=60) as client:
507
- agent_id = _resolve_agent_id(client, base, pub_headers(), name)
504
+ # FDP-7177: agent-hub returns the agent id with the version, so publish needs no
505
+ # list call — that call needs agent:read on the WORKSPACE, which a CI service
506
+ # account otherwise has no reason to hold. An older agent-hub omits the field:
507
+ # fall back to the lookup by name.
508
+ agent_id = str(body.get("agent_id") or "") or _resolve_agent_id(client, base, pub_headers(), name)
508
509
  status = _wait_for_staged(client, base, pub_headers, agent_id, str(version_id))
509
510
  if status == "live":
510
511
  typer.echo(f" {version} is already live — nothing to publish.")
@@ -598,12 +599,9 @@ def invoke(
598
599
  if not base:
599
600
  typer.secho("✗ no --api-base (or KEYSTONE_API_BASE) configured", fg=typer.colors.RED)
600
601
  raise typer.Exit(1)
601
- workspace = workspace_id or cfg.workspace_id
602
+ workspace = ts.resolve_workspace(cfg, workspace_id)
602
603
  if not workspace:
603
- typer.secho(
604
- "✗ no workspace selected — run `keystone workspace use <name|id>` (or pass --workspace-id).",
605
- fg=typer.colors.RED,
606
- )
604
+ typer.secho(ts.no_workspace_message(), fg=typer.colors.RED)
607
605
  raise typer.Exit(1)
608
606
  try:
609
607
  token = df.get_access_token(cfg, save=ts.save) # cached, or refreshed via the stored refresh token
@@ -840,7 +838,7 @@ def _agent_api_base(cfg: ts.AuthConfig, control_base: str) -> str:
840
838
  lands the new domain this falls back to the control-plane base, so behavior is unchanged.
841
839
  Resolution: ``KEYSTONE_AGENT_API_BASE`` env → ``agent_api_base`` in the config file → fallback.
842
840
  """
843
- return (os.environ.get("KEYSTONE_AGENT_API_BASE") or cfg.agent_api_base or control_base).rstrip("/")
841
+ return ts.agent_platform_base(cfg, control_base)
844
842
 
845
843
 
846
844
  HeaderSource = agent_logs.HeaderSource
@@ -867,6 +865,11 @@ def _header_source(cfg: ts.AuthConfig, extra: dict[str, str]) -> HeaderSource:
867
865
 
868
866
  def _token_refused_hint() -> str:
869
867
  """What to do about a 401 that a just-renewed token still gets."""
868
+ if (os.environ.get(df.ENV_API_KEY) or "").strip():
869
+ return (
870
+ f" {df.ENV_API_KEY} was refused — check that the service-account key is not revoked, belongs to "
871
+ "this environment, and that the service account holds the permission for this action."
872
+ )
870
873
  if (os.environ.get(df.ENV_TOKEN) or "").strip():
871
874
  return (
872
875
  f" {df.ENV_TOKEN} is used as given and cannot be renewed — supply a token that lives "
@@ -879,9 +882,12 @@ def _gateway_auth(api_base: str | None, workspace_id: str | None) -> tuple[str,
879
882
  """Like :func:`_gateway_ctx`, but returns a header SOURCE for loops that outlive one token."""
880
883
  cfg = ts.load()
881
884
  base = ts.platform_base(cfg, api_base)
882
- workspace = workspace_id or cfg.workspace_id
883
- if not base or not workspace:
884
- typer.secho("✗ need an api base + workspace — run `keystone workspace use` first.", fg=typer.colors.RED)
885
+ workspace = ts.resolve_workspace(cfg, workspace_id)
886
+ if not base:
887
+ typer.secho("✗ no --api-base (or KEYSTONE_API_BASE) configured", fg=typer.colors.RED)
888
+ raise typer.Exit(1)
889
+ if not workspace:
890
+ typer.secho(ts.no_workspace_message(), fg=typer.colors.RED)
885
891
  raise typer.Exit(1)
886
892
  extra = {"X-Workspace-ID": workspace, "X-Correlation-ID": str(uuid.uuid4())}
887
893
  return _agent_api_base(cfg, base), _header_source(cfg, extra)
@@ -1213,6 +1219,13 @@ def hitl_respond(
1213
1219
  "platform user ids — `keystone whoami` prints yours.",
1214
1220
  fg=typer.colors.RED,
1215
1221
  )
1222
+ elif code == "GW_HITL_USER_ONLY":
1223
+ # FDP-7177: a grant does not help here — only a person can approve.
1224
+ typer.secho(
1225
+ f"✗ a service account cannot answer an approval (run {run_id}). A person listed as an "
1226
+ "approver must answer it, from the portal or with their own `keystone login`.",
1227
+ fg=typer.colors.RED,
1228
+ )
1216
1229
  else:
1217
1230
  typer.secho(
1218
1231
  f"✗ respond refused ({code or 403}): you lack the permission to answer this agent's "
@@ -1434,9 +1447,12 @@ def _control_auth(api_base: str | None, workspace_id: str | None) -> tuple[str,
1434
1447
  """Like :func:`_control_ctx`, but returns a header SOURCE for loops that outlive one token."""
1435
1448
  cfg = ts.load()
1436
1449
  base = ts.platform_base(cfg, api_base)
1437
- workspace = workspace_id or cfg.workspace_id
1438
- if not base or not workspace:
1439
- typer.secho("✗ need an api base + workspace — run `keystone workspace use` first.", fg=typer.colors.RED)
1450
+ workspace = ts.resolve_workspace(cfg, workspace_id)
1451
+ if not base:
1452
+ typer.secho("✗ no --api-base (or KEYSTONE_API_BASE) configured", fg=typer.colors.RED)
1453
+ raise typer.Exit(1)
1454
+ if not workspace:
1455
+ typer.secho(ts.no_workspace_message(), fg=typer.colors.RED)
1440
1456
  raise typer.Exit(1)
1441
1457
  return base, _header_source(cfg, {"X-Workspace-ID": workspace, "X-Correlation-ID": str(uuid.uuid4())})
1442
1458
 
@@ -2100,7 +2116,7 @@ def fetch_cmd(
2100
2116
  manifest = {
2101
2117
  "agent": agent,
2102
2118
  "agent_id": agent_id,
2103
- "workspace_id": workspace_id or ts.load().workspace_id,
2119
+ "workspace_id": ts.resolve_workspace(ts.load(), workspace_id),
2104
2120
  "api_base": base,
2105
2121
  "fetched_at": agent_logs.iso(now),
2106
2122
  "cli_version": cli_version(),
@@ -2921,12 +2937,9 @@ def publish(
2921
2937
  """
2922
2938
  cfg = ts.load()
2923
2939
  base = ts.platform_base(cfg, api_base)
2924
- workspace = workspace_id or cfg.workspace_id
2940
+ workspace = ts.resolve_workspace(cfg, workspace_id)
2925
2941
  if not workspace:
2926
- typer.secho(
2927
- "✗ no workspace selected — run `keystone workspace use <name|id>` (or pass --workspace-id).",
2928
- fg=typer.colors.RED,
2929
- )
2942
+ typer.secho(ts.no_workspace_message(), fg=typer.colors.RED)
2930
2943
  raise typer.Exit(1)
2931
2944
  try:
2932
2945
  token = df.get_access_token(cfg, save=ts.save)
@@ -3058,9 +3071,9 @@ def doctor(
3058
3071
  out = _DoctorOut(agent, json_out)
3059
3072
  cfg = ts.load()
3060
3073
  base = ts.platform_base(cfg, api_base)
3061
- workspace = workspace_id or cfg.workspace_id
3074
+ workspace = ts.resolve_workspace(cfg, workspace_id)
3062
3075
  if not workspace:
3063
- out.fail("✗ no workspace selected — run `keystone workspace use <name|id>` (or pass --workspace-id).")
3076
+ out.fail(ts.no_workspace_message())
3064
3077
  try:
3065
3078
  token = df.get_access_token(cfg, save=ts.save)
3066
3079
  except df.DeviceFlowError as exc:
@@ -3396,12 +3409,9 @@ def status(
3396
3409
  """
3397
3410
  cfg = ts.load()
3398
3411
  base = ts.platform_base(cfg, api_base)
3399
- workspace = workspace_id or cfg.workspace_id
3412
+ workspace = ts.resolve_workspace(cfg, workspace_id)
3400
3413
  if not workspace:
3401
- typer.secho(
3402
- "✗ no workspace selected — run `keystone workspace use <name|id>` (or pass --workspace-id).",
3403
- fg=typer.colors.RED,
3404
- )
3414
+ typer.secho(ts.no_workspace_message(), fg=typer.colors.RED)
3405
3415
  raise typer.Exit(1)
3406
3416
  try:
3407
3417
  token = df.get_access_token(cfg, save=ts.save)
@@ -3453,12 +3463,9 @@ def _hub_auth(api_base: str | None, workspace_id: str | None) -> tuple[str, dict
3453
3463
  """(base, headers) for agent-hub lifecycle calls — the login/workspace dance."""
3454
3464
  cfg = ts.load()
3455
3465
  base = ts.platform_base(cfg, api_base)
3456
- workspace = workspace_id or cfg.workspace_id
3466
+ workspace = ts.resolve_workspace(cfg, workspace_id)
3457
3467
  if not workspace:
3458
- typer.secho(
3459
- "✗ no workspace selected — run `keystone workspace use <name|id>` (or pass --workspace-id).",
3460
- fg=typer.colors.RED,
3461
- )
3468
+ typer.secho(ts.no_workspace_message(), fg=typer.colors.RED)
3462
3469
  raise typer.Exit(1)
3463
3470
  try:
3464
3471
  token = df.get_access_token(cfg, save=ts.save)
@@ -49,9 +49,10 @@ def login(
49
49
  False,
50
50
  "--client-credentials",
51
51
  help=(
52
- f"Non-interactive service-account login for CI. Reads the secret from ${df.ENV_CLIENT_SECRET} "
52
+ f"Non-interactive Keycloak client login. Reads the secret from ${df.ENV_CLIENT_SECRET} "
53
53
  f"only — there is deliberately no --client-secret flag, because a secret on argv is visible in "
54
- f"`ps`, shell history and CI build logs."
54
+ f"`ps`, shell history and CI build logs. For CI, the recommended path is a platform "
55
+ f"service-account API key in ${df.ENV_API_KEY} (no login step needed)."
55
56
  ),
56
57
  ),
57
58
  ) -> None:
@@ -66,7 +67,17 @@ def login(
66
67
  keystone login
67
68
  keystone login --no-browser
68
69
  KEYSTONE_CLIENT_SECRET=... keystone login --client-credentials
70
+
71
+ For CI, prefer a service-account API key: set KEYSTONE_API_KEY and
72
+ KEYSTONE_WORKSPACE_ID, and skip `keystone login`.
69
73
  """
74
+ if (os.environ.get(df.ENV_API_KEY) or "").strip():
75
+ # FDP-7177: the key outranks any cached login, so this session would be shadowed anyway.
76
+ typer.secho(
77
+ f" ⚠ {df.ENV_API_KEY} is set — later commands use that service-account key, not this login. "
78
+ f"Unset it to use the login.",
79
+ fg=typer.colors.YELLOW,
80
+ )
70
81
  # Precedence: --flag / KEYSTONE_* env (typer) > cached config (SAME env only) > baked per-env default.
71
82
  # Per-env packages own their endpoints, so a cross-env cached config must NOT shadow them: if the
72
83
  # cached config was written for a different env (its stamped `env` differs, or — pre-env configs —
@@ -111,7 +122,7 @@ def login(
111
122
  if not interactive:
112
123
  typer.secho(
113
124
  " (no TTY — not opening a browser; the device grant still needs a human to approve the URL below. "
114
- f"For unattended runs use --client-credentials or set ${df.ENV_TOKEN}.)",
125
+ f"For unattended runs set ${df.ENV_API_KEY} (service account) — no login needed.)",
115
126
  fg=typer.colors.YELLOW,
116
127
  )
117
128