keystone-cli 0.4.0__tar.gz → 0.5.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.
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/PKG-INFO +68 -2
- keystone_cli-0.5.0/README.md +140 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/pyproject.toml +11 -4
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/__main__.py +4 -1
- keystone_cli-0.5.0/src/keystone_cli/auth/api_key.py +48 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/auth/device_flow.py +19 -5
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/auth/token_store.py +31 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/commands/agent.py +43 -36
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/commands/login.py +14 -3
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/commands/tool_credentials.py +100 -12
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/commands/whoami.py +34 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/commands/workspace.py +16 -4
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/runner.py +68 -7
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/scaffold.py +6 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/data-space/pkg/graph.py.tmpl +7 -3
- keystone_cli-0.5.0/src/keystone_cli/templates/file-tools/README.md.tmpl +57 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/file-tools/agent.yaml.tmpl +27 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/file-tools/pkg/graph.py.tmpl +99 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/file-tools/pyproject.toml.tmpl +24 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/file-tools/tests/test_graph.py.tmpl +141 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/generative-ui/README.md.tmpl +49 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/generative-ui/agent.yaml.tmpl +25 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/generative-ui/pkg/graph.py.tmpl +61 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/generative-ui/pyproject.toml.tmpl +24 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/generative-ui/tests/test_graph.py.tmpl +84 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/ui-card/env.tmpl +10 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/ui-card/gitignore.tmpl +7 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/ui-card/pkg/__init__.py.tmpl +0 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/ui-card/tests/conftest.py.tmpl +25 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/user-tools/env.tmpl +10 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/user-tools/gitignore.tmpl +7 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/user-tools/pkg/__init__.py.tmpl +0 -0
- keystone_cli-0.5.0/src/keystone_cli/templates/user-tools/tests/conftest.py.tmpl +25 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli.egg-info/PKG-INFO +68 -2
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli.egg-info/SOURCES.txt +20 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli.egg-info/requires.txt +1 -1
- keystone_cli-0.5.0/tests/test_api_key.py +284 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_deploy.py +32 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_hitl_approver_cli.py +8 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_init.py +5 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_offline.py +74 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_tool_credentials.py +62 -0
- keystone_cli-0.4.0/README.md +0 -74
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/setup.cfg +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/__init__.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/_build_env.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/_jsonout.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/_stdio.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/_tls.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/_version.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/agent_logs.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/agent_skills.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/auth/__init__.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/commands/__init__.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/commands/dev.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/commands/kb.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/commands/kb_metadata.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/commands/prompt.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/commands/skills.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/devloop.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/fetch.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/manifest_ref.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/platform_client.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/run_debug.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/skills/keystone-debug/SKILL.md +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/blank/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/blank/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/blank/env.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/blank/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/blank/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/blank/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/blank/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/blank/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/blank/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/data-space/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/data-space/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/data-space/env.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/data-space/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/data-space/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/data-space/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/data-space/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/data-space/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/guardrails → keystone_cli-0.5.0/src/keystone_cli/templates/file-tools}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/guardrails → keystone_cli-0.5.0/src/keystone_cli/templates/file-tools}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/guardrails → keystone_cli-0.5.0/src/keystone_cli/templates/file-tools}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/guardrails → keystone_cli-0.5.0/src/keystone_cli/templates/file-tools}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/hitl → keystone_cli-0.5.0/src/keystone_cli/templates/generative-ui}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/hitl → keystone_cli-0.5.0/src/keystone_cli/templates/generative-ui}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/hitl → keystone_cli-0.5.0/src/keystone_cli/templates/generative-ui}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/hitl → keystone_cli-0.5.0/src/keystone_cli/templates/generative-ui}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/guardrails/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/guardrails/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/kb-builder → keystone_cli-0.5.0/src/keystone_cli/templates/guardrails}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/kb-builder → keystone_cli-0.5.0/src/keystone_cli/templates/guardrails}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/kb-builder → keystone_cli-0.5.0/src/keystone_cli/templates/guardrails}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/guardrails/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/guardrails/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/kb-builder → keystone_cli-0.5.0/src/keystone_cli/templates/guardrails}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/guardrails/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/hitl/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/hitl/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/llm-streaming → keystone_cli-0.5.0/src/keystone_cli/templates/hitl}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/llm-streaming → keystone_cli-0.5.0/src/keystone_cli/templates/hitl}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/llm-streaming → keystone_cli-0.5.0/src/keystone_cli/templates/hitl}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/hitl/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/hitl/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/llm-streaming → keystone_cli-0.5.0/src/keystone_cli/templates/hitl}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/hitl/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/kb-builder/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/kb-builder/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/llm → keystone_cli-0.5.0/src/keystone_cli/templates/kb-builder}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/llm → keystone_cli-0.5.0/src/keystone_cli/templates/kb-builder}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/llm → keystone_cli-0.5.0/src/keystone_cli/templates/kb-builder}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/kb-builder/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/kb-builder/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/llm → keystone_cli-0.5.0/src/keystone_cli/templates/kb-builder}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/kb-builder/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/llm/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/llm/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/memory → keystone_cli-0.5.0/src/keystone_cli/templates/llm}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/memory → keystone_cli-0.5.0/src/keystone_cli/templates/llm}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/memory → keystone_cli-0.5.0/src/keystone_cli/templates/llm}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/llm/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/llm/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/llm/system-prompt.txt.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/memory → keystone_cli-0.5.0/src/keystone_cli/templates/llm}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/llm/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/llm-streaming/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/llm-streaming/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/memory-service → keystone_cli-0.5.0/src/keystone_cli/templates/llm-streaming}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/memory-service → keystone_cli-0.5.0/src/keystone_cli/templates/llm-streaming}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/memory-service → keystone_cli-0.5.0/src/keystone_cli/templates/llm-streaming}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/llm-streaming/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/llm-streaming/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/llm-streaming/system-prompt.txt.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/memory-service → keystone_cli-0.5.0/src/keystone_cli/templates/llm-streaming}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/llm-streaming/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/memory/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/memory/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/rag-qa → keystone_cli-0.5.0/src/keystone_cli/templates/memory}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/rag-qa → keystone_cli-0.5.0/src/keystone_cli/templates/memory}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/rag-qa → keystone_cli-0.5.0/src/keystone_cli/templates/memory}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/memory/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/memory/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/rag-qa → keystone_cli-0.5.0/src/keystone_cli/templates/memory}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/memory/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/memory-service/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/memory-service/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/ocr → keystone_cli-0.5.0/src/keystone_cli/templates/memory-service}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/ocr → keystone_cli-0.5.0/src/keystone_cli/templates/memory-service}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/ocr → keystone_cli-0.5.0/src/keystone_cli/templates/memory-service}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/memory-service/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/memory-service/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/ocr → keystone_cli-0.5.0/src/keystone_cli/templates/memory-service}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/memory-service/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/ocr/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/ocr/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/skill → keystone_cli-0.5.0/src/keystone_cli/templates/ocr}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/skill → keystone_cli-0.5.0/src/keystone_cli/templates/ocr}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/skill → keystone_cli-0.5.0/src/keystone_cli/templates/ocr}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/ocr/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/ocr/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/skill → keystone_cli-0.5.0/src/keystone_cli/templates/ocr}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/ocr/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/rag-qa/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/rag-qa/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/tool-agent → keystone_cli-0.5.0/src/keystone_cli/templates/rag-qa}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/tool-agent → keystone_cli-0.5.0/src/keystone_cli/templates/rag-qa}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/tool-agent → keystone_cli-0.5.0/src/keystone_cli/templates/rag-qa}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/rag-qa/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/rag-qa/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/rag-qa/system-prompt.txt.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/tool-agent → keystone_cli-0.5.0/src/keystone_cli/templates/rag-qa}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/rag-qa/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/skill/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/skill/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/ui-card → keystone_cli-0.5.0/src/keystone_cli/templates/skill}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/ui-card → keystone_cli-0.5.0/src/keystone_cli/templates/skill}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/ui-card → keystone_cli-0.5.0/src/keystone_cli/templates/skill}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/skill/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/skill/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/ui-card → keystone_cli-0.5.0/src/keystone_cli/templates/skill}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/skill/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/tool-agent/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/tool-agent/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/user-tools → keystone_cli-0.5.0/src/keystone_cli/templates/tool-agent}/env.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/user-tools → keystone_cli-0.5.0/src/keystone_cli/templates/tool-agent}/gitignore.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/user-tools → keystone_cli-0.5.0/src/keystone_cli/templates/tool-agent}/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/tool-agent/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/tool-agent/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/tool-agent/system-prompt.txt.tmpl +0 -0
- {keystone_cli-0.4.0/src/keystone_cli/templates/user-tools → keystone_cli-0.5.0/src/keystone_cli/templates/tool-agent}/tests/conftest.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/tool-agent/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/ui-card/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/ui-card/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/ui-card/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/ui-card/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/ui-card/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/user-tools/README.md.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/user-tools/agent.yaml.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/user-tools/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/user-tools/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli/templates/user-tools/tests/test_graph.py.tmpl +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli.egg-info/dependency_links.txt +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli.egg-info/entry_points.txt +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/src/keystone_cli.egg-info/top_level.txt +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_agent.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_agent_manifest_cmd.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_agent_test_cmd.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_assistant_provenance_and_flags.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_auth.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_build_progress.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_ci_token.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_config_env_mismatch.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_dev.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_doctor.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_encoding.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_fetch.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_help_quality.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_hitl_decisions_cli.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_invoke.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_invoke_stream.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_json_output.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_jsonout.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_kb_commands.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_kb_metadata_commands.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_login_command.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_logs.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_platform_client.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_prompt_commands.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_publish.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_retire_delete.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_run_debug.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_runs.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_skills.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_status.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_stdio.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_template_prompt_guard.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_template_unit_tests.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_threads.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_tls.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_traces.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.0}/tests/test_version.py +0 -0
- {keystone_cli-0.4.0 → keystone_cli-0.5.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.
|
|
3
|
+
Version: 0.5.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.
|
|
11
|
+
Requires-Dist: keystone-agent-sdk<0.14,>=0.13.27
|
|
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.
|
|
7
|
+
version = "0.5.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.
|
|
23
|
-
|
|
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.27,<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.
|
|
71
|
+
target-version = "0.5.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
|
|
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:
|
|
159
|
-
|
|
160
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
883
|
-
if not base
|
|
884
|
-
typer.secho("✗
|
|
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 =
|
|
1438
|
-
if not base
|
|
1439
|
-
typer.secho("✗
|
|
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":
|
|
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 =
|
|
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 =
|
|
3074
|
+
workspace = ts.resolve_workspace(cfg, workspace_id)
|
|
3062
3075
|
if not workspace:
|
|
3063
|
-
out.fail(
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|