Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ CLAUDE.md
__pycache__
.env
.dispatch/

# Webpack source maps for the local router UI — regenerated on every
# `npm run deploy-update` in cli/dispatch-local-ui. No runtime use and
# embeds our JSX source; leave them untracked.
dispatch_cli/router/static/*.map
4 changes: 2 additions & 2 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
## Features
- **OAuth authentication support**: The CLI now includes an OAuth-based login flow with secure session management and system keychain integration for credential storage. Run `dispatch login` to authenticate using the new provider-backed auth system.
## Bug Fixes
- Improved SDK setup guidance provided by `dispatch init`, ensuring users receive accurate instructions for integrating the packaged SDK.
3 changes: 0 additions & 3 deletions dispatch_cli/commands/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,9 +768,6 @@ async def hello_world(payload: HelloWorldRequest) -> HelloWorldResponse:
with open(claude_md_path, "w") as f:
f.write("Read @./AGENTS.md\n")

# Check if agent's SDK version matches CLI's suggested version (warn only for init)
_check_and_suggest_sdk_update(path, warn_only=True)

logger.success("Agent Created!")
logger.info(f"You successfully created {agent_name}.")
logger.info(f"Take a look in {entrypoint_file} to see the agent code.")
Expand Down
33 changes: 17 additions & 16 deletions dispatch_cli/router/static/components.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dispatch_cli/router/static/components.js.map

This file was deleted.

24 changes: 5 additions & 19 deletions dispatch_cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,15 @@
"asyncio",
]
# Legacy constant for backwards compatibility - prefer get_sdk_dependency() function
SDK_DEPENDENCY = os.getenv(
"SDK_DEPENDENCY",
"git+ssh://git@github.com/datadog-labs/dispatch_agents_sdk.git",
)
SDK_DEPENDENCY = os.getenv("SDK_DEPENDENCY", "dispatch-agents")


def get_sdk_dependency() -> str:
"""Get the SDK dependency string using the CLI's bundled SDK version.
"""Get the SDK dependency string for agent projects.

Returns a git+ssh URL pointing to the specific SDK version tag that
matches the SDK version bundled with this CLI. Falls back to the default
SDK_DEPENDENCY if version detection fails.
Defaults to the published PyPI package so `dispatch agent init` does not
send users through a GitHub install flow. An explicit `SDK_DEPENDENCY`
environment override still wins for local development and testing.

Returns:
SDK dependency string for use with 'uv add'
Expand All @@ -78,17 +75,6 @@ def get_sdk_dependency() -> str:
if os.getenv("SDK_DEPENDENCY"):
return os.getenv("SDK_DEPENDENCY", SDK_DEPENDENCY)

# Try to get the CLI's bundled SDK version
try:
from dispatch_cli.version_check import get_cli_suggested_sdk_version

version = get_cli_suggested_sdk_version()
if version:
return "git+ssh://git@github.com/datadog-labs/dispatch_agents_sdk.git"
except ImportError:
pass

# Fall back to branch-based dependency
return SDK_DEPENDENCY


Expand Down
12 changes: 3 additions & 9 deletions dispatch_cli/version_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,7 @@ def validate_sdk_version(
minimum = Version(requirements["sdk_minimum"])

if detected < minimum:
update_cmd = (
"uv add git+ssh://git@github.com/datadog-labs/dispatch_agents_sdk.git"
)
update_cmd = "uv add dispatch-agents --upgrade"
return (
"blocked",
f"SDK version {detected_version} is below minimum required version {requirements['sdk_minimum']}.\n\n"
Expand Down Expand Up @@ -269,9 +267,7 @@ def check_sdk_version_suggestion(
return ("error", "Could not determine CLI's suggested SDK version.")

if detected_version is None:
update_cmd = (
"uv add git+ssh://git@github.com/datadog-labs/dispatch_agents_sdk.git"
)
update_cmd = "uv add dispatch-agents"
return (
"not_installed",
f"SDK not installed. To add it, run:\n{update_cmd}",
Expand All @@ -282,9 +278,7 @@ def check_sdk_version_suggestion(
suggested_ver = Version(suggested)

if detected < suggested_ver:
update_cmd = (
"uv add git+ssh://git@github.com/datadog-labs/dispatch_agents_sdk.git"
)
update_cmd = "uv add dispatch-agents --upgrade"
return (
"outdated",
f"SDK version {detected_version} is older than CLI's suggested version {suggested}.\n\n"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "dispatch-cli"
version = "0.8.3"
version = "0.8.6"
description = ""
authors = [
{name = "Diamond Bishop", email = "diamond.bishop@datadoghq.com"},
Expand Down
34 changes: 34 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,37 @@ def mock_subprocess_run(args, **kwargs):
with open(os.path.join(tmpdir, DISPATCH_YAML)) as fh:
config = yaml.safe_load(fh)
assert config["entrypoint"] == "my_agent.py"

def test_init_does_not_prompt_for_sdk_upgrade_after_scaffolding(self):
"""Init should not run the SDK suggestion check after creating the project."""
runner = CliRunner()

with tempfile.TemporaryDirectory() as tmpdir:

def mock_subprocess_run(args, **kwargs):
if args[0] == "uv" and args[1] == "init":
pyproject_path = os.path.join(
kwargs.get("cwd", tmpdir), "pyproject.toml"
)
with open(pyproject_path, "w") as f:
f.write(
'[project]\nname = "test"\nrequires-python = ">=3.13"\n'
)
mock_result = MagicMock()
mock_result.returncode = 0
mock_result.stdout = ""
mock_result.stderr = ""
return mock_result

with patch("typer.prompt", return_value="agent.py"):
with patch("typer.confirm", return_value=True):
with patch("subprocess.run", side_effect=mock_subprocess_run):
with patch(
"dispatch_cli.commands.agent._check_and_suggest_sdk_update"
) as suggest_sdk_update:
result = runner.invoke(
app, ["agent", "init", "--path", tmpdir]
)

assert result.exit_code == 0
suggest_sdk_update.assert_not_called()
66 changes: 24 additions & 42 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,29 @@
validate_dispatch_project,
)

PUBLIC_SDK_REPO = "datadog-labs/dispatch_agents_sdk"
PUBLIC_CLI_REPO = "datadog-labs/dispatch_agents_cli"
SDK_PACKAGE = "dispatch-agents"


class TestPublicRepoUrls:
"""Ensure all customer-facing URLs point to the public repos."""
class TestSdkInstallGuidance:
"""Ensure customer-facing SDK guidance uses the packaged install path."""

def test_sdk_dependency_constant_points_to_public_repo(self):
"""SDK_DEPENDENCY must reference the public SDK repo."""
assert PUBLIC_SDK_REPO in SDK_DEPENDENCY
assert "DataDog/dispatch_agents" not in SDK_DEPENDENCY
def test_sdk_dependency_constant_points_to_package(self):
"""SDK_DEPENDENCY should default to the published SDK package."""
assert SDK_DEPENDENCY == SDK_PACKAGE

def test_get_sdk_dependency_with_version_points_to_public_repo(self):
"""get_sdk_dependency() should return a URL to the public SDK repo with --upgrade."""
def test_get_sdk_dependency_with_version_points_to_package(self):
"""get_sdk_dependency() should resolve to the packaged SDK name."""
with patch.dict(os.environ, {}, clear=False):
os.environ.pop("SDK_DEPENDENCY", None)
with patch(
"dispatch_cli.version_check.get_cli_suggested_sdk_version",
return_value="1.2.3",
):
result = get_sdk_dependency()
assert PUBLIC_SDK_REPO in result
assert "@v1.2.3" not in result
assert "#subdirectory=" not in result
result = get_sdk_dependency()
assert result == SDK_PACKAGE

def test_get_sdk_dependency_fallback_points_to_public_repo(self):
"""Fallback SDK dependency (no version detected) must use public repo."""
def test_get_sdk_dependency_fallback_points_to_package(self):
"""Fallback SDK dependency (no version detected) should use the package."""
with patch.dict(os.environ, {}, clear=False):
os.environ.pop("SDK_DEPENDENCY", None)
with patch(
"dispatch_cli.version_check.get_cli_suggested_sdk_version",
return_value=None,
):
result = get_sdk_dependency()
assert PUBLIC_SDK_REPO in result
assert "#subdirectory=" not in result
result = get_sdk_dependency()
assert result == SDK_PACKAGE

def test_validate_sdk_version_above_minimum_is_valid(self):
"""SDK above minimum but below current should be 'valid', not 'outdated'."""
Expand All @@ -68,8 +55,8 @@ def test_validate_sdk_version_above_minimum_is_valid(self):
assert status == "valid"
assert message is None

def test_version_check_sdk_blocked_url(self):
"""Blocked SDK upgrade message must reference the public SDK repo without version pin."""
def test_version_check_sdk_blocked_command(self):
"""Blocked SDK upgrade message should use the packaged upgrade command."""
from dispatch_cli.version_check import validate_sdk_version

with patch(
Expand All @@ -83,12 +70,10 @@ def test_version_check_sdk_blocked_url(self):
):
status, message = validate_sdk_version("0.0.1", "http://fake")
assert status == "blocked"
assert PUBLIC_SDK_REPO in message
assert "@v" not in message
assert "#subdirectory=" not in message
assert "uv add dispatch-agents --upgrade" in message

def test_version_check_sdk_not_installed_url(self):
"""SDK not-installed message must reference the public SDK repo with --upgrade."""
def test_version_check_sdk_not_installed_command(self):
"""SDK not-installed message should use the packaged install command."""
from dispatch_cli.version_check import check_sdk_version_suggestion

with patch(
Expand All @@ -97,12 +82,11 @@ def test_version_check_sdk_not_installed_url(self):
):
status, message = check_sdk_version_suggestion(None)
assert status == "not_installed"
assert PUBLIC_SDK_REPO in message
assert "@v" not in message
assert "#subdirectory=" not in message
assert "uv add dispatch-agents" in message
assert "--upgrade" not in message

def test_version_check_sdk_outdated_url(self):
"""SDK outdated message must reference the public SDK repo with --upgrade."""
def test_version_check_sdk_outdated_command(self):
"""SDK outdated message should use the packaged upgrade command."""
from dispatch_cli.version_check import check_sdk_version_suggestion

with patch(
Expand All @@ -111,9 +95,7 @@ def test_version_check_sdk_outdated_url(self):
):
status, message = check_sdk_version_suggestion("1.0.0")
assert status == "outdated"
assert PUBLIC_SDK_REPO in message
assert "@v" not in message
assert "#subdirectory=" not in message
assert "uv add dispatch-agents --upgrade" in message

def test_cli_update_check_is_silent_when_stdout_is_not_a_tty(self, capsys):
"""Machine-readable invocations should not emit update notices on stdout."""
Expand Down
Loading
Loading