From 48b2346e33aea7577d6d41dd04301033c8391b32 Mon Sep 17 00:00:00 2001 From: gdibben Date: Thu, 8 Oct 2026 07:57:05 +0100 Subject: [PATCH] feat(cli): add --json to deploy listeners list The table output isn't scriptable, so a deploy automation pipeline has no reliable way to extract a listener's id to pass to --listener-id. --json prints the listeners as a JSON array instead (an empty array when there are none, so callers never need to special-case the human-readable "no listeners" message). --- libs/cli/langgraph_cli/deploy.py | 14 ++++- libs/cli/tests/unit_tests/cli/test_cli.py | 73 +++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/libs/cli/langgraph_cli/deploy.py b/libs/cli/langgraph_cli/deploy.py index f04e9b31a..7507482c1 100644 --- a/libs/cli/langgraph_cli/deploy.py +++ b/libs/cli/langgraph_cli/deploy.py @@ -2518,6 +2518,13 @@ def deploy_listeners() -> None: @OPT_HOST_API_KEY @OPT_HOST_URL +@click.option( + "--json", + "json_output", + is_flag=True, + default=False, + help="Emit the listeners as a JSON array instead of a table.", +) @deploy_listeners.command( "list", help=( @@ -2526,11 +2533,16 @@ def deploy_listeners() -> None: "--listener-id ` to deploy through it." ), ) -def deploy_listeners_list(api_key: str | None, host_url: str | None) -> None: +def deploy_listeners_list( + api_key: str | None, host_url: str | None, json_output: bool +) -> None: client = _create_host_backend_client(host_url, api_key) listeners = _call_host_backend_with_optional_tenant( client, lambda c: c.list_listeners() ) + if json_output: + click.echo(json_mod.dumps(listeners)) + return if not listeners: click.echo("No listeners found for this workspace.") return diff --git a/libs/cli/tests/unit_tests/cli/test_cli.py b/libs/cli/tests/unit_tests/cli/test_cli.py index 7b3441877..01838dec2 100644 --- a/libs/cli/tests/unit_tests/cli/test_cli.py +++ b/libs/cli/tests/unit_tests/cli/test_cli.py @@ -535,6 +535,79 @@ def test_deploy_listeners_list_command_no_results(monkeypatch) -> None: assert result.output.strip() == "No listeners found for this workspace." +def test_deploy_listeners_list_command_json(monkeypatch) -> None: + runner = CliRunner() + + listeners = [ + { + "id": "listener-1", + "compute_id": "prod-cluster", + "compute_config": {"k8s_namespaces": ["agents"]}, + }, + { + "id": "listener-2", + "compute_id": "multi-cluster", + "compute_config": {"k8s_namespaces": ["agents", "agents-staging"]}, + }, + ] + + class FakeClient: + def __init__(self, host_url: str, api_key: str, tenant_id: str | None = None): + pass + + def list_listeners(self): + return listeners + + monkeypatch.setattr(deploy_module, "HostBackendClient", FakeClient) + + result = runner.invoke( + cli, + [ + "deploy", + "listeners", + "list", + "--json", + "--api-key", + "test-key", + "--host-url", + "https://api.example.com", + ], + ) + + assert result.exit_code == 0, result.output + assert json.loads(result.output) == listeners + + +def test_deploy_listeners_list_command_json_no_results(monkeypatch) -> None: + runner = CliRunner() + + class FakeClient: + def __init__(self, host_url: str, api_key: str, tenant_id: str | None = None): + pass + + def list_listeners(self): + return [] + + monkeypatch.setattr(deploy_module, "HostBackendClient", FakeClient) + + result = runner.invoke( + cli, + [ + "deploy", + "listeners", + "list", + "--json", + "--api-key", + "test-key", + "--host-url", + "https://api.example.com", + ], + ) + + assert result.exit_code == 0, result.output + assert json.loads(result.output) == [] + + def test_deploy_revisions_list_command(monkeypatch) -> None: runner = CliRunner() captured: dict[str, str] = {}