keystone-cli 0.2.1__tar.gz → 0.2.2__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.2.1 → keystone_cli-0.2.2}/PKG-INFO +2 -1
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/pyproject.toml +5 -2
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/__main__.py +4 -0
- keystone_cli-0.2.2/src/keystone_cli/_tls.py +40 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli.egg-info/PKG-INFO +2 -1
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli.egg-info/SOURCES.txt +2 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli.egg-info/requires.txt +1 -0
- keystone_cli-0.2.2/tests/test_tls.py +125 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/setup.cfg +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/__init__.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/_build_env.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/_stdio.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/_version.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/agent_logs.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/agent_skills.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/auth/__init__.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/auth/device_flow.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/auth/token_store.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/commands/__init__.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/commands/agent.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/commands/dev.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/commands/kb.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/commands/kb_metadata.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/commands/login.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/commands/skills.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/commands/tool_credentials.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/commands/whoami.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/commands/workspace.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/devloop.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/platform_client.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/run_debug.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/runner.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/scaffold.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/skills/keystone-debug/SKILL.md +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/blank/README.md.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/blank/agent.yaml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/blank/env.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/blank/gitignore.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/blank/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/blank/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/blank/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/README.md.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/agent.yaml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/env.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/gitignore.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/hitl/README.md.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/hitl/agent.yaml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/hitl/env.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/hitl/gitignore.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/hitl/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/hitl/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/hitl/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/README.md.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/agent.yaml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/env.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/gitignore.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm/README.md.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm/agent.yaml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm/env.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm/gitignore.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm-streaming/README.md.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm-streaming/agent.yaml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm-streaming/env.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm-streaming/gitignore.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm-streaming/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm-streaming/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm-streaming/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory/README.md.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory/agent.yaml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory/env.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory/gitignore.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory-service/README.md.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory-service/agent.yaml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory-service/env.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory-service/gitignore.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory-service/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory-service/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory-service/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/ocr/README.md.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/ocr/agent.yaml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/ocr/env.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/ocr/gitignore.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/ocr/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/ocr/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/ocr/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/rag-qa/README.md.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/rag-qa/agent.yaml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/rag-qa/env.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/rag-qa/gitignore.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/rag-qa/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/rag-qa/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/rag-qa/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/skill/README.md.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/skill/agent.yaml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/skill/env.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/skill/gitignore.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/skill/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/skill/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/skill/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/README.md.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/agent.yaml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/env.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/gitignore.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/pkg/__init__.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/pkg/graph.py.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/pyproject.toml.tmpl +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli.egg-info/dependency_links.txt +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli.egg-info/entry_points.txt +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli.egg-info/top_level.txt +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_agent.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_assistant_provenance_and_flags.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_auth.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_build_progress.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_ci_token.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_config_env_mismatch.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_deploy.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_dev.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_doctor.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_encoding.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_help_quality.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_hitl_approver_cli.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_hitl_decisions_cli.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_init.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_invoke.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_invoke_stream.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_kb_commands.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_kb_metadata_commands.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_login_command.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_logs.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_offline.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_platform_client.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_publish.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_retire_delete.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_run_debug.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_runs.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_skills.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_status.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_stdio.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_threads.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_tool_credentials.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_traces.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_version.py +0 -0
- {keystone_cli-0.2.1 → keystone_cli-0.2.2}/tests/test_workspace.py +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: keystone-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Keystone pro-code agent CLI — build, test, deploy and operate agents on the Keystone platform.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Requires-Dist: keystone-agent-sdk<0.14,>=0.13.3
|
|
7
7
|
Requires-Dist: keystone-request-context<0.4,>=0.3
|
|
8
8
|
Requires-Dist: typer<1,>=0.12
|
|
9
9
|
Requires-Dist: httpx<1,>=0.27
|
|
10
|
+
Requires-Dist: truststore<1,>=0.10
|
|
10
11
|
Provides-Extra: dev
|
|
11
12
|
Requires-Dist: pytest<9,>=8; extra == "dev"
|
|
12
13
|
Requires-Dist: pytest-cov<7,>=6; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "keystone-cli"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Keystone pro-code agent CLI — build, test, deploy and operate agents on the Keystone platform."
|
|
9
9
|
requires-python = ">=3.12"
|
|
10
10
|
dependencies = [
|
|
@@ -18,6 +18,9 @@ dependencies = [
|
|
|
18
18
|
"typer>=0.12,<1",
|
|
19
19
|
# `login` (device-flow → Keycloak) + `deploy` (bundle upload → agent-hub via Kong). FDP-3170/3169.
|
|
20
20
|
"httpx>=0.27,<1",
|
|
21
|
+
# FDP-6328 — verify TLS through the OS trust store (keystone_cli/_tls.py), so a corporate CA in
|
|
22
|
+
# the macOS Keychain / Windows store works without SSL_CERT_FILE. Pure Python, no dependencies.
|
|
23
|
+
"truststore>=0.10,<1",
|
|
21
24
|
]
|
|
22
25
|
|
|
23
26
|
[project.optional-dependencies]
|
|
@@ -45,7 +48,7 @@ where = ["src"]
|
|
|
45
48
|
keystone_cli = ["templates/*/*.tmpl", "templates/*/pkg/*.tmpl", "skills/*/SKILL.md"]
|
|
46
49
|
|
|
47
50
|
[tool.ruff]
|
|
48
|
-
target-version = "0.2.
|
|
51
|
+
target-version = "0.2.2"
|
|
49
52
|
line-length = 120
|
|
50
53
|
|
|
51
54
|
[tool.ruff.lint]
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import typer
|
|
6
6
|
|
|
7
7
|
from keystone_cli._stdio import make_stdio_unicode_safe
|
|
8
|
+
from keystone_cli._tls import use_system_trust_store
|
|
8
9
|
from keystone_cli._version import version_line
|
|
9
10
|
from keystone_cli.commands.agent import agent_app
|
|
10
11
|
from keystone_cli.commands.dev import dev
|
|
@@ -45,6 +46,9 @@ def _main(
|
|
|
45
46
|
),
|
|
46
47
|
) -> None:
|
|
47
48
|
"""Keystone pro-code agent CLI."""
|
|
49
|
+
# FDP-6328 — here, not at import: every command passes through this callback before it opens
|
|
50
|
+
# a connection, while merely importing the app (tests do) leaves the process's `ssl` alone.
|
|
51
|
+
use_system_trust_store()
|
|
48
52
|
|
|
49
53
|
|
|
50
54
|
@app.command(name="version")
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Verify TLS against the operating system's trust store (FDP-6328).
|
|
2
|
+
|
|
3
|
+
httpx verifies servers against ``certifi``'s bundled CA list, never the OS store. On a network
|
|
4
|
+
that inspects TLS — TCB's, where a corporate proxy re-signs every HTTPS connection with the
|
|
5
|
+
bank's own CA — that CA is installed in the OS store (macOS Keychain, Windows certificate
|
|
6
|
+
store), so browsers work, but certifi does not know it. Every CLI command then failed with
|
|
7
|
+
``CERTIFICATE_VERIFY_FAILED`` until the user exported ``SSL_CERT_FILE=<corp bundle>`` on each
|
|
8
|
+
call — which REPLACES certifi rather than adding to it, so hosts outside the proxy broke instead.
|
|
9
|
+
|
|
10
|
+
``truststore`` makes Python's ``ssl`` verify through the OS (Security.framework on macOS,
|
|
11
|
+
CryptoAPI on Windows, OpenSSL with the system paths on Linux) — the approach pip takes for the
|
|
12
|
+
same reason. ``SSL_CERT_FILE`` keeps working on top: httpx hands it to ``load_verify_locations``,
|
|
13
|
+
which truststore adds as extra trust anchors (checked with a local CA and httpx 0.28.1: OS-trusted
|
|
14
|
+
and SSL_CERT_FILE-trusted servers both verify).
|
|
15
|
+
|
|
16
|
+
``KEYSTONE_SYSTEM_TRUST=0`` turns it off (certifi only, the old behaviour), for the day the OS
|
|
17
|
+
store itself is the problem.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import os
|
|
23
|
+
from collections.abc import Mapping
|
|
24
|
+
|
|
25
|
+
import truststore
|
|
26
|
+
|
|
27
|
+
ENV_SYSTEM_TRUST = "KEYSTONE_SYSTEM_TRUST"
|
|
28
|
+
_OFF = {"0", "false", "no", "off"}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def use_system_trust_store(env: Mapping[str, str] | None = None) -> bool:
|
|
32
|
+
"""Make every TLS connection this process opens verify through the OS trust store.
|
|
33
|
+
|
|
34
|
+
Call once, before any HTTP client exists (an ``httpx.Client`` builds its SSL context when it
|
|
35
|
+
is created). Returns whether it is on. Idempotent: truststore ignores a second injection."""
|
|
36
|
+
env = os.environ if env is None else env
|
|
37
|
+
if (env.get(ENV_SYSTEM_TRUST) or "").strip().lower() in _OFF:
|
|
38
|
+
return False
|
|
39
|
+
truststore.inject_into_ssl()
|
|
40
|
+
return True
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: keystone-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Keystone pro-code agent CLI — build, test, deploy and operate agents on the Keystone platform.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Requires-Dist: keystone-agent-sdk<0.14,>=0.13.3
|
|
7
7
|
Requires-Dist: keystone-request-context<0.4,>=0.3
|
|
8
8
|
Requires-Dist: typer<1,>=0.12
|
|
9
9
|
Requires-Dist: httpx<1,>=0.27
|
|
10
|
+
Requires-Dist: truststore<1,>=0.10
|
|
10
11
|
Provides-Extra: dev
|
|
11
12
|
Requires-Dist: pytest<9,>=8; extra == "dev"
|
|
12
13
|
Requires-Dist: pytest-cov<7,>=6; extra == "dev"
|
|
@@ -3,6 +3,7 @@ src/keystone_cli/__init__.py
|
|
|
3
3
|
src/keystone_cli/__main__.py
|
|
4
4
|
src/keystone_cli/_build_env.py
|
|
5
5
|
src/keystone_cli/_stdio.py
|
|
6
|
+
src/keystone_cli/_tls.py
|
|
6
7
|
src/keystone_cli/_version.py
|
|
7
8
|
src/keystone_cli/agent_logs.py
|
|
8
9
|
src/keystone_cli/agent_skills.py
|
|
@@ -145,6 +146,7 @@ tests/test_skills.py
|
|
|
145
146
|
tests/test_status.py
|
|
146
147
|
tests/test_stdio.py
|
|
147
148
|
tests/test_threads.py
|
|
149
|
+
tests/test_tls.py
|
|
148
150
|
tests/test_tool_credentials.py
|
|
149
151
|
tests/test_traces.py
|
|
150
152
|
tests/test_version.py
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""FDP-6328 — TLS verifies through the OS trust store, so a corporate CA works without SSL_CERT_FILE."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import http.server
|
|
6
|
+
import shutil
|
|
7
|
+
import ssl
|
|
8
|
+
import subprocess
|
|
9
|
+
import threading
|
|
10
|
+
from collections.abc import Iterator
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
import pytest
|
|
15
|
+
import truststore
|
|
16
|
+
from typer.testing import CliRunner
|
|
17
|
+
|
|
18
|
+
from keystone_cli import _tls
|
|
19
|
+
from keystone_cli.__main__ import app
|
|
20
|
+
|
|
21
|
+
runner = CliRunner()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _record_injections(monkeypatch) -> list[bool]:
|
|
25
|
+
calls: list[bool] = []
|
|
26
|
+
monkeypatch.setattr(_tls.truststore, "inject_into_ssl", lambda: calls.append(True))
|
|
27
|
+
return calls
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_on_by_default(monkeypatch) -> None:
|
|
31
|
+
calls = _record_injections(monkeypatch)
|
|
32
|
+
assert _tls.use_system_trust_store(env={}) is True
|
|
33
|
+
assert calls == [True]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.mark.parametrize("value", ["0", "false", "No", " off "])
|
|
37
|
+
def test_opt_out_keeps_certifi(monkeypatch, value) -> None:
|
|
38
|
+
calls = _record_injections(monkeypatch)
|
|
39
|
+
assert _tls.use_system_trust_store(env={"KEYSTONE_SYSTEM_TRUST": value}) is False
|
|
40
|
+
assert calls == []
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_every_command_turns_it_on(monkeypatch) -> None:
|
|
44
|
+
seen: list[bool] = []
|
|
45
|
+
monkeypatch.setattr("keystone_cli.__main__.use_system_trust_store", lambda: seen.append(True) or True)
|
|
46
|
+
result = runner.invoke(app, ["version"])
|
|
47
|
+
assert result.exit_code == 0, result.output
|
|
48
|
+
assert seen == [True]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ── end to end: a server signed by a "corporate" CA, as a TLS-inspecting proxy presents ──
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _openssl(*args: str, cwd: Path) -> None:
|
|
55
|
+
subprocess.run(["openssl", *args], cwd=cwd, check=True, capture_output=True) # noqa: S603, S607
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@pytest.fixture
|
|
59
|
+
def corp_server(tmp_path: Path) -> Iterator[tuple[str, Path]]:
|
|
60
|
+
if shutil.which("openssl") is None:
|
|
61
|
+
pytest.skip("openssl binary not available")
|
|
62
|
+
_openssl("req", "-x509", "-newkey", "rsa:2048", "-nodes", "-keyout", "ca.key", "-out", "ca.pem",
|
|
63
|
+
"-days", "2", "-subj", "/CN=Test Corp CA", cwd=tmp_path) # fmt: skip
|
|
64
|
+
_openssl("req", "-newkey", "rsa:2048", "-nodes", "-keyout", "srv.key", "-out", "srv.csr",
|
|
65
|
+
"-subj", "/CN=localhost", cwd=tmp_path) # fmt: skip
|
|
66
|
+
# macOS (Security.framework) refuses a server cert without serverAuth usage.
|
|
67
|
+
(tmp_path / "ext.cnf").write_text(
|
|
68
|
+
"subjectAltName=DNS:localhost,IP:127.0.0.1\nextendedKeyUsage=serverAuth\n"
|
|
69
|
+
"keyUsage=digitalSignature,keyEncipherment\nbasicConstraints=CA:FALSE\n",
|
|
70
|
+
encoding="utf-8",
|
|
71
|
+
)
|
|
72
|
+
_openssl("x509", "-req", "-in", "srv.csr", "-CA", "ca.pem", "-CAkey", "ca.key", "-CAcreateserial",
|
|
73
|
+
"-out", "srv.pem", "-days", "2", "-extfile", "ext.cnf", cwd=tmp_path) # fmt: skip
|
|
74
|
+
|
|
75
|
+
server = http.server.HTTPServer(("127.0.0.1", 0), http.server.BaseHTTPRequestHandler)
|
|
76
|
+
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
77
|
+
ctx.load_cert_chain(tmp_path / "srv.pem", tmp_path / "srv.key")
|
|
78
|
+
server.socket = ctx.wrap_socket(server.socket, server_side=True)
|
|
79
|
+
threading.Thread(target=server.serve_forever, daemon=True).start()
|
|
80
|
+
try:
|
|
81
|
+
yield f"https://localhost:{server.server_address[1]}/", tmp_path / "ca.pem"
|
|
82
|
+
finally:
|
|
83
|
+
server.shutdown()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@pytest.fixture
|
|
87
|
+
def system_trust(monkeypatch) -> Iterator[None]:
|
|
88
|
+
monkeypatch.delenv("KEYSTONE_SYSTEM_TRUST", raising=False)
|
|
89
|
+
assert _tls.use_system_trust_store() is True
|
|
90
|
+
try:
|
|
91
|
+
yield
|
|
92
|
+
finally:
|
|
93
|
+
truststore.extract_from_ssl() # undo the process-wide patch for the rest of the suite
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _verifies(url: str) -> bool:
|
|
97
|
+
try:
|
|
98
|
+
with httpx.Client(timeout=10) as client:
|
|
99
|
+
client.get(url) # any HTTP status is fine — only the handshake matters
|
|
100
|
+
except httpx.ConnectError as exc:
|
|
101
|
+
assert "certif" in str(exc).lower() or "trust" in str(exc).lower(), exc
|
|
102
|
+
return False
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_httpx_verifies_through_the_os_store_once_on(system_trust) -> None:
|
|
107
|
+
# httpx (0.27/0.28) builds its context with ssl.create_default_context(); after the injection
|
|
108
|
+
# that is truststore's, i.e. verification goes through Keychain / the Windows store.
|
|
109
|
+
assert isinstance(ssl.create_default_context(), truststore.SSLContext)
|
|
110
|
+
with httpx.Client() as client:
|
|
111
|
+
transport = client._transport # noqa: SLF001 — the only place httpx keeps the context
|
|
112
|
+
assert isinstance(transport._pool._ssl_context, truststore.SSLContext) # noqa: SLF001
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_an_unknown_corporate_ca_is_still_rejected(corp_server, system_trust, monkeypatch) -> None:
|
|
116
|
+
url, _ca = corp_server
|
|
117
|
+
monkeypatch.delenv("SSL_CERT_FILE", raising=False)
|
|
118
|
+
assert _verifies(url) is False # the OS store is used, verification is NOT switched off
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_ssl_cert_file_still_adds_a_ca_on_top_of_the_os_store(corp_server, system_trust, monkeypatch) -> None:
|
|
122
|
+
# Users who already export SSL_CERT_FILE (the 0.2.1 workaround) must not break on upgrade.
|
|
123
|
+
url, ca = corp_server
|
|
124
|
+
monkeypatch.setenv("SSL_CERT_FILE", str(ca))
|
|
125
|
+
assert _verifies(url) is True
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/blank/pkg/__init__.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/blank/pkg/graph.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/blank/pyproject.toml.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/README.md.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/agent.yaml.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/gitignore.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/pkg/__init__.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/pkg/graph.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/guardrails/pyproject.toml.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/hitl/pkg/__init__.py.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/hitl/pyproject.toml.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/README.md.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/agent.yaml.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/gitignore.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/pkg/__init__.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/pkg/graph.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/kb-builder/pyproject.toml.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm/pkg/__init__.py.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm/pyproject.toml.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm-streaming/README.md.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm-streaming/agent.yaml.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm-streaming/gitignore.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/llm-streaming/pkg/graph.py.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory/pkg/__init__.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory/pkg/graph.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory/pyproject.toml.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory-service/README.md.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory-service/agent.yaml.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory-service/env.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/memory-service/gitignore.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/ocr/pkg/__init__.py.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/ocr/pyproject.toml.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/rag-qa/pkg/__init__.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/rag-qa/pkg/graph.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/rag-qa/pyproject.toml.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/skill/pkg/__init__.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/skill/pkg/graph.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/skill/pyproject.toml.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/README.md.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/agent.yaml.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/gitignore.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/pkg/__init__.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/pkg/graph.py.tmpl
RENAMED
|
File without changes
|
{keystone_cli-0.2.1 → keystone_cli-0.2.2}/src/keystone_cli/templates/tool-agent/pyproject.toml.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|